xref: /netbsd-src/usr.sbin/tprof/tprof_top.c (revision 867d70fc718005c0918b8b8b2f9d7f2d52d0a0db)
1 /*	$NetBSD: tprof_top.c,v 1.7 2022/12/16 08:02:04 ryo 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.7 2022/12/16 08:02:04 ryo 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 = alloca(*lim + 1);
198 
199 	va_start(ap, fmt);
200 	vsnprintf(p, *lim + 1, fmt, ap);
201 	va_end(ap);
202 
203 	written = strlen(p);
204 	if (written == 0) {
205 		*lim = 0;
206 		return 0;
207 	}
208 
209 	fwrite(p, written, 1, stdout);
210 	*lim -= written;
211 
212 	return written;
213 }
214 
215 static void
216 sigwinch_handler(int signo)
217 {
218 	char *p;
219 
220 	win.ws_col = tigetnum("lines");
221 	win.ws_row = tigetnum("cols");
222 
223 	nontty = ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
224 	if (nontty != 0) {
225 		nontty = !isatty(STDOUT_FILENO);
226 		win.ws_col = 65535;
227 		win.ws_row = 65535;
228 	}
229 
230 	if ((p = getenv("LINES")) != NULL)
231 		win.ws_row = strtoul(p, NULL, 0);
232 	if ((p = getenv("COLUMNS")) != NULL)
233 		win.ws_col = strtoul(p, NULL, 0);
234 
235 	do_redraw = true;
236 }
237 
238 static void
239 tty_setup(void)
240 {
241 	struct termios termios;
242 
243 	term = getenv("TERM");
244 	if (term != NULL)
245 		setupterm(term, 0, NULL);
246 
247 	sigwinch_handler(0);
248 
249 	if (tcgetattr(STDOUT_FILENO, &termios_save) == 0) {
250 		termios_saved = true;
251 
252 		/* stty cbreak */
253 		termios = termios_save;
254 		termios.c_iflag |= BRKINT|IXON|IMAXBEL;
255 		termios.c_oflag |= OPOST;
256 		termios.c_lflag |= ISIG|IEXTEN;
257 		termios.c_lflag &= ~(ICANON|ECHO);
258 		tcsetattr(STDOUT_FILENO, TCSADRAIN, &termios);
259 	}
260 }
261 
262 static void
263 tty_restore(void)
264 {
265 	if (termios_saved) {
266 		tcsetattr(STDOUT_FILENO, TCSADRAIN, &termios_save);
267 		termios_saved = false;
268 	}
269 }
270 
271 static void
272 sigtstp_handler(int signo)
273 {
274 	tty_restore();
275 
276 	signal(SIGWINCH, SIG_DFL);
277 	signal(SIGINT, SIG_DFL);
278 	signal(SIGQUIT, SIG_DFL);
279 	signal(SIGTERM, SIG_DFL);
280 	signal(SIGTSTP, SIG_DFL);
281 	kill(0, SIGTSTP);
282 	nshow = 0;
283 }
284 
285 static void
286 sigalrm_handler(int signo)
287 {
288 	sigalrm = 1;
289 }
290 
291 __dead static void
292 die(int signo)
293 {
294 	tty_restore();
295 	printf("\n");
296 
297 	exit(EXIT_SUCCESS);
298 }
299 
300 __dead static void
301 die_errc(int status, int code, const char *fmt, ...)
302 {
303 	va_list ap;
304 
305 	tty_restore();
306 
307 	va_start(ap, fmt);
308 	if (code == 0)
309 		verrx(status, fmt, ap);
310 	else
311 		verrc(status, code, fmt, ap);
312 	va_end(ap);
313 }
314 
315 static void
316 ptrarray_push(struct ptrarray *ptrarray, void *ptr)
317 {
318 	int error;
319 
320 	if (ptrarray->pa_inuse >= ptrarray->pa_allocnum) {
321 		/* increase buffer */
322 		ptrarray->pa_allocnum += 1024;
323 		error = reallocarr(&ptrarray->pa_ptrs, ptrarray->pa_allocnum,
324 		    sizeof(*ptrarray->pa_ptrs));
325 		if (error != 0)
326 			die_errc(EXIT_FAILURE, error, "rellocarr failed");
327 	}
328 	ptrarray->pa_ptrs[ptrarray->pa_inuse++] = ptr;
329 }
330 
331 static void
332 ptrarray_iterate(struct ptrarray *ptrarray, void (*ifunc)(void *))
333 {
334 	size_t i;
335 
336 	for (i = 0; i < ptrarray->pa_inuse; i++) {
337 		(*ifunc)(ptrarray->pa_ptrs[i]);
338 	}
339 }
340 
341 static void
342 ptrarray_clear(struct ptrarray *ptrarray)
343 {
344 	ptrarray->pa_inuse = 0;
345 }
346 
347 static int
348 sample_compare_key(void *ctx, const void *n1, const void *keyp)
349 {
350 	const struct sample_elm *a1 = n1;
351 	const struct sample_elm *a2 = (const struct sample_elm *)keyp;
352 	return a1->addr - a2->addr;
353 }
354 
355 static signed int
356 sample_compare_nodes(void *ctx, const void *n1, const void *n2)
357 {
358 	const struct addr *a2 = n2;
359 	return sample_compare_key(ctx, n1, a2);
360 }
361 
362 static const rb_tree_ops_t sample_ops = {
363 	.rbto_compare_nodes = sample_compare_nodes,
364 	.rbto_compare_key = sample_compare_key
365 };
366 
367 static u_int
368 n_align(u_int n, u_int align)
369 {
370 	return (n + align - 1) / align * align;
371 }
372 
373 static void
374 sample_init(void)
375 {
376 	const struct sample_elm *e;
377 	int l, mode, n;
378 	u_int size;
379 	char buf[16];
380 
381 	size = sizeof(struct sample_elm) +
382 	    sizeof(e->num_cpu[0]) * SAMPLE_MODE_NUM * ncpu;
383 	sizeof_sample_elm = n_align(size, __alignof(struct sample_elm));
384 
385 	sample_cpu_width = ecalloc(1, sizeof(*sample_cpu_width) * ncpu);
386 	for (n = 0; n < ncpu; n++) {
387 		sample_cpu_width[n] = 5;
388 		l = snprintf(buf, sizeof(buf), "CPU%d", n);
389 		if (sample_cpu_width[n] < (u_int)l)
390 			sample_cpu_width[n] = l;
391 	}
392 
393 	for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
394 		sample_n_kern_per_cpu[mode] = ecalloc(1,
395 		    sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu);
396 		sample_n_user_per_cpu[mode] = ecalloc(1,
397 		    sizeof(typeof(*sample_n_user_per_cpu[mode])) * ncpu);
398 		sample_n_per_event[mode] = ecalloc(1,
399 		    sizeof(typeof(*sample_n_per_event[mode])) * nevent);
400 		sample_n_per_event_cpu[mode] = ecalloc(1,
401 		    sizeof(typeof(*sample_n_per_event_cpu[mode])) *
402 		    nevent * ncpu);
403 	}
404 }
405 
406 static void
407 sample_clear_instantaneous(void *arg)
408 {
409 	struct sample_elm *e = (void *)arg;
410 
411 	e->num[SAMPLE_MODE_INSTANTANEOUS] = 0;
412 	memset(SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_INSTANTANEOUS),
413 	    0, sizeof(e->num_cpu[0]) * ncpu);
414 }
415 
416 static void
417 sample_reset(bool reset_accumulative)
418 {
419 	int mode;
420 
421 	for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
422 		if (mode == SAMPLE_MODE_ACCUMULATIVE && !reset_accumulative)
423 			continue;
424 
425 		sample_n_kern[mode] = 0;
426 		sample_n_user[mode] = 0;
427 		memset(sample_n_kern_per_cpu[mode], 0,
428 		    sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu);
429 		memset(sample_n_user_per_cpu[mode], 0,
430 		    sizeof(typeof(*sample_n_user_per_cpu[mode])) * ncpu);
431 		memset(sample_n_per_event[mode], 0,
432 		    sizeof(typeof(*sample_n_per_event[mode])) * nevent);
433 		memset(sample_n_per_event_cpu[mode], 0,
434 		    sizeof(typeof(*sample_n_per_event_cpu[mode])) *
435 		    nevent * ncpu);
436 	}
437 
438 	if (reset_accumulative) {
439 		rb_tree_init(&rb_tree_sample, &sample_ops);
440 		ptrarray_iterate(&sample_list[SAMPLE_MODE_ACCUMULATIVE], free);
441 		ptrarray_clear(&sample_list[SAMPLE_MODE_ACCUMULATIVE]);
442 		ptrarray_clear(&sample_list[SAMPLE_MODE_INSTANTANEOUS]);
443 	} else {
444 		ptrarray_iterate(&sample_list[SAMPLE_MODE_INSTANTANEOUS],
445 		    sample_clear_instantaneous);
446 		ptrarray_clear(&sample_list[SAMPLE_MODE_INSTANTANEOUS]);
447 	}
448 }
449 
450 static int __unused
451 sample_sortfunc_accumulative(const void *a, const void *b)
452 {
453 	struct sample_elm * const *ea = a;
454 	struct sample_elm * const *eb = b;
455 	return (*eb)->num[SAMPLE_MODE_ACCUMULATIVE] -
456 	    (*ea)->num[SAMPLE_MODE_ACCUMULATIVE];
457 }
458 
459 static int
460 sample_sortfunc_instantaneous(const void *a, const void *b)
461 {
462 	struct sample_elm * const *ea = a;
463 	struct sample_elm * const *eb = b;
464 	return (*eb)->num[SAMPLE_MODE_INSTANTANEOUS] -
465 	    (*ea)->num[SAMPLE_MODE_INSTANTANEOUS];
466 }
467 
468 static void
469 sample_sort_accumulative(void)
470 {
471 	qsort(sample_list[SAMPLE_MODE_ACCUMULATIVE].pa_ptrs,
472 	    sample_list[SAMPLE_MODE_ACCUMULATIVE].pa_inuse,
473 	    sizeof(struct sample_elm *), sample_sortfunc_accumulative);
474 }
475 
476 static void
477 sample_sort_instantaneous(void)
478 {
479 	qsort(sample_list[SAMPLE_MODE_INSTANTANEOUS].pa_ptrs,
480 	    sample_list[SAMPLE_MODE_INSTANTANEOUS].pa_inuse,
481 	    sizeof(struct sample_elm *), sample_sortfunc_instantaneous);
482 }
483 
484 static void
485 sample_collect(tprof_sample_t *s)
486 {
487 	struct sample_elm *e, *o;
488 	const char *name;
489 	size_t symid;
490 	uint64_t addr, offset;
491 	uint32_t flags = 0;
492 	uint32_t eventid, cpuid;
493 	int mode;
494 
495 	eventid = __SHIFTOUT(s->s_flags, TPROF_SAMPLE_COUNTER_MASK);
496 	cpuid = s->s_cpuid;
497 
498 	if (eventid >= nevent)	/* unknown event from tprof? */
499 		return;
500 
501 	for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
502 		sample_n_per_event[mode][eventid]++;
503 		sample_n_per_event_cpu[mode][nevent * cpuid + eventid]++;
504 	}
505 
506 	if ((s->s_flags & TPROF_SAMPLE_INKERNEL) == 0) {
507 		sample_n_user[SAMPLE_MODE_ACCUMULATIVE]++;
508 		sample_n_user[SAMPLE_MODE_INSTANTANEOUS]++;
509 		sample_n_user_per_cpu[SAMPLE_MODE_ACCUMULATIVE][cpuid]++;
510 		sample_n_user_per_cpu[SAMPLE_MODE_INSTANTANEOUS][cpuid]++;
511 
512 		name = NULL;
513 		addr = s->s_pid;	/* XXX */
514 		flags |= SAMPLE_ELM_FLAGS_USER;
515 
516 		if (!opt_userland)
517 			return;
518 	} else {
519 		sample_n_kern[SAMPLE_MODE_ACCUMULATIVE]++;
520 		sample_n_kern[SAMPLE_MODE_INSTANTANEOUS]++;
521 		sample_n_kern_per_cpu[SAMPLE_MODE_ACCUMULATIVE][cpuid]++;
522 		sample_n_kern_per_cpu[SAMPLE_MODE_INSTANTANEOUS][cpuid]++;
523 
524 		name = ksymlookup(s->s_pc, &offset, &symid);
525 		if (name != NULL) {
526 			addr = ksyms[symid]->value;
527 		} else {
528 			addr = s->s_pc;
529 		}
530 	}
531 
532 	e = ecalloc(1, sizeof_sample_elm);
533 	e->addr = addr;
534 	e->name = name;
535 	e->flags = flags;
536 	e->num[SAMPLE_MODE_ACCUMULATIVE] = 1;
537 	e->num[SAMPLE_MODE_INSTANTANEOUS] = 1;
538 	SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_ACCUMULATIVE)[cpuid] = 1;
539 	SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_INSTANTANEOUS)[cpuid] = 1;
540 	o = rb_tree_insert_node(&rb_tree_sample, e);
541 	if (o == e) {
542 		/* new symbol. add to list for sort */
543 		ptrarray_push(&sample_list[SAMPLE_MODE_ACCUMULATIVE], o);
544 		ptrarray_push(&sample_list[SAMPLE_MODE_INSTANTANEOUS], o);
545 	} else {
546 		/* already exists */
547 		free(e);
548 
549 		o->num[SAMPLE_MODE_ACCUMULATIVE]++;
550 		if (o->num[SAMPLE_MODE_INSTANTANEOUS]++ == 0) {
551 			/* new instantaneous symbols. add to list for sort */
552 			ptrarray_push(&sample_list[SAMPLE_MODE_INSTANTANEOUS],
553 			    o);
554 		}
555 		SAMPLE_ELM_NUM_CPU(o, SAMPLE_MODE_ACCUMULATIVE)[cpuid]++;
556 		SAMPLE_ELM_NUM_CPU(o, SAMPLE_MODE_INSTANTANEOUS)[cpuid]++;
557 	}
558 }
559 
560 static void
561 show_tprof_stat(int *lim)
562 {
563 	static struct tprof_stat tsbuf[2], *ts0, *ts;
564 	static u_int ts_i = 0;
565 	static int tprofstat_width[6];
566 	int ret, l;
567 	char tmpbuf[128];
568 
569 	ts0 = &tsbuf[ts_i++ & 1];
570 	ts = &tsbuf[ts_i & 1];
571 	ret = ioctl(devfd, TPROF_IOC_GETSTAT, ts);
572 	if (ret == -1)
573 		die_errc(EXIT_FAILURE, errno, "TPROF_IOC_GETSTAT");
574 
575 #define TS_PRINT(idx, label, _m)					\
576 	do {								\
577 		__CTASSERT(idx < __arraycount(tprofstat_width));	\
578 		lim_printf(lim, "%s", label);			\
579 		l = snprintf(tmpbuf, sizeof(tmpbuf), "%"PRIu64, ts->_m);\
580 		if (ts->_m != ts0->_m)					\
581 			l += snprintf(tmpbuf + l, sizeof(tmpbuf) - l,	\
582 			    "(+%"PRIu64")", ts->_m - ts0->_m);		\
583 		assert(l < (int)sizeof(tmpbuf));			\
584 		if (tprofstat_width[idx] < l)				\
585 			tprofstat_width[idx] = l;			\
586 		lim_printf(lim, "%-*.*s  ", tprofstat_width[idx],	\
587 		    tprofstat_width[idx], tmpbuf);			\
588 	} while (0)
589 	lim_printf(lim, "tprof ");
590 	TS_PRINT(0, "sample:", ts_sample);
591 	TS_PRINT(1, "overflow:", ts_overflow);
592 	TS_PRINT(2, "buf:", ts_buf);
593 	TS_PRINT(3, "emptybuf:", ts_emptybuf);
594 	TS_PRINT(4, "dropbuf:", ts_dropbuf);
595 	TS_PRINT(5, "dropbuf_sample:", ts_dropbuf_sample);
596 }
597 
598 static void
599 show_timestamp(void)
600 {
601 	struct timeval tv;
602 	gettimeofday(&tv, NULL);
603 	printf("%-8.8s", &(ctime((time_t *)&tv.tv_sec)[11]));
604 }
605 
606 static void
607 show_counters_alloc(void)
608 {
609 	size_t sz = 2 * ncpu * nevent * sizeof(*counters);
610 	counters = ecalloc(1, sz);
611 }
612 
613 static void
614 show_counters(int *lim)
615 {
616 	tprof_counts_t countsbuf;
617 	uint64_t *cn[2], *c0, *c;
618 	u_int i;
619 	int n, ret;
620 
621 	cn[0] = counters;
622 	cn[1] = counters + ncpu * nevent;
623 	c0 = cn[counters_i++ & 1];
624 	c = cn[counters_i & 1];
625 
626 	for (n = 0; n < ncpu; n++) {
627 		countsbuf.c_cpu = n;
628 		ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &countsbuf);
629 		if (ret == -1)
630 			die_errc(EXIT_FAILURE, errno, "TPROF_IOC_GETCOUNTS");
631 
632 		for (i = 0; i < nevent; i++)
633 			c[n * nevent + i] = countsbuf.c_count[i];
634 	}
635 
636 	if (do_redraw) {
637 		lim_printf(lim, "%-22s", "Event counter (delta)");
638 		for (n = 0; n < ncpu; n++) {
639 			char cpuname[16];
640 			snprintf(cpuname, sizeof(cpuname), "CPU%u", n);
641 			lim_printf(lim, "%11s", cpuname);
642 		}
643 		lim_newline(lim);
644 	} else {
645 		printf("\n");
646 	}
647 
648 	for (i = 0; i < nevent; i++) {
649 		lim_printf(lim, "%-22.22s", eventname[i]);
650 		for (n = 0; n < ncpu; n++) {
651 			lim_printf(lim, "%11"PRIu64,
652 			    c[n * nevent + i] - c0[n * nevent + i]);
653 		}
654 		lim_newline(lim);
655 	}
656 	lim_newline(lim);
657 }
658 
659 static void
660 show_count_per_event(int *lim)
661 {
662 	u_int i, nsample_total;
663 	int n, l;
664 	char buf[32];
665 
666 	nsample_total = sample_n_kern[opt_mode] + sample_n_user[opt_mode];
667 	if (nsample_total == 0)
668 		nsample_total = 1;
669 
670 	/* calc width in advance */
671 	for (i = 0; i < nevent; i++) {
672 		l = snprintf(buf, sizeof(buf), "%"PRIu64,
673 		    sample_n_per_event[opt_mode][i]);
674 		if (sample_event_width < (u_int)l) {
675 			sample_event_width = l;
676 			do_redraw = true;
677 		}
678 	}
679 	for (n = 0; n < ncpu; n++) {
680 		uint64_t sum = 0;
681 		for (i = 0; i < nevent; i++)
682 			sum += sample_n_per_event_cpu[opt_mode][nevent * n + i];
683 		l = snprintf(buf, sizeof(buf), "%"PRIu64, sum);
684 		if (sample_cpu_width[n] < (u_int)l) {
685 			sample_cpu_width[n] = l;
686 			do_redraw = true;
687 		}
688 	}
689 
690 	if (do_redraw) {
691 		lim_printf(lim, "  Rate %*s %-*s",
692 		    sample_event_width, "Sample#",
693 		    SYMBOL_LEN, "Eventname");
694 		for (n = 0; n < ncpu; n++) {
695 			snprintf(buf, sizeof(buf), "CPU%d", n);
696 			lim_printf(lim, " %*s", sample_cpu_width[n], buf);
697 		}
698 		lim_newline(lim);
699 
700 		lim_printf(lim, "------ %*.*s %*.*s",
701 		    sample_event_width, sample_event_width, LINESTR,
702 		    SYMBOL_LEN, SYMBOL_LEN, LINESTR);
703 		for (n = 0; n < ncpu; n++) {
704 			lim_printf(lim, " %*.*s",
705 			    sample_cpu_width[n], sample_cpu_width[n], LINESTR);
706 		}
707 		lim_newline(lim);
708 	} else {
709 		printf("\n\n");
710 	}
711 
712 	for (i = 0; i < nevent; i++) {
713 		if (sample_n_per_event[opt_mode][i] >= nsample_total) {
714 			lim_printf(lim, "%5.1f%%", 100.0 *
715 			    sample_n_per_event[opt_mode][i] / nsample_total);
716 		} else {
717 			lim_printf(lim, "%5.2f%%", 100.0 *
718 			    sample_n_per_event[opt_mode][i] / nsample_total);
719 		}
720 		lim_printf(lim, " %*"PRIu64" ", sample_event_width,
721 		    sample_n_per_event[opt_mode][i]);
722 
723 		lim_printf(lim, "%-32.32s", eventname[i]);
724 		for (n = 0; n < ncpu; n++) {
725 			lim_printf(lim, " %*"PRIu64, sample_cpu_width[n],
726 			    sample_n_per_event_cpu[opt_mode][nevent * n + i]);
727 		}
728 		lim_newline(lim);
729 	}
730 }
731 
732 static void
733 sample_show(void)
734 {
735 	struct sample_elm *e;
736 	struct ptrarray *samples;
737 	u_int nsample_total;
738 	int i, l, lim, n, ndisp;
739 	char namebuf[32];
740 	const char *name;
741 
742 	if (nshow++ == 0) {
743 		printf("\n");
744 		if (!nontty) {
745 			signal(SIGWINCH, sigwinch_handler);
746 			signal(SIGINT, die);
747 			signal(SIGQUIT, die);
748 			signal(SIGTERM, die);
749 			signal(SIGTSTP, sigtstp_handler);
750 
751 			tty_setup();
752 		}
753 	} else {
754 		reset_cursor_pos();
755 	}
756 
757 	int margin_lines = 7;
758 
759 	margin_lines += 3 + nevent;	/* show_counter_per_event() */
760 
761 	if (opt_mode == SAMPLE_MODE_INSTANTANEOUS)
762 		sample_sort_instantaneous();
763 	else
764 		sample_sort_accumulative();
765 	samples = &sample_list[opt_mode];
766 
767 	if (opt_showcounter)
768 		margin_lines += 2 + nevent;
769 	if (opt_userland)
770 		margin_lines += 1;
771 
772 	ndisp = samples->pa_inuse;
773 	if (!nontty && ndisp > (win.ws_row - margin_lines))
774 		ndisp = win.ws_row - margin_lines;
775 
776 	lim = win.ws_col;
777 	if (opt_mode == SAMPLE_MODE_ACCUMULATIVE)
778 		lim_printf(&lim, "[Accumulative mode] ");
779 	show_tprof_stat(&lim);
780 
781 	if (lim >= 16) {
782 		l = win.ws_col - lim;
783 		if (!nontty) {
784 			clr_to_eol();
785 			for (; l <= win.ws_col - 17; l = ((l + 8) & -8))
786 				printf("\t");
787 		}
788 		show_timestamp();
789 	}
790 	lim_newline(&lim);
791 	lim_newline(&lim);
792 
793 	if (opt_showcounter)
794 		show_counters(&lim);
795 
796 	show_count_per_event(&lim);
797 	lim_newline(&lim);
798 
799 	if (do_redraw) {
800 		lim_printf(&lim, "  Rate %*s %-*s",
801 		    sample_event_width, "Sample#",
802 		    SYMBOL_LEN, "Symbol");
803 		for (n = 0; n < ncpu; n++) {
804 			snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
805 			lim_printf(&lim, " %*s", sample_cpu_width[n], namebuf);
806 		}
807 		lim_newline(&lim);
808 
809 		lim_printf(&lim, "------ %*.*s %*.*s",
810 		    sample_event_width, sample_event_width, LINESTR,
811 		    SYMBOL_LEN, SYMBOL_LEN, LINESTR);
812 		for (n = 0; n < ncpu; n++) {
813 			lim_printf(&lim, " %*.*s", sample_cpu_width[n],
814 			    sample_cpu_width[n], LINESTR);
815 		}
816 		lim_newline(&lim);
817 	} else {
818 		printf("\n\n");
819 	}
820 
821 	for (i = 0; i < ndisp; i++) {
822 		e = (struct sample_elm *)samples->pa_ptrs[i];
823 		name = e->name;
824 		if (name == NULL) {
825 			if (e->flags & SAMPLE_ELM_FLAGS_USER) {
826 				snprintf(namebuf, sizeof(namebuf),
827 				    "<PID:%"PRIu64">", e->addr);
828 			} else {
829 				snprintf(namebuf, sizeof(namebuf),
830 				    "0x%016"PRIx64, e->addr);
831 			}
832 			name = namebuf;
833 		}
834 
835 		nsample_total = sample_n_kern[opt_mode];
836 		if (opt_userland)
837 			nsample_total += sample_n_user[opt_mode];
838 		/*
839 		 * even when only kernel mode events are configured,
840 		 * interrupts may still occur in the user mode state.
841 		 */
842 		if (nsample_total == 0)
843 			nsample_total = 1;
844 
845 		if (e->num[opt_mode] >= nsample_total) {
846 			lim_printf(&lim, "%5.1f%%", 100.0 *
847 			    e->num[opt_mode] / nsample_total);
848 		} else {
849 			lim_printf(&lim, "%5.2f%%", 100.0 *
850 			    e->num[opt_mode] / nsample_total);
851 		}
852 		lim_printf(&lim, " %*u %-32.32s", sample_event_width,
853 		    e->num[opt_mode], name);
854 
855 		for (n = 0; n < ncpu; n++) {
856 			if (SAMPLE_ELM_NUM_CPU(e, opt_mode)[n] == 0) {
857 				lim_printf(&lim, " %*s", sample_cpu_width[n],
858 				    ".");
859 			} else {
860 				lim_printf(&lim, " %*u", sample_cpu_width[n],
861 				    SAMPLE_ELM_NUM_CPU(e, opt_mode)[n]);
862 			}
863 		}
864 		lim_newline(&lim);
865 	}
866 
867 	if ((u_int)ndisp != samples->pa_inuse) {
868 		lim_printf(&lim, "     : %*s (more %zu symbols omitted)",
869 		    sample_event_width, ":", samples->pa_inuse - ndisp);
870 		lim_newline(&lim);
871 	} else if (!nontty) {
872 		for (i = ndisp; i <= win.ws_row - margin_lines; i++) {
873 			printf("~");
874 			lim_newline(&lim);
875 		}
876 	}
877 
878 	if (do_redraw) {
879 		lim_printf(&lim, "------ %*.*s %*.*s",
880 		    sample_event_width, sample_event_width, LINESTR,
881 		    SYMBOL_LEN, SYMBOL_LEN, LINESTR);
882 		for (n = 0; n < ncpu; n++) {
883 			lim_printf(&lim, " %*.*s",
884 			    sample_cpu_width[n], sample_cpu_width[n], LINESTR);
885 		}
886 		lim_newline(&lim);
887 	} else {
888 		printf("\n");
889 	}
890 
891 	lim_printf(&lim, "Total  %*u %-32.32s",
892 	    sample_event_width, sample_n_kern[opt_mode], "in-kernel");
893 	for (n = 0; n < ncpu; n++) {
894 		lim_printf(&lim, " %*u", sample_cpu_width[n],
895 		    sample_n_kern_per_cpu[opt_mode][n]);
896 	}
897 
898 	if (opt_userland) {
899 		lim_newline(&lim);
900 		lim_printf(&lim, "       %*u %-32.32s",
901 		    sample_event_width, sample_n_user[opt_mode], "userland");
902 		for (n = 0; n < ncpu; n++) {
903 			lim_printf(&lim, " %*u", sample_cpu_width[n],
904 			    sample_n_user_per_cpu[opt_mode][n]);
905 		}
906 	}
907 
908 	if (nontty)
909 		printf("\n");
910 	else
911 		clr_to_eol();
912 }
913 
914 __dead static void
915 tprof_top_usage(void)
916 {
917 	fprintf(stderr, "%s top [-acu] [-e name[,scale] [-e ...]]"
918 	    " [-i interval]\n", getprogname());
919 	exit(EXIT_FAILURE);
920 }
921 
922 __dead void
923 tprof_top(int argc, char **argv)
924 {
925 	tprof_param_t params[TPROF_MAXCOUNTERS];
926 	struct itimerval it;
927 	ssize_t tprof_bufsize, len;
928 	u_int i;
929 	int ch, ret;
930 	char *tprof_buf, *p, *errmsg;
931 	bool noinput = false;
932 
933 	memset(params, 0, sizeof(params));
934 	nevent = 0;
935 
936 	while ((ch = getopt(argc, argv, "ace:i:L:u")) != -1) {
937 		switch (ch) {
938 		case 'a':
939 			opt_mode = SAMPLE_MODE_ACCUMULATIVE;
940 			break;
941 		case 'c':
942 			opt_showcounter = 1;
943 			break;
944 		case 'e':
945 			if (tprof_parse_event(&params[nevent], optarg,
946 			    TPROF_PARSE_EVENT_F_ALLOWSCALE,
947 			    &eventname[nevent], &errmsg) != 0) {
948 				die_errc(EXIT_FAILURE, 0, "%s", errmsg);
949 			}
950 			nevent++;
951 			if (nevent > __arraycount(params) ||
952 			    nevent > ncounters)
953 				die_errc(EXIT_FAILURE, 0,
954 				    "Too many events. Only a maximum of %d "
955 				    "counters can be used.", ncounters);
956 			break;
957 		case 'i':
958 			top_interval = strtol(optarg, &p, 10);
959 			if (*p != '\0' || top_interval <= 0)
960 				die_errc(EXIT_FAILURE, 0,
961 				    "Bad/invalid interval: %s", optarg);
962 			break;
963 		case 'u':
964 			opt_userland = 1;
965 			break;
966 		default:
967 			tprof_top_usage();
968 		}
969 	}
970 	argc -= optind;
971 	argv += optind;
972 
973 	if (argc != 0)
974 		tprof_top_usage();
975 
976 	if (nevent == 0) {
977 		const char *defaultevent = cycle_event_name();
978 		if (defaultevent == NULL)
979 			die_errc(EXIT_FAILURE, 0, "cpu not supported");
980 
981 		tprof_event_lookup(defaultevent, &params[nevent]);
982 		eventname[nevent] = defaultevent;
983 		nevent++;
984 	}
985 
986 	sample_init();
987 	show_counters_alloc();
988 
989 	for (i = 0; i < nevent; i++) {
990 		params[i].p_counter = i;
991 		params[i].p_flags |= TPROF_PARAM_KERN | TPROF_PARAM_PROFILE;
992 		if (opt_userland)
993 			params[i].p_flags |= TPROF_PARAM_USER;
994 		ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, &params[i]);
995 		if (ret == -1)
996 			die_errc(EXIT_FAILURE, errno,
997 			    "TPROF_IOC_CONFIGURE_EVENT: %s", eventname[i]);
998 	}
999 
1000 	tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
1001 	ret = ioctl(devfd, TPROF_IOC_START, &mask);
1002 	if (ret == -1)
1003 		die_errc(EXIT_FAILURE, errno, "TPROF_IOC_START");
1004 
1005 	ksyms = ksymload(&nksyms);
1006 
1007 	signal(SIGALRM, sigalrm_handler);
1008 
1009 	it.it_interval.tv_sec = it.it_value.tv_sec = top_interval;
1010 	it.it_interval.tv_usec = it.it_value.tv_usec = 0;
1011 	setitimer(ITIMER_REAL, &it, NULL);
1012 
1013 	sample_reset(true);
1014 	printf("collecting samples...");
1015 	fflush(stdout);
1016 
1017 	tprof_bufsize = sizeof(tprof_sample_t) * 1024 * 32;
1018 	tprof_buf = emalloc(tprof_bufsize);
1019 	do {
1020 		bool force_update = false;
1021 
1022 		while (sigalrm == 0 && !force_update) {
1023 			fd_set r;
1024 			int nfound;
1025 			char c;
1026 
1027 			FD_ZERO(&r);
1028 			if (!noinput)
1029 				FD_SET(STDIN_FILENO, &r);
1030 			FD_SET(devfd, &r);
1031 			nfound = select(devfd + 1, &r, NULL, NULL, NULL);
1032 			if (nfound == -1) {
1033 				if (errno == EINTR)
1034 					break;
1035 				die_errc(EXIT_FAILURE, errno, "select");
1036 			}
1037 
1038 			if (FD_ISSET(STDIN_FILENO, &r)) {
1039 				len = read(STDIN_FILENO, &c, 1);
1040 				if (len <= 0) {
1041 					noinput = true;
1042 					continue;
1043 				}
1044 				switch (c) {
1045 				case 0x0c:	/* ^L */
1046 					do_redraw = true;
1047 					break;
1048 				case 'a':
1049 					/* toggle mode */
1050 					opt_mode = (opt_mode + 1) %
1051 					    SAMPLE_MODE_NUM;
1052 					do_redraw = true;
1053 					break;
1054 				case 'c':
1055 					/* toggle mode */
1056 					opt_showcounter ^= 1;
1057 					do_redraw = true;
1058 					break;
1059 				case 'q':
1060 					goto done;
1061 				case 'z':
1062 					sample_reset(true);
1063 					break;
1064 				default:
1065 					continue;
1066 				}
1067 				force_update = true;
1068 			}
1069 
1070 			if (FD_ISSET(devfd, &r)) {
1071 				len = read(devfd, tprof_buf, tprof_bufsize);
1072 				if (len == -1 && errno != EINTR)
1073 					die_errc(EXIT_FAILURE, errno, "read");
1074 				if (len > 0) {
1075 					tprof_sample_t *s =
1076 					    (tprof_sample_t *)tprof_buf;
1077 					while (s <
1078 					    (tprof_sample_t *)(tprof_buf + len))
1079 						sample_collect(s++);
1080 				}
1081 			}
1082 		}
1083 		sigalrm = 0;
1084 
1085 		/* update screen */
1086 		sample_show();
1087 		fflush(stdout);
1088 		do_redraw = false;
1089 		if (force_update)
1090 			continue;
1091 
1092 		sample_reset(false);
1093 
1094 	} while (!nontty);
1095 
1096  done:
1097 	die(0);
1098 }
1099