1*297f4619Sad /* $NetBSD: main.c,v 1.1 2006/09/07 00:50:45 ad Exp $ */ 2*297f4619Sad 3*297f4619Sad /*- 4*297f4619Sad * Copyright (c) 2006 The NetBSD Foundation, Inc. 5*297f4619Sad * All rights reserved. 6*297f4619Sad * 7*297f4619Sad * This code is derived from software contributed to The NetBSD Foundation 8*297f4619Sad * by Andrew Doran. 9*297f4619Sad * 10*297f4619Sad * Redistribution and use in source and binary forms, with or without 11*297f4619Sad * modification, are permitted provided that the following conditions 12*297f4619Sad * are met: 13*297f4619Sad * 1. Redistributions of source code must retain the above copyright 14*297f4619Sad * notice, this list of conditions and the following disclaimer. 15*297f4619Sad * 2. Redistributions in binary form must reproduce the above copyright 16*297f4619Sad * notice, this list of conditions and the following disclaimer in the 17*297f4619Sad * documentation and/or other materials provided with the distribution. 18*297f4619Sad * 3. All advertising materials mentioning features or use of this software 19*297f4619Sad * must display the following acknowledgement: 20*297f4619Sad * This product includes software developed by the NetBSD 21*297f4619Sad * Foundation, Inc. and its contributors. 22*297f4619Sad * 4. Neither the name of The NetBSD Foundation nor the names of its 23*297f4619Sad * contributors may be used to endorse or promote products derived 24*297f4619Sad * from this software without specific prior written permission. 25*297f4619Sad * 26*297f4619Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27*297f4619Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28*297f4619Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29*297f4619Sad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30*297f4619Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31*297f4619Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32*297f4619Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33*297f4619Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34*297f4619Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35*297f4619Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36*297f4619Sad * POSSIBILITY OF SUCH DAMAGE. 37*297f4619Sad */ 38*297f4619Sad 39*297f4619Sad /* 40*297f4619Sad * TODO: 41*297f4619Sad * 42*297f4619Sad * - Need better analysis and tracking of events. 43*297f4619Sad * - Should be binary format agnostic, but given that we're likely to be using 44*297f4619Sad * ELF for quite a while that's not a big problem. 45*297f4619Sad * - Shouldn't have to parse the namelist here. We should use something like 46*297f4619Sad * FreeBSD's libelf. 47*297f4619Sad * - The way the namelist is searched sucks, is it worth doing something 48*297f4619Sad * better? 49*297f4619Sad */ 50*297f4619Sad 51*297f4619Sad #include <sys/cdefs.h> 52*297f4619Sad #ifndef lint 53*297f4619Sad __RCSID("$NetBSD: main.c,v 1.1 2006/09/07 00:50:45 ad Exp $"); 54*297f4619Sad #endif /* not lint */ 55*297f4619Sad 56*297f4619Sad #include <sys/types.h> 57*297f4619Sad #include <sys/param.h> 58*297f4619Sad #include <sys/time.h> 59*297f4619Sad #include <sys/fcntl.h> 60*297f4619Sad #include <sys/ioctl.h> 61*297f4619Sad #include <sys/wait.h> 62*297f4619Sad #include <sys/signal.h> 63*297f4619Sad #include <sys/lockstat.h> 64*297f4619Sad #include <sys/sysctl.h> 65*297f4619Sad 66*297f4619Sad #include <stdio.h> 67*297f4619Sad #include <stdlib.h> 68*297f4619Sad #include <string.h> 69*297f4619Sad #include <limits.h> 70*297f4619Sad #include <unistd.h> 71*297f4619Sad #include <err.h> 72*297f4619Sad #include <paths.h> 73*297f4619Sad #include <util.h> 74*297f4619Sad #include <ctype.h> 75*297f4619Sad #include <errno.h> 76*297f4619Sad 77*297f4619Sad #include "extern.h" 78*297f4619Sad 79*297f4619Sad #define _PATH_DEV_LOCKSTAT "/dev/lockstat" 80*297f4619Sad 81*297f4619Sad #define MILLI 1000.0 82*297f4619Sad #define MICRO 1000000.0 83*297f4619Sad #define NANO 1000000000.0 84*297f4619Sad #define PICO 1000000000000.0 85*297f4619Sad 86*297f4619Sad TAILQ_HEAD(lock_head, lockstruct); 87*297f4619Sad typedef struct lock_head locklist_t; 88*297f4619Sad TAILQ_HEAD(buf_head, lsbuf); 89*297f4619Sad typedef struct buf_head buflist_t; 90*297f4619Sad 91*297f4619Sad typedef struct lockstruct { 92*297f4619Sad TAILQ_ENTRY(lockstruct) chain; 93*297f4619Sad buflist_t bufs; 94*297f4619Sad uintptr_t lock; 95*297f4619Sad double times[LB_NEVENT]; 96*297f4619Sad uint32_t counts[LB_NEVENT]; 97*297f4619Sad u_int flags; 98*297f4619Sad } lock_t; 99*297f4619Sad 100*297f4619Sad typedef struct name { 101*297f4619Sad const char *name; 102*297f4619Sad int mask; 103*297f4619Sad } name_t; 104*297f4619Sad 105*297f4619Sad const name_t locknames[] = { 106*297f4619Sad { "adaptive_mutex", LB_ADAPTIVE_MUTEX }, 107*297f4619Sad { "adaptive_rwlock", LB_ADAPTIVE_RWLOCK }, 108*297f4619Sad { "spin_mutex", LB_SPIN_MUTEX }, 109*297f4619Sad { "spin_rwlock", LB_SPIN_RWLOCK }, 110*297f4619Sad { "lockmgr", LB_LOCKMGR }, 111*297f4619Sad { NULL, 0 } 112*297f4619Sad }; 113*297f4619Sad 114*297f4619Sad const name_t eventnames[] = { 115*297f4619Sad { "spin", LB_SPIN }, 116*297f4619Sad { "sleep", LB_SLEEP }, 117*297f4619Sad { NULL, 0 }, 118*297f4619Sad }; 119*297f4619Sad 120*297f4619Sad const name_t alltypes[] = { 121*297f4619Sad { "Adaptive mutex spin", LB_ADAPTIVE_MUTEX | LB_SPIN }, 122*297f4619Sad { "Adaptive mutex sleep", LB_ADAPTIVE_MUTEX | LB_SLEEP }, 123*297f4619Sad { "Adaptive RW lock spin", LB_ADAPTIVE_RWLOCK | LB_SPIN }, 124*297f4619Sad { "Adaptive RW lock sleep", LB_ADAPTIVE_RWLOCK | LB_SPIN }, 125*297f4619Sad { "Spin mutex spin", LB_SPIN_MUTEX | LB_SPIN }, 126*297f4619Sad { "Spin RW lock spin", LB_SPIN_RWLOCK | LB_SPIN }, 127*297f4619Sad { "lockmgr sleep", LB_LOCKMGR | LB_SLEEP }, 128*297f4619Sad { NULL, 0 } 129*297f4619Sad }; 130*297f4619Sad 131*297f4619Sad locklist_t locklist[LB_NLOCK]; 132*297f4619Sad 133*297f4619Sad lsbuf_t *bufs; 134*297f4619Sad lsdisable_t ld; 135*297f4619Sad int lflag; 136*297f4619Sad int nbufs; 137*297f4619Sad int cflag; 138*297f4619Sad int lsfd; 139*297f4619Sad int displayed; 140*297f4619Sad int bin64; 141*297f4619Sad double tscale; 142*297f4619Sad double cscale; 143*297f4619Sad double cpuscale[sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0])]; 144*297f4619Sad FILE *outfp; 145*297f4619Sad 146*297f4619Sad void findsym(findsym_t, char *, uintptr_t *, uintptr_t *); 147*297f4619Sad void spawn(int, char **); 148*297f4619Sad void display(int, const char *name); 149*297f4619Sad void listnames(const name_t *); 150*297f4619Sad int matchname(const name_t *, const char *); 151*297f4619Sad void makelists(void); 152*297f4619Sad void nullsig(int); 153*297f4619Sad void usage(void); 154*297f4619Sad void resort(int, int); 155*297f4619Sad int ncpu(void); 156*297f4619Sad 157*297f4619Sad int 158*297f4619Sad main(int argc, char **argv) 159*297f4619Sad { 160*297f4619Sad int eventtype, locktype, ch, nlfd, sflag, fd, i, pflag; 161*297f4619Sad const char *nlistf, *outf; 162*297f4619Sad char *lockname, *funcname; 163*297f4619Sad const name_t *name; 164*297f4619Sad lsenable_t le; 165*297f4619Sad double ms; 166*297f4619Sad char *p; 167*297f4619Sad 168*297f4619Sad nlistf = NULL; 169*297f4619Sad outf = NULL; 170*297f4619Sad lockname = NULL; 171*297f4619Sad funcname = NULL; 172*297f4619Sad eventtype = -1; 173*297f4619Sad locktype = -1; 174*297f4619Sad nbufs = 0; 175*297f4619Sad sflag = 0; 176*297f4619Sad pflag = 0; 177*297f4619Sad 178*297f4619Sad while ((ch = getopt(argc, argv, "E:F:L:M:N:T:b:ceflo:pst")) != -1) 179*297f4619Sad switch (ch) { 180*297f4619Sad case 'E': 181*297f4619Sad eventtype = matchname(eventnames, optarg); 182*297f4619Sad break; 183*297f4619Sad case 'F': 184*297f4619Sad funcname = optarg; 185*297f4619Sad break; 186*297f4619Sad case 'L': 187*297f4619Sad lockname = optarg; 188*297f4619Sad break; 189*297f4619Sad case 'N': 190*297f4619Sad nlistf = optarg; 191*297f4619Sad break; 192*297f4619Sad case 'T': 193*297f4619Sad locktype = matchname(locknames, optarg); 194*297f4619Sad break; 195*297f4619Sad case 'b': 196*297f4619Sad nbufs = (int)strtol(optarg, &p, 0); 197*297f4619Sad if (!isdigit((u_int)*optarg) || *p != '\0') 198*297f4619Sad usage(); 199*297f4619Sad break; 200*297f4619Sad case 'c': 201*297f4619Sad cflag = 1; 202*297f4619Sad break; 203*297f4619Sad case 'e': 204*297f4619Sad listnames(eventnames); 205*297f4619Sad break; 206*297f4619Sad case 'l': 207*297f4619Sad lflag = 1; 208*297f4619Sad break; 209*297f4619Sad case 'o': 210*297f4619Sad outf = optarg; 211*297f4619Sad break; 212*297f4619Sad case 'p': 213*297f4619Sad pflag = 1; 214*297f4619Sad break; 215*297f4619Sad case 's': 216*297f4619Sad sflag = 1; 217*297f4619Sad break; 218*297f4619Sad case 't': 219*297f4619Sad listnames(locknames); 220*297f4619Sad break; 221*297f4619Sad default: 222*297f4619Sad usage(); 223*297f4619Sad } 224*297f4619Sad argc -= optind; 225*297f4619Sad argv += optind; 226*297f4619Sad 227*297f4619Sad if (*argv == NULL) 228*297f4619Sad usage(); 229*297f4619Sad 230*297f4619Sad if (outf) { 231*297f4619Sad if ((fd = open(outf, O_WRONLY | O_CREAT, 0600)) == -1) 232*297f4619Sad err(EXIT_FAILURE, "opening %s", outf); 233*297f4619Sad outfp = fdopen(fd, "w"); 234*297f4619Sad } else 235*297f4619Sad outfp = stdout; 236*297f4619Sad 237*297f4619Sad /* 238*297f4619Sad * Find the name list for resolving symbol names, and load it into 239*297f4619Sad * memory. 240*297f4619Sad */ 241*297f4619Sad if (nlistf == NULL) { 242*297f4619Sad nlfd = open(_PATH_KSYMS, O_RDONLY); 243*297f4619Sad nlistf = getbootfile(); 244*297f4619Sad } else 245*297f4619Sad nlfd = -1; 246*297f4619Sad if (nlfd == -1) { 247*297f4619Sad if ((nlfd = open(nlistf, O_RDONLY)) < 0) 248*297f4619Sad err(EXIT_FAILURE, "cannot open " _PATH_KSYMS " or %s", 249*297f4619Sad nlistf); 250*297f4619Sad } 251*297f4619Sad if (loadsym32(nlfd) != 0) { 252*297f4619Sad if (loadsym64(nlfd) != 0) 253*297f4619Sad errx(EXIT_FAILURE, "unable to load symbol table"); 254*297f4619Sad bin64 = 1; 255*297f4619Sad } 256*297f4619Sad close(nlfd); 257*297f4619Sad 258*297f4619Sad memset(&le, 0, sizeof(le)); 259*297f4619Sad le.le_nbufs = nbufs; 260*297f4619Sad 261*297f4619Sad /* 262*297f4619Sad * Set up initial filtering. 263*297f4619Sad */ 264*297f4619Sad if (lockname != NULL) { 265*297f4619Sad findsym(LOCK_BYNAME, lockname, &le.le_lock, NULL); 266*297f4619Sad le.le_flags |= LE_ONE_LOCK; 267*297f4619Sad } 268*297f4619Sad if (!lflag) 269*297f4619Sad le.le_flags |= LE_CALLSITE; 270*297f4619Sad if (funcname != NULL) { 271*297f4619Sad if (lflag) 272*297f4619Sad usage(); 273*297f4619Sad findsym(FUNC_BYNAME, funcname, &le.le_csstart, &le.le_csend); 274*297f4619Sad le.le_flags |= LE_ONE_CALLSITE; 275*297f4619Sad } 276*297f4619Sad le.le_mask = (eventtype & LB_EVENT_MASK) | (locktype & LB_LOCK_MASK); 277*297f4619Sad 278*297f4619Sad /* 279*297f4619Sad * Start tracing. 280*297f4619Sad */ 281*297f4619Sad if ((lsfd = open(_PATH_DEV_LOCKSTAT, O_RDONLY)) < 0) 282*297f4619Sad err(EXIT_FAILURE, "cannot open " _PATH_DEV_LOCKSTAT); 283*297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_GVERSION, &ch) < 0) 284*297f4619Sad err(EXIT_FAILURE, "ioctl"); 285*297f4619Sad if (ch != LS_VERSION) 286*297f4619Sad errx(EXIT_FAILURE, "incompatible lockstat interface version"); 287*297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_ENABLE, &le)) 288*297f4619Sad err(EXIT_FAILURE, "cannot enable tracing"); 289*297f4619Sad 290*297f4619Sad /* 291*297f4619Sad * Execute the traced program. 292*297f4619Sad */ 293*297f4619Sad spawn(argc, argv); 294*297f4619Sad 295*297f4619Sad /* 296*297f4619Sad * Stop tracing, and read the trace buffers from the kernel. 297*297f4619Sad */ 298*297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_DISABLE, &ld) == -1) { 299*297f4619Sad if (errno == EOVERFLOW) { 300*297f4619Sad warnx("overflowed available kernel trace buffers"); 301*297f4619Sad exit(EXIT_FAILURE); 302*297f4619Sad } 303*297f4619Sad err(EXIT_FAILURE, "cannot disable tracing"); 304*297f4619Sad } 305*297f4619Sad if ((bufs = malloc(ld.ld_size)) == NULL) 306*297f4619Sad err(EXIT_FAILURE, "cannot allocate memory for user buffers"); 307*297f4619Sad if (read(lsfd, bufs, ld.ld_size) != ld.ld_size) 308*297f4619Sad err(EXIT_FAILURE, "reading from " _PATH_DEV_LOCKSTAT); 309*297f4619Sad if (close(lsfd)) 310*297f4619Sad err(EXIT_FAILURE, "close(" _PATH_DEV_LOCKSTAT ")"); 311*297f4619Sad 312*297f4619Sad /* 313*297f4619Sad * Figure out how to scale the results, and build the lists. For 314*297f4619Sad * internal use we convert all times from CPU frequency based to 315*297f4619Sad * picoseconds, and values are eventually displayed in ms. 316*297f4619Sad */ 317*297f4619Sad for (i = 0; i < sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0]); i++) 318*297f4619Sad if (ld.ld_freq[i] != 0) 319*297f4619Sad cpuscale[i] = PICO / ld.ld_freq[i]; 320*297f4619Sad ms = ld.ld_time.tv_sec * MILLI + ld.ld_time.tv_nsec / MICRO; 321*297f4619Sad if (pflag) 322*297f4619Sad cscale = 1.0 / ncpu(); 323*297f4619Sad else 324*297f4619Sad cscale = 1.0; 325*297f4619Sad cscale *= (sflag ? MILLI / ms : 1.0); 326*297f4619Sad tscale = cscale / NANO; 327*297f4619Sad nbufs = (int)(ld.ld_size / sizeof(lsbuf_t)); 328*297f4619Sad makelists(); 329*297f4619Sad 330*297f4619Sad /* 331*297f4619Sad * Display the results. 332*297f4619Sad */ 333*297f4619Sad fprintf(outfp, "Elapsed time: %.2f seconds.", ms / MILLI); 334*297f4619Sad if (sflag || pflag) { 335*297f4619Sad fprintf(outfp, " Displaying "); 336*297f4619Sad if (pflag) 337*297f4619Sad fprintf(outfp, "per-CPU "); 338*297f4619Sad if (sflag) 339*297f4619Sad fprintf(outfp, "per-second "); 340*297f4619Sad fprintf(outfp, "averages."); 341*297f4619Sad } 342*297f4619Sad putc('\n', outfp); 343*297f4619Sad 344*297f4619Sad for (name = alltypes; name->name != NULL; name++) { 345*297f4619Sad if (eventtype != -1 && 346*297f4619Sad (name->mask & LB_EVENT_MASK) != eventtype) 347*297f4619Sad continue; 348*297f4619Sad if (locktype != -1 && 349*297f4619Sad (name->mask & LB_LOCK_MASK) != locktype) 350*297f4619Sad continue; 351*297f4619Sad 352*297f4619Sad display(name->mask, name->name); 353*297f4619Sad } 354*297f4619Sad 355*297f4619Sad if (displayed == 0) 356*297f4619Sad fprintf(outfp, "None of the selected events were recorded.\n"); 357*297f4619Sad exit(EXIT_SUCCESS); 358*297f4619Sad } 359*297f4619Sad 360*297f4619Sad void 361*297f4619Sad usage(void) 362*297f4619Sad { 363*297f4619Sad 364*297f4619Sad fprintf(stderr, 365*297f4619Sad "%s: usage:\n" 366*297f4619Sad "%s [options] <command>\n\n" 367*297f4619Sad "-F func\t\tlimit trace to one function\n" 368*297f4619Sad "-E evt\t\tdisplay only one type of event\n" 369*297f4619Sad "-L lock\t\tlimit trace to one lock (name, or address)\n" 370*297f4619Sad "-N nlist\tspecify name list file\n" 371*297f4619Sad "-T type\t\tdisplay only one type of lock\n" 372*297f4619Sad "-b nbuf\t\tset number of event buffers to allocate\n" 373*297f4619Sad "-c\t\treport percentage of total events by count, not time\n" 374*297f4619Sad "-e\t\tlist event types\n" 375*297f4619Sad "-l\t\ttrace only by lock\n" 376*297f4619Sad "-o file\t\tsend output to named file, not stdout\n" 377*297f4619Sad "-p\t\tshow average count/time per CPU, not total\n" 378*297f4619Sad "-s\t\tshow average count/time per second, not total\n" 379*297f4619Sad "-t\t\tlist lock types\n", 380*297f4619Sad getprogname(), getprogname()); 381*297f4619Sad 382*297f4619Sad exit(EXIT_FAILURE); 383*297f4619Sad } 384*297f4619Sad 385*297f4619Sad void 386*297f4619Sad nullsig(int junk) 387*297f4619Sad { 388*297f4619Sad 389*297f4619Sad (void)junk; 390*297f4619Sad } 391*297f4619Sad 392*297f4619Sad void 393*297f4619Sad listnames(const name_t *name) 394*297f4619Sad { 395*297f4619Sad 396*297f4619Sad for (; name->name != NULL; name++) 397*297f4619Sad printf("%s\n", name->name); 398*297f4619Sad 399*297f4619Sad exit(EXIT_SUCCESS); 400*297f4619Sad } 401*297f4619Sad 402*297f4619Sad int 403*297f4619Sad matchname(const name_t *name, const char *string) 404*297f4619Sad { 405*297f4619Sad 406*297f4619Sad for (; name->name != NULL; name++) 407*297f4619Sad if (strcasecmp(name->name, string) == 0) 408*297f4619Sad return name->mask; 409*297f4619Sad 410*297f4619Sad warnx("unknown type `%s'", string); 411*297f4619Sad usage(); 412*297f4619Sad return 0; 413*297f4619Sad } 414*297f4619Sad 415*297f4619Sad /* 416*297f4619Sad * Return the number of CPUs in the running system. 417*297f4619Sad */ 418*297f4619Sad int 419*297f4619Sad ncpu(void) 420*297f4619Sad { 421*297f4619Sad int rv, mib[2]; 422*297f4619Sad size_t varlen; 423*297f4619Sad 424*297f4619Sad mib[0] = CTL_HW; 425*297f4619Sad mib[1] = HW_NCPU; 426*297f4619Sad varlen = sizeof(rv); 427*297f4619Sad if (sysctl(mib, 2, &rv, &varlen, NULL, (size_t)0) < 0) 428*297f4619Sad rv = 1; 429*297f4619Sad 430*297f4619Sad return (rv); 431*297f4619Sad } 432*297f4619Sad 433*297f4619Sad /* 434*297f4619Sad * Call into the ELF parser and look up a symbol by name or by address. 435*297f4619Sad */ 436*297f4619Sad void 437*297f4619Sad findsym(findsym_t find, char *name, uintptr_t *start, uintptr_t *end) 438*297f4619Sad { 439*297f4619Sad uintptr_t tend; 440*297f4619Sad char *p; 441*297f4619Sad int rv; 442*297f4619Sad 443*297f4619Sad if (end == NULL) 444*297f4619Sad end = &tend; 445*297f4619Sad 446*297f4619Sad if (find == LOCK_BYNAME) { 447*297f4619Sad if (isdigit((u_int)name[0])) { 448*297f4619Sad *start = (uintptr_t)strtoul(name, &p, 0); 449*297f4619Sad if (*p == '\0') 450*297f4619Sad return; 451*297f4619Sad } 452*297f4619Sad } 453*297f4619Sad 454*297f4619Sad if (bin64) 455*297f4619Sad rv = findsym64(find, name, start, end); 456*297f4619Sad else 457*297f4619Sad rv = findsym32(find, name, start, end); 458*297f4619Sad 459*297f4619Sad if (find == FUNC_BYNAME || find == LOCK_BYNAME) { 460*297f4619Sad if (rv == -1) 461*297f4619Sad errx(EXIT_FAILURE, "unable to find symbol `%s'", name); 462*297f4619Sad return; 463*297f4619Sad } 464*297f4619Sad 465*297f4619Sad if (rv == -1) 466*297f4619Sad sprintf(name, "0x%016lx", (long)*start); 467*297f4619Sad } 468*297f4619Sad 469*297f4619Sad /* 470*297f4619Sad * Fork off the child process and wait for it to complete. We trap SIGINT 471*297f4619Sad * so that the caller can use Ctrl-C to stop tracing early and still get 472*297f4619Sad * useful results. 473*297f4619Sad */ 474*297f4619Sad void 475*297f4619Sad spawn(int argc, char **argv) 476*297f4619Sad { 477*297f4619Sad pid_t pid; 478*297f4619Sad 479*297f4619Sad switch (pid = fork()) { 480*297f4619Sad case 0: 481*297f4619Sad close(lsfd); 482*297f4619Sad if (execvp(argv[0], argv) == -1) 483*297f4619Sad err(EXIT_FAILURE, "cannot exec"); 484*297f4619Sad break; 485*297f4619Sad case -1: 486*297f4619Sad err(EXIT_FAILURE, "cannot fork to exec"); 487*297f4619Sad break; 488*297f4619Sad default: 489*297f4619Sad signal(SIGINT, nullsig); 490*297f4619Sad wait(NULL); 491*297f4619Sad signal(SIGINT, SIG_DFL); 492*297f4619Sad break; 493*297f4619Sad } 494*297f4619Sad } 495*297f4619Sad 496*297f4619Sad /* 497*297f4619Sad * From the kernel supplied data, construct two dimensional lists of locks 498*297f4619Sad * and event buffers, indexed by lock type. 499*297f4619Sad */ 500*297f4619Sad void 501*297f4619Sad makelists(void) 502*297f4619Sad { 503*297f4619Sad lsbuf_t *lb, *lb2, *max; 504*297f4619Sad int i, type; 505*297f4619Sad lock_t *l; 506*297f4619Sad 507*297f4619Sad for (i = 0; i < LB_NLOCK; i++) 508*297f4619Sad TAILQ_INIT(&locklist[i]); 509*297f4619Sad 510*297f4619Sad for (lb = bufs, max = bufs + nbufs; lb < max; lb++) { 511*297f4619Sad if (lb->lb_flags == 0) 512*297f4619Sad continue; 513*297f4619Sad 514*297f4619Sad /* 515*297f4619Sad * Look for a record descibing this lock, and allocate a 516*297f4619Sad * new one if needed. 517*297f4619Sad */ 518*297f4619Sad type = ((lb->lb_flags & LB_LOCK_MASK) >> LB_LOCK_SHIFT) - 1; 519*297f4619Sad TAILQ_FOREACH(l, &locklist[type], chain) { 520*297f4619Sad if (l->lock == lb->lb_lock) 521*297f4619Sad break; 522*297f4619Sad } 523*297f4619Sad if (l == NULL) { 524*297f4619Sad l = (lock_t *)malloc(sizeof(*l)); 525*297f4619Sad l->flags = lb->lb_flags; 526*297f4619Sad l->lock = lb->lb_lock; 527*297f4619Sad memset(&l->counts, 0, sizeof(l->counts)); 528*297f4619Sad memset(&l->times, 0, sizeof(l->times)); 529*297f4619Sad TAILQ_INIT(&l->bufs); 530*297f4619Sad TAILQ_INSERT_TAIL(&locklist[type], l, chain); 531*297f4619Sad } 532*297f4619Sad 533*297f4619Sad /* 534*297f4619Sad * Scale the time values per buffer and summarise 535*297f4619Sad * times+counts per lock. 536*297f4619Sad */ 537*297f4619Sad for (i = 0; i < LB_NEVENT; i++) { 538*297f4619Sad lb->lb_times[i] *= cpuscale[lb->lb_cpu]; 539*297f4619Sad l->counts[i] += lb->lb_counts[i]; 540*297f4619Sad l->times[i] += lb->lb_times[i]; 541*297f4619Sad } 542*297f4619Sad 543*297f4619Sad /* 544*297f4619Sad * Merge same lock+callsite pairs from multiple CPUs 545*297f4619Sad * together. 546*297f4619Sad */ 547*297f4619Sad TAILQ_FOREACH(lb2, &l->bufs, lb_chain.tailq) { 548*297f4619Sad if (lb->lb_callsite == lb2->lb_callsite) 549*297f4619Sad break; 550*297f4619Sad } 551*297f4619Sad if (lb2 != NULL) { 552*297f4619Sad for (i = 0; i < LB_NEVENT; i++) { 553*297f4619Sad lb2->lb_counts[i] += lb->lb_counts[i]; 554*297f4619Sad lb2->lb_times[i] += lb->lb_times[i]; 555*297f4619Sad } 556*297f4619Sad } else 557*297f4619Sad TAILQ_INSERT_HEAD(&l->bufs, lb, lb_chain.tailq); 558*297f4619Sad } 559*297f4619Sad } 560*297f4619Sad 561*297f4619Sad /* 562*297f4619Sad * Re-sort one list of locks / lock buffers by event type. 563*297f4619Sad */ 564*297f4619Sad void 565*297f4619Sad resort(int type, int event) 566*297f4619Sad { 567*297f4619Sad lsbuf_t *lb, *lb2; 568*297f4619Sad locklist_t llist; 569*297f4619Sad buflist_t blist; 570*297f4619Sad lock_t *l, *l2; 571*297f4619Sad 572*297f4619Sad TAILQ_INIT(&llist); 573*297f4619Sad while ((l = TAILQ_FIRST(&locklist[type])) != NULL) { 574*297f4619Sad TAILQ_REMOVE(&locklist[type], l, chain); 575*297f4619Sad 576*297f4619Sad /* 577*297f4619Sad * Sort the buffers into the per-lock list. 578*297f4619Sad */ 579*297f4619Sad TAILQ_INIT(&blist); 580*297f4619Sad while ((lb = TAILQ_FIRST(&l->bufs)) != NULL) { 581*297f4619Sad TAILQ_REMOVE(&l->bufs, lb, lb_chain.tailq); 582*297f4619Sad 583*297f4619Sad lb2 = TAILQ_FIRST(&blist); 584*297f4619Sad while (lb2 != NULL) { 585*297f4619Sad if (cflag) { 586*297f4619Sad if (lb->lb_counts[event] > 587*297f4619Sad lb2->lb_counts[event]) 588*297f4619Sad break; 589*297f4619Sad } else if (lb->lb_times[event] > 590*297f4619Sad lb2->lb_times[event]) 591*297f4619Sad break; 592*297f4619Sad lb2 = TAILQ_NEXT(lb2, lb_chain.tailq); 593*297f4619Sad } 594*297f4619Sad if (lb2 == NULL) 595*297f4619Sad TAILQ_INSERT_TAIL(&blist, lb, lb_chain.tailq); 596*297f4619Sad else 597*297f4619Sad TAILQ_INSERT_BEFORE(lb2, lb, lb_chain.tailq); 598*297f4619Sad } 599*297f4619Sad l->bufs = blist; 600*297f4619Sad 601*297f4619Sad /* 602*297f4619Sad * Sort this lock into the per-type list, based on the 603*297f4619Sad * totals per lock. 604*297f4619Sad */ 605*297f4619Sad l2 = TAILQ_FIRST(&llist); 606*297f4619Sad while (l2 != NULL) { 607*297f4619Sad if (cflag) { 608*297f4619Sad if (l->counts[event] > l2->counts[event]) 609*297f4619Sad break; 610*297f4619Sad } else if (l->times[event] > l2->times[event]) 611*297f4619Sad break; 612*297f4619Sad l2 = TAILQ_NEXT(l2, chain); 613*297f4619Sad } 614*297f4619Sad if (l2 == NULL) 615*297f4619Sad TAILQ_INSERT_TAIL(&llist, l, chain); 616*297f4619Sad else 617*297f4619Sad TAILQ_INSERT_BEFORE(l2, l, chain); 618*297f4619Sad } 619*297f4619Sad locklist[type] = llist; 620*297f4619Sad } 621*297f4619Sad 622*297f4619Sad /* 623*297f4619Sad * Display a summary table for one lock type / event type pair. 624*297f4619Sad */ 625*297f4619Sad void 626*297f4619Sad display(int mask, const char *name) 627*297f4619Sad { 628*297f4619Sad lock_t *l; 629*297f4619Sad lsbuf_t *lb; 630*297f4619Sad int event, type; 631*297f4619Sad double pcscale, metric; 632*297f4619Sad char lname[256], fname[256]; 633*297f4619Sad 634*297f4619Sad type = ((mask & LB_LOCK_MASK) >> LB_LOCK_SHIFT) - 1; 635*297f4619Sad if (TAILQ_FIRST(&locklist[type]) == NULL) 636*297f4619Sad return; 637*297f4619Sad 638*297f4619Sad event = (mask & LB_EVENT_MASK) - 1; 639*297f4619Sad resort(type, event); 640*297f4619Sad 641*297f4619Sad fprintf(outfp, "\n-- %s\n\n" 642*297f4619Sad "Total%% Count Time/ms Lock Caller\n" 643*297f4619Sad "------ ------- --------- ------------------ ----------------------------------\n", 644*297f4619Sad name); 645*297f4619Sad 646*297f4619Sad /* 647*297f4619Sad * Sum up all events for this type of lock + event. 648*297f4619Sad */ 649*297f4619Sad pcscale = 0; 650*297f4619Sad TAILQ_FOREACH(l, &locklist[type], chain) { 651*297f4619Sad if (cflag) 652*297f4619Sad pcscale += l->counts[event]; 653*297f4619Sad else 654*297f4619Sad pcscale += l->times[event]; 655*297f4619Sad displayed++; 656*297f4619Sad } 657*297f4619Sad if (pcscale == 0) 658*297f4619Sad pcscale = 100; 659*297f4619Sad else 660*297f4619Sad pcscale = (100.0 / pcscale); 661*297f4619Sad 662*297f4619Sad /* 663*297f4619Sad * For each lock, print a summary total, followed by a breakdown by 664*297f4619Sad * caller. 665*297f4619Sad */ 666*297f4619Sad TAILQ_FOREACH(l, &locklist[type], chain) { 667*297f4619Sad if (cflag) 668*297f4619Sad metric = l->counts[event]; 669*297f4619Sad else 670*297f4619Sad metric = l->times[event]; 671*297f4619Sad metric *= pcscale; 672*297f4619Sad 673*297f4619Sad findsym(LOCK_BYADDR, lname, &l->lock, NULL); 674*297f4619Sad 675*297f4619Sad fprintf(outfp, "%6.2f %7d %9.2f %-18s <all>\n", metric, 676*297f4619Sad (int)(l->counts[event] * cscale), 677*297f4619Sad l->times[event] * tscale, lname); 678*297f4619Sad 679*297f4619Sad if (lflag) 680*297f4619Sad continue; 681*297f4619Sad 682*297f4619Sad TAILQ_FOREACH(lb, &l->bufs, lb_chain.tailq) { 683*297f4619Sad if (cflag) 684*297f4619Sad metric = lb->lb_counts[event]; 685*297f4619Sad else 686*297f4619Sad metric = lb->lb_times[event]; 687*297f4619Sad metric *= pcscale; 688*297f4619Sad 689*297f4619Sad findsym(LOCK_BYADDR, lname, &lb->lb_lock, NULL); 690*297f4619Sad findsym(FUNC_BYADDR, fname, &lb->lb_callsite, NULL); 691*297f4619Sad fprintf(outfp, "%6.2f %7d %9.2f %-18s %s\n", metric, 692*297f4619Sad (int)(lb->lb_counts[event] * cscale), 693*297f4619Sad lb->lb_times[event] * tscale, 694*297f4619Sad lname, fname); 695*297f4619Sad } 696*297f4619Sad } 697*297f4619Sad } 698