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