1*12793652Sad /* $NetBSD: main.c,v 1.5 2006/11/08 23:12:57 ad Exp $ */ 2297f4619Sad 3297f4619Sad /*- 4297f4619Sad * Copyright (c) 2006 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 * 42297f4619Sad * - Need better analysis and tracking of events. 43297f4619Sad * - Should be binary format agnostic, but given that we're likely to be using 44297f4619Sad * ELF for quite a while that's not a big problem. 45297f4619Sad * - Shouldn't have to parse the namelist here. We should use something like 46297f4619Sad * FreeBSD's libelf. 47297f4619Sad * - The way the namelist is searched sucks, is it worth doing something 48297f4619Sad * better? 49d8323c46Sad * - Might be nice to record events and replay later, like ktrace/kdump. 50297f4619Sad */ 51297f4619Sad 52297f4619Sad #include <sys/cdefs.h> 53297f4619Sad #ifndef lint 54*12793652Sad __RCSID("$NetBSD: main.c,v 1.5 2006/11/08 23:12:57 ad Exp $"); 55297f4619Sad #endif /* not lint */ 56297f4619Sad 57297f4619Sad #include <sys/types.h> 58297f4619Sad #include <sys/param.h> 59297f4619Sad #include <sys/time.h> 60297f4619Sad #include <sys/fcntl.h> 61297f4619Sad #include <sys/ioctl.h> 62297f4619Sad #include <sys/wait.h> 63297f4619Sad #include <sys/signal.h> 64297f4619Sad #include <sys/sysctl.h> 65297f4619Sad 6696d4a987Sad #include <dev/lockstat.h> 6796d4a987Sad 68297f4619Sad #include <stdio.h> 69297f4619Sad #include <stdlib.h> 70297f4619Sad #include <string.h> 71297f4619Sad #include <limits.h> 72297f4619Sad #include <unistd.h> 73297f4619Sad #include <err.h> 74297f4619Sad #include <paths.h> 75297f4619Sad #include <util.h> 76297f4619Sad #include <ctype.h> 77297f4619Sad #include <errno.h> 78297f4619Sad 79297f4619Sad #include "extern.h" 80297f4619Sad 81297f4619Sad #define _PATH_DEV_LOCKSTAT "/dev/lockstat" 82297f4619Sad 83297f4619Sad #define MILLI 1000.0 84297f4619Sad #define MICRO 1000000.0 85297f4619Sad #define NANO 1000000000.0 86297f4619Sad #define PICO 1000000000000.0 87297f4619Sad 88297f4619Sad TAILQ_HEAD(lock_head, lockstruct); 89297f4619Sad typedef struct lock_head locklist_t; 90297f4619Sad TAILQ_HEAD(buf_head, lsbuf); 91297f4619Sad typedef struct buf_head buflist_t; 92297f4619Sad 93297f4619Sad typedef struct lockstruct { 94297f4619Sad TAILQ_ENTRY(lockstruct) chain; 95297f4619Sad buflist_t bufs; 96297f4619Sad uintptr_t lock; 97297f4619Sad double times[LB_NEVENT]; 98297f4619Sad uint32_t counts[LB_NEVENT]; 99297f4619Sad u_int flags; 100*12793652Sad u_int nbufs; 101297f4619Sad } lock_t; 102297f4619Sad 103297f4619Sad typedef struct name { 104297f4619Sad const char *name; 105297f4619Sad int mask; 106297f4619Sad } name_t; 107297f4619Sad 108297f4619Sad const name_t locknames[] = { 109297f4619Sad { "adaptive_mutex", LB_ADAPTIVE_MUTEX }, 110297f4619Sad { "adaptive_rwlock", LB_ADAPTIVE_RWLOCK }, 111297f4619Sad { "spin_mutex", LB_SPIN_MUTEX }, 112297f4619Sad { "spin_rwlock", LB_SPIN_RWLOCK }, 113297f4619Sad { "lockmgr", LB_LOCKMGR }, 114297f4619Sad { NULL, 0 } 115297f4619Sad }; 116297f4619Sad 117297f4619Sad const name_t eventnames[] = { 118297f4619Sad { "spin", LB_SPIN }, 119297f4619Sad { "sleep", LB_SLEEP }, 120297f4619Sad { NULL, 0 }, 121297f4619Sad }; 122297f4619Sad 123297f4619Sad const name_t alltypes[] = { 124297f4619Sad { "Adaptive mutex spin", LB_ADAPTIVE_MUTEX | LB_SPIN }, 125297f4619Sad { "Adaptive mutex sleep", LB_ADAPTIVE_MUTEX | LB_SLEEP }, 126297f4619Sad { "Adaptive RW lock spin", LB_ADAPTIVE_RWLOCK | LB_SPIN }, 127d8323c46Sad { "Adaptive RW lock sleep", LB_ADAPTIVE_RWLOCK | LB_SLEEP }, 128297f4619Sad { "Spin mutex spin", LB_SPIN_MUTEX | LB_SPIN }, 129297f4619Sad { "lockmgr sleep", LB_LOCKMGR | LB_SLEEP }, 130*12793652Sad #ifdef LB_KERNEL_LOCK 131*12793652Sad /* XXX newlock2 */ 132*12793652Sad { "Kernel lock spin", LB_KERNEL_LOCK | LB_SPIN }, 133*12793652Sad #endif 134297f4619Sad { NULL, 0 } 135297f4619Sad }; 136297f4619Sad 137*12793652Sad locklist_t locklist[LB_NLOCK >> LB_LOCK_SHIFT]; 138297f4619Sad 139297f4619Sad lsbuf_t *bufs; 140297f4619Sad lsdisable_t ld; 141297f4619Sad int lflag; 142297f4619Sad int nbufs; 143297f4619Sad int cflag; 144297f4619Sad int lsfd; 145297f4619Sad int displayed; 146297f4619Sad int bin64; 147297f4619Sad double tscale; 148297f4619Sad double cscale; 149297f4619Sad double cpuscale[sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0])]; 150297f4619Sad FILE *outfp; 151297f4619Sad 152297f4619Sad void findsym(findsym_t, char *, uintptr_t *, uintptr_t *); 153297f4619Sad void spawn(int, char **); 154297f4619Sad void display(int, const char *name); 155297f4619Sad void listnames(const name_t *); 156297f4619Sad int matchname(const name_t *, const char *); 157297f4619Sad void makelists(void); 158297f4619Sad void nullsig(int); 159297f4619Sad void usage(void); 160297f4619Sad void resort(int, int); 161297f4619Sad int ncpu(void); 162297f4619Sad 163297f4619Sad int 164297f4619Sad main(int argc, char **argv) 165297f4619Sad { 166297f4619Sad int eventtype, locktype, ch, nlfd, sflag, fd, i, pflag; 167297f4619Sad const char *nlistf, *outf; 168297f4619Sad char *lockname, *funcname; 169297f4619Sad const name_t *name; 170297f4619Sad lsenable_t le; 171297f4619Sad double ms; 172297f4619Sad char *p; 173297f4619Sad 174297f4619Sad nlistf = NULL; 175297f4619Sad outf = NULL; 176297f4619Sad lockname = NULL; 177297f4619Sad funcname = NULL; 178297f4619Sad eventtype = -1; 179297f4619Sad locktype = -1; 180297f4619Sad nbufs = 0; 181297f4619Sad sflag = 0; 182297f4619Sad pflag = 0; 183297f4619Sad 184297f4619Sad while ((ch = getopt(argc, argv, "E:F:L:M:N:T:b:ceflo:pst")) != -1) 185297f4619Sad switch (ch) { 186297f4619Sad case 'E': 187297f4619Sad eventtype = matchname(eventnames, optarg); 188297f4619Sad break; 189297f4619Sad case 'F': 190297f4619Sad funcname = optarg; 191297f4619Sad break; 192297f4619Sad case 'L': 193297f4619Sad lockname = optarg; 194297f4619Sad break; 195297f4619Sad case 'N': 196297f4619Sad nlistf = optarg; 197297f4619Sad break; 198297f4619Sad case 'T': 199297f4619Sad locktype = matchname(locknames, optarg); 200297f4619Sad break; 201297f4619Sad case 'b': 202297f4619Sad nbufs = (int)strtol(optarg, &p, 0); 203297f4619Sad if (!isdigit((u_int)*optarg) || *p != '\0') 204297f4619Sad usage(); 205297f4619Sad break; 206297f4619Sad case 'c': 207297f4619Sad cflag = 1; 208297f4619Sad break; 209297f4619Sad case 'e': 210297f4619Sad listnames(eventnames); 211297f4619Sad break; 212297f4619Sad case 'l': 213297f4619Sad lflag = 1; 214297f4619Sad break; 215297f4619Sad case 'o': 216297f4619Sad outf = optarg; 217297f4619Sad break; 218297f4619Sad case 'p': 219297f4619Sad pflag = 1; 220297f4619Sad break; 221297f4619Sad case 's': 222297f4619Sad sflag = 1; 223297f4619Sad break; 224297f4619Sad case 't': 225297f4619Sad listnames(locknames); 226297f4619Sad break; 227297f4619Sad default: 228297f4619Sad usage(); 229297f4619Sad } 230297f4619Sad argc -= optind; 231297f4619Sad argv += optind; 232297f4619Sad 233297f4619Sad if (*argv == NULL) 234297f4619Sad usage(); 235297f4619Sad 236297f4619Sad if (outf) { 237*12793652Sad fd = open(outf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 238*12793652Sad if (fd == -1) 239297f4619Sad err(EXIT_FAILURE, "opening %s", outf); 240297f4619Sad outfp = fdopen(fd, "w"); 241297f4619Sad } else 242297f4619Sad outfp = stdout; 243297f4619Sad 244297f4619Sad /* 245297f4619Sad * Find the name list for resolving symbol names, and load it into 246297f4619Sad * memory. 247297f4619Sad */ 248297f4619Sad if (nlistf == NULL) { 249297f4619Sad nlfd = open(_PATH_KSYMS, O_RDONLY); 250297f4619Sad nlistf = getbootfile(); 251297f4619Sad } else 252297f4619Sad nlfd = -1; 253297f4619Sad if (nlfd == -1) { 254297f4619Sad if ((nlfd = open(nlistf, O_RDONLY)) < 0) 255297f4619Sad err(EXIT_FAILURE, "cannot open " _PATH_KSYMS " or %s", 256297f4619Sad nlistf); 257297f4619Sad } 258297f4619Sad if (loadsym32(nlfd) != 0) { 259297f4619Sad if (loadsym64(nlfd) != 0) 260297f4619Sad errx(EXIT_FAILURE, "unable to load symbol table"); 261297f4619Sad bin64 = 1; 262297f4619Sad } 263297f4619Sad close(nlfd); 264297f4619Sad 265297f4619Sad memset(&le, 0, sizeof(le)); 266297f4619Sad le.le_nbufs = nbufs; 267297f4619Sad 268297f4619Sad /* 269297f4619Sad * Set up initial filtering. 270297f4619Sad */ 271297f4619Sad if (lockname != NULL) { 272297f4619Sad findsym(LOCK_BYNAME, lockname, &le.le_lock, NULL); 273297f4619Sad le.le_flags |= LE_ONE_LOCK; 274297f4619Sad } 275297f4619Sad if (!lflag) 276297f4619Sad le.le_flags |= LE_CALLSITE; 277297f4619Sad if (funcname != NULL) { 278297f4619Sad if (lflag) 279297f4619Sad usage(); 280297f4619Sad findsym(FUNC_BYNAME, funcname, &le.le_csstart, &le.le_csend); 281297f4619Sad le.le_flags |= LE_ONE_CALLSITE; 282297f4619Sad } 283297f4619Sad le.le_mask = (eventtype & LB_EVENT_MASK) | (locktype & LB_LOCK_MASK); 284297f4619Sad 285297f4619Sad /* 286297f4619Sad * Start tracing. 287297f4619Sad */ 288297f4619Sad if ((lsfd = open(_PATH_DEV_LOCKSTAT, O_RDONLY)) < 0) 289297f4619Sad err(EXIT_FAILURE, "cannot open " _PATH_DEV_LOCKSTAT); 290297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_GVERSION, &ch) < 0) 291297f4619Sad err(EXIT_FAILURE, "ioctl"); 292297f4619Sad if (ch != LS_VERSION) 293297f4619Sad errx(EXIT_FAILURE, "incompatible lockstat interface version"); 294297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_ENABLE, &le)) 295297f4619Sad err(EXIT_FAILURE, "cannot enable tracing"); 296297f4619Sad 297297f4619Sad /* 298297f4619Sad * Execute the traced program. 299297f4619Sad */ 300297f4619Sad spawn(argc, argv); 301297f4619Sad 302297f4619Sad /* 303297f4619Sad * Stop tracing, and read the trace buffers from the kernel. 304297f4619Sad */ 305297f4619Sad if (ioctl(lsfd, IOC_LOCKSTAT_DISABLE, &ld) == -1) { 306297f4619Sad if (errno == EOVERFLOW) { 307297f4619Sad warnx("overflowed available kernel trace buffers"); 308297f4619Sad exit(EXIT_FAILURE); 309297f4619Sad } 310297f4619Sad err(EXIT_FAILURE, "cannot disable tracing"); 311297f4619Sad } 312297f4619Sad if ((bufs = malloc(ld.ld_size)) == NULL) 313297f4619Sad err(EXIT_FAILURE, "cannot allocate memory for user buffers"); 314297f4619Sad if (read(lsfd, bufs, ld.ld_size) != ld.ld_size) 315297f4619Sad err(EXIT_FAILURE, "reading from " _PATH_DEV_LOCKSTAT); 316297f4619Sad if (close(lsfd)) 317297f4619Sad err(EXIT_FAILURE, "close(" _PATH_DEV_LOCKSTAT ")"); 318297f4619Sad 319297f4619Sad /* 320297f4619Sad * Figure out how to scale the results, and build the lists. For 321297f4619Sad * internal use we convert all times from CPU frequency based to 322297f4619Sad * picoseconds, and values are eventually displayed in ms. 323297f4619Sad */ 324297f4619Sad for (i = 0; i < sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0]); i++) 325297f4619Sad if (ld.ld_freq[i] != 0) 326297f4619Sad cpuscale[i] = PICO / ld.ld_freq[i]; 327297f4619Sad ms = ld.ld_time.tv_sec * MILLI + ld.ld_time.tv_nsec / MICRO; 328297f4619Sad if (pflag) 329297f4619Sad cscale = 1.0 / ncpu(); 330297f4619Sad else 331297f4619Sad cscale = 1.0; 332297f4619Sad cscale *= (sflag ? MILLI / ms : 1.0); 333297f4619Sad tscale = cscale / NANO; 334297f4619Sad nbufs = (int)(ld.ld_size / sizeof(lsbuf_t)); 335297f4619Sad makelists(); 336297f4619Sad 337297f4619Sad /* 338297f4619Sad * Display the results. 339297f4619Sad */ 340297f4619Sad fprintf(outfp, "Elapsed time: %.2f seconds.", ms / MILLI); 341297f4619Sad if (sflag || pflag) { 342297f4619Sad fprintf(outfp, " Displaying "); 343297f4619Sad if (pflag) 344297f4619Sad fprintf(outfp, "per-CPU "); 345297f4619Sad if (sflag) 346297f4619Sad fprintf(outfp, "per-second "); 347297f4619Sad fprintf(outfp, "averages."); 348297f4619Sad } 349297f4619Sad putc('\n', outfp); 350297f4619Sad 351297f4619Sad for (name = alltypes; name->name != NULL; name++) { 352297f4619Sad if (eventtype != -1 && 353297f4619Sad (name->mask & LB_EVENT_MASK) != eventtype) 354297f4619Sad continue; 355297f4619Sad if (locktype != -1 && 356297f4619Sad (name->mask & LB_LOCK_MASK) != locktype) 357297f4619Sad continue; 358297f4619Sad 359297f4619Sad display(name->mask, name->name); 360297f4619Sad } 361297f4619Sad 362297f4619Sad if (displayed == 0) 363297f4619Sad fprintf(outfp, "None of the selected events were recorded.\n"); 364297f4619Sad exit(EXIT_SUCCESS); 365297f4619Sad } 366297f4619Sad 367297f4619Sad void 368297f4619Sad usage(void) 369297f4619Sad { 370297f4619Sad 371297f4619Sad fprintf(stderr, 372297f4619Sad "%s: usage:\n" 373297f4619Sad "%s [options] <command>\n\n" 374297f4619Sad "-b nbuf\t\tset number of event buffers to allocate\n" 375297f4619Sad "-c\t\treport percentage of total events by count, not time\n" 37697c200c8Swiz "-E evt\t\tdisplay only one type of event\n" 377297f4619Sad "-e\t\tlist event types\n" 37897c200c8Swiz "-F func\t\tlimit trace to one function\n" 37997c200c8Swiz "-L lock\t\tlimit trace to one lock (name, or address)\n" 380297f4619Sad "-l\t\ttrace only by lock\n" 38197c200c8Swiz "-N nlist\tspecify name list file\n" 382297f4619Sad "-o file\t\tsend output to named file, not stdout\n" 383297f4619Sad "-p\t\tshow average count/time per CPU, not total\n" 384297f4619Sad "-s\t\tshow average count/time per second, not total\n" 38597c200c8Swiz "-T type\t\tdisplay only one type of lock\n" 386297f4619Sad "-t\t\tlist lock types\n", 387297f4619Sad getprogname(), getprogname()); 388297f4619Sad 389297f4619Sad exit(EXIT_FAILURE); 390297f4619Sad } 391297f4619Sad 392297f4619Sad void 393297f4619Sad nullsig(int junk) 394297f4619Sad { 395297f4619Sad 396297f4619Sad (void)junk; 397297f4619Sad } 398297f4619Sad 399297f4619Sad void 400297f4619Sad listnames(const name_t *name) 401297f4619Sad { 402297f4619Sad 403297f4619Sad for (; name->name != NULL; name++) 404297f4619Sad printf("%s\n", name->name); 405297f4619Sad 406297f4619Sad exit(EXIT_SUCCESS); 407297f4619Sad } 408297f4619Sad 409297f4619Sad int 410297f4619Sad matchname(const name_t *name, const char *string) 411297f4619Sad { 412297f4619Sad 413297f4619Sad for (; name->name != NULL; name++) 414297f4619Sad if (strcasecmp(name->name, string) == 0) 415297f4619Sad return name->mask; 416297f4619Sad 417297f4619Sad warnx("unknown type `%s'", string); 418297f4619Sad usage(); 419297f4619Sad return 0; 420297f4619Sad } 421297f4619Sad 422297f4619Sad /* 423297f4619Sad * Return the number of CPUs in the running system. 424297f4619Sad */ 425297f4619Sad int 426297f4619Sad ncpu(void) 427297f4619Sad { 428297f4619Sad int rv, mib[2]; 429297f4619Sad size_t varlen; 430297f4619Sad 431297f4619Sad mib[0] = CTL_HW; 432297f4619Sad mib[1] = HW_NCPU; 433297f4619Sad varlen = sizeof(rv); 434297f4619Sad if (sysctl(mib, 2, &rv, &varlen, NULL, (size_t)0) < 0) 435297f4619Sad rv = 1; 436297f4619Sad 437297f4619Sad return (rv); 438297f4619Sad } 439297f4619Sad 440297f4619Sad /* 441297f4619Sad * Call into the ELF parser and look up a symbol by name or by address. 442297f4619Sad */ 443297f4619Sad void 444297f4619Sad findsym(findsym_t find, char *name, uintptr_t *start, uintptr_t *end) 445297f4619Sad { 446297f4619Sad uintptr_t tend; 447297f4619Sad char *p; 448297f4619Sad int rv; 449297f4619Sad 450297f4619Sad if (end == NULL) 451297f4619Sad end = &tend; 452297f4619Sad 453297f4619Sad if (find == LOCK_BYNAME) { 454297f4619Sad if (isdigit((u_int)name[0])) { 455297f4619Sad *start = (uintptr_t)strtoul(name, &p, 0); 456297f4619Sad if (*p == '\0') 457297f4619Sad return; 458297f4619Sad } 459297f4619Sad } 460297f4619Sad 461297f4619Sad if (bin64) 462297f4619Sad rv = findsym64(find, name, start, end); 463297f4619Sad else 464297f4619Sad rv = findsym32(find, name, start, end); 465297f4619Sad 466297f4619Sad if (find == FUNC_BYNAME || find == LOCK_BYNAME) { 467297f4619Sad if (rv == -1) 468297f4619Sad errx(EXIT_FAILURE, "unable to find symbol `%s'", name); 469297f4619Sad return; 470297f4619Sad } 471297f4619Sad 472297f4619Sad if (rv == -1) 473297f4619Sad sprintf(name, "0x%016lx", (long)*start); 474297f4619Sad } 475297f4619Sad 476297f4619Sad /* 477297f4619Sad * Fork off the child process and wait for it to complete. We trap SIGINT 478297f4619Sad * so that the caller can use Ctrl-C to stop tracing early and still get 479297f4619Sad * useful results. 480297f4619Sad */ 481297f4619Sad void 482297f4619Sad spawn(int argc, char **argv) 483297f4619Sad { 484297f4619Sad pid_t pid; 485297f4619Sad 486297f4619Sad switch (pid = fork()) { 487297f4619Sad case 0: 488297f4619Sad close(lsfd); 489297f4619Sad if (execvp(argv[0], argv) == -1) 490297f4619Sad err(EXIT_FAILURE, "cannot exec"); 491297f4619Sad break; 492297f4619Sad case -1: 493297f4619Sad err(EXIT_FAILURE, "cannot fork to exec"); 494297f4619Sad break; 495297f4619Sad default: 496297f4619Sad signal(SIGINT, nullsig); 497297f4619Sad wait(NULL); 498297f4619Sad signal(SIGINT, SIG_DFL); 499297f4619Sad break; 500297f4619Sad } 501297f4619Sad } 502297f4619Sad 503297f4619Sad /* 504297f4619Sad * From the kernel supplied data, construct two dimensional lists of locks 505297f4619Sad * and event buffers, indexed by lock type. 506297f4619Sad */ 507297f4619Sad void 508297f4619Sad makelists(void) 509297f4619Sad { 510297f4619Sad lsbuf_t *lb, *lb2, *max; 511297f4619Sad int i, type; 512297f4619Sad lock_t *l; 513297f4619Sad 514*12793652Sad for (i = 0; i < LB_NLOCK >> LB_LOCK_SHIFT; i++) 515297f4619Sad TAILQ_INIT(&locklist[i]); 516297f4619Sad 517297f4619Sad for (lb = bufs, max = bufs + nbufs; lb < max; lb++) { 518297f4619Sad if (lb->lb_flags == 0) 519297f4619Sad continue; 520297f4619Sad 521297f4619Sad /* 522297f4619Sad * Look for a record descibing this lock, and allocate a 523297f4619Sad * new one if needed. 524297f4619Sad */ 525297f4619Sad type = ((lb->lb_flags & LB_LOCK_MASK) >> LB_LOCK_SHIFT) - 1; 526297f4619Sad TAILQ_FOREACH(l, &locklist[type], chain) { 527297f4619Sad if (l->lock == lb->lb_lock) 528297f4619Sad break; 529297f4619Sad } 530297f4619Sad if (l == NULL) { 531297f4619Sad l = (lock_t *)malloc(sizeof(*l)); 532297f4619Sad l->flags = lb->lb_flags; 533297f4619Sad l->lock = lb->lb_lock; 534*12793652Sad l->nbufs = 0; 535297f4619Sad memset(&l->counts, 0, sizeof(l->counts)); 536297f4619Sad memset(&l->times, 0, sizeof(l->times)); 537297f4619Sad TAILQ_INIT(&l->bufs); 538297f4619Sad TAILQ_INSERT_TAIL(&locklist[type], l, chain); 539297f4619Sad } 540297f4619Sad 541297f4619Sad /* 542297f4619Sad * Scale the time values per buffer and summarise 543297f4619Sad * times+counts per lock. 544297f4619Sad */ 545297f4619Sad for (i = 0; i < LB_NEVENT; i++) { 546297f4619Sad lb->lb_times[i] *= cpuscale[lb->lb_cpu]; 547297f4619Sad l->counts[i] += lb->lb_counts[i]; 548297f4619Sad l->times[i] += lb->lb_times[i]; 549297f4619Sad } 550297f4619Sad 551297f4619Sad /* 552297f4619Sad * Merge same lock+callsite pairs from multiple CPUs 553297f4619Sad * together. 554297f4619Sad */ 555297f4619Sad TAILQ_FOREACH(lb2, &l->bufs, lb_chain.tailq) { 556297f4619Sad if (lb->lb_callsite == lb2->lb_callsite) 557297f4619Sad break; 558297f4619Sad } 559297f4619Sad if (lb2 != NULL) { 560297f4619Sad for (i = 0; i < LB_NEVENT; i++) { 561297f4619Sad lb2->lb_counts[i] += lb->lb_counts[i]; 562297f4619Sad lb2->lb_times[i] += lb->lb_times[i]; 563297f4619Sad } 564*12793652Sad } else { 565297f4619Sad TAILQ_INSERT_HEAD(&l->bufs, lb, lb_chain.tailq); 566*12793652Sad l->nbufs++; 567*12793652Sad } 568297f4619Sad } 569297f4619Sad } 570297f4619Sad 571297f4619Sad /* 572297f4619Sad * Re-sort one list of locks / lock buffers by event type. 573297f4619Sad */ 574297f4619Sad void 575297f4619Sad resort(int type, int event) 576297f4619Sad { 577297f4619Sad lsbuf_t *lb, *lb2; 578297f4619Sad locklist_t llist; 579297f4619Sad buflist_t blist; 580297f4619Sad lock_t *l, *l2; 581297f4619Sad 582297f4619Sad TAILQ_INIT(&llist); 583297f4619Sad while ((l = TAILQ_FIRST(&locklist[type])) != NULL) { 584297f4619Sad TAILQ_REMOVE(&locklist[type], l, chain); 585297f4619Sad 586297f4619Sad /* 587297f4619Sad * Sort the buffers into the per-lock list. 588297f4619Sad */ 589297f4619Sad TAILQ_INIT(&blist); 590297f4619Sad while ((lb = TAILQ_FIRST(&l->bufs)) != NULL) { 591297f4619Sad TAILQ_REMOVE(&l->bufs, lb, lb_chain.tailq); 592297f4619Sad 593297f4619Sad lb2 = TAILQ_FIRST(&blist); 594297f4619Sad while (lb2 != NULL) { 595297f4619Sad if (cflag) { 596297f4619Sad if (lb->lb_counts[event] > 597297f4619Sad lb2->lb_counts[event]) 598297f4619Sad break; 599297f4619Sad } else if (lb->lb_times[event] > 600297f4619Sad lb2->lb_times[event]) 601297f4619Sad break; 602297f4619Sad lb2 = TAILQ_NEXT(lb2, lb_chain.tailq); 603297f4619Sad } 604297f4619Sad if (lb2 == NULL) 605297f4619Sad TAILQ_INSERT_TAIL(&blist, lb, lb_chain.tailq); 606297f4619Sad else 607297f4619Sad TAILQ_INSERT_BEFORE(lb2, lb, lb_chain.tailq); 608297f4619Sad } 609297f4619Sad l->bufs = blist; 610297f4619Sad 611297f4619Sad /* 612297f4619Sad * Sort this lock into the per-type list, based on the 613297f4619Sad * totals per lock. 614297f4619Sad */ 615297f4619Sad l2 = TAILQ_FIRST(&llist); 616297f4619Sad while (l2 != NULL) { 617297f4619Sad if (cflag) { 618297f4619Sad if (l->counts[event] > l2->counts[event]) 619297f4619Sad break; 620297f4619Sad } else if (l->times[event] > l2->times[event]) 621297f4619Sad break; 622297f4619Sad l2 = TAILQ_NEXT(l2, chain); 623297f4619Sad } 624297f4619Sad if (l2 == NULL) 625297f4619Sad TAILQ_INSERT_TAIL(&llist, l, chain); 626297f4619Sad else 627297f4619Sad TAILQ_INSERT_BEFORE(l2, l, chain); 628297f4619Sad } 629297f4619Sad locklist[type] = llist; 630297f4619Sad } 631297f4619Sad 632297f4619Sad /* 633297f4619Sad * Display a summary table for one lock type / event type pair. 634297f4619Sad */ 635297f4619Sad void 636297f4619Sad display(int mask, const char *name) 637297f4619Sad { 638297f4619Sad lock_t *l; 639297f4619Sad lsbuf_t *lb; 640297f4619Sad int event, type; 641297f4619Sad double pcscale, metric; 642297f4619Sad char lname[256], fname[256]; 643297f4619Sad 644297f4619Sad type = ((mask & LB_LOCK_MASK) >> LB_LOCK_SHIFT) - 1; 645*12793652Sad if (TAILQ_EMPTY(&locklist[type])) 646297f4619Sad return; 647297f4619Sad 648297f4619Sad event = (mask & LB_EVENT_MASK) - 1; 649297f4619Sad resort(type, event); 650297f4619Sad 651297f4619Sad fprintf(outfp, "\n-- %s\n\n" 652297f4619Sad "Total%% Count Time/ms Lock Caller\n" 653297f4619Sad "------ ------- --------- ---------------------- ------------------------------\n", 654297f4619Sad name); 655297f4619Sad 656297f4619Sad /* 657297f4619Sad * Sum up all events for this type of lock + event. 658297f4619Sad */ 659297f4619Sad pcscale = 0; 660297f4619Sad TAILQ_FOREACH(l, &locklist[type], chain) { 661297f4619Sad if (cflag) 662297f4619Sad pcscale += l->counts[event]; 663297f4619Sad else 664297f4619Sad pcscale += l->times[event]; 665297f4619Sad displayed++; 666297f4619Sad } 667297f4619Sad if (pcscale == 0) 668297f4619Sad pcscale = 100; 669297f4619Sad else 670297f4619Sad pcscale = (100.0 / pcscale); 671297f4619Sad 672297f4619Sad /* 673297f4619Sad * For each lock, print a summary total, followed by a breakdown by 674297f4619Sad * caller. 675297f4619Sad */ 676297f4619Sad TAILQ_FOREACH(l, &locklist[type], chain) { 677297f4619Sad if (cflag) 678297f4619Sad metric = l->counts[event]; 679297f4619Sad else 680297f4619Sad metric = l->times[event]; 681297f4619Sad metric *= pcscale; 682297f4619Sad 683297f4619Sad findsym(LOCK_BYADDR, lname, &l->lock, NULL); 684297f4619Sad 685*12793652Sad if (l->nbufs > 1) 686*12793652Sad fprintf(outfp, "%6.2f %7d %9.2f %-22s <all>\n", metric, 687297f4619Sad (int)(l->counts[event] * cscale), 688297f4619Sad l->times[event] * tscale, lname); 689297f4619Sad 690297f4619Sad if (lflag) 691297f4619Sad continue; 692297f4619Sad 693297f4619Sad TAILQ_FOREACH(lb, &l->bufs, lb_chain.tailq) { 694297f4619Sad if (cflag) 695297f4619Sad metric = lb->lb_counts[event]; 696297f4619Sad else 697297f4619Sad metric = lb->lb_times[event]; 698297f4619Sad metric *= pcscale; 699297f4619Sad 700297f4619Sad findsym(FUNC_BYADDR, fname, &lb->lb_callsite, NULL); 701*12793652Sad fprintf(outfp, "%6.2f %7d %9.2f %-22s %s\n", metric, 702297f4619Sad (int)(lb->lb_counts[event] * cscale), 703297f4619Sad lb->lb_times[event] * tscale, 704297f4619Sad lname, fname); 705297f4619Sad } 706297f4619Sad } 707297f4619Sad } 708