xref: /netbsd-src/usr.sbin/tprof/tprof_top.c (revision 90313c06e62e910bf0d1bb24faa9d17dcefd0ab6)
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
reset_cursor_pos(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
clr_to_eol(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
lim_newline(int * lim)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
lim_printf(int * lim,const char * fmt,...)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
sigwinch_handler(int signo)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
tty_setup(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
tty_restore(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
sigtstp_handler(int signo)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
sigalrm_handler(int signo)268 sigalrm_handler(int signo)
269 {
270 	sigalrm = 1;
271 }
272 
273 __dead static void
die(int signo)274 die(int signo)
275 {
276 	tty_restore();
277 	printf("\n");
278 
279 	exit(EXIT_SUCCESS);
280 }
281 
282 __dead static void
die_errc(int status,int code,const char * fmt,...)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
ptrarray_push(struct ptrarray * ptrarray,void * ptr)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
ptrarray_iterate(struct ptrarray * ptrarray,void (* ifunc)(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
ptrarray_clear(struct ptrarray * ptrarray)324 ptrarray_clear(struct ptrarray *ptrarray)
325 {
326 	ptrarray->pa_inuse = 0;
327 }
328 
329 static int
sample_compare_key(void * ctx,const void * n1,const void * keyp)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
sample_compare_nodes(void * ctx,const void * n1,const void * n2)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
n_align(u_int n,u_int align)350 n_align(u_int n, u_int align)
351 {
352 	return (n + align - 1) / align * align;
353 }
354 
355 static void
sample_init(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
sample_clear_instantaneous(void * arg)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
sample_reset(bool reset_accumulative)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
sample_sortfunc_accumulative(const void * a,const void * b)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
sample_sortfunc_instantaneous(const void * a,const void * b)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
sample_sort_accumulative(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
sample_sort_instantaneous(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
sample_collect(tprof_sample_t * s)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
show_tprof_stat(int * lim)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
show_timestamp(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
show_counters_alloc(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
show_counters(int * lim)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
show_count_per_event(int * lim)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
sample_show(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
tprof_top_usage(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
tprof_top(int argc,char ** argv)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