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