Home | History | Annotate | Line # | Download | only in tprof
tprof_top.c revision 1.6
      1 /*	$NetBSD: tprof_top.c,v 1.6 2022/12/16 08:00:47 ryo 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.6 2022/12/16 08:00:47 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 static int
    923 parse_event_scale(tprof_param_t *param, const char *str)
    924 {
    925 	double d;
    926 	uint64_t n;
    927 	char *p;
    928 
    929 	if (str[0] == '=') {
    930 		str++;
    931 		n = strtoull(str, &p, 0);
    932 		if (*p != '\0')
    933 			return -1;
    934 		param->p_value2 = n;
    935 		param->p_flags |= TPROF_PARAM_VALUE2_TRIGGERCOUNT;
    936 	} else {
    937 		if (strncasecmp("0x", str, 2) == 0)
    938 			d = strtol(str, &p, 0);
    939 		else
    940 			d = strtod(str, &p);
    941 		if (*p != '\0')
    942 			return -1;
    943 		param->p_value2 = 0x100000000ULL / d;
    944 		param->p_flags |= TPROF_PARAM_VALUE2_SCALE;
    945 	}
    946 	return 0;
    947 }
    948 
    949 __dead void
    950 tprof_top(int argc, char **argv)
    951 {
    952 	tprof_param_t params[TPROF_MAXCOUNTERS];
    953 	struct itimerval it;
    954 	ssize_t tprof_bufsize, len;
    955 	u_int i;
    956 	int ch, ret;
    957 	char *tprof_buf, *tokens[2], *p;
    958 	bool noinput = false;
    959 
    960 	memset(params, 0, sizeof(params));
    961 	nevent = 0;
    962 
    963 	while ((ch = getopt(argc, argv, "ace:i:L:u")) != -1) {
    964 		switch (ch) {
    965 		case 'a':
    966 			opt_mode = SAMPLE_MODE_ACCUMULATIVE;
    967 			break;
    968 		case 'c':
    969 			opt_showcounter = 1;
    970 			break;
    971 		case 'e':
    972 			p = estrdup(optarg);
    973 			tokens[0] = strtok(p, ",");
    974 			tokens[1] = strtok(NULL, ",");
    975 			tprof_event_lookup(tokens[0], &params[nevent]);
    976 			if (tokens[1] != NULL) {
    977 				if (parse_event_scale(&params[nevent],
    978 				    tokens[1]) != 0) {
    979 					die_errc(EXIT_FAILURE, 0,
    980 					    "invalid scale: %s", tokens[1]);
    981 				}
    982 			}
    983 			eventname[nevent] = tokens[0];
    984 			nevent++;
    985 			if (nevent > __arraycount(params) ||
    986 			    nevent > ncounters)
    987 				die_errc(EXIT_FAILURE, 0,
    988 				    "Too many events. Only a maximum of %d "
    989 				    "counters can be used.", ncounters);
    990 			break;
    991 		case 'i':
    992 			top_interval = strtol(optarg, &p, 10);
    993 			if (*p != '\0' || top_interval <= 0)
    994 				die_errc(EXIT_FAILURE, 0,
    995 				    "Bad/invalid interval: %s", optarg);
    996 			break;
    997 		case 'u':
    998 			opt_userland = 1;
    999 			break;
   1000 		default:
   1001 			tprof_top_usage();
   1002 		}
   1003 	}
   1004 	argc -= optind;
   1005 	argv += optind;
   1006 
   1007 	if (argc != 0)
   1008 		tprof_top_usage();
   1009 
   1010 	if (nevent == 0) {
   1011 		const char *defaultevent = cycle_event_name();
   1012 		if (defaultevent == NULL)
   1013 			die_errc(EXIT_FAILURE, 0, "cpu not supported");
   1014 
   1015 		tprof_event_lookup(defaultevent, &params[nevent]);
   1016 		eventname[nevent] = defaultevent;
   1017 		nevent++;
   1018 	}
   1019 
   1020 	sample_init();
   1021 	show_counters_alloc();
   1022 
   1023 	for (i = 0; i < nevent; i++) {
   1024 		params[i].p_counter = i;
   1025 		params[i].p_flags |= TPROF_PARAM_KERN | TPROF_PARAM_PROFILE;
   1026 		if (opt_userland)
   1027 			params[i].p_flags |= TPROF_PARAM_USER;
   1028 		ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, &params[i]);
   1029 		if (ret == -1)
   1030 			die_errc(EXIT_FAILURE, errno,
   1031 			    "TPROF_IOC_CONFIGURE_EVENT: %s", eventname[i]);
   1032 	}
   1033 
   1034 	tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
   1035 	ret = ioctl(devfd, TPROF_IOC_START, &mask);
   1036 	if (ret == -1)
   1037 		die_errc(EXIT_FAILURE, errno, "TPROF_IOC_START");
   1038 
   1039 	ksyms = ksymload(&nksyms);
   1040 
   1041 	signal(SIGALRM, sigalrm_handler);
   1042 
   1043 	it.it_interval.tv_sec = it.it_value.tv_sec = top_interval;
   1044 	it.it_interval.tv_usec = it.it_value.tv_usec = 0;
   1045 	setitimer(ITIMER_REAL, &it, NULL);
   1046 
   1047 	sample_reset(true);
   1048 	printf("collecting samples...");
   1049 	fflush(stdout);
   1050 
   1051 	tprof_bufsize = sizeof(tprof_sample_t) * 1024 * 32;
   1052 	tprof_buf = emalloc(tprof_bufsize);
   1053 	do {
   1054 		bool force_update = false;
   1055 
   1056 		while (sigalrm == 0 && !force_update) {
   1057 			fd_set r;
   1058 			int nfound;
   1059 			char c;
   1060 
   1061 			FD_ZERO(&r);
   1062 			if (!noinput)
   1063 				FD_SET(STDIN_FILENO, &r);
   1064 			FD_SET(devfd, &r);
   1065 			nfound = select(devfd + 1, &r, NULL, NULL, NULL);
   1066 			if (nfound == -1) {
   1067 				if (errno == EINTR)
   1068 					break;
   1069 				die_errc(EXIT_FAILURE, errno, "select");
   1070 			}
   1071 
   1072 			if (FD_ISSET(STDIN_FILENO, &r)) {
   1073 				len = read(STDIN_FILENO, &c, 1);
   1074 				if (len <= 0) {
   1075 					noinput = true;
   1076 					continue;
   1077 				}
   1078 				switch (c) {
   1079 				case 0x0c:	/* ^L */
   1080 					do_redraw = true;
   1081 					break;
   1082 				case 'a':
   1083 					/* toggle mode */
   1084 					opt_mode = (opt_mode + 1) %
   1085 					    SAMPLE_MODE_NUM;
   1086 					do_redraw = true;
   1087 					break;
   1088 				case 'c':
   1089 					/* toggle mode */
   1090 					opt_showcounter ^= 1;
   1091 					do_redraw = true;
   1092 					break;
   1093 				case 'q':
   1094 					goto done;
   1095 				case 'z':
   1096 					sample_reset(true);
   1097 					break;
   1098 				default:
   1099 					continue;
   1100 				}
   1101 				force_update = true;
   1102 			}
   1103 
   1104 			if (FD_ISSET(devfd, &r)) {
   1105 				len = read(devfd, tprof_buf, tprof_bufsize);
   1106 				if (len == -1 && errno != EINTR)
   1107 					die_errc(EXIT_FAILURE, errno, "read");
   1108 				if (len > 0) {
   1109 					tprof_sample_t *s =
   1110 					    (tprof_sample_t *)tprof_buf;
   1111 					while (s <
   1112 					    (tprof_sample_t *)(tprof_buf + len))
   1113 						sample_collect(s++);
   1114 				}
   1115 			}
   1116 		}
   1117 		sigalrm = 0;
   1118 
   1119 		/* update screen */
   1120 		sample_show();
   1121 		fflush(stdout);
   1122 		do_redraw = false;
   1123 		if (force_update)
   1124 			continue;
   1125 
   1126 		sample_reset(false);
   1127 
   1128 	} while (!nontty);
   1129 
   1130  done:
   1131 	die(0);
   1132 }
   1133