1*b54d6aadSad /* $NetBSD: main.c,v 1.20 2020/04/04 16:40:50 ad Exp $ */ 2297f4619Sad 3297f4619Sad /*- 45d2a6ba9Sad * Copyright (c) 2006, 2007, 2009 The NetBSD Foundation, Inc. 5297f4619Sad * All rights reserved. 6297f4619Sad * 7297f4619Sad * This code is derived from software contributed to The NetBSD Foundation 8297f4619Sad * by Andrew Doran. 9297f4619Sad * 10297f4619Sad * Redistribution and use in source and binary forms, with or without 11297f4619Sad * modification, are permitted provided that the following conditions 12297f4619Sad * are met: 13297f4619Sad * 1. Redistributions of source code must retain the above copyright 14297f4619Sad * notice, this list of conditions and the following disclaimer. 15297f4619Sad * 2. Redistributions in binary form must reproduce the above copyright 16297f4619Sad * notice, this list of conditions and the following disclaimer in the 17297f4619Sad * documentation and/or other materials provided with the distribution. 18297f4619Sad * 19297f4619Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20297f4619Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21297f4619Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22297f4619Sad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23297f4619Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24297f4619Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25297f4619Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26297f4619Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27297f4619Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28297f4619Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29297f4619Sad * POSSIBILITY OF SUCH DAMAGE. 30297f4619Sad */ 31297f4619Sad 32297f4619Sad #include <sys/cdefs.h> 33297f4619Sad #ifndef lint 34*b54d6aadSad __RCSID("$NetBSD: main.c,v 1.20 2020/04/04 16:40:50 ad Exp $"); 35297f4619Sad #endif /* not lint */ 36297f4619Sad 37297f4619Sad #include <sys/types.h> 38297f4619Sad #include <sys/param.h> 39297f4619Sad #include <sys/time.h> 40297f4619Sad #include <sys/fcntl.h> 41297f4619Sad #include <sys/ioctl.h> 42297f4619Sad #include <sys/wait.h> 43297f4619Sad #include <sys/signal.h> 44297f4619Sad #include <sys/sysctl.h> 45297f4619Sad 4696d4a987Sad #include <dev/lockstat.h> 4796d4a987Sad 48297f4619Sad #include <stdio.h> 49297f4619Sad #include <stdlib.h> 50297f4619Sad #include <string.h> 51297f4619Sad #include <limits.h> 52297f4619Sad #include <unistd.h> 53297f4619Sad #include <err.h> 54297f4619Sad #include <paths.h> 55297f4619Sad #include <util.h> 56297f4619Sad #include <ctype.h> 57297f4619Sad #include <errno.h> 585c2240bbSad #include <stdbool.h> 59297f4619Sad 60297f4619Sad #include "extern.h" 61297f4619Sad 62297f4619Sad #define _PATH_DEV_LOCKSTAT "/dev/lockstat" 63297f4619Sad 64297f4619Sad #define MILLI 1000.0 65297f4619Sad #define MICRO 1000000.0 66297f4619Sad #define NANO 1000000000.0 67297f4619Sad #define PICO 1000000000000.0 68297f4619Sad 69297f4619Sad TAILQ_HEAD(lock_head, lockstruct); 70297f4619Sad typedef struct lock_head locklist_t; 71297f4619Sad TAILQ_HEAD(buf_head, lsbuf); 72297f4619Sad typedef struct buf_head buflist_t; 73*b54d6aadSad SLIST_HEAD(bucket, lockstruct); 74*b54d6aadSad typedef struct bucket bucket_t; 75297f4619Sad 76297f4619Sad typedef struct lockstruct { 77297f4619Sad TAILQ_ENTRY(lockstruct) chain; 78*b54d6aadSad SLIST_ENTRY(lockstruct) bucket; 79297f4619Sad buflist_t bufs; 809f07c24eSad buflist_t tosort; 81297f4619Sad uintptr_t lock; 829f07c24eSad double time; 839f07c24eSad uint32_t count; 84297f4619Sad u_int flags; 8512793652Sad u_int nbufs; 869f07c24eSad char name[NAME_SIZE]; 87297f4619Sad } lock_t; 88297f4619Sad 89297f4619Sad typedef struct name { 90297f4619Sad const char *name; 91297f4619Sad int mask; 92297f4619Sad } name_t; 93297f4619Sad 94f2b5175aSjoerg static const name_t locknames[] = { 95297f4619Sad { "adaptive_mutex", LB_ADAPTIVE_MUTEX }, 96297f4619Sad { "spin_mutex", LB_SPIN_MUTEX }, 979f07c24eSad { "rwlock", LB_RWLOCK }, 98048c3d68Sad { "kernel_lock", LB_KERNEL_LOCK }, 994c7ba244Sad { "preemption", LB_NOPREEMPT }, 10033774d32Sad { "misc", LB_MISC }, 101297f4619Sad { NULL, 0 } 102297f4619Sad }; 103297f4619Sad 104f2b5175aSjoerg static const name_t eventnames[] = { 105297f4619Sad { "spin", LB_SPIN }, 1069f07c24eSad { "sleep_exclusive", LB_SLEEP1 }, 1079f07c24eSad { "sleep_shared", LB_SLEEP2 }, 108297f4619Sad { NULL, 0 }, 109297f4619Sad }; 110297f4619Sad 111f2b5175aSjoerg static const name_t alltypes[] = { 112297f4619Sad { "Adaptive mutex spin", LB_ADAPTIVE_MUTEX | LB_SPIN }, 1139f07c24eSad { "Adaptive mutex sleep", LB_ADAPTIVE_MUTEX | LB_SLEEP1 }, 114297f4619Sad { "Spin mutex spin", LB_SPIN_MUTEX | LB_SPIN }, 1159f07c24eSad { "RW lock sleep (writer)", LB_RWLOCK | LB_SLEEP1 }, 1169f07c24eSad { "RW lock sleep (reader)", LB_RWLOCK | LB_SLEEP2 }, 11715efd9adSad { "RW lock spin", LB_RWLOCK | LB_SPIN }, 11812793652Sad { "Kernel lock spin", LB_KERNEL_LOCK | LB_SPIN }, 1194c7ba244Sad { "Kernel preemption defer", LB_NOPREEMPT | LB_SPIN }, 12033774d32Sad { "Miscellaneous wait", LB_MISC | LB_SPIN }, 121297f4619Sad { NULL, 0 } 122297f4619Sad }; 123297f4619Sad 124f2b5175aSjoerg static const name_t xtypes[] = { 1255d2a6ba9Sad { "Spin", LB_SPIN }, 1265d2a6ba9Sad { "Sleep (writer)", LB_SLEEP1 }, 1275d2a6ba9Sad { "Sleep (reader)", LB_SLEEP2 }, 1285d2a6ba9Sad { NULL, 0 } 1295d2a6ba9Sad }; 1305d2a6ba9Sad 131f2b5175aSjoerg static locklist_t locklist; 132f2b5175aSjoerg static locklist_t freelist; 133f2b5175aSjoerg static locklist_t sortlist; 134*b54d6aadSad static bucket_t bucket[256]; 135*b54d6aadSad 136*b54d6aadSad #define HASH(a) (&bucket[((a) >> 6) & (__arraycount(bucket) - 1)]) 137297f4619Sad 138f2b5175aSjoerg static lsbuf_t *bufs; 139f2b5175aSjoerg static lsdisable_t ld; 140f2b5175aSjoerg static bool lflag; 141f2b5175aSjoerg static bool fflag; 142f2b5175aSjoerg static int nbufs; 143f2b5175aSjoerg static bool cflag; 144ed689a7cSyamt static bool dflag; 145f2b5175aSjoerg static bool xflag; 146f2b5175aSjoerg static int lsfd; 147f2b5175aSjoerg static int displayed; 148f2b5175aSjoerg static int bin64; 149f2b5175aSjoerg static double tscale; 150f2b5175aSjoerg static double cscale; 151f2b5175aSjoerg static double cpuscale[sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0])]; 152f2b5175aSjoerg static FILE *outfp; 153297f4619Sad 154f2b5175aSjoerg static void findsym(findsym_t, char *, uintptr_t *, uintptr_t *, bool); 155f2b5175aSjoerg static void spawn(int, char **); 156f2b5175aSjoerg static void display(int, const char *name); 157f2b5175aSjoerg __dead static void listnames(const name_t *); 158f2b5175aSjoerg static void collapse(bool, bool); 159f2b5175aSjoerg static int matchname(const name_t *, char *); 160f2b5175aSjoerg static void makelists(int, int); 161f2b5175aSjoerg static void nullsig(int); 162f2b5175aSjoerg __dead static void usage(void); 163f2b5175aSjoerg static int ncpu(void); 164f2b5175aSjoerg static lock_t *morelocks(void); 165297f4619Sad 166297f4619Sad int 167297f4619Sad main(int argc, char **argv) 168297f4619Sad { 1693365bf71Slukem int eventtype, locktype, ch, nlfd, fd; 1703365bf71Slukem size_t i; 1715c2240bbSad bool sflag, pflag, mflag, Mflag; 172297f4619Sad const char *nlistf, *outf; 173297f4619Sad char *lockname, *funcname; 174297f4619Sad const name_t *name; 175297f4619Sad lsenable_t le; 176297f4619Sad double ms; 177297f4619Sad char *p; 178297f4619Sad 179297f4619Sad nlistf = NULL; 180297f4619Sad outf = NULL; 181297f4619Sad lockname = NULL; 182297f4619Sad funcname = NULL; 183297f4619Sad eventtype = -1; 184297f4619Sad locktype = -1; 185297f4619Sad nbufs = 0; 1865c2240bbSad sflag = false; 1875c2240bbSad pflag = false; 1885c2240bbSad mflag = false; 1895c2240bbSad Mflag = false; 190297f4619Sad 191ed689a7cSyamt while ((ch = getopt(argc, argv, "E:F:L:MN:T:b:cdeflmo:pstx")) != -1) 192297f4619Sad switch (ch) { 193297f4619Sad case 'E': 194297f4619Sad eventtype = matchname(eventnames, optarg); 195297f4619Sad break; 196297f4619Sad case 'F': 197297f4619Sad funcname = optarg; 198297f4619Sad break; 199297f4619Sad case 'L': 200297f4619Sad lockname = optarg; 201297f4619Sad break; 202297f4619Sad case 'N': 203297f4619Sad nlistf = optarg; 204297f4619Sad break; 205297f4619Sad case 'T': 206297f4619Sad locktype = matchname(locknames, optarg); 207297f4619Sad break; 208297f4619Sad case 'b': 209297f4619Sad nbufs = (int)strtol(optarg, &p, 0); 210297f4619Sad if (!isdigit((u_int)*optarg) || *p != '\0') 211297f4619Sad usage(); 212297f4619Sad break; 213297f4619Sad case 'c': 2145c2240bbSad cflag = true; 215297f4619Sad break; 216ed689a7cSyamt case 'd': 217ed689a7cSyamt dflag = true; 218ed689a7cSyamt break; 219297f4619Sad case 'e': 220297f4619Sad listnames(eventnames); 221297f4619Sad break; 2225c2240bbSad case 'f': 2235c2240bbSad fflag = true; 2245c2240bbSad break; 225297f4619Sad case 'l': 2265c2240bbSad lflag = true; 2275c2240bbSad break; 2285c2240bbSad case 'm': 2295c2240bbSad mflag = true; 2305c2240bbSad break; 2315c2240bbSad case 'M': 2325c2240bbSad Mflag = true; 233297f4619Sad break; 234297f4619Sad case 'o': 235297f4619Sad outf = optarg; 236297f4619Sad break; 237297f4619Sad case 'p': 2385c2240bbSad pflag = true; 239297f4619Sad break; 240297f4619Sad case 's': 2415c2240bbSad sflag = true; 242297f4619Sad break; 243297f4619Sad case 't': 244297f4619Sad listnames(locknames); 245297f4619Sad break; 2465d2a6ba9Sad case 'x': 2475d2a6ba9Sad xflag = true; 2485d2a6ba9Sad break; 249297f4619Sad default: 250297f4619Sad usage(); 251297f4619Sad } 252297f4619Sad argc -= optind; 253297f4619Sad argv += optind; 254297f4619Sad 255ed689a7cSyamt if (*argv == NULL && !dflag) 256297f4619Sad usage(); 257297f4619Sad 258297f4619Sad if (outf) { 25912793652Sad fd = open(outf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 26012793652Sad if (fd == -1) 261297f4619Sad err(EXIT_FAILURE, "opening %s", outf); 262297f4619Sad outfp = fdopen(fd, "w"); 263297f4619Sad } else 264297f4619Sad outfp = stdout; 265297f4619Sad 266297f4619Sad /* 267297f4619Sad * Find the name list for resolving symbol names, and load it into 268297f4619Sad * memory. 269297f4619Sad */ 270297f4619Sad if (nlistf == NULL) { 271297f4619Sad nlfd = open(_PATH_KSYMS, O_RDONLY); 272297f4619Sad nlistf = getbootfile(); 273297f4619Sad } else 274297f4619Sad nlfd = -1; 275297f4619Sad if (nlfd == -1) { 276297f4619Sad if ((nlfd = open(nlistf, O_RDONLY)) < 0) 277297f4619Sad err(EXIT_FAILURE, "cannot open " _PATH_KSYMS " or %s", 278297f4619Sad nlistf); 279297f4619Sad } 280297f4619Sad if (loadsym32(nlfd) != 0) { 281297f4619Sad if (loadsym64(nlfd) != 0) 282297f4619Sad errx(EXIT_FAILURE, "unable to load symbol table"); 283297f4619Sad bin64 = 1; 284297f4619Sad } 285297f4619Sad close(nlfd); 286297f4619Sad 287297f4619Sad memset(&le, 0, sizeof(le)); 288297f4619Sad le.le_nbufs = nbufs; 289297f4619Sad 290297f4619Sad /* 291297f4619Sad * Set up initial filtering. 292297f4619Sad */ 293297f4619Sad if (lockname != NULL) { 2949f07c24eSad findsym(LOCK_BYNAME, lockname, &le.le_lockstart, 2955c2240bbSad &le.le_lockend, true); 296297f4619Sad le.le_flags |= LE_ONE_LOCK; 297297f4619Sad } 298297f4619Sad if (!lflag) 299297f4619Sad le.le_flags |= LE_CALLSITE; 3005c2240bbSad if (!fflag) 3015c2240bbSad le.le_flags |= LE_LOCK; 302297f4619Sad if (funcname != NULL) { 303297f4619Sad if (lflag) 304297f4619Sad usage(); 3055c2240bbSad findsym(FUNC_BYNAME, funcname, &le.le_csstart, &le.le_csend, true); 306297f4619Sad le.le_flags |= LE_ONE_CALLSITE; 307297f4619Sad } 308297f4619Sad le.le_mask = (eventtype & LB_EVENT_MASK) | (locktype & LB_LOCK_MASK); 309297f4619Sad 310297f4619Sad /* 311297f4619Sad * Start tracing. 312297f4619Sad */ 313297f4619Sad if ((lsfd = open(_PATH_DEV_LOCKSTAT, O_RDONLY)) < 0) 314297f4619Sad err(EXIT_FAILURE, "cannot open " _PATH_DEV_LOCKSTAT); 315297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_GVERSION, &ch) < 0) 316297f4619Sad err(EXIT_FAILURE, "ioctl"); 317297f4619Sad if (ch != LS_VERSION) 3189f07c24eSad errx(EXIT_FAILURE, 3199f07c24eSad "incompatible lockstat interface version (%d, kernel %d)", 3209f07c24eSad LS_VERSION, ch); 321ed689a7cSyamt if (dflag) { 322ed689a7cSyamt goto disable; 323ed689a7cSyamt } 324297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_ENABLE, &le)) 325297f4619Sad err(EXIT_FAILURE, "cannot enable tracing"); 326297f4619Sad 327297f4619Sad /* 328297f4619Sad * Execute the traced program. 329297f4619Sad */ 330297f4619Sad spawn(argc, argv); 331297f4619Sad 332ed689a7cSyamt disable: 333297f4619Sad /* 334297f4619Sad * Stop tracing, and read the trace buffers from the kernel. 335297f4619Sad */ 336297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_DISABLE, &ld) == -1) { 337297f4619Sad if (errno == EOVERFLOW) { 338297f4619Sad warnx("overflowed available kernel trace buffers"); 339297f4619Sad exit(EXIT_FAILURE); 340297f4619Sad } 341297f4619Sad err(EXIT_FAILURE, "cannot disable tracing"); 342297f4619Sad } 343297f4619Sad if ((bufs = malloc(ld.ld_size)) == NULL) 344297f4619Sad err(EXIT_FAILURE, "cannot allocate memory for user buffers"); 3453365bf71Slukem if ((size_t)read(lsfd, bufs, ld.ld_size) != ld.ld_size) 346297f4619Sad err(EXIT_FAILURE, "reading from " _PATH_DEV_LOCKSTAT); 347297f4619Sad if (close(lsfd)) 348297f4619Sad err(EXIT_FAILURE, "close(" _PATH_DEV_LOCKSTAT ")"); 349297f4619Sad 350297f4619Sad /* 3519f07c24eSad * Figure out how to scale the results. For internal use we convert 3529f07c24eSad * all times from CPU frequency based to picoseconds, and values are 3539f07c24eSad * eventually displayed in ms. 354297f4619Sad */ 355297f4619Sad for (i = 0; i < sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0]); i++) 356297f4619Sad if (ld.ld_freq[i] != 0) 357297f4619Sad cpuscale[i] = PICO / ld.ld_freq[i]; 358297f4619Sad ms = ld.ld_time.tv_sec * MILLI + ld.ld_time.tv_nsec / MICRO; 359297f4619Sad if (pflag) 360297f4619Sad cscale = 1.0 / ncpu(); 361297f4619Sad else 362297f4619Sad cscale = 1.0; 363297f4619Sad cscale *= (sflag ? MILLI / ms : 1.0); 364297f4619Sad tscale = cscale / NANO; 365297f4619Sad nbufs = (int)(ld.ld_size / sizeof(lsbuf_t)); 3669f07c24eSad 3679f07c24eSad TAILQ_INIT(&locklist); 3689f07c24eSad TAILQ_INIT(&sortlist); 3699f07c24eSad TAILQ_INIT(&freelist); 370297f4619Sad 3715c2240bbSad if ((mflag | Mflag) != 0) 3725c2240bbSad collapse(mflag, Mflag); 3735c2240bbSad 374297f4619Sad /* 375297f4619Sad * Display the results. 376297f4619Sad */ 377297f4619Sad fprintf(outfp, "Elapsed time: %.2f seconds.", ms / MILLI); 378297f4619Sad if (sflag || pflag) { 379297f4619Sad fprintf(outfp, " Displaying "); 380297f4619Sad if (pflag) 381297f4619Sad fprintf(outfp, "per-CPU "); 382297f4619Sad if (sflag) 383297f4619Sad fprintf(outfp, "per-second "); 384297f4619Sad fprintf(outfp, "averages."); 385297f4619Sad } 386297f4619Sad putc('\n', outfp); 387297f4619Sad 3885d2a6ba9Sad for (name = xflag ? xtypes : alltypes; name->name != NULL; name++) { 389297f4619Sad if (eventtype != -1 && 390297f4619Sad (name->mask & LB_EVENT_MASK) != eventtype) 391297f4619Sad continue; 392297f4619Sad if (locktype != -1 && 393297f4619Sad (name->mask & LB_LOCK_MASK) != locktype) 394297f4619Sad continue; 395297f4619Sad display(name->mask, name->name); 396297f4619Sad } 397297f4619Sad 398297f4619Sad if (displayed == 0) 399297f4619Sad fprintf(outfp, "None of the selected events were recorded.\n"); 400297f4619Sad exit(EXIT_SUCCESS); 401297f4619Sad } 402297f4619Sad 403f2b5175aSjoerg static void 404297f4619Sad usage(void) 405297f4619Sad { 406297f4619Sad 407297f4619Sad fprintf(stderr, 408297f4619Sad "%s: usage:\n" 409297f4619Sad "%s [options] <command>\n\n" 410297f4619Sad "-b nbuf\t\tset number of event buffers to allocate\n" 411297f4619Sad "-c\t\treport percentage of total events by count, not time\n" 412ed689a7cSyamt "-d\t\tdisable lockstat\n" 413ed689a7cSyamt "-E event\tdisplay only one type of event\n" 414297f4619Sad "-e\t\tlist event types\n" 41597c200c8Swiz "-F func\t\tlimit trace to one function\n" 416833f95f3Swiz "-f\t\ttrace only by function\n" 41797c200c8Swiz "-L lock\t\tlimit trace to one lock (name, or address)\n" 418297f4619Sad "-l\t\ttrace only by lock\n" 4195c2240bbSad "-M\t\tmerge lock addresses within unique objects\n" 420833f95f3Swiz "-m\t\tmerge call sites within unique functions\n" 42197c200c8Swiz "-N nlist\tspecify name list file\n" 422297f4619Sad "-o file\t\tsend output to named file, not stdout\n" 423297f4619Sad "-p\t\tshow average count/time per CPU, not total\n" 424297f4619Sad "-s\t\tshow average count/time per second, not total\n" 42597c200c8Swiz "-T type\t\tdisplay only one type of lock\n" 4265d2a6ba9Sad "-t\t\tlist lock types\n" 4275d2a6ba9Sad "-x\t\tdon't differentiate event types\n", 428297f4619Sad getprogname(), getprogname()); 429297f4619Sad 430297f4619Sad exit(EXIT_FAILURE); 431297f4619Sad } 432297f4619Sad 433f2b5175aSjoerg static void 434297f4619Sad nullsig(int junk) 435297f4619Sad { 436297f4619Sad 437297f4619Sad (void)junk; 438297f4619Sad } 439297f4619Sad 440f2b5175aSjoerg static void 441297f4619Sad listnames(const name_t *name) 442297f4619Sad { 443297f4619Sad 444297f4619Sad for (; name->name != NULL; name++) 445297f4619Sad printf("%s\n", name->name); 446297f4619Sad 447297f4619Sad exit(EXIT_SUCCESS); 448297f4619Sad } 449297f4619Sad 450f2b5175aSjoerg static int 4515c2240bbSad matchname(const name_t *name, char *string) 452297f4619Sad { 4535c2240bbSad int empty, mask; 4545c2240bbSad char *sp; 455297f4619Sad 4565c2240bbSad empty = 1; 4575c2240bbSad mask = 0; 458297f4619Sad 4595c2240bbSad while ((sp = strsep(&string, ",")) != NULL) { 4605c2240bbSad if (*sp == '\0') 461297f4619Sad usage(); 4625c2240bbSad 4635c2240bbSad for (; name->name != NULL; name++) { 4645c2240bbSad if (strcasecmp(name->name, sp) == 0) { 4655c2240bbSad mask |= name->mask; 4665c2240bbSad break; 4675c2240bbSad } 4685c2240bbSad } 4695c2240bbSad if (name->name == NULL) 4705c2240bbSad errx(EXIT_FAILURE, "unknown identifier `%s'", sp); 4715c2240bbSad empty = 0; 4725c2240bbSad } 4735c2240bbSad 4745c2240bbSad if (empty) 4755c2240bbSad usage(); 4765c2240bbSad 4775c2240bbSad return mask; 478297f4619Sad } 479297f4619Sad 480297f4619Sad /* 481297f4619Sad * Return the number of CPUs in the running system. 482297f4619Sad */ 483f2b5175aSjoerg static int 484297f4619Sad ncpu(void) 485297f4619Sad { 486297f4619Sad int rv, mib[2]; 487297f4619Sad size_t varlen; 488297f4619Sad 489297f4619Sad mib[0] = CTL_HW; 490297f4619Sad mib[1] = HW_NCPU; 491297f4619Sad varlen = sizeof(rv); 492297f4619Sad if (sysctl(mib, 2, &rv, &varlen, NULL, (size_t)0) < 0) 493297f4619Sad rv = 1; 494297f4619Sad 495297f4619Sad return (rv); 496297f4619Sad } 497297f4619Sad 498297f4619Sad /* 499297f4619Sad * Call into the ELF parser and look up a symbol by name or by address. 500297f4619Sad */ 501f2b5175aSjoerg static void 5025c2240bbSad findsym(findsym_t find, char *name, uintptr_t *start, uintptr_t *end, bool chg) 503297f4619Sad { 5045c2240bbSad uintptr_t tend, sa, ea; 505297f4619Sad char *p; 506297f4619Sad int rv; 507297f4619Sad 5085c2240bbSad if (!chg) { 5095c2240bbSad sa = *start; 5105c2240bbSad start = &sa; 5115c2240bbSad end = &ea; 5125c2240bbSad } 5135c2240bbSad 514297f4619Sad if (end == NULL) 515297f4619Sad end = &tend; 516297f4619Sad 517297f4619Sad if (find == LOCK_BYNAME) { 518297f4619Sad if (isdigit((u_int)name[0])) { 519297f4619Sad *start = (uintptr_t)strtoul(name, &p, 0); 520297f4619Sad if (*p == '\0') 521297f4619Sad return; 522297f4619Sad } 523297f4619Sad } 524297f4619Sad 525297f4619Sad if (bin64) 526297f4619Sad rv = findsym64(find, name, start, end); 527297f4619Sad else 528297f4619Sad rv = findsym32(find, name, start, end); 529297f4619Sad 530297f4619Sad if (find == FUNC_BYNAME || find == LOCK_BYNAME) { 531297f4619Sad if (rv == -1) 532297f4619Sad errx(EXIT_FAILURE, "unable to find symbol `%s'", name); 533297f4619Sad return; 534297f4619Sad } 535297f4619Sad 536297f4619Sad if (rv == -1) 5379f07c24eSad snprintf(name, NAME_SIZE, "%016lx", (long)*start); 538297f4619Sad } 539297f4619Sad 540297f4619Sad /* 541297f4619Sad * Fork off the child process and wait for it to complete. We trap SIGINT 542297f4619Sad * so that the caller can use Ctrl-C to stop tracing early and still get 543297f4619Sad * useful results. 544297f4619Sad */ 545f2b5175aSjoerg static void 546297f4619Sad spawn(int argc, char **argv) 547297f4619Sad { 548297f4619Sad pid_t pid; 549297f4619Sad 550297f4619Sad switch (pid = fork()) { 551297f4619Sad case 0: 552297f4619Sad close(lsfd); 553297f4619Sad if (execvp(argv[0], argv) == -1) 554297f4619Sad err(EXIT_FAILURE, "cannot exec"); 555297f4619Sad break; 556297f4619Sad case -1: 557297f4619Sad err(EXIT_FAILURE, "cannot fork to exec"); 558297f4619Sad break; 559297f4619Sad default: 560297f4619Sad signal(SIGINT, nullsig); 561297f4619Sad wait(NULL); 562297f4619Sad signal(SIGINT, SIG_DFL); 563297f4619Sad break; 564297f4619Sad } 565297f4619Sad } 566297f4619Sad 567297f4619Sad /* 5689f07c24eSad * Allocate a new block of lock_t structures. 5699f07c24eSad */ 570f2b5175aSjoerg static lock_t * 5719f07c24eSad morelocks(void) 5729f07c24eSad { 5733365bf71Slukem const int batch = 32; 5749f07c24eSad lock_t *l, *lp, *max; 5759f07c24eSad 5769f07c24eSad l = (lock_t *)malloc(sizeof(*l) * batch); 5779f07c24eSad 5789f07c24eSad for (lp = l, max = l + batch; lp < max; lp++) 5799f07c24eSad TAILQ_INSERT_TAIL(&freelist, lp, chain); 5809f07c24eSad 5819f07c24eSad return l; 5829f07c24eSad } 5839f07c24eSad 5849f07c24eSad /* 5855c2240bbSad * Collapse addresses from unique objects. 5865c2240bbSad */ 587f2b5175aSjoerg static void 5885c2240bbSad collapse(bool func, bool lock) 5895c2240bbSad { 5905c2240bbSad lsbuf_t *lb, *max; 5915c2240bbSad 5925c2240bbSad for (lb = bufs, max = bufs + nbufs; lb < max; lb++) { 5935c2240bbSad if (func && lb->lb_callsite != 0) { 5945c2240bbSad findsym(FUNC_BYADDR, NULL, &lb->lb_callsite, NULL, 5955c2240bbSad true); 5965c2240bbSad } 5975c2240bbSad if (lock && lb->lb_lock != 0) { 5985c2240bbSad findsym(LOCK_BYADDR, NULL, &lb->lb_lock, NULL, 5995c2240bbSad true); 6005c2240bbSad } 6015c2240bbSad } 6025c2240bbSad } 6035c2240bbSad 6045c2240bbSad /* 605297f4619Sad * From the kernel supplied data, construct two dimensional lists of locks 6069f07c24eSad * and event buffers, indexed by lock type and sorted by event type. 607297f4619Sad */ 608f2b5175aSjoerg static void 6099f07c24eSad makelists(int mask, int event) 610297f4619Sad { 611297f4619Sad lsbuf_t *lb, *lb2, *max; 6129f07c24eSad lock_t *l, *l2; 613*b54d6aadSad bucket_t *bp; 6149f07c24eSad int type; 615*b54d6aadSad size_t i; 616297f4619Sad 6179f07c24eSad /* 6189f07c24eSad * Recycle lock_t structures from the last run. 6199f07c24eSad */ 620*b54d6aadSad TAILQ_CONCAT(&freelist, &locklist, chain); 621*b54d6aadSad for (i = 0; i < __arraycount(bucket); i++) { 622*b54d6aadSad SLIST_INIT(&bucket[i]); 6239f07c24eSad } 6249f07c24eSad 6259f07c24eSad type = mask & LB_LOCK_MASK; 626297f4619Sad 627297f4619Sad for (lb = bufs, max = bufs + nbufs; lb < max; lb++) { 6285d2a6ba9Sad if (!xflag && (lb->lb_flags & LB_LOCK_MASK) != type) 6295d2a6ba9Sad continue; 6305d2a6ba9Sad if (lb->lb_counts[event] == 0) 631297f4619Sad continue; 632297f4619Sad 633297f4619Sad /* 634297f4619Sad * Look for a record descibing this lock, and allocate a 635297f4619Sad * new one if needed. 636297f4619Sad */ 637*b54d6aadSad bp = HASH(lb->lb_lock); 638*b54d6aadSad SLIST_FOREACH(l, bp, bucket) { 639297f4619Sad if (l->lock == lb->lb_lock) 640297f4619Sad break; 641297f4619Sad } 642297f4619Sad if (l == NULL) { 6439f07c24eSad if ((l = TAILQ_FIRST(&freelist)) == NULL) 6449f07c24eSad l = morelocks(); 6459f07c24eSad TAILQ_REMOVE(&freelist, l, chain); 646297f4619Sad l->flags = lb->lb_flags; 647297f4619Sad l->lock = lb->lb_lock; 64812793652Sad l->nbufs = 0; 6499f07c24eSad l->name[0] = '\0'; 6509f07c24eSad l->count = 0; 6519f07c24eSad l->time = 0; 6529f07c24eSad TAILQ_INIT(&l->tosort); 653297f4619Sad TAILQ_INIT(&l->bufs); 6549f07c24eSad TAILQ_INSERT_TAIL(&sortlist, l, chain); 655*b54d6aadSad SLIST_INSERT_HEAD(bp, l, bucket); 656297f4619Sad } 657297f4619Sad 658297f4619Sad /* 659297f4619Sad * Scale the time values per buffer and summarise 660297f4619Sad * times+counts per lock. 661297f4619Sad */ 6629f07c24eSad lb->lb_times[event] *= cpuscale[lb->lb_cpu]; 6639f07c24eSad l->count += lb->lb_counts[event]; 6649f07c24eSad l->time += lb->lb_times[event]; 665297f4619Sad 666297f4619Sad /* 667297f4619Sad * Merge same lock+callsite pairs from multiple CPUs 668297f4619Sad * together. 669297f4619Sad */ 6709f07c24eSad TAILQ_FOREACH(lb2, &l->tosort, lb_chain.tailq) { 671297f4619Sad if (lb->lb_callsite == lb2->lb_callsite) 672297f4619Sad break; 673297f4619Sad } 674297f4619Sad if (lb2 != NULL) { 6759f07c24eSad lb2->lb_counts[event] += lb->lb_counts[event]; 6769f07c24eSad lb2->lb_times[event] += lb->lb_times[event]; 67712793652Sad } else { 6789f07c24eSad TAILQ_INSERT_HEAD(&l->tosort, lb, lb_chain.tailq); 67912793652Sad l->nbufs++; 68012793652Sad } 681297f4619Sad } 682297f4619Sad 683297f4619Sad /* 6849f07c24eSad * Now sort the lists. 685297f4619Sad */ 6869f07c24eSad while ((l = TAILQ_FIRST(&sortlist)) != NULL) { 6879f07c24eSad TAILQ_REMOVE(&sortlist, l, chain); 688297f4619Sad 689297f4619Sad /* 690297f4619Sad * Sort the buffers into the per-lock list. 691297f4619Sad */ 6929f07c24eSad while ((lb = TAILQ_FIRST(&l->tosort)) != NULL) { 6939f07c24eSad TAILQ_REMOVE(&l->tosort, lb, lb_chain.tailq); 694297f4619Sad 6959f07c24eSad lb2 = TAILQ_FIRST(&l->bufs); 696297f4619Sad while (lb2 != NULL) { 697297f4619Sad if (cflag) { 698297f4619Sad if (lb->lb_counts[event] > 699297f4619Sad lb2->lb_counts[event]) 700297f4619Sad break; 701297f4619Sad } else if (lb->lb_times[event] > 702297f4619Sad lb2->lb_times[event]) 703297f4619Sad break; 704297f4619Sad lb2 = TAILQ_NEXT(lb2, lb_chain.tailq); 705297f4619Sad } 706297f4619Sad if (lb2 == NULL) 7079f07c24eSad TAILQ_INSERT_TAIL(&l->bufs, lb, 7089f07c24eSad lb_chain.tailq); 709297f4619Sad else 710297f4619Sad TAILQ_INSERT_BEFORE(lb2, lb, lb_chain.tailq); 711297f4619Sad } 712297f4619Sad 713297f4619Sad /* 714297f4619Sad * Sort this lock into the per-type list, based on the 715297f4619Sad * totals per lock. 716297f4619Sad */ 7179f07c24eSad l2 = TAILQ_FIRST(&locklist); 718297f4619Sad while (l2 != NULL) { 719297f4619Sad if (cflag) { 7209f07c24eSad if (l->count > l2->count) 721297f4619Sad break; 7229f07c24eSad } else if (l->time > l2->time) 723297f4619Sad break; 724297f4619Sad l2 = TAILQ_NEXT(l2, chain); 725297f4619Sad } 726297f4619Sad if (l2 == NULL) 7279f07c24eSad TAILQ_INSERT_TAIL(&locklist, l, chain); 728297f4619Sad else 729297f4619Sad TAILQ_INSERT_BEFORE(l2, l, chain); 730297f4619Sad } 731297f4619Sad } 732297f4619Sad 733297f4619Sad /* 734297f4619Sad * Display a summary table for one lock type / event type pair. 735297f4619Sad */ 736f2b5175aSjoerg static void 737297f4619Sad display(int mask, const char *name) 738297f4619Sad { 739297f4619Sad lock_t *l; 740297f4619Sad lsbuf_t *lb; 741297f4619Sad double pcscale, metric; 7429f07c24eSad char fname[NAME_SIZE]; 7439f07c24eSad int event; 744297f4619Sad 745297f4619Sad event = (mask & LB_EVENT_MASK) - 1; 7469f07c24eSad makelists(mask, event); 7479f07c24eSad 7489f07c24eSad if (TAILQ_EMPTY(&locklist)) 7499f07c24eSad return; 750297f4619Sad 751297f4619Sad fprintf(outfp, "\n-- %s\n\n" 752297f4619Sad "Total%% Count Time/ms Lock Caller\n" 753297f4619Sad "------ ------- --------- ---------------------- ------------------------------\n", 754297f4619Sad name); 755297f4619Sad 756297f4619Sad /* 757297f4619Sad * Sum up all events for this type of lock + event. 758297f4619Sad */ 759297f4619Sad pcscale = 0; 7609f07c24eSad TAILQ_FOREACH(l, &locklist, chain) { 761297f4619Sad if (cflag) 7629f07c24eSad pcscale += l->count; 763297f4619Sad else 7649f07c24eSad pcscale += l->time; 765297f4619Sad displayed++; 766297f4619Sad } 767297f4619Sad if (pcscale == 0) 768297f4619Sad pcscale = 100; 769297f4619Sad else 770297f4619Sad pcscale = (100.0 / pcscale); 771297f4619Sad 772297f4619Sad /* 773297f4619Sad * For each lock, print a summary total, followed by a breakdown by 774297f4619Sad * caller. 775297f4619Sad */ 7769f07c24eSad TAILQ_FOREACH(l, &locklist, chain) { 777297f4619Sad if (cflag) 7789f07c24eSad metric = l->count; 779297f4619Sad else 7809f07c24eSad metric = l->time; 781297f4619Sad metric *= pcscale; 782297f4619Sad 7839f07c24eSad if (l->name[0] == '\0') 7845c2240bbSad findsym(LOCK_BYADDR, l->name, &l->lock, NULL, false); 785297f4619Sad 786048c3d68Sad if (lflag || l->nbufs > 1) 7879f07c24eSad fprintf(outfp, "%6.2f %7d %9.2f %-22s <all>\n", 7889f07c24eSad metric, (int)(l->count * cscale), 7899f07c24eSad l->time * tscale, l->name); 790297f4619Sad 791297f4619Sad if (lflag) 792297f4619Sad continue; 793297f4619Sad 794297f4619Sad TAILQ_FOREACH(lb, &l->bufs, lb_chain.tailq) { 795297f4619Sad if (cflag) 796297f4619Sad metric = lb->lb_counts[event]; 797297f4619Sad else 798297f4619Sad metric = lb->lb_times[event]; 799297f4619Sad metric *= pcscale; 800297f4619Sad 8015c2240bbSad findsym(FUNC_BYADDR, fname, &lb->lb_callsite, NULL, 8025c2240bbSad false); 8039f07c24eSad fprintf(outfp, "%6.2f %7d %9.2f %-22s %s\n", 8049f07c24eSad metric, (int)(lb->lb_counts[event] * cscale), 8059f07c24eSad lb->lb_times[event] * tscale, l->name, fname); 806297f4619Sad } 807297f4619Sad } 808297f4619Sad } 809