Home | History | Annotate | Line # | Download | only in tprof
tprof_top.c revision 1.8
      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 (at) 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