xref: /netbsd-src/usr.sbin/tprof/tprof_top.c (revision 7d62b00eb9ad855ffcd7da46b41e23feb5476fac)
1 /*	$NetBSD: tprof_top.c,v 1.8 2022/12/23 19:37:06 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2022 Ryo Shimizu <ryo@nerv.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #ifndef lint
31 __RCSID("$NetBSD: tprof_top.c,v 1.8 2022/12/23 19:37:06 christos Exp $");
32 #endif /* not lint */
33 
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 #include <sys/rbtree.h>
38 #include <sys/select.h>
39 #include <sys/time.h>
40 
41 #include <assert.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <inttypes.h>
46 #include <math.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <term.h>
52 #include <termios.h>
53 #include <unistd.h>
54 #include <util.h>
55 
56 #include <dev/tprof/tprof_ioctl.h>
57 #include "tprof.h"
58 #include "ksyms.h"
59 
60 #define SAMPLE_MODE_ACCUMULATIVE	0
61 #define SAMPLE_MODE_INSTANTANEOUS	1
62 #define SAMPLE_MODE_NUM			2
63 
64 #define LINESTR	"-------------------------------------------------------------"
65 #define SYMBOL_LEN			32	/* symbol and event name */
66 
67 struct sample_elm {
68 	struct rb_node node;
69 	uint64_t addr;
70 	const char *name;
71 	uint32_t flags;
72 #define SAMPLE_ELM_FLAGS_USER	0x00000001
73 	uint32_t num[SAMPLE_MODE_NUM];
74 	uint32_t num_cpu[];	/* [SAMPLE_MODE_NUM][ncpu] */
75 #define SAMPLE_ELM_NUM_CPU(e, k)		\
76 	((e)->num_cpu + (k) *  ncpu)
77 };
78 
79 struct ptrarray {
80 	void **pa_ptrs;
81 	size_t pa_allocnum;
82 	size_t pa_inuse;
83 };
84 
85 static int opt_mode = SAMPLE_MODE_INSTANTANEOUS;
86 static int opt_userland = 0;
87 static int opt_showcounter = 0;
88 
89 /* for display */
90 static char *term;
91 static struct winsize win;
92 static int nontty;
93 static struct termios termios_save;
94 static bool termios_saved;
95 static long top_interval = 1;
96 static bool do_redraw;
97 static u_int nshow;
98 
99 /* for profiling and counting samples */
100 static sig_atomic_t sigalrm;
101 static struct sym **ksyms;
102 static size_t nksyms;
103 static u_int nevent;
104 static const char *eventname[TPROF_MAXCOUNTERS];
105 static size_t sizeof_sample_elm;
106 static rb_tree_t rb_tree_sample;
107 struct ptrarray sample_list[SAMPLE_MODE_NUM];
108 static u_int sample_n_kern[SAMPLE_MODE_NUM];
109 static u_int sample_n_user[SAMPLE_MODE_NUM];
110 static u_int sample_event_width = 7;
111 static u_int *sample_cpu_width;					/* [ncpu] */
112 static uint32_t *sample_n_kern_per_cpu[SAMPLE_MODE_NUM];	/* [ncpu] */
113 static uint32_t *sample_n_user_per_cpu[SAMPLE_MODE_NUM];	/* [ncpu] */
114 static uint64_t *sample_n_per_event[SAMPLE_MODE_NUM];		/* [nevent] */
115 static uint64_t *sample_n_per_event_cpu[SAMPLE_MODE_NUM];	/* [ncpu] */
116 
117 /* raw event counter */
118 static uint64_t *counters;	/* counters[2][ncpu][nevent] */
119 static u_int counters_i;
120 
121 static const char *
122 cycle_event_name(void)
123 {
124 	const char *cycleevent;
125 
126 	switch (tprof_info.ti_ident) {
127 	case TPROF_IDENT_INTEL_GENERIC:
128 		cycleevent = "unhalted-core-cycles";
129 		break;
130 	case TPROF_IDENT_AMD_GENERIC:
131 		cycleevent = "LsNotHaltedCyc";
132 		break;
133 	case TPROF_IDENT_ARMV8_GENERIC:
134 	case TPROF_IDENT_ARMV7_GENERIC:
135 		cycleevent = "CPU_CYCLES";
136 		break;
137 	default:
138 		cycleevent = NULL;
139 		break;
140 	}
141 	return cycleevent;
142 }
143 
144 static void
145 reset_cursor_pos(void)
146 {
147 	int i;
148 	char *p;
149 
150 	if (nontty || term == NULL)
151 		return;
152 
153 	printf("\r");
154 
155 	/* cursor_up * n */
156 	if ((p = tigetstr("cuu")) != NULL) {
157 		putp(tparm(p, win.ws_row - 1, 0, 0, 0, 0, 0, 0, 0, 0));
158 	} else if ((p = tigetstr("cuu1")) != NULL) {
159 		for (i = win.ws_row - 1; i > 0; i--)
160 			putp(p);
161 	}
162 }
163 
164 static void
165 clr_to_eol(void)
166 {
167 	char *p;
168 
169 	if (nontty || term == NULL)
170 		return;
171 
172 	if ((p = tigetstr("el")) != NULL)
173 		putp(p);
174 }
175 
176 /* newline, and clearing to end of line if needed */
177 static void
178 lim_newline(int *lim)
179 {
180 	if (*lim >= 1)
181 		clr_to_eol();
182 
183 	printf("\n");
184 	*lim = win.ws_col;
185 }
186 
187 static int
188 lim_printf(int *lim, const char *fmt, ...)
189 {
190 	va_list ap;
191 	size_t written;
192 	char *p;
193 
194 	if (*lim <= 0)
195 		return 0;
196 
197 	p = malloc(*lim + 1);
198 	if (p == NULL)
199 		return -1;
200 
201 	va_start(ap, fmt);
202 	vsnprintf(p, *lim + 1, fmt, ap);
203 	va_end(ap);
204 
205 	written = strlen(p);
206 	if (written == 0) {
207 		free(p);
208 		*lim = 0;
209 		return 0;
210 	}
211 
212 	fwrite(p, written, 1, stdout);
213 	*lim -= written;
214 
215 	free(p);
216 	return written;
217 }
218 
219 static void
220 sigwinch_handler(int signo)
221 {
222 	char *p;
223 
224 	win.ws_col = tigetnum("lines");
225 	win.ws_row = tigetnum("cols");
226 
227 	nontty = ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
228 	if (nontty != 0) {
229 		nontty = !isatty(STDOUT_FILENO);
230 		win.ws_col = 65535;
231 		win.ws_row = 65535;
232 	}
233 
234 	if ((p = getenv("LINES")) != NULL)
235 		win.ws_row = strtoul(p, NULL, 0);
236 	if ((p = getenv("COLUMNS")) != NULL)
237 		win.ws_col = strtoul(p, NULL, 0);
238 
239 	do_redraw = true;
240 }
241 
242 static void
243 tty_setup(void)
244 {
245 	struct termios termios;
246 
247 	term = getenv("TERM");
248 	if (term != NULL)
249 		setupterm(term, 0, NULL);
250 
251 	sigwinch_handler(0);
252 
253 	if (tcgetattr(STDOUT_FILENO, &termios_save) == 0) {
254 		termios_saved = true;
255 
256 		/* stty cbreak */
257 		termios = termios_save;
258 		termios.c_iflag |= BRKINT|IXON|IMAXBEL;
259 		termios.c_oflag |= OPOST;
260 		termios.c_lflag |= ISIG|IEXTEN;
261 		termios.c_lflag &= ~(ICANON|ECHO);
262 		tcsetattr(STDOUT_FILENO, TCSADRAIN, &termios);
263 	}
264 }
265 
266 static void
267 tty_restore(void)
268 {
269 	if (termios_saved) {
270 		tcsetattr(STDOUT_FILENO, TCSADRAIN, &termios_save);
271 		termios_saved = false;
272 	}
273 }
274 
275 static void
276 sigtstp_handler(int signo)
277 {
278 	tty_restore();
279 
280 	signal(SIGWINCH, SIG_DFL);
281 	signal(SIGINT, SIG_DFL);
282 	signal(SIGQUIT, SIG_DFL);
283 	signal(SIGTERM, SIG_DFL);
284 	signal(SIGTSTP, SIG_DFL);
285 	kill(0, SIGTSTP);
286 	nshow = 0;
287 }
288 
289 static void
290 sigalrm_handler(int signo)
291 {
292 	sigalrm = 1;
293 }
294 
295 __dead static void
296 die(int signo)
297 {
298 	tty_restore();
299 	printf("\n");
300 
301 	exit(EXIT_SUCCESS);
302 }
303 
304 __dead static void
305 die_errc(int status, int code, const char *fmt, ...)
306 {
307 	va_list ap;
308 
309 	tty_restore();
310 
311 	va_start(ap, fmt);
312 	if (code == 0)
313 		verrx(status, fmt, ap);
314 	else
315 		verrc(status, code, fmt, ap);
316 	va_end(ap);
317 }
318 
319 static void
320 ptrarray_push(struct ptrarray *ptrarray, void *ptr)
321 {
322 	int error;
323 
324 	if (ptrarray->pa_inuse >= ptrarray->pa_allocnum) {
325 		/* increase buffer */
326 		ptrarray->pa_allocnum += 1024;
327 		error = reallocarr(&ptrarray->pa_ptrs, ptrarray->pa_allocnum,
328 		    sizeof(*ptrarray->pa_ptrs));
329 		if (error != 0)
330 			die_errc(EXIT_FAILURE, error, "rellocarr failed");
331 	}
332 	ptrarray->pa_ptrs[ptrarray->pa_inuse++] = ptr;
333 }
334 
335 static void
336 ptrarray_iterate(struct ptrarray *ptrarray, void (*ifunc)(void *))
337 {
338 	size_t i;
339 
340 	for (i = 0; i < ptrarray->pa_inuse; i++) {
341 		(*ifunc)(ptrarray->pa_ptrs[i]);
342 	}
343 }
344 
345 static void
346 ptrarray_clear(struct ptrarray *ptrarray)
347 {
348 	ptrarray->pa_inuse = 0;
349 }
350 
351 static int
352 sample_compare_key(void *ctx, const void *n1, const void *keyp)
353 {
354 	const struct sample_elm *a1 = n1;
355 	const struct sample_elm *a2 = (const struct sample_elm *)keyp;
356 	return a1->addr - a2->addr;
357 }
358 
359 static signed int
360 sample_compare_nodes(void *ctx, const void *n1, const void *n2)
361 {
362 	const struct addr *a2 = n2;
363 	return sample_compare_key(ctx, n1, a2);
364 }
365 
366 static const rb_tree_ops_t sample_ops = {
367 	.rbto_compare_nodes = sample_compare_nodes,
368 	.rbto_compare_key = sample_compare_key
369 };
370 
371 static u_int
372 n_align(u_int n, u_int align)
373 {
374 	return (n + align - 1) / align * align;
375 }
376 
377 static void
378 sample_init(void)
379 {
380 	const struct sample_elm *e;
381 	int l, mode, n;
382 	u_int size;
383 	char buf[16];
384 
385 	size = sizeof(struct sample_elm) +
386 	    sizeof(e->num_cpu[0]) * SAMPLE_MODE_NUM * ncpu;
387 	sizeof_sample_elm = n_align(size, __alignof(struct sample_elm));
388 
389 	sample_cpu_width = ecalloc(1, sizeof(*sample_cpu_width) * ncpu);
390 	for (n = 0; n < ncpu; n++) {
391 		sample_cpu_width[n] = 5;
392 		l = snprintf(buf, sizeof(buf), "CPU%d", n);
393 		if (sample_cpu_width[n] < (u_int)l)
394 			sample_cpu_width[n] = l;
395 	}
396 
397 	for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
398 		sample_n_kern_per_cpu[mode] = ecalloc(1,
399 		    sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu);
400 		sample_n_user_per_cpu[mode] = ecalloc(1,
401 		    sizeof(typeof(*sample_n_user_per_cpu[mode])) * ncpu);
402 		sample_n_per_event[mode] = ecalloc(1,
403 		    sizeof(typeof(*sample_n_per_event[mode])) * nevent);
404 		sample_n_per_event_cpu[mode] = ecalloc(1,
405 		    sizeof(typeof(*sample_n_per_event_cpu[mode])) *
406 		    nevent * ncpu);
407 	}
408 }
409 
410 static void
411 sample_clear_instantaneous(void *arg)
412 {
413 	struct sample_elm *e = (void *)arg;
414 
415 	e->num[SAMPLE_MODE_INSTANTANEOUS] = 0;
416 	memset(SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_INSTANTANEOUS),
417 	    0, sizeof(e->num_cpu[0]) * ncpu);
418 }
419 
420 static void
421 sample_reset(bool reset_accumulative)
422 {
423 	int mode;
424 
425 	for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
426 		if (mode == SAMPLE_MODE_ACCUMULATIVE && !reset_accumulative)
427 			continue;
428 
429 		sample_n_kern[mode] = 0;
430 		sample_n_user[mode] = 0;
431 		memset(sample_n_kern_per_cpu[mode], 0,
432 		    sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu);
433 		memset(sample_n_user_per_cpu[mode], 0,
434 		    sizeof(typeof(*sample_n_user_per_cpu[mode])) * ncpu);
435 		memset(sample_n_per_event[mode], 0,
436 		    sizeof(typeof(*sample_n_per_event[mode])) * nevent);
437 		memset(sample_n_per_event_cpu[mode], 0,
438 		    sizeof(typeof(*sample_n_per_event_cpu[mode])) *
439 		    nevent * ncpu);
440 	}
441 
442 	if (reset_accumulative) {
443 		rb_tree_init(&rb_tree_sample, &sample_ops);
444 		ptrarray_iterate(&sample_list[SAMPLE_MODE_ACCUMULATIVE], free);
445 		ptrarray_clear(&sample_list[SAMPLE_MODE_ACCUMULATIVE]);
446 		ptrarray_clear(&sample_list[SAMPLE_MODE_INSTANTANEOUS]);
447 	} else {
448 		ptrarray_iterate(&sample_list[SAMPLE_MODE_INSTANTANEOUS],
449 		    sample_clear_instantaneous);
450 		ptrarray_clear(&sample_list[SAMPLE_MODE_INSTANTANEOUS]);
451 	}
452 }
453 
454 static int __unused
455 sample_sortfunc_accumulative(const void *a, const void *b)
456 {
457 	struct sample_elm * const *ea = a;
458 	struct sample_elm * const *eb = b;
459 	return (*eb)->num[SAMPLE_MODE_ACCUMULATIVE] -
460 	    (*ea)->num[SAMPLE_MODE_ACCUMULATIVE];
461 }
462 
463 static int
464 sample_sortfunc_instantaneous(const void *a, const void *b)
465 {
466 	struct sample_elm * const *ea = a;
467 	struct sample_elm * const *eb = b;
468 	return (*eb)->num[SAMPLE_MODE_INSTANTANEOUS] -
469 	    (*ea)->num[SAMPLE_MODE_INSTANTANEOUS];
470 }
471 
472 static void
473 sample_sort_accumulative(void)
474 {
475 	qsort(sample_list[SAMPLE_MODE_ACCUMULATIVE].pa_ptrs,
476 	    sample_list[SAMPLE_MODE_ACCUMULATIVE].pa_inuse,
477 	    sizeof(struct sample_elm *), sample_sortfunc_accumulative);
478 }
479 
480 static void
481 sample_sort_instantaneous(void)
482 {
483 	qsort(sample_list[SAMPLE_MODE_INSTANTANEOUS].pa_ptrs,
484 	    sample_list[SAMPLE_MODE_INSTANTANEOUS].pa_inuse,
485 	    sizeof(struct sample_elm *), sample_sortfunc_instantaneous);
486 }
487 
488 static void
489 sample_collect(tprof_sample_t *s)
490 {
491 	struct sample_elm *e, *o;
492 	const char *name;
493 	size_t symid;
494 	uint64_t addr, offset;
495 	uint32_t flags = 0;
496 	uint32_t eventid, cpuid;
497 	int mode;
498 
499 	eventid = __SHIFTOUT(s->s_flags, TPROF_SAMPLE_COUNTER_MASK);
500 	cpuid = s->s_cpuid;
501 
502 	if (eventid >= nevent)	/* unknown event from tprof? */
503 		return;
504 
505 	for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
506 		sample_n_per_event[mode][eventid]++;
507 		sample_n_per_event_cpu[mode][nevent * cpuid + eventid]++;
508 	}
509 
510 	if ((s->s_flags & TPROF_SAMPLE_INKERNEL) == 0) {
511 		sample_n_user[SAMPLE_MODE_ACCUMULATIVE]++;
512 		sample_n_user[SAMPLE_MODE_INSTANTANEOUS]++;
513 		sample_n_user_per_cpu[SAMPLE_MODE_ACCUMULATIVE][cpuid]++;
514 		sample_n_user_per_cpu[SAMPLE_MODE_INSTANTANEOUS][cpuid]++;
515 
516 		name = NULL;
517 		addr = s->s_pid;	/* XXX */
518 		flags |= SAMPLE_ELM_FLAGS_USER;
519 
520 		if (!opt_userland)
521 			return;
522 	} else {
523 		sample_n_kern[SAMPLE_MODE_ACCUMULATIVE]++;
524 		sample_n_kern[SAMPLE_MODE_INSTANTANEOUS]++;
525 		sample_n_kern_per_cpu[SAMPLE_MODE_ACCUMULATIVE][cpuid]++;
526 		sample_n_kern_per_cpu[SAMPLE_MODE_INSTANTANEOUS][cpuid]++;
527 
528 		name = ksymlookup(s->s_pc, &offset, &symid);
529 		if (name != NULL) {
530 			addr = ksyms[symid]->value;
531 		} else {
532 			addr = s->s_pc;
533 		}
534 	}
535 
536 	e = ecalloc(1, sizeof_sample_elm);
537 	e->addr = addr;
538 	e->name = name;
539 	e->flags = flags;
540 	e->num[SAMPLE_MODE_ACCUMULATIVE] = 1;
541 	e->num[SAMPLE_MODE_INSTANTANEOUS] = 1;
542 	SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_ACCUMULATIVE)[cpuid] = 1;
543 	SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_INSTANTANEOUS)[cpuid] = 1;
544 	o = rb_tree_insert_node(&rb_tree_sample, e);
545 	if (o == e) {
546 		/* new symbol. add to list for sort */
547 		ptrarray_push(&sample_list[SAMPLE_MODE_ACCUMULATIVE], o);
548 		ptrarray_push(&sample_list[SAMPLE_MODE_INSTANTANEOUS], o);
549 	} else {
550 		/* already exists */
551 		free(e);
552 
553 		o->num[SAMPLE_MODE_ACCUMULATIVE]++;
554 		if (o->num[SAMPLE_MODE_INSTANTANEOUS]++ == 0) {
555 			/* new instantaneous symbols. add to list for sort */
556 			ptrarray_push(&sample_list[SAMPLE_MODE_INSTANTANEOUS],
557 			    o);
558 		}
559 		SAMPLE_ELM_NUM_CPU(o, SAMPLE_MODE_ACCUMULATIVE)[cpuid]++;
560 		SAMPLE_ELM_NUM_CPU(o, SAMPLE_MODE_INSTANTANEOUS)[cpuid]++;
561 	}
562 }
563 
564 static void
565 show_tprof_stat(int *lim)
566 {
567 	static struct tprof_stat tsbuf[2], *ts0, *ts;
568 	static u_int ts_i = 0;
569 	static int tprofstat_width[6];
570 	int ret, l;
571 	char tmpbuf[128];
572 
573 	ts0 = &tsbuf[ts_i++ & 1];
574 	ts = &tsbuf[ts_i & 1];
575 	ret = ioctl(devfd, TPROF_IOC_GETSTAT, ts);
576 	if (ret == -1)
577 		die_errc(EXIT_FAILURE, errno, "TPROF_IOC_GETSTAT");
578 
579 #define TS_PRINT(idx, label, _m)					\
580 	do {								\
581 		__CTASSERT(idx < __arraycount(tprofstat_width));	\
582 		lim_printf(lim, "%s", label);			\
583 		l = snprintf(tmpbuf, sizeof(tmpbuf), "%"PRIu64, ts->_m);\
584 		if (ts->_m != ts0->_m)					\
585 			l += snprintf(tmpbuf + l, sizeof(tmpbuf) - l,	\
586 			    "(+%"PRIu64")", ts->_m - ts0->_m);		\
587 		assert(l < (int)sizeof(tmpbuf));			\
588 		if (tprofstat_width[idx] < l)				\
589 			tprofstat_width[idx] = l;			\
590 		lim_printf(lim, "%-*.*s  ", tprofstat_width[idx],	\
591 		    tprofstat_width[idx], tmpbuf);			\
592 	} while (0)
593 	lim_printf(lim, "tprof ");
594 	TS_PRINT(0, "sample:", ts_sample);
595 	TS_PRINT(1, "overflow:", ts_overflow);
596 	TS_PRINT(2, "buf:", ts_buf);
597 	TS_PRINT(3, "emptybuf:", ts_emptybuf);
598 	TS_PRINT(4, "dropbuf:", ts_dropbuf);
599 	TS_PRINT(5, "dropbuf_sample:", ts_dropbuf_sample);
600 }
601 
602 static void
603 show_timestamp(void)
604 {
605 	struct timeval tv;
606 	gettimeofday(&tv, NULL);
607 	printf("%-8.8s", &(ctime((time_t *)&tv.tv_sec)[11]));
608 }
609 
610 static void
611 show_counters_alloc(void)
612 {
613 	size_t sz = 2 * ncpu * nevent * sizeof(*counters);
614 	counters = ecalloc(1, sz);
615 }
616 
617 static void
618 show_counters(int *lim)
619 {
620 	tprof_counts_t countsbuf;
621 	uint64_t *cn[2], *c0, *c;
622 	u_int i;
623 	int n, ret;
624 
625 	cn[0] = counters;
626 	cn[1] = counters + ncpu * nevent;
627 	c0 = cn[counters_i++ & 1];
628 	c = cn[counters_i & 1];
629 
630 	for (n = 0; n < ncpu; n++) {
631 		countsbuf.c_cpu = n;
632 		ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &countsbuf);
633 		if (ret == -1)
634 			die_errc(EXIT_FAILURE, errno, "TPROF_IOC_GETCOUNTS");
635 
636 		for (i = 0; i < nevent; i++)
637 			c[n * nevent + i] = countsbuf.c_count[i];
638 	}
639 
640 	if (do_redraw) {
641 		lim_printf(lim, "%-22s", "Event counter (delta)");
642 		for (n = 0; n < ncpu; n++) {
643 			char cpuname[16];
644 			snprintf(cpuname, sizeof(cpuname), "CPU%u", n);
645 			lim_printf(lim, "%11s", cpuname);
646 		}
647 		lim_newline(lim);
648 	} else {
649 		printf("\n");
650 	}
651 
652 	for (i = 0; i < nevent; i++) {
653 		lim_printf(lim, "%-22.22s", eventname[i]);
654 		for (n = 0; n < ncpu; n++) {
655 			lim_printf(lim, "%11"PRIu64,
656 			    c[n * nevent + i] - c0[n * nevent + i]);
657 		}
658 		lim_newline(lim);
659 	}
660 	lim_newline(lim);
661 }
662 
663 static void
664 show_count_per_event(int *lim)
665 {
666 	u_int i, nsample_total;
667 	int n, l;
668 	char buf[32];
669 
670 	nsample_total = sample_n_kern[opt_mode] + sample_n_user[opt_mode];
671 	if (nsample_total == 0)
672 		nsample_total = 1;
673 
674 	/* calc width in advance */
675 	for (i = 0; i < nevent; i++) {
676 		l = snprintf(buf, sizeof(buf), "%"PRIu64,
677 		    sample_n_per_event[opt_mode][i]);
678 		if (sample_event_width < (u_int)l) {
679 			sample_event_width = l;
680 			do_redraw = true;
681 		}
682 	}
683 	for (n = 0; n < ncpu; n++) {
684 		uint64_t sum = 0;
685 		for (i = 0; i < nevent; i++)
686 			sum += sample_n_per_event_cpu[opt_mode][nevent * n + i];
687 		l = snprintf(buf, sizeof(buf), "%"PRIu64, sum);
688 		if (sample_cpu_width[n] < (u_int)l) {
689 			sample_cpu_width[n] = l;
690 			do_redraw = true;
691 		}
692 	}
693 
694 	if (do_redraw) {
695 		lim_printf(lim, "  Rate %*s %-*s",
696 		    sample_event_width, "Sample#",
697 		    SYMBOL_LEN, "Eventname");
698 		for (n = 0; n < ncpu; n++) {
699 			snprintf(buf, sizeof(buf), "CPU%d", n);
700 			lim_printf(lim, " %*s", sample_cpu_width[n], buf);
701 		}
702 		lim_newline(lim);
703 
704 		lim_printf(lim, "------ %*.*s %*.*s",
705 		    sample_event_width, sample_event_width, LINESTR,
706 		    SYMBOL_LEN, SYMBOL_LEN, LINESTR);
707 		for (n = 0; n < ncpu; n++) {
708 			lim_printf(lim, " %*.*s",
709 			    sample_cpu_width[n], sample_cpu_width[n], LINESTR);
710 		}
711 		lim_newline(lim);
712 	} else {
713 		printf("\n\n");
714 	}
715 
716 	for (i = 0; i < nevent; i++) {
717 		if (sample_n_per_event[opt_mode][i] >= nsample_total) {
718 			lim_printf(lim, "%5.1f%%", 100.0 *
719 			    sample_n_per_event[opt_mode][i] / nsample_total);
720 		} else {
721 			lim_printf(lim, "%5.2f%%", 100.0 *
722 			    sample_n_per_event[opt_mode][i] / nsample_total);
723 		}
724 		lim_printf(lim, " %*"PRIu64" ", sample_event_width,
725 		    sample_n_per_event[opt_mode][i]);
726 
727 		lim_printf(lim, "%-32.32s", eventname[i]);
728 		for (n = 0; n < ncpu; n++) {
729 			lim_printf(lim, " %*"PRIu64, sample_cpu_width[n],
730 			    sample_n_per_event_cpu[opt_mode][nevent * n + i]);
731 		}
732 		lim_newline(lim);
733 	}
734 }
735 
736 static void
737 sample_show(void)
738 {
739 	struct sample_elm *e;
740 	struct ptrarray *samples;
741 	u_int nsample_total;
742 	int i, l, lim, n, ndisp;
743 	char namebuf[32];
744 	const char *name;
745 
746 	if (nshow++ == 0) {
747 		printf("\n");
748 		if (!nontty) {
749 			signal(SIGWINCH, sigwinch_handler);
750 			signal(SIGINT, die);
751 			signal(SIGQUIT, die);
752 			signal(SIGTERM, die);
753 			signal(SIGTSTP, sigtstp_handler);
754 
755 			tty_setup();
756 		}
757 	} else {
758 		reset_cursor_pos();
759 	}
760 
761 	int margin_lines = 7;
762 
763 	margin_lines += 3 + nevent;	/* show_counter_per_event() */
764 
765 	if (opt_mode == SAMPLE_MODE_INSTANTANEOUS)
766 		sample_sort_instantaneous();
767 	else
768 		sample_sort_accumulative();
769 	samples = &sample_list[opt_mode];
770 
771 	if (opt_showcounter)
772 		margin_lines += 2 + nevent;
773 	if (opt_userland)
774 		margin_lines += 1;
775 
776 	ndisp = samples->pa_inuse;
777 	if (!nontty && ndisp > (win.ws_row - margin_lines))
778 		ndisp = win.ws_row - margin_lines;
779 
780 	lim = win.ws_col;
781 	if (opt_mode == SAMPLE_MODE_ACCUMULATIVE)
782 		lim_printf(&lim, "[Accumulative mode] ");
783 	show_tprof_stat(&lim);
784 
785 	if (lim >= 16) {
786 		l = win.ws_col - lim;
787 		if (!nontty) {
788 			clr_to_eol();
789 			for (; l <= win.ws_col - 17; l = ((l + 8) & -8))
790 				printf("\t");
791 		}
792 		show_timestamp();
793 	}
794 	lim_newline(&lim);
795 	lim_newline(&lim);
796 
797 	if (opt_showcounter)
798 		show_counters(&lim);
799 
800 	show_count_per_event(&lim);
801 	lim_newline(&lim);
802 
803 	if (do_redraw) {
804 		lim_printf(&lim, "  Rate %*s %-*s",
805 		    sample_event_width, "Sample#",
806 		    SYMBOL_LEN, "Symbol");
807 		for (n = 0; n < ncpu; n++) {
808 			snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
809 			lim_printf(&lim, " %*s", sample_cpu_width[n], namebuf);
810 		}
811 		lim_newline(&lim);
812 
813 		lim_printf(&lim, "------ %*.*s %*.*s",
814 		    sample_event_width, sample_event_width, LINESTR,
815 		    SYMBOL_LEN, SYMBOL_LEN, LINESTR);
816 		for (n = 0; n < ncpu; n++) {
817 			lim_printf(&lim, " %*.*s", sample_cpu_width[n],
818 			    sample_cpu_width[n], LINESTR);
819 		}
820 		lim_newline(&lim);
821 	} else {
822 		printf("\n\n");
823 	}
824 
825 	for (i = 0; i < ndisp; i++) {
826 		e = (struct sample_elm *)samples->pa_ptrs[i];
827 		name = e->name;
828 		if (name == NULL) {
829 			if (e->flags & SAMPLE_ELM_FLAGS_USER) {
830 				snprintf(namebuf, sizeof(namebuf),
831 				    "<PID:%"PRIu64">", e->addr);
832 			} else {
833 				snprintf(namebuf, sizeof(namebuf),
834 				    "0x%016"PRIx64, e->addr);
835 			}
836 			name = namebuf;
837 		}
838 
839 		nsample_total = sample_n_kern[opt_mode];
840 		if (opt_userland)
841 			nsample_total += sample_n_user[opt_mode];
842 		/*
843 		 * even when only kernel mode events are configured,
844 		 * interrupts may still occur in the user mode state.
845 		 */
846 		if (nsample_total == 0)
847 			nsample_total = 1;
848 
849 		if (e->num[opt_mode] >= nsample_total) {
850 			lim_printf(&lim, "%5.1f%%", 100.0 *
851 			    e->num[opt_mode] / nsample_total);
852 		} else {
853 			lim_printf(&lim, "%5.2f%%", 100.0 *
854 			    e->num[opt_mode] / nsample_total);
855 		}
856 		lim_printf(&lim, " %*u %-32.32s", sample_event_width,
857 		    e->num[opt_mode], name);
858 
859 		for (n = 0; n < ncpu; n++) {
860 			if (SAMPLE_ELM_NUM_CPU(e, opt_mode)[n] == 0) {
861 				lim_printf(&lim, " %*s", sample_cpu_width[n],
862 				    ".");
863 			} else {
864 				lim_printf(&lim, " %*u", sample_cpu_width[n],
865 				    SAMPLE_ELM_NUM_CPU(e, opt_mode)[n]);
866 			}
867 		}
868 		lim_newline(&lim);
869 	}
870 
871 	if ((u_int)ndisp != samples->pa_inuse) {
872 		lim_printf(&lim, "     : %*s (more %zu symbols omitted)",
873 		    sample_event_width, ":", samples->pa_inuse - ndisp);
874 		lim_newline(&lim);
875 	} else if (!nontty) {
876 		for (i = ndisp; i <= win.ws_row - margin_lines; i++) {
877 			printf("~");
878 			lim_newline(&lim);
879 		}
880 	}
881 
882 	if (do_redraw) {
883 		lim_printf(&lim, "------ %*.*s %*.*s",
884 		    sample_event_width, sample_event_width, LINESTR,
885 		    SYMBOL_LEN, SYMBOL_LEN, LINESTR);
886 		for (n = 0; n < ncpu; n++) {
887 			lim_printf(&lim, " %*.*s",
888 			    sample_cpu_width[n], sample_cpu_width[n], LINESTR);
889 		}
890 		lim_newline(&lim);
891 	} else {
892 		printf("\n");
893 	}
894 
895 	lim_printf(&lim, "Total  %*u %-32.32s",
896 	    sample_event_width, sample_n_kern[opt_mode], "in-kernel");
897 	for (n = 0; n < ncpu; n++) {
898 		lim_printf(&lim, " %*u", sample_cpu_width[n],
899 		    sample_n_kern_per_cpu[opt_mode][n]);
900 	}
901 
902 	if (opt_userland) {
903 		lim_newline(&lim);
904 		lim_printf(&lim, "       %*u %-32.32s",
905 		    sample_event_width, sample_n_user[opt_mode], "userland");
906 		for (n = 0; n < ncpu; n++) {
907 			lim_printf(&lim, " %*u", sample_cpu_width[n],
908 			    sample_n_user_per_cpu[opt_mode][n]);
909 		}
910 	}
911 
912 	if (nontty)
913 		printf("\n");
914 	else
915 		clr_to_eol();
916 }
917 
918 __dead static void
919 tprof_top_usage(void)
920 {
921 	fprintf(stderr, "%s top [-acu] [-e name[,scale] [-e ...]]"
922 	    " [-i interval]\n", getprogname());
923 	exit(EXIT_FAILURE);
924 }
925 
926 __dead void
927 tprof_top(int argc, char **argv)
928 {
929 	tprof_param_t params[TPROF_MAXCOUNTERS];
930 	struct itimerval it;
931 	ssize_t tprof_bufsize, len;
932 	u_int i;
933 	int ch, ret;
934 	char *tprof_buf, *p, *errmsg;
935 	bool noinput = false;
936 
937 	memset(params, 0, sizeof(params));
938 	nevent = 0;
939 
940 	while ((ch = getopt(argc, argv, "ace:i:L:u")) != -1) {
941 		switch (ch) {
942 		case 'a':
943 			opt_mode = SAMPLE_MODE_ACCUMULATIVE;
944 			break;
945 		case 'c':
946 			opt_showcounter = 1;
947 			break;
948 		case 'e':
949 			if (tprof_parse_event(&params[nevent], optarg,
950 			    TPROF_PARSE_EVENT_F_ALLOWSCALE,
951 			    &eventname[nevent], &errmsg) != 0) {
952 				die_errc(EXIT_FAILURE, 0, "%s", errmsg);
953 			}
954 			nevent++;
955 			if (nevent > __arraycount(params) ||
956 			    nevent > ncounters)
957 				die_errc(EXIT_FAILURE, 0,
958 				    "Too many events. Only a maximum of %d "
959 				    "counters can be used.", ncounters);
960 			break;
961 		case 'i':
962 			top_interval = strtol(optarg, &p, 10);
963 			if (*p != '\0' || top_interval <= 0)
964 				die_errc(EXIT_FAILURE, 0,
965 				    "Bad/invalid interval: %s", optarg);
966 			break;
967 		case 'u':
968 			opt_userland = 1;
969 			break;
970 		default:
971 			tprof_top_usage();
972 		}
973 	}
974 	argc -= optind;
975 	argv += optind;
976 
977 	if (argc != 0)
978 		tprof_top_usage();
979 
980 	if (nevent == 0) {
981 		const char *defaultevent = cycle_event_name();
982 		if (defaultevent == NULL)
983 			die_errc(EXIT_FAILURE, 0, "cpu not supported");
984 
985 		tprof_event_lookup(defaultevent, &params[nevent]);
986 		eventname[nevent] = defaultevent;
987 		nevent++;
988 	}
989 
990 	sample_init();
991 	show_counters_alloc();
992 
993 	for (i = 0; i < nevent; i++) {
994 		params[i].p_counter = i;
995 		params[i].p_flags |= TPROF_PARAM_KERN | TPROF_PARAM_PROFILE;
996 		if (opt_userland)
997 			params[i].p_flags |= TPROF_PARAM_USER;
998 		ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, &params[i]);
999 		if (ret == -1)
1000 			die_errc(EXIT_FAILURE, errno,
1001 			    "TPROF_IOC_CONFIGURE_EVENT: %s", eventname[i]);
1002 	}
1003 
1004 	tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
1005 	ret = ioctl(devfd, TPROF_IOC_START, &mask);
1006 	if (ret == -1)
1007 		die_errc(EXIT_FAILURE, errno, "TPROF_IOC_START");
1008 
1009 	ksyms = ksymload(&nksyms);
1010 
1011 	signal(SIGALRM, sigalrm_handler);
1012 
1013 	it.it_interval.tv_sec = it.it_value.tv_sec = top_interval;
1014 	it.it_interval.tv_usec = it.it_value.tv_usec = 0;
1015 	setitimer(ITIMER_REAL, &it, NULL);
1016 
1017 	sample_reset(true);
1018 	printf("collecting samples...");
1019 	fflush(stdout);
1020 
1021 	tprof_bufsize = sizeof(tprof_sample_t) * 1024 * 32;
1022 	tprof_buf = emalloc(tprof_bufsize);
1023 	do {
1024 		bool force_update = false;
1025 
1026 		while (sigalrm == 0 && !force_update) {
1027 			fd_set r;
1028 			int nfound;
1029 			char c;
1030 
1031 			FD_ZERO(&r);
1032 			if (!noinput)
1033 				FD_SET(STDIN_FILENO, &r);
1034 			FD_SET(devfd, &r);
1035 			nfound = select(devfd + 1, &r, NULL, NULL, NULL);
1036 			if (nfound == -1) {
1037 				if (errno == EINTR)
1038 					break;
1039 				die_errc(EXIT_FAILURE, errno, "select");
1040 			}
1041 
1042 			if (FD_ISSET(STDIN_FILENO, &r)) {
1043 				len = read(STDIN_FILENO, &c, 1);
1044 				if (len <= 0) {
1045 					noinput = true;
1046 					continue;
1047 				}
1048 				switch (c) {
1049 				case 0x0c:	/* ^L */
1050 					do_redraw = true;
1051 					break;
1052 				case 'a':
1053 					/* toggle mode */
1054 					opt_mode = (opt_mode + 1) %
1055 					    SAMPLE_MODE_NUM;
1056 					do_redraw = true;
1057 					break;
1058 				case 'c':
1059 					/* toggle mode */
1060 					opt_showcounter ^= 1;
1061 					do_redraw = true;
1062 					break;
1063 				case 'q':
1064 					goto done;
1065 				case 'z':
1066 					sample_reset(true);
1067 					break;
1068 				default:
1069 					continue;
1070 				}
1071 				force_update = true;
1072 			}
1073 
1074 			if (FD_ISSET(devfd, &r)) {
1075 				len = read(devfd, tprof_buf, tprof_bufsize);
1076 				if (len == -1 && errno != EINTR)
1077 					die_errc(EXIT_FAILURE, errno, "read");
1078 				if (len > 0) {
1079 					tprof_sample_t *s =
1080 					    (tprof_sample_t *)tprof_buf;
1081 					while (s <
1082 					    (tprof_sample_t *)(tprof_buf + len))
1083 						sample_collect(s++);
1084 				}
1085 			}
1086 		}
1087 		sigalrm = 0;
1088 
1089 		/* update screen */
1090 		sample_show();
1091 		fflush(stdout);
1092 		do_redraw = false;
1093 		if (force_update)
1094 			continue;
1095 
1096 		sample_reset(false);
1097 
1098 	} while (!nontty);
1099 
1100  done:
1101 	die(0);
1102 }
1103