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