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