tprof_top.c revision 1.5 1 /* $NetBSD: tprof_top.c,v 1.5 2022/12/09 02:19:07 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.5 2022/12/09 02:19:07 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 printf("%s", 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 printf("%s", 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 printf("%s", 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 static void
292 die(int signo)
293 {
294 tty_restore();
295 printf("\n");
296
297 exit(EXIT_SUCCESS);
298 }
299
300 static void __dead
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 (i = 0; i < nevent; i++) {
680 for (n = 0; n < ncpu; n++) {
681 l = snprintf(buf, sizeof(buf), "%"PRIu64,
682 sample_n_per_event_cpu[opt_mode][nevent * n + i]);
683 if (sample_cpu_width[n] < (u_int)l) {
684 sample_cpu_width[n] = l;
685 do_redraw = true;
686 }
687 }
688 }
689
690 if (do_redraw) {
691 lim_printf(lim, " Rate %*s Eventname ",
692 sample_event_width, "Sample#");
693 for (n = 0; n < ncpu; n++) {
694 snprintf(buf, sizeof(buf), "CPU%d", n);
695 lim_printf(lim, " %*s", sample_cpu_width[n], buf);
696 }
697 lim_newline(lim);
698
699 lim_printf(lim, "------ %*.*s %*.*s",
700 sample_event_width, sample_event_width, LINESTR,
701 SYMBOL_LEN, SYMBOL_LEN, LINESTR);
702 for (n = 0; n < ncpu; n++) {
703 lim_printf(lim, " %*.*s",
704 sample_cpu_width[n], sample_cpu_width[n], LINESTR);
705 }
706 lim_newline(lim);
707 } else {
708 printf("\n\n");
709 }
710
711 for (i = 0; i < nevent; i++) {
712 if (sample_n_per_event[opt_mode][i] >= nsample_total) {
713 lim_printf(lim, "%5.1f%%", 100.0 *
714 sample_n_per_event[opt_mode][i] / nsample_total);
715 } else {
716 lim_printf(lim, "%5.2f%%", 100.0 *
717 sample_n_per_event[opt_mode][i] / nsample_total);
718 }
719 lim_printf(lim, " %*"PRIu64" ", sample_event_width,
720 sample_n_per_event[opt_mode][i]);
721
722 lim_printf(lim, "%-32.32s", eventname[i]);
723 for (n = 0; n < ncpu; n++) {
724 lim_printf(lim, " %*"PRIu64, sample_cpu_width[n],
725 sample_n_per_event_cpu[opt_mode][nevent * n + i]);
726 }
727 lim_newline(lim);
728 }
729 }
730
731 static void
732 sample_show(void)
733 {
734 struct sample_elm *e;
735 struct ptrarray *samples;
736 u_int nsample_total;
737 int i, l, lim, n, ndisp;
738 char namebuf[32];
739 const char *name;
740
741 if (nshow++ == 0) {
742 printf("\n");
743 if (!nontty) {
744 signal(SIGWINCH, sigwinch_handler);
745 signal(SIGINT, die);
746 signal(SIGQUIT, die);
747 signal(SIGTERM, die);
748 signal(SIGTSTP, sigtstp_handler);
749
750 tty_setup();
751 }
752 } else {
753 reset_cursor_pos();
754 }
755
756 int margin_lines = 7;
757
758 margin_lines += 3 + nevent; /* show_counter_per_event() */
759
760 if (opt_mode == SAMPLE_MODE_INSTANTANEOUS)
761 sample_sort_instantaneous();
762 else
763 sample_sort_accumulative();
764 samples = &sample_list[opt_mode];
765
766 if (opt_showcounter)
767 margin_lines += 2 + nevent;
768 if (opt_userland)
769 margin_lines += 1;
770
771 ndisp = samples->pa_inuse;
772 if (!nontty && ndisp > (win.ws_row - margin_lines))
773 ndisp = win.ws_row - margin_lines;
774
775 lim = win.ws_col;
776 if (opt_mode == SAMPLE_MODE_ACCUMULATIVE)
777 lim_printf(&lim, "[Accumulative mode] ");
778 show_tprof_stat(&lim);
779
780 if (lim >= 16) {
781 l = win.ws_col - lim;
782 if (!nontty) {
783 clr_to_eol();
784 for (; l <= win.ws_col - 17; l = ((l + 8) & -8))
785 printf("\t");
786 }
787 show_timestamp();
788 }
789 lim_newline(&lim);
790 lim_newline(&lim);
791
792 if (opt_showcounter)
793 show_counters(&lim);
794
795 show_count_per_event(&lim);
796 lim_newline(&lim);
797
798 if (do_redraw) {
799 lim_printf(&lim, " Rate %*s Symbol ",
800 sample_event_width, "Sample#");
801 for (n = 0; n < ncpu; n++) {
802 snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
803 lim_printf(&lim, " %*s", sample_cpu_width[n], namebuf);
804 }
805 lim_newline(&lim);
806
807 lim_printf(&lim, "------ %*.*s %*.*s",
808 sample_event_width, sample_event_width, LINESTR,
809 SYMBOL_LEN, SYMBOL_LEN, LINESTR);
810 for (n = 0; n < ncpu; n++) {
811 lim_printf(&lim, " %*.*s", sample_cpu_width[n],
812 sample_cpu_width[n], LINESTR);
813 }
814 lim_newline(&lim);
815 } else {
816 printf("\n\n");
817 }
818
819 for (i = 0; i < ndisp; i++) {
820 e = (struct sample_elm *)samples->pa_ptrs[i];
821 name = e->name;
822 if (name == NULL) {
823 if (e->flags & SAMPLE_ELM_FLAGS_USER) {
824 snprintf(namebuf, sizeof(namebuf),
825 "<PID:%"PRIu64">", e->addr);
826 } else {
827 snprintf(namebuf, sizeof(namebuf),
828 "0x%016"PRIx64, e->addr);
829 }
830 name = namebuf;
831 }
832
833 nsample_total = sample_n_kern[opt_mode];
834 if (opt_userland)
835 nsample_total += sample_n_user[opt_mode];
836 /*
837 * even when only kernel mode events are configured,
838 * interrupts may still occur in the user mode state.
839 */
840 if (nsample_total == 0)
841 nsample_total = 1;
842
843 if (e->num[opt_mode] >= nsample_total) {
844 lim_printf(&lim, "%5.1f%%", 100.0 *
845 e->num[opt_mode] / nsample_total);
846 } else {
847 lim_printf(&lim, "%5.2f%%", 100.0 *
848 e->num[opt_mode] / nsample_total);
849 }
850 lim_printf(&lim, " %*u %-32.32s", sample_event_width,
851 e->num[opt_mode], name);
852
853 for (n = 0; n < ncpu; n++) {
854 if (SAMPLE_ELM_NUM_CPU(e, opt_mode)[n] == 0) {
855 lim_printf(&lim, " %*s", sample_cpu_width[n],
856 ".");
857 } else {
858 lim_printf(&lim, " %*u", sample_cpu_width[n],
859 SAMPLE_ELM_NUM_CPU(e, opt_mode)[n]);
860 }
861 }
862 lim_newline(&lim);
863 }
864
865 if ((u_int)ndisp != samples->pa_inuse) {
866 lim_printf(&lim, " : %*s (more %zu symbols omitted)",
867 sample_event_width, ":", samples->pa_inuse - ndisp);
868 lim_newline(&lim);
869 } else if (!nontty) {
870 for (i = ndisp; i <= win.ws_row - margin_lines; i++) {
871 printf("~");
872 lim_newline(&lim);
873 }
874 }
875
876 if (do_redraw) {
877 lim_printf(&lim, "------ %*.*s %*.*s",
878 sample_event_width, sample_event_width, LINESTR,
879 SYMBOL_LEN, SYMBOL_LEN, LINESTR);
880 for (n = 0; n < ncpu; n++) {
881 lim_printf(&lim, " %*.*s",
882 sample_cpu_width[n], sample_cpu_width[n], LINESTR);
883 }
884 lim_newline(&lim);
885 } else {
886 printf("\n");
887 }
888
889 lim_printf(&lim, "Total %*u %-32.32s",
890 sample_event_width, sample_n_kern[opt_mode], "in-kernel");
891 for (n = 0; n < ncpu; n++) {
892 lim_printf(&lim, " %*u", sample_cpu_width[n],
893 sample_n_kern_per_cpu[opt_mode][n]);
894 }
895
896 if (opt_userland) {
897 lim_newline(&lim);
898 lim_printf(&lim, " %*u %-32.32s",
899 sample_event_width, sample_n_user[opt_mode], "userland");
900 for (n = 0; n < ncpu; n++) {
901 lim_printf(&lim, " %*u", sample_cpu_width[n],
902 sample_n_user_per_cpu[opt_mode][n]);
903 }
904 }
905
906 if (nontty)
907 printf("\n");
908 else
909 clr_to_eol();
910 }
911
912 __dead static void
913 tprof_top_usage(void)
914 {
915 fprintf(stderr, "%s top [-acu] [-e name[,scale] [-e ...]]"
916 " [-i interval]\n", getprogname());
917 exit(EXIT_FAILURE);
918 }
919
920 static int
921 parse_event_scale(tprof_param_t *param, const char *str)
922 {
923 double d;
924 uint64_t n;
925 char *p;
926
927 if (str[0] == '=') {
928 str++;
929 n = strtoull(str, &p, 0);
930 if (*p != '\0')
931 return -1;
932 param->p_value2 = n;
933 param->p_flags |= TPROF_PARAM_VALUE2_TRIGGERCOUNT;
934 } else {
935 if (strncasecmp("0x", str, 2) == 0)
936 d = strtol(str, &p, 0);
937 else
938 d = strtod(str, &p);
939 if (*p != '\0')
940 return -1;
941 param->p_value2 = 0x100000000ULL / d;
942 param->p_flags |= TPROF_PARAM_VALUE2_SCALE;
943 }
944 return 0;
945 }
946
947 void
948 tprof_top(int argc, char **argv)
949 {
950 tprof_param_t params[TPROF_MAXCOUNTERS];
951 struct itimerval it;
952 ssize_t tprof_bufsize, len;
953 u_int i;
954 int ch, ret;
955 char *tprof_buf, *tokens[2], *p;
956 bool noinput = false;
957
958 memset(params, 0, sizeof(params));
959 nevent = 0;
960
961 while ((ch = getopt(argc, argv, "ace:i:L:u")) != -1) {
962 switch (ch) {
963 case 'a':
964 opt_mode = SAMPLE_MODE_ACCUMULATIVE;
965 break;
966 case 'c':
967 opt_showcounter = 1;
968 break;
969 case 'e':
970 p = estrdup(optarg);
971 tokens[0] = strtok(p, ",");
972 tokens[1] = strtok(NULL, ",");
973 tprof_event_lookup(tokens[0], ¶ms[nevent]);
974 if (tokens[1] != NULL) {
975 if (parse_event_scale(¶ms[nevent],
976 tokens[1]) != 0) {
977 die_errc(EXIT_FAILURE, 0,
978 "invalid scale: %s", tokens[1]);
979 }
980 }
981 eventname[nevent] = tokens[0];
982 nevent++;
983 if (nevent > __arraycount(params) ||
984 nevent > ncounters)
985 die_errc(EXIT_FAILURE, 0,
986 "Too many events. Only a maximum of %d "
987 "counters can be used.", ncounters);
988 break;
989 case 'i':
990 top_interval = strtol(optarg, &p, 10);
991 if (*p != '\0' || top_interval <= 0)
992 die_errc(EXIT_FAILURE, 0,
993 "Bad/invalid interval: %s", optarg);
994 break;
995 case 'u':
996 opt_userland = 1;
997 break;
998 default:
999 tprof_top_usage();
1000 }
1001 }
1002 argc -= optind;
1003 argv += optind;
1004
1005 if (argc != 0)
1006 tprof_top_usage();
1007
1008 if (nevent == 0) {
1009 const char *defaultevent = cycle_event_name();
1010 if (defaultevent == NULL)
1011 die_errc(EXIT_FAILURE, 0, "cpu not supported");
1012
1013 tprof_event_lookup(defaultevent, ¶ms[nevent]);
1014 eventname[nevent] = defaultevent;
1015 nevent++;
1016 }
1017
1018 sample_init();
1019 show_counters_alloc();
1020
1021 for (i = 0; i < nevent; i++) {
1022 params[i].p_counter = i;
1023 params[i].p_flags |= TPROF_PARAM_KERN | TPROF_PARAM_PROFILE;
1024 if (opt_userland)
1025 params[i].p_flags |= TPROF_PARAM_USER;
1026 ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, ¶ms[i]);
1027 if (ret == -1)
1028 die_errc(EXIT_FAILURE, errno,
1029 "TPROF_IOC_CONFIGURE_EVENT: %s", eventname[i]);
1030 }
1031
1032 tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
1033 ret = ioctl(devfd, TPROF_IOC_START, &mask);
1034 if (ret == -1)
1035 die_errc(EXIT_FAILURE, errno, "TPROF_IOC_START");
1036
1037 ksyms = ksymload(&nksyms);
1038
1039 signal(SIGALRM, sigalrm_handler);
1040
1041 it.it_interval.tv_sec = it.it_value.tv_sec = top_interval;
1042 it.it_interval.tv_usec = it.it_value.tv_usec = 0;
1043 setitimer(ITIMER_REAL, &it, NULL);
1044
1045 sample_reset(true);
1046 printf("collecting samples...");
1047 fflush(stdout);
1048
1049 tprof_bufsize = sizeof(tprof_sample_t) * 8192;
1050 tprof_buf = emalloc(tprof_bufsize);
1051 do {
1052 bool force_update = false;
1053
1054 for (;;) {
1055 fd_set r;
1056 int nfound;
1057 char c;
1058
1059 FD_ZERO(&r);
1060 if (!noinput)
1061 FD_SET(STDIN_FILENO, &r);
1062 FD_SET(devfd, &r);
1063 nfound = select(devfd + 1, &r, NULL, NULL, NULL);
1064 if (nfound == -1) {
1065 if (errno == EINTR)
1066 break;
1067 die_errc(EXIT_FAILURE, errno, "select");
1068 }
1069
1070 if (FD_ISSET(STDIN_FILENO, &r)) {
1071 len = read(STDIN_FILENO, &c, 1);
1072 if (len <= 0) {
1073 noinput = true;
1074 continue;
1075 }
1076 switch (c) {
1077 case 0x0c: /* ^L */
1078 do_redraw = true;
1079 break;
1080 case 'a':
1081 /* toggle mode */
1082 opt_mode = (opt_mode + 1) %
1083 SAMPLE_MODE_NUM;
1084 break;
1085 case 'q':
1086 goto done;
1087 case 'z':
1088 sample_reset(true);
1089 break;
1090 default:
1091 printf("[%02x]", c);
1092 fflush(stdout);
1093 }
1094 force_update = true;
1095 continue;
1096 }
1097 if (sigalrm != 0 || force_update)
1098 break;
1099
1100 if (FD_ISSET(devfd, &r)) {
1101 len = read(devfd, tprof_buf, tprof_bufsize);
1102 if (len == -1 && errno != EINTR)
1103 die_errc(EXIT_FAILURE, errno, "read");
1104 if (len > 0) {
1105 tprof_sample_t *s =
1106 (tprof_sample_t *)tprof_buf;
1107 while (s <
1108 (tprof_sample_t *)(tprof_buf + len))
1109 sample_collect(s++);
1110 }
1111 }
1112 }
1113 sigalrm = 0;
1114
1115 /* update screen */
1116 sample_show();
1117 fflush(stdout);
1118 do_redraw = false;
1119 if (force_update)
1120 continue;
1121
1122 sample_reset(false);
1123
1124 } while (!nontty);
1125
1126 done:
1127 die(0);
1128 }
1129