1*33b82b03Srillig /* $NetBSD: tprof_analyze.c,v 1.9 2024/11/03 10:43:27 rillig Exp $ */ 296b21aedSmaxv 396b21aedSmaxv /* 496b21aedSmaxv * Copyright (c) 2010,2011,2012 YAMAMOTO Takashi, 596b21aedSmaxv * All rights reserved. 696b21aedSmaxv * 796b21aedSmaxv * Redistribution and use in source and binary forms, with or without 896b21aedSmaxv * modification, are permitted provided that the following conditions 996b21aedSmaxv * are met: 1096b21aedSmaxv * 1. Redistributions of source code must retain the above copyright 1196b21aedSmaxv * notice, this list of conditions and the following disclaimer. 1296b21aedSmaxv * 2. Redistributions in binary form must reproduce the above copyright 1396b21aedSmaxv * notice, this list of conditions and the following disclaimer in the 1496b21aedSmaxv * documentation and/or other materials provided with the distribution. 1596b21aedSmaxv * 1696b21aedSmaxv * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1796b21aedSmaxv * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1896b21aedSmaxv * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1996b21aedSmaxv * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2096b21aedSmaxv * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2196b21aedSmaxv * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2296b21aedSmaxv * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2396b21aedSmaxv * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2496b21aedSmaxv * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2596b21aedSmaxv * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2696b21aedSmaxv * SUCH DAMAGE. 2796b21aedSmaxv */ 2896b21aedSmaxv 2996b21aedSmaxv #include <sys/cdefs.h> 3096b21aedSmaxv #ifndef lint 31*33b82b03Srillig __RCSID("$NetBSD: tprof_analyze.c,v 1.9 2024/11/03 10:43:27 rillig Exp $"); 3296b21aedSmaxv #endif /* not lint */ 3396b21aedSmaxv 3496b21aedSmaxv #include <assert.h> 3596b21aedSmaxv #include <err.h> 3696b21aedSmaxv #include <errno.h> 3796b21aedSmaxv #include <fcntl.h> 3896b21aedSmaxv #include <gelf.h> 3996b21aedSmaxv #include <inttypes.h> 4096b21aedSmaxv #include <libelf.h> 4196b21aedSmaxv #include <stdbool.h> 4296b21aedSmaxv #include <stdlib.h> 4396b21aedSmaxv #include <stdio.h> 4496b21aedSmaxv #include <unistd.h> 4596b21aedSmaxv #include <string.h> 4696b21aedSmaxv #include <util.h> 4796b21aedSmaxv #include <dev/tprof/tprof_ioctl.h> 4896b21aedSmaxv #include "tprof.h" 4914ff263eSryo #include "ksyms.h" 5096b21aedSmaxv 5196b21aedSmaxv #include <sys/rbtree.h> 5296b21aedSmaxv 5396b21aedSmaxv static bool filter_by_pid; 5496b21aedSmaxv static pid_t target_pid; 5596b21aedSmaxv static bool per_symbol; 5696b21aedSmaxv 5796b21aedSmaxv struct addr { 5896b21aedSmaxv struct rb_node node; 5996b21aedSmaxv uint64_t addr; /* address */ 6096b21aedSmaxv uint32_t pid; /* process id */ 6196b21aedSmaxv uint32_t lwpid; /* lwp id */ 6296b21aedSmaxv uint32_t cpuid; /* cpu id */ 6396b21aedSmaxv bool in_kernel; /* if addr is in the kernel address space */ 6496b21aedSmaxv unsigned int nsamples; /* number of samples taken for the address */ 65caba1a6fSryo unsigned int ncount[TPROF_MAXCOUNTERS]; /* count per event */ 6696b21aedSmaxv }; 6796b21aedSmaxv 6896b21aedSmaxv static rb_tree_t addrtree; 6996b21aedSmaxv 7096b21aedSmaxv static signed int 7196b21aedSmaxv addrtree_compare_key(void *ctx, const void *n1, const void *keyp) 7296b21aedSmaxv { 7396b21aedSmaxv const struct addr *a1 = n1; 7496b21aedSmaxv const struct addr *a2 = (const struct addr *)keyp; 7596b21aedSmaxv 7696b21aedSmaxv if (a1->addr > a2->addr) { 7796b21aedSmaxv return 1; 7896b21aedSmaxv } else if (a1->addr < a2->addr) { 7996b21aedSmaxv return -1; 8096b21aedSmaxv } 8196b21aedSmaxv if (a1->pid > a2->pid) { 8296b21aedSmaxv return -1; 8396b21aedSmaxv } else if (a1->pid < a2->pid) { 8496b21aedSmaxv return 1; 8596b21aedSmaxv } 8696b21aedSmaxv if (a1->lwpid > a2->lwpid) { 8796b21aedSmaxv return -1; 8896b21aedSmaxv } else if (a1->lwpid < a2->lwpid) { 8996b21aedSmaxv return 1; 9096b21aedSmaxv } 9196b21aedSmaxv if (a1->cpuid > a2->cpuid) { 9296b21aedSmaxv return -1; 9396b21aedSmaxv } else if (a1->cpuid < a2->cpuid) { 9496b21aedSmaxv return 1; 9596b21aedSmaxv } 9696b21aedSmaxv if (a1->in_kernel > a2->in_kernel) { 9796b21aedSmaxv return -1; 9896b21aedSmaxv } else if (a1->in_kernel < a2->in_kernel) { 9996b21aedSmaxv return 1; 10096b21aedSmaxv } 10196b21aedSmaxv return 0; 10296b21aedSmaxv } 10396b21aedSmaxv 10496b21aedSmaxv static signed int 10596b21aedSmaxv addrtree_compare_nodes(void *ctx, const void *n1, const void *n2) 10696b21aedSmaxv { 10796b21aedSmaxv const struct addr *a2 = n2; 10896b21aedSmaxv 10996b21aedSmaxv return addrtree_compare_key(ctx, n1, a2); 11096b21aedSmaxv } 11196b21aedSmaxv 11296b21aedSmaxv static const rb_tree_ops_t addrtree_ops = { 11396b21aedSmaxv .rbto_compare_nodes = addrtree_compare_nodes, 11496b21aedSmaxv .rbto_compare_key = addrtree_compare_key, 11596b21aedSmaxv }; 11696b21aedSmaxv 11796b21aedSmaxv static int 11896b21aedSmaxv compare_nsamples(const void *p1, const void *p2) 11996b21aedSmaxv { 12096b21aedSmaxv const struct addr *a1 = *(const struct addr * const *)p1; 12196b21aedSmaxv const struct addr *a2 = *(const struct addr * const *)p2; 12296b21aedSmaxv 12396b21aedSmaxv if (a1->nsamples > a2->nsamples) { 12496b21aedSmaxv return -1; 12596b21aedSmaxv } else if (a1->nsamples < a2->nsamples) { 12696b21aedSmaxv return 1; 12796b21aedSmaxv } 12896b21aedSmaxv return 0; 12996b21aedSmaxv } 13096b21aedSmaxv 13196b21aedSmaxv void 13296b21aedSmaxv tprof_analyze(int argc, char **argv) 13396b21aedSmaxv { 13496b21aedSmaxv struct addr *a; 13596b21aedSmaxv struct addr **l; 13696b21aedSmaxv struct addr **p; 1379896bc73Smaxv size_t naddrs, nsamples, i; 1389896bc73Smaxv float perc; 13996b21aedSmaxv int ch; 140caba1a6fSryo u_int c, maxevent = 0; 14196b21aedSmaxv bool distinguish_processes = true; 14296b21aedSmaxv bool distinguish_cpus = true; 14396b21aedSmaxv bool distinguish_lwps = true; 14496b21aedSmaxv bool kernel_only = false; 145c52e6df8Smaxv FILE *f; 14696b21aedSmaxv 14796b21aedSmaxv while ((ch = getopt(argc, argv, "CkLPp:s")) != -1) { 14896b21aedSmaxv uintmax_t val; 14996b21aedSmaxv char *ep; 15096b21aedSmaxv 15196b21aedSmaxv switch (ch) { 15296b21aedSmaxv case 'C': /* don't distinguish cpus */ 15396b21aedSmaxv distinguish_cpus = false; 15496b21aedSmaxv break; 15596b21aedSmaxv case 'k': /* kernel only */ 15696b21aedSmaxv kernel_only = true; 15796b21aedSmaxv break; 15896b21aedSmaxv case 'L': /* don't distinguish lwps */ 15996b21aedSmaxv distinguish_lwps = false; 16096b21aedSmaxv break; 16196b21aedSmaxv case 'p': /* only for the process for the given pid */ 16296b21aedSmaxv errno = 0; 16396b21aedSmaxv val = strtoumax(optarg, &ep, 10); 16496b21aedSmaxv if (optarg[0] == 0 || *ep != 0 || 16596b21aedSmaxv val > INT32_MAX) { 16696b21aedSmaxv errx(EXIT_FAILURE, "invalid p option"); 16796b21aedSmaxv } 16896b21aedSmaxv target_pid = val; 16996b21aedSmaxv filter_by_pid = true; 17096b21aedSmaxv break; 17196b21aedSmaxv case 'P': /* don't distinguish processes */ 17296b21aedSmaxv distinguish_processes = false; 17396b21aedSmaxv break; 17496b21aedSmaxv case 's': /* per symbol */ 17596b21aedSmaxv per_symbol = true; 17696b21aedSmaxv break; 17796b21aedSmaxv default: 17896b21aedSmaxv exit(EXIT_FAILURE); 17996b21aedSmaxv } 18096b21aedSmaxv } 18196b21aedSmaxv argc -= optind; 18296b21aedSmaxv argv += optind; 18396b21aedSmaxv 184c52e6df8Smaxv if (argc == 0) { 185c52e6df8Smaxv errx(EXIT_FAILURE, "missing file name"); 186c52e6df8Smaxv } 187c52e6df8Smaxv 188c52e6df8Smaxv f = fopen(argv[0], "rb"); 189c52e6df8Smaxv if (f == NULL) { 190c52e6df8Smaxv errx(EXIT_FAILURE, "fopen"); 191c52e6df8Smaxv } 192c52e6df8Smaxv 193e93233dbSryo ksymload(NULL); 19496b21aedSmaxv rb_tree_init(&addrtree, &addrtree_ops); 19596b21aedSmaxv 19696b21aedSmaxv /* 19796b21aedSmaxv * read and count samples. 19896b21aedSmaxv */ 19996b21aedSmaxv 20096b21aedSmaxv naddrs = 0; 2019896bc73Smaxv nsamples = 0; 20296b21aedSmaxv while (/*CONSTCOND*/true) { 20396b21aedSmaxv struct addr *o; 20496b21aedSmaxv tprof_sample_t sample; 205c52e6df8Smaxv size_t n = fread(&sample, sizeof(sample), 1, f); 20696b21aedSmaxv bool in_kernel; 20796b21aedSmaxv 20896b21aedSmaxv if (n == 0) { 209c52e6df8Smaxv if (feof(f)) { 21096b21aedSmaxv break; 21196b21aedSmaxv } 212c52e6df8Smaxv if (ferror(f)) { 21396b21aedSmaxv err(EXIT_FAILURE, "fread"); 21496b21aedSmaxv } 21596b21aedSmaxv } 21696b21aedSmaxv if (filter_by_pid && (pid_t)sample.s_pid != target_pid) { 21796b21aedSmaxv continue; 21896b21aedSmaxv } 21996b21aedSmaxv in_kernel = (sample.s_flags & TPROF_SAMPLE_INKERNEL) != 0; 22096b21aedSmaxv if (kernel_only && !in_kernel) { 22196b21aedSmaxv continue; 22296b21aedSmaxv } 22396b21aedSmaxv a = emalloc(sizeof(*a)); 224caba1a6fSryo memset(a, 0, sizeof(*a)); 22596b21aedSmaxv a->addr = (uint64_t)sample.s_pc; 22696b21aedSmaxv if (distinguish_processes) { 22796b21aedSmaxv a->pid = sample.s_pid; 22896b21aedSmaxv } else { 22996b21aedSmaxv a->pid = 0; 23096b21aedSmaxv } 23196b21aedSmaxv if (distinguish_lwps) { 23296b21aedSmaxv a->lwpid = sample.s_lwpid; 23396b21aedSmaxv } else { 23496b21aedSmaxv a->lwpid = 0; 23596b21aedSmaxv } 23696b21aedSmaxv if (distinguish_cpus) { 23796b21aedSmaxv a->cpuid = sample.s_cpuid; 23896b21aedSmaxv } else { 23996b21aedSmaxv a->cpuid = 0; 24096b21aedSmaxv } 24196b21aedSmaxv a->in_kernel = in_kernel; 24296b21aedSmaxv if (per_symbol) { 24396b21aedSmaxv const char *name; 24496b21aedSmaxv uint64_t offset; 24596b21aedSmaxv 246e93233dbSryo name = ksymlookup(a->addr, &offset, NULL); 24796b21aedSmaxv if (name != NULL) { 24896b21aedSmaxv a->addr -= offset; 24996b21aedSmaxv } 25096b21aedSmaxv } 251caba1a6fSryo c = __SHIFTOUT(sample.s_flags, TPROF_SAMPLE_COUNTER_MASK); 252caba1a6fSryo assert(c < TPROF_MAXCOUNTERS); 253caba1a6fSryo if (maxevent < c) 254caba1a6fSryo maxevent = c; 255caba1a6fSryo 25696b21aedSmaxv a->nsamples = 1; 257caba1a6fSryo a->ncount[c] = 1; 25896b21aedSmaxv o = rb_tree_insert_node(&addrtree, a); 25996b21aedSmaxv if (o != a) { 26096b21aedSmaxv assert(a->addr == o->addr); 26196b21aedSmaxv assert(a->pid == o->pid); 26296b21aedSmaxv assert(a->lwpid == o->lwpid); 26396b21aedSmaxv assert(a->cpuid == o->cpuid); 26496b21aedSmaxv assert(a->in_kernel == o->in_kernel); 26596b21aedSmaxv free(a); 266caba1a6fSryo 26796b21aedSmaxv o->nsamples++; 268caba1a6fSryo o->ncount[c]++; 26996b21aedSmaxv } else { 27096b21aedSmaxv naddrs++; 27196b21aedSmaxv } 2729896bc73Smaxv nsamples++; 27396b21aedSmaxv } 27496b21aedSmaxv 27596b21aedSmaxv /* 27696b21aedSmaxv * sort samples by addresses. 27796b21aedSmaxv */ 27896b21aedSmaxv 27996b21aedSmaxv l = emalloc(naddrs * sizeof(*l)); 28096b21aedSmaxv p = l; 28196b21aedSmaxv RB_TREE_FOREACH(a, &addrtree) { 28296b21aedSmaxv *p++ = a; 28396b21aedSmaxv } 28496b21aedSmaxv assert(l + naddrs == p); 28596b21aedSmaxv qsort(l, naddrs, sizeof(*l), compare_nsamples); 28696b21aedSmaxv 28796b21aedSmaxv /* 28896b21aedSmaxv * print addresses and number of samples, preferably with 28996b21aedSmaxv * resolved symbol names. 29096b21aedSmaxv */ 2919896bc73Smaxv printf("File: %s\n", argv[0]); 2929896bc73Smaxv printf("Number of samples: %zu\n\n", nsamples); 293caba1a6fSryo 294caba1a6fSryo printf("percentage nsamples "); 295caba1a6fSryo for (c = 0; c <= maxevent; c++) 296caba1a6fSryo printf("event#%02u ", c); 297caba1a6fSryo printf("pid lwp cpu k address symbol\n"); 298caba1a6fSryo 299caba1a6fSryo printf("------------ -------- "); 300caba1a6fSryo for (c = 0; c <= maxevent; c++) 301caba1a6fSryo printf("-------- "); 302caba1a6fSryo 303caba1a6fSryo printf("------ ------ ---- - ---------------- ------\n"); 30496b21aedSmaxv for (i = 0; i < naddrs; i++) { 30596b21aedSmaxv const char *name; 30696b21aedSmaxv char buf[100]; 30796b21aedSmaxv uint64_t offset; 30896b21aedSmaxv 30996b21aedSmaxv a = l[i]; 31096b21aedSmaxv if (a->in_kernel) { 311e93233dbSryo name = ksymlookup(a->addr, &offset, NULL); 31296b21aedSmaxv } else { 31396b21aedSmaxv name = NULL; 31496b21aedSmaxv } 31596b21aedSmaxv if (name == NULL) { 31696b21aedSmaxv (void)snprintf(buf, sizeof(buf), "<%016" PRIx64 ">", 31796b21aedSmaxv a->addr); 31896b21aedSmaxv name = buf; 31996b21aedSmaxv } else if (offset != 0) { 32096b21aedSmaxv (void)snprintf(buf, sizeof(buf), "%s+0x%" PRIx64, name, 32196b21aedSmaxv offset); 32296b21aedSmaxv name = buf; 32396b21aedSmaxv } 3249896bc73Smaxv 3259896bc73Smaxv perc = ((float)a->nsamples / (float)nsamples) * 100.0; 3269896bc73Smaxv 327caba1a6fSryo printf("%11f%% %8u", perc, a->nsamples); 328caba1a6fSryo 329caba1a6fSryo for (c = 0; c <= maxevent; c++) 330caba1a6fSryo printf(" %8u", a->ncount[c]); 331caba1a6fSryo 332caba1a6fSryo printf(" %6" PRIu32 " %6" PRIu32 " %4" PRIu32 " %u %016" 333caba1a6fSryo PRIx64" %s", 334caba1a6fSryo a->pid, a->lwpid, a->cpuid, a->in_kernel, a->addr, name); 335caba1a6fSryo 336caba1a6fSryo 337caba1a6fSryo printf("\n"); 33896b21aedSmaxv } 339c52e6df8Smaxv 340c52e6df8Smaxv fclose(f); 34196b21aedSmaxv } 342