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