xref: /netbsd-src/usr.sbin/lockstat/main.c (revision 6cf6fe02a981b55727c49c3d37b0d8191a98c0ee)
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