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(¶ms[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, ¶ms[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, ¶ms[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