1*5c2240bbSad /* $NetBSD: main.c,v 1.9 2007/07/14 13:30:44 ad Exp $ */ 2297f4619Sad 3297f4619Sad /*- 4*5c2240bbSad * Copyright (c) 2006, 2007 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 * 3. All advertising materials mentioning features or use of this software 19297f4619Sad * must display the following acknowledgement: 20297f4619Sad * This product includes software developed by the NetBSD 21297f4619Sad * Foundation, Inc. and its contributors. 22297f4619Sad * 4. Neither the name of The NetBSD Foundation nor the names of its 23297f4619Sad * contributors may be used to endorse or promote products derived 24297f4619Sad * from this software without specific prior written permission. 25297f4619Sad * 26297f4619Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27297f4619Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28297f4619Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29297f4619Sad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30297f4619Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31297f4619Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32297f4619Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33297f4619Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34297f4619Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35297f4619Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36297f4619Sad * POSSIBILITY OF SUCH DAMAGE. 37297f4619Sad */ 38297f4619Sad 39297f4619Sad /* 40297f4619Sad * TODO: 41297f4619Sad * 429f07c24eSad * - Tracking of times for sleep locks is broken. 43297f4619Sad * - Need better analysis and tracking of events. 44297f4619Sad * - Shouldn't have to parse the namelist here. We should use something like 45297f4619Sad * FreeBSD's libelf. 46297f4619Sad * - The way the namelist is searched sucks, is it worth doing something 47297f4619Sad * better? 48297f4619Sad */ 49297f4619Sad 50297f4619Sad #include <sys/cdefs.h> 51297f4619Sad #ifndef lint 52*5c2240bbSad __RCSID("$NetBSD: main.c,v 1.9 2007/07/14 13:30:44 ad Exp $"); 53297f4619Sad #endif /* not lint */ 54297f4619Sad 55297f4619Sad #include <sys/types.h> 56297f4619Sad #include <sys/param.h> 57297f4619Sad #include <sys/time.h> 58297f4619Sad #include <sys/fcntl.h> 59297f4619Sad #include <sys/ioctl.h> 60297f4619Sad #include <sys/wait.h> 61297f4619Sad #include <sys/signal.h> 62297f4619Sad #include <sys/sysctl.h> 63297f4619Sad 6496d4a987Sad #include <dev/lockstat.h> 6596d4a987Sad 66297f4619Sad #include <stdio.h> 67297f4619Sad #include <stdlib.h> 68297f4619Sad #include <string.h> 69297f4619Sad #include <limits.h> 70297f4619Sad #include <unistd.h> 71297f4619Sad #include <err.h> 72297f4619Sad #include <paths.h> 73297f4619Sad #include <util.h> 74297f4619Sad #include <ctype.h> 75297f4619Sad #include <errno.h> 76*5c2240bbSad #include <stdbool.h> 77297f4619Sad 78297f4619Sad #include "extern.h" 79297f4619Sad 80297f4619Sad #define _PATH_DEV_LOCKSTAT "/dev/lockstat" 81297f4619Sad 82297f4619Sad #define MILLI 1000.0 83297f4619Sad #define MICRO 1000000.0 84297f4619Sad #define NANO 1000000000.0 85297f4619Sad #define PICO 1000000000000.0 86297f4619Sad 87297f4619Sad TAILQ_HEAD(lock_head, lockstruct); 88297f4619Sad typedef struct lock_head locklist_t; 89297f4619Sad TAILQ_HEAD(buf_head, lsbuf); 90297f4619Sad typedef struct buf_head buflist_t; 91297f4619Sad 92297f4619Sad typedef struct lockstruct { 93297f4619Sad TAILQ_ENTRY(lockstruct) chain; 94297f4619Sad buflist_t bufs; 959f07c24eSad buflist_t tosort; 96297f4619Sad uintptr_t lock; 979f07c24eSad double time; 989f07c24eSad uint32_t count; 99297f4619Sad u_int flags; 10012793652Sad u_int nbufs; 1019f07c24eSad char name[NAME_SIZE]; 102297f4619Sad } lock_t; 103297f4619Sad 104297f4619Sad typedef struct name { 105297f4619Sad const char *name; 106297f4619Sad int mask; 107297f4619Sad } name_t; 108297f4619Sad 109297f4619Sad const name_t locknames[] = { 110297f4619Sad { "adaptive_mutex", LB_ADAPTIVE_MUTEX }, 111297f4619Sad { "spin_mutex", LB_SPIN_MUTEX }, 112297f4619Sad { "lockmgr", LB_LOCKMGR }, 1139f07c24eSad { "rwlock", LB_RWLOCK }, 114048c3d68Sad { "kernel_lock", LB_KERNEL_LOCK }, 115297f4619Sad { NULL, 0 } 116297f4619Sad }; 117297f4619Sad 118297f4619Sad const name_t eventnames[] = { 119297f4619Sad { "spin", LB_SPIN }, 1209f07c24eSad { "sleep_exclusive", LB_SLEEP1 }, 1219f07c24eSad { "sleep_shared", LB_SLEEP2 }, 122297f4619Sad { NULL, 0 }, 123297f4619Sad }; 124297f4619Sad 125297f4619Sad const name_t alltypes[] = { 126297f4619Sad { "Adaptive mutex spin", LB_ADAPTIVE_MUTEX | LB_SPIN }, 1279f07c24eSad { "Adaptive mutex sleep", LB_ADAPTIVE_MUTEX | LB_SLEEP1 }, 128297f4619Sad { "Spin mutex spin", LB_SPIN_MUTEX | LB_SPIN }, 1299f07c24eSad { "lockmgr sleep", LB_LOCKMGR | LB_SLEEP1 }, 1309f07c24eSad { "RW lock sleep (writer)", LB_RWLOCK | LB_SLEEP1 }, 1319f07c24eSad { "RW lock sleep (reader)", LB_RWLOCK | LB_SLEEP2 }, 13212793652Sad { "Kernel lock spin", LB_KERNEL_LOCK | LB_SPIN }, 133297f4619Sad { NULL, 0 } 134297f4619Sad }; 135297f4619Sad 1369f07c24eSad locklist_t locklist; 1379f07c24eSad locklist_t freelist; 1389f07c24eSad locklist_t sortlist; 139297f4619Sad 140297f4619Sad lsbuf_t *bufs; 141297f4619Sad lsdisable_t ld; 142*5c2240bbSad bool lflag; 143*5c2240bbSad bool fflag; 144297f4619Sad int nbufs; 145*5c2240bbSad bool cflag; 146297f4619Sad int lsfd; 147297f4619Sad int displayed; 148297f4619Sad int bin64; 149297f4619Sad double tscale; 150297f4619Sad double cscale; 151297f4619Sad double cpuscale[sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0])]; 152297f4619Sad FILE *outfp; 153297f4619Sad 154*5c2240bbSad void findsym(findsym_t, char *, uintptr_t *, uintptr_t *, bool); 155297f4619Sad void spawn(int, char **); 156297f4619Sad void display(int, const char *name); 157297f4619Sad void listnames(const name_t *); 158*5c2240bbSad void collapse(bool, bool); 159*5c2240bbSad int matchname(const name_t *, char *); 1609f07c24eSad void makelists(int, int); 161297f4619Sad void nullsig(int); 162297f4619Sad void usage(void); 163297f4619Sad int ncpu(void); 1649f07c24eSad lock_t *morelocks(void); 165297f4619Sad 166297f4619Sad int 167297f4619Sad main(int argc, char **argv) 168297f4619Sad { 169*5c2240bbSad int eventtype, locktype, ch, nlfd, fd, i; 170*5c2240bbSad bool sflag, pflag, mflag, Mflag; 171297f4619Sad const char *nlistf, *outf; 172297f4619Sad char *lockname, *funcname; 173297f4619Sad const name_t *name; 174297f4619Sad lsenable_t le; 175297f4619Sad double ms; 176297f4619Sad char *p; 177297f4619Sad 178297f4619Sad nlistf = NULL; 179297f4619Sad outf = NULL; 180297f4619Sad lockname = NULL; 181297f4619Sad funcname = NULL; 182297f4619Sad eventtype = -1; 183297f4619Sad locktype = -1; 184297f4619Sad nbufs = 0; 185*5c2240bbSad sflag = false; 186*5c2240bbSad pflag = false; 187*5c2240bbSad mflag = false; 188*5c2240bbSad Mflag = false; 189297f4619Sad 190*5c2240bbSad while ((ch = getopt(argc, argv, "E:F:L:MN:T:b:ceflmo:pst")) != -1) 191297f4619Sad switch (ch) { 192297f4619Sad case 'E': 193297f4619Sad eventtype = matchname(eventnames, optarg); 194297f4619Sad break; 195297f4619Sad case 'F': 196297f4619Sad funcname = optarg; 197297f4619Sad break; 198297f4619Sad case 'L': 199297f4619Sad lockname = optarg; 200297f4619Sad break; 201297f4619Sad case 'N': 202297f4619Sad nlistf = optarg; 203297f4619Sad break; 204297f4619Sad case 'T': 205297f4619Sad locktype = matchname(locknames, optarg); 206297f4619Sad break; 207297f4619Sad case 'b': 208297f4619Sad nbufs = (int)strtol(optarg, &p, 0); 209297f4619Sad if (!isdigit((u_int)*optarg) || *p != '\0') 210297f4619Sad usage(); 211297f4619Sad break; 212297f4619Sad case 'c': 213*5c2240bbSad cflag = true; 214297f4619Sad break; 215297f4619Sad case 'e': 216297f4619Sad listnames(eventnames); 217297f4619Sad break; 218*5c2240bbSad case 'f': 219*5c2240bbSad fflag = true; 220*5c2240bbSad break; 221297f4619Sad case 'l': 222*5c2240bbSad lflag = true; 223*5c2240bbSad break; 224*5c2240bbSad case 'm': 225*5c2240bbSad mflag = true; 226*5c2240bbSad break; 227*5c2240bbSad case 'M': 228*5c2240bbSad Mflag = true; 229297f4619Sad break; 230297f4619Sad case 'o': 231297f4619Sad outf = optarg; 232297f4619Sad break; 233297f4619Sad case 'p': 234*5c2240bbSad pflag = true; 235297f4619Sad break; 236297f4619Sad case 's': 237*5c2240bbSad sflag = true; 238297f4619Sad break; 239297f4619Sad case 't': 240297f4619Sad listnames(locknames); 241297f4619Sad break; 242297f4619Sad default: 243297f4619Sad usage(); 244297f4619Sad } 245297f4619Sad argc -= optind; 246297f4619Sad argv += optind; 247297f4619Sad 248297f4619Sad if (*argv == NULL) 249297f4619Sad usage(); 250297f4619Sad 251297f4619Sad if (outf) { 25212793652Sad fd = open(outf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 25312793652Sad if (fd == -1) 254297f4619Sad err(EXIT_FAILURE, "opening %s", outf); 255297f4619Sad outfp = fdopen(fd, "w"); 256297f4619Sad } else 257297f4619Sad outfp = stdout; 258297f4619Sad 259297f4619Sad /* 260297f4619Sad * Find the name list for resolving symbol names, and load it into 261297f4619Sad * memory. 262297f4619Sad */ 263297f4619Sad if (nlistf == NULL) { 264297f4619Sad nlfd = open(_PATH_KSYMS, O_RDONLY); 265297f4619Sad nlistf = getbootfile(); 266297f4619Sad } else 267297f4619Sad nlfd = -1; 268297f4619Sad if (nlfd == -1) { 269297f4619Sad if ((nlfd = open(nlistf, O_RDONLY)) < 0) 270297f4619Sad err(EXIT_FAILURE, "cannot open " _PATH_KSYMS " or %s", 271297f4619Sad nlistf); 272297f4619Sad } 273297f4619Sad if (loadsym32(nlfd) != 0) { 274297f4619Sad if (loadsym64(nlfd) != 0) 275297f4619Sad errx(EXIT_FAILURE, "unable to load symbol table"); 276297f4619Sad bin64 = 1; 277297f4619Sad } 278297f4619Sad close(nlfd); 279297f4619Sad 280297f4619Sad memset(&le, 0, sizeof(le)); 281297f4619Sad le.le_nbufs = nbufs; 282297f4619Sad 283297f4619Sad /* 284297f4619Sad * Set up initial filtering. 285297f4619Sad */ 286297f4619Sad if (lockname != NULL) { 2879f07c24eSad findsym(LOCK_BYNAME, lockname, &le.le_lockstart, 288*5c2240bbSad &le.le_lockend, true); 289297f4619Sad le.le_flags |= LE_ONE_LOCK; 290297f4619Sad } 291297f4619Sad if (!lflag) 292297f4619Sad le.le_flags |= LE_CALLSITE; 293*5c2240bbSad if (!fflag) 294*5c2240bbSad le.le_flags |= LE_LOCK; 295297f4619Sad if (funcname != NULL) { 296297f4619Sad if (lflag) 297297f4619Sad usage(); 298*5c2240bbSad findsym(FUNC_BYNAME, funcname, &le.le_csstart, &le.le_csend, true); 299297f4619Sad le.le_flags |= LE_ONE_CALLSITE; 300297f4619Sad } 301297f4619Sad le.le_mask = (eventtype & LB_EVENT_MASK) | (locktype & LB_LOCK_MASK); 302297f4619Sad 303297f4619Sad /* 304297f4619Sad * Start tracing. 305297f4619Sad */ 306297f4619Sad if ((lsfd = open(_PATH_DEV_LOCKSTAT, O_RDONLY)) < 0) 307297f4619Sad err(EXIT_FAILURE, "cannot open " _PATH_DEV_LOCKSTAT); 308297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_GVERSION, &ch) < 0) 309297f4619Sad err(EXIT_FAILURE, "ioctl"); 310297f4619Sad if (ch != LS_VERSION) 3119f07c24eSad errx(EXIT_FAILURE, 3129f07c24eSad "incompatible lockstat interface version (%d, kernel %d)", 3139f07c24eSad LS_VERSION, ch); 314297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_ENABLE, &le)) 315297f4619Sad err(EXIT_FAILURE, "cannot enable tracing"); 316297f4619Sad 317297f4619Sad /* 318297f4619Sad * Execute the traced program. 319297f4619Sad */ 320297f4619Sad spawn(argc, argv); 321297f4619Sad 322297f4619Sad /* 323297f4619Sad * Stop tracing, and read the trace buffers from the kernel. 324297f4619Sad */ 325297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_DISABLE, &ld) == -1) { 326297f4619Sad if (errno == EOVERFLOW) { 327297f4619Sad warnx("overflowed available kernel trace buffers"); 328297f4619Sad exit(EXIT_FAILURE); 329297f4619Sad } 330297f4619Sad err(EXIT_FAILURE, "cannot disable tracing"); 331297f4619Sad } 332297f4619Sad if ((bufs = malloc(ld.ld_size)) == NULL) 333297f4619Sad err(EXIT_FAILURE, "cannot allocate memory for user buffers"); 334297f4619Sad if (read(lsfd, bufs, ld.ld_size) != ld.ld_size) 335297f4619Sad err(EXIT_FAILURE, "reading from " _PATH_DEV_LOCKSTAT); 336297f4619Sad if (close(lsfd)) 337297f4619Sad err(EXIT_FAILURE, "close(" _PATH_DEV_LOCKSTAT ")"); 338297f4619Sad 339297f4619Sad /* 3409f07c24eSad * Figure out how to scale the results. For internal use we convert 3419f07c24eSad * all times from CPU frequency based to picoseconds, and values are 3429f07c24eSad * eventually displayed in ms. 343297f4619Sad */ 344297f4619Sad for (i = 0; i < sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0]); i++) 345297f4619Sad if (ld.ld_freq[i] != 0) 346297f4619Sad cpuscale[i] = PICO / ld.ld_freq[i]; 347297f4619Sad ms = ld.ld_time.tv_sec * MILLI + ld.ld_time.tv_nsec / MICRO; 348297f4619Sad if (pflag) 349297f4619Sad cscale = 1.0 / ncpu(); 350297f4619Sad else 351297f4619Sad cscale = 1.0; 352297f4619Sad cscale *= (sflag ? MILLI / ms : 1.0); 353297f4619Sad tscale = cscale / NANO; 354297f4619Sad nbufs = (int)(ld.ld_size / sizeof(lsbuf_t)); 3559f07c24eSad 3569f07c24eSad TAILQ_INIT(&locklist); 3579f07c24eSad TAILQ_INIT(&sortlist); 3589f07c24eSad TAILQ_INIT(&freelist); 359297f4619Sad 360*5c2240bbSad if ((mflag | Mflag) != 0) 361*5c2240bbSad collapse(mflag, Mflag); 362*5c2240bbSad 363297f4619Sad /* 364297f4619Sad * Display the results. 365297f4619Sad */ 366297f4619Sad fprintf(outfp, "Elapsed time: %.2f seconds.", ms / MILLI); 367297f4619Sad if (sflag || pflag) { 368297f4619Sad fprintf(outfp, " Displaying "); 369297f4619Sad if (pflag) 370297f4619Sad fprintf(outfp, "per-CPU "); 371297f4619Sad if (sflag) 372297f4619Sad fprintf(outfp, "per-second "); 373297f4619Sad fprintf(outfp, "averages."); 374297f4619Sad } 375297f4619Sad putc('\n', outfp); 376297f4619Sad 377297f4619Sad for (name = alltypes; name->name != NULL; name++) { 378297f4619Sad if (eventtype != -1 && 379297f4619Sad (name->mask & LB_EVENT_MASK) != eventtype) 380297f4619Sad continue; 381297f4619Sad if (locktype != -1 && 382297f4619Sad (name->mask & LB_LOCK_MASK) != locktype) 383297f4619Sad continue; 384297f4619Sad 385297f4619Sad display(name->mask, name->name); 386297f4619Sad } 387297f4619Sad 388297f4619Sad if (displayed == 0) 389297f4619Sad fprintf(outfp, "None of the selected events were recorded.\n"); 390297f4619Sad exit(EXIT_SUCCESS); 391297f4619Sad } 392297f4619Sad 393297f4619Sad void 394297f4619Sad usage(void) 395297f4619Sad { 396297f4619Sad 397297f4619Sad fprintf(stderr, 398297f4619Sad "%s: usage:\n" 399297f4619Sad "%s [options] <command>\n\n" 400297f4619Sad "-b nbuf\t\tset number of event buffers to allocate\n" 401297f4619Sad "-c\t\treport percentage of total events by count, not time\n" 40297c200c8Swiz "-E evt\t\tdisplay only one type of event\n" 403297f4619Sad "-e\t\tlist event types\n" 404*5c2240bbSad "-f\t\ttrace only by function\n" 40597c200c8Swiz "-F func\t\tlimit trace to one function\n" 40697c200c8Swiz "-L lock\t\tlimit trace to one lock (name, or address)\n" 407297f4619Sad "-l\t\ttrace only by lock\n" 408*5c2240bbSad "-m\t\tmerge call sites within unique functions\n" 409*5c2240bbSad "-M\t\tmerge lock addresses within unique objects\n" 41097c200c8Swiz "-N nlist\tspecify name list file\n" 411297f4619Sad "-o file\t\tsend output to named file, not stdout\n" 412297f4619Sad "-p\t\tshow average count/time per CPU, not total\n" 413297f4619Sad "-s\t\tshow average count/time per second, not total\n" 41497c200c8Swiz "-T type\t\tdisplay only one type of lock\n" 415297f4619Sad "-t\t\tlist lock types\n", 416297f4619Sad getprogname(), getprogname()); 417297f4619Sad 418297f4619Sad exit(EXIT_FAILURE); 419297f4619Sad } 420297f4619Sad 421297f4619Sad void 422297f4619Sad nullsig(int junk) 423297f4619Sad { 424297f4619Sad 425297f4619Sad (void)junk; 426297f4619Sad } 427297f4619Sad 428297f4619Sad void 429297f4619Sad listnames(const name_t *name) 430297f4619Sad { 431297f4619Sad 432297f4619Sad for (; name->name != NULL; name++) 433297f4619Sad printf("%s\n", name->name); 434297f4619Sad 435297f4619Sad exit(EXIT_SUCCESS); 436297f4619Sad } 437297f4619Sad 438297f4619Sad int 439*5c2240bbSad matchname(const name_t *name, char *string) 440297f4619Sad { 441*5c2240bbSad int empty, mask; 442*5c2240bbSad char *sp; 443297f4619Sad 444*5c2240bbSad empty = 1; 445*5c2240bbSad mask = 0; 446297f4619Sad 447*5c2240bbSad while ((sp = strsep(&string, ",")) != NULL) { 448*5c2240bbSad if (*sp == '\0') 449297f4619Sad usage(); 450*5c2240bbSad 451*5c2240bbSad for (; name->name != NULL; name++) { 452*5c2240bbSad if (strcasecmp(name->name, sp) == 0) { 453*5c2240bbSad mask |= name->mask; 454*5c2240bbSad break; 455*5c2240bbSad } 456*5c2240bbSad } 457*5c2240bbSad if (name->name == NULL) 458*5c2240bbSad errx(EXIT_FAILURE, "unknown identifier `%s'", sp); 459*5c2240bbSad empty = 0; 460*5c2240bbSad } 461*5c2240bbSad 462*5c2240bbSad if (empty) 463*5c2240bbSad usage(); 464*5c2240bbSad 465*5c2240bbSad return mask; 466297f4619Sad } 467297f4619Sad 468297f4619Sad /* 469297f4619Sad * Return the number of CPUs in the running system. 470297f4619Sad */ 471297f4619Sad int 472297f4619Sad ncpu(void) 473297f4619Sad { 474297f4619Sad int rv, mib[2]; 475297f4619Sad size_t varlen; 476297f4619Sad 477297f4619Sad mib[0] = CTL_HW; 478297f4619Sad mib[1] = HW_NCPU; 479297f4619Sad varlen = sizeof(rv); 480297f4619Sad if (sysctl(mib, 2, &rv, &varlen, NULL, (size_t)0) < 0) 481297f4619Sad rv = 1; 482297f4619Sad 483297f4619Sad return (rv); 484297f4619Sad } 485297f4619Sad 486297f4619Sad /* 487297f4619Sad * Call into the ELF parser and look up a symbol by name or by address. 488297f4619Sad */ 489297f4619Sad void 490*5c2240bbSad findsym(findsym_t find, char *name, uintptr_t *start, uintptr_t *end, bool chg) 491297f4619Sad { 492*5c2240bbSad uintptr_t tend, sa, ea; 493297f4619Sad char *p; 494297f4619Sad int rv; 495297f4619Sad 496*5c2240bbSad if (!chg) { 497*5c2240bbSad sa = *start; 498*5c2240bbSad start = &sa; 499*5c2240bbSad end = &ea; 500*5c2240bbSad } 501*5c2240bbSad 502297f4619Sad if (end == NULL) 503297f4619Sad end = &tend; 504297f4619Sad 505297f4619Sad if (find == LOCK_BYNAME) { 506297f4619Sad if (isdigit((u_int)name[0])) { 507297f4619Sad *start = (uintptr_t)strtoul(name, &p, 0); 508297f4619Sad if (*p == '\0') 509297f4619Sad return; 510297f4619Sad } 511297f4619Sad } 512297f4619Sad 513297f4619Sad if (bin64) 514297f4619Sad rv = findsym64(find, name, start, end); 515297f4619Sad else 516297f4619Sad rv = findsym32(find, name, start, end); 517297f4619Sad 518297f4619Sad if (find == FUNC_BYNAME || find == LOCK_BYNAME) { 519297f4619Sad if (rv == -1) 520297f4619Sad errx(EXIT_FAILURE, "unable to find symbol `%s'", name); 521297f4619Sad return; 522297f4619Sad } 523297f4619Sad 524297f4619Sad if (rv == -1) 5259f07c24eSad snprintf(name, NAME_SIZE, "%016lx", (long)*start); 526297f4619Sad } 527297f4619Sad 528297f4619Sad /* 529297f4619Sad * Fork off the child process and wait for it to complete. We trap SIGINT 530297f4619Sad * so that the caller can use Ctrl-C to stop tracing early and still get 531297f4619Sad * useful results. 532297f4619Sad */ 533297f4619Sad void 534297f4619Sad spawn(int argc, char **argv) 535297f4619Sad { 536297f4619Sad pid_t pid; 537297f4619Sad 538297f4619Sad switch (pid = fork()) { 539297f4619Sad case 0: 540297f4619Sad close(lsfd); 541297f4619Sad if (execvp(argv[0], argv) == -1) 542297f4619Sad err(EXIT_FAILURE, "cannot exec"); 543297f4619Sad break; 544297f4619Sad case -1: 545297f4619Sad err(EXIT_FAILURE, "cannot fork to exec"); 546297f4619Sad break; 547297f4619Sad default: 548297f4619Sad signal(SIGINT, nullsig); 549297f4619Sad wait(NULL); 550297f4619Sad signal(SIGINT, SIG_DFL); 551297f4619Sad break; 552297f4619Sad } 553297f4619Sad } 554297f4619Sad 555297f4619Sad /* 5569f07c24eSad * Allocate a new block of lock_t structures. 5579f07c24eSad */ 5589f07c24eSad lock_t * 5599f07c24eSad morelocks(void) 5609f07c24eSad { 5619f07c24eSad const static int batch = 32; 5629f07c24eSad lock_t *l, *lp, *max; 5639f07c24eSad 5649f07c24eSad l = (lock_t *)malloc(sizeof(*l) * batch); 5659f07c24eSad 5669f07c24eSad for (lp = l, max = l + batch; lp < max; lp++) 5679f07c24eSad TAILQ_INSERT_TAIL(&freelist, lp, chain); 5689f07c24eSad 5699f07c24eSad return l; 5709f07c24eSad } 5719f07c24eSad 5729f07c24eSad /* 573*5c2240bbSad * Collapse addresses from unique objects. 574*5c2240bbSad */ 575*5c2240bbSad void 576*5c2240bbSad collapse(bool func, bool lock) 577*5c2240bbSad { 578*5c2240bbSad lsbuf_t *lb, *max; 579*5c2240bbSad 580*5c2240bbSad for (lb = bufs, max = bufs + nbufs; lb < max; lb++) { 581*5c2240bbSad if (func && lb->lb_callsite != 0) { 582*5c2240bbSad findsym(FUNC_BYADDR, NULL, &lb->lb_callsite, NULL, 583*5c2240bbSad true); 584*5c2240bbSad } 585*5c2240bbSad if (lock && lb->lb_lock != 0) { 586*5c2240bbSad findsym(LOCK_BYADDR, NULL, &lb->lb_lock, NULL, 587*5c2240bbSad true); 588*5c2240bbSad } 589*5c2240bbSad } 590*5c2240bbSad } 591*5c2240bbSad 592*5c2240bbSad /* 593297f4619Sad * From the kernel supplied data, construct two dimensional lists of locks 5949f07c24eSad * and event buffers, indexed by lock type and sorted by event type. 595297f4619Sad */ 596297f4619Sad void 5979f07c24eSad makelists(int mask, int event) 598297f4619Sad { 599297f4619Sad lsbuf_t *lb, *lb2, *max; 6009f07c24eSad lock_t *l, *l2; 6019f07c24eSad int type; 602297f4619Sad 6039f07c24eSad /* 6049f07c24eSad * Recycle lock_t structures from the last run. 6059f07c24eSad */ 6069f07c24eSad while ((l = TAILQ_FIRST(&locklist)) != NULL) { 6079f07c24eSad TAILQ_REMOVE(&locklist, l, chain); 6089f07c24eSad TAILQ_INSERT_HEAD(&freelist, l, chain); 6099f07c24eSad } 6109f07c24eSad 6119f07c24eSad type = mask & LB_LOCK_MASK; 612297f4619Sad 613297f4619Sad for (lb = bufs, max = bufs + nbufs; lb < max; lb++) { 61475922662Sad if ((lb->lb_flags & LB_LOCK_MASK) != type || 61575922662Sad lb->lb_counts[event] == 0) 616297f4619Sad continue; 617297f4619Sad 618297f4619Sad /* 619297f4619Sad * Look for a record descibing this lock, and allocate a 620297f4619Sad * new one if needed. 621297f4619Sad */ 6229f07c24eSad TAILQ_FOREACH(l, &sortlist, chain) { 623297f4619Sad if (l->lock == lb->lb_lock) 624297f4619Sad break; 625297f4619Sad } 626297f4619Sad if (l == NULL) { 6279f07c24eSad if ((l = TAILQ_FIRST(&freelist)) == NULL) 6289f07c24eSad l = morelocks(); 6299f07c24eSad TAILQ_REMOVE(&freelist, l, chain); 630297f4619Sad l->flags = lb->lb_flags; 631297f4619Sad l->lock = lb->lb_lock; 63212793652Sad l->nbufs = 0; 6339f07c24eSad l->name[0] = '\0'; 6349f07c24eSad l->count = 0; 6359f07c24eSad l->time = 0; 6369f07c24eSad TAILQ_INIT(&l->tosort); 637297f4619Sad TAILQ_INIT(&l->bufs); 6389f07c24eSad TAILQ_INSERT_TAIL(&sortlist, l, chain); 639297f4619Sad } 640297f4619Sad 641297f4619Sad /* 642297f4619Sad * Scale the time values per buffer and summarise 643297f4619Sad * times+counts per lock. 644297f4619Sad */ 6459f07c24eSad lb->lb_times[event] *= cpuscale[lb->lb_cpu]; 6469f07c24eSad l->count += lb->lb_counts[event]; 6479f07c24eSad l->time += lb->lb_times[event]; 648297f4619Sad 649297f4619Sad /* 650297f4619Sad * Merge same lock+callsite pairs from multiple CPUs 651297f4619Sad * together. 652297f4619Sad */ 6539f07c24eSad TAILQ_FOREACH(lb2, &l->tosort, lb_chain.tailq) { 654297f4619Sad if (lb->lb_callsite == lb2->lb_callsite) 655297f4619Sad break; 656297f4619Sad } 657297f4619Sad if (lb2 != NULL) { 6589f07c24eSad lb2->lb_counts[event] += lb->lb_counts[event]; 6599f07c24eSad lb2->lb_times[event] += lb->lb_times[event]; 66012793652Sad } else { 6619f07c24eSad TAILQ_INSERT_HEAD(&l->tosort, lb, lb_chain.tailq); 66212793652Sad l->nbufs++; 66312793652Sad } 664297f4619Sad } 665297f4619Sad 666297f4619Sad /* 6679f07c24eSad * Now sort the lists. 668297f4619Sad */ 6699f07c24eSad while ((l = TAILQ_FIRST(&sortlist)) != NULL) { 6709f07c24eSad TAILQ_REMOVE(&sortlist, l, chain); 671297f4619Sad 672297f4619Sad /* 673297f4619Sad * Sort the buffers into the per-lock list. 674297f4619Sad */ 6759f07c24eSad while ((lb = TAILQ_FIRST(&l->tosort)) != NULL) { 6769f07c24eSad TAILQ_REMOVE(&l->tosort, lb, lb_chain.tailq); 677297f4619Sad 6789f07c24eSad lb2 = TAILQ_FIRST(&l->bufs); 679297f4619Sad while (lb2 != NULL) { 680297f4619Sad if (cflag) { 681297f4619Sad if (lb->lb_counts[event] > 682297f4619Sad lb2->lb_counts[event]) 683297f4619Sad break; 684297f4619Sad } else if (lb->lb_times[event] > 685297f4619Sad lb2->lb_times[event]) 686297f4619Sad break; 687297f4619Sad lb2 = TAILQ_NEXT(lb2, lb_chain.tailq); 688297f4619Sad } 689297f4619Sad if (lb2 == NULL) 6909f07c24eSad TAILQ_INSERT_TAIL(&l->bufs, lb, 6919f07c24eSad lb_chain.tailq); 692297f4619Sad else 693297f4619Sad TAILQ_INSERT_BEFORE(lb2, lb, lb_chain.tailq); 694297f4619Sad } 695297f4619Sad 696297f4619Sad /* 697297f4619Sad * Sort this lock into the per-type list, based on the 698297f4619Sad * totals per lock. 699297f4619Sad */ 7009f07c24eSad l2 = TAILQ_FIRST(&locklist); 701297f4619Sad while (l2 != NULL) { 702297f4619Sad if (cflag) { 7039f07c24eSad if (l->count > l2->count) 704297f4619Sad break; 7059f07c24eSad } else if (l->time > l2->time) 706297f4619Sad break; 707297f4619Sad l2 = TAILQ_NEXT(l2, chain); 708297f4619Sad } 709297f4619Sad if (l2 == NULL) 7109f07c24eSad TAILQ_INSERT_TAIL(&locklist, l, chain); 711297f4619Sad else 712297f4619Sad TAILQ_INSERT_BEFORE(l2, l, chain); 713297f4619Sad } 714297f4619Sad } 715297f4619Sad 716297f4619Sad /* 717297f4619Sad * Display a summary table for one lock type / event type pair. 718297f4619Sad */ 719297f4619Sad void 720297f4619Sad display(int mask, const char *name) 721297f4619Sad { 722297f4619Sad lock_t *l; 723297f4619Sad lsbuf_t *lb; 724297f4619Sad double pcscale, metric; 7259f07c24eSad char fname[NAME_SIZE]; 7269f07c24eSad int event; 727297f4619Sad 728297f4619Sad event = (mask & LB_EVENT_MASK) - 1; 7299f07c24eSad makelists(mask, event); 7309f07c24eSad 7319f07c24eSad if (TAILQ_EMPTY(&locklist)) 7329f07c24eSad return; 733297f4619Sad 734297f4619Sad fprintf(outfp, "\n-- %s\n\n" 735297f4619Sad "Total%% Count Time/ms Lock Caller\n" 736297f4619Sad "------ ------- --------- ---------------------- ------------------------------\n", 737297f4619Sad name); 738297f4619Sad 739297f4619Sad /* 740297f4619Sad * Sum up all events for this type of lock + event. 741297f4619Sad */ 742297f4619Sad pcscale = 0; 7439f07c24eSad TAILQ_FOREACH(l, &locklist, chain) { 744297f4619Sad if (cflag) 7459f07c24eSad pcscale += l->count; 746297f4619Sad else 7479f07c24eSad pcscale += l->time; 748297f4619Sad displayed++; 749297f4619Sad } 750297f4619Sad if (pcscale == 0) 751297f4619Sad pcscale = 100; 752297f4619Sad else 753297f4619Sad pcscale = (100.0 / pcscale); 754297f4619Sad 755297f4619Sad /* 756297f4619Sad * For each lock, print a summary total, followed by a breakdown by 757297f4619Sad * caller. 758297f4619Sad */ 7599f07c24eSad TAILQ_FOREACH(l, &locklist, chain) { 760297f4619Sad if (cflag) 7619f07c24eSad metric = l->count; 762297f4619Sad else 7639f07c24eSad metric = l->time; 764297f4619Sad metric *= pcscale; 765297f4619Sad 7669f07c24eSad if (l->name[0] == '\0') 767*5c2240bbSad findsym(LOCK_BYADDR, l->name, &l->lock, NULL, false); 768297f4619Sad 769048c3d68Sad if (lflag || l->nbufs > 1) 7709f07c24eSad fprintf(outfp, "%6.2f %7d %9.2f %-22s <all>\n", 7719f07c24eSad metric, (int)(l->count * cscale), 7729f07c24eSad l->time * tscale, l->name); 773297f4619Sad 774297f4619Sad if (lflag) 775297f4619Sad continue; 776297f4619Sad 777297f4619Sad TAILQ_FOREACH(lb, &l->bufs, lb_chain.tailq) { 778297f4619Sad if (cflag) 779297f4619Sad metric = lb->lb_counts[event]; 780297f4619Sad else 781297f4619Sad metric = lb->lb_times[event]; 782297f4619Sad metric *= pcscale; 783297f4619Sad 784*5c2240bbSad findsym(FUNC_BYADDR, fname, &lb->lb_callsite, NULL, 785*5c2240bbSad false); 7869f07c24eSad fprintf(outfp, "%6.2f %7d %9.2f %-22s %s\n", 7879f07c24eSad metric, (int)(lb->lb_counts[event] * cscale), 7889f07c24eSad lb->lb_times[event] * tscale, l->name, fname); 789297f4619Sad } 790297f4619Sad } 791297f4619Sad } 792