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