tprof_top.c revision 1.1 1 1.1 ryo /* $NetBSD: tprof_top.c,v 1.1 2022/12/01 00:43:27 ryo Exp $ */
2 1.1 ryo
3 1.1 ryo /*-
4 1.1 ryo * Copyright (c) 2022 Ryo Shimizu <ryo (at) nerv.org>
5 1.1 ryo * All rights reserved.
6 1.1 ryo *
7 1.1 ryo * Redistribution and use in source and binary forms, with or without
8 1.1 ryo * modification, are permitted provided that the following conditions
9 1.1 ryo * are met:
10 1.1 ryo * 1. Redistributions of source code must retain the above copyright
11 1.1 ryo * notice, this list of conditions and the following disclaimer.
12 1.1 ryo * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 ryo * notice, this list of conditions and the following disclaimer in the
14 1.1 ryo * documentation and/or other materials provided with the distribution.
15 1.1 ryo *
16 1.1 ryo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 1.1 ryo * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 1.1 ryo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 1.1 ryo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 1.1 ryo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 1.1 ryo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 1.1 ryo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 1.1 ryo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 1.1 ryo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25 1.1 ryo * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 ryo * POSSIBILITY OF SUCH DAMAGE.
27 1.1 ryo */
28 1.1 ryo
29 1.1 ryo #include <sys/cdefs.h>
30 1.1 ryo #ifndef lint
31 1.1 ryo __RCSID("$NetBSD: tprof_top.c,v 1.1 2022/12/01 00:43:27 ryo Exp $");
32 1.1 ryo #endif /* not lint */
33 1.1 ryo
34 1.1 ryo #include <sys/param.h>
35 1.1 ryo #include <sys/types.h>
36 1.1 ryo #include <sys/rbtree.h>
37 1.1 ryo #include <sys/ioctl.h>
38 1.1 ryo #include <sys/time.h>
39 1.1 ryo
40 1.1 ryo #include <assert.h>
41 1.1 ryo #include <err.h>
42 1.1 ryo #include <errno.h>
43 1.1 ryo #include <fcntl.h>
44 1.1 ryo #include <inttypes.h>
45 1.1 ryo #include <math.h>
46 1.1 ryo #include <signal.h>
47 1.1 ryo #include <stdio.h>
48 1.1 ryo #include <stdlib.h>
49 1.1 ryo #include <string.h>
50 1.1 ryo #include <unistd.h>
51 1.1 ryo #include <util.h>
52 1.1 ryo
53 1.1 ryo #include <dev/tprof/tprof_ioctl.h>
54 1.1 ryo #include "tprof.h"
55 1.1 ryo #include "ksyms.h"
56 1.1 ryo
57 1.1 ryo static struct sym **ksyms;
58 1.1 ryo static size_t nksyms;
59 1.1 ryo static sig_atomic_t sigalrm;
60 1.1 ryo static struct winsize win;
61 1.1 ryo static long top_interval = 1;
62 1.1 ryo static u_int nevent;
63 1.1 ryo static const char *eventname[TPROF_MAXCOUNTERS];
64 1.1 ryo static int nontty;
65 1.1 ryo
66 1.1 ryo /* XXX: use terminfo or curses */
67 1.1 ryo static void
68 1.1 ryo cursor_address(u_int x, u_int y)
69 1.1 ryo {
70 1.1 ryo if (nontty)
71 1.1 ryo return;
72 1.1 ryo printf("\e[%u;%uH", y - 1, x - 1);
73 1.1 ryo }
74 1.1 ryo
75 1.1 ryo static void
76 1.1 ryo cursor_home(void)
77 1.1 ryo {
78 1.1 ryo if (nontty)
79 1.1 ryo return;
80 1.1 ryo printf("\e[H");
81 1.1 ryo }
82 1.1 ryo
83 1.1 ryo static void
84 1.1 ryo cls_eol(void)
85 1.1 ryo {
86 1.1 ryo if (nontty)
87 1.1 ryo return;
88 1.1 ryo printf("\e[K");
89 1.1 ryo }
90 1.1 ryo
91 1.1 ryo static void
92 1.1 ryo cls_eos(void)
93 1.1 ryo {
94 1.1 ryo if (nontty)
95 1.1 ryo return;
96 1.1 ryo printf("\e[J");
97 1.1 ryo }
98 1.1 ryo
99 1.1 ryo static void
100 1.1 ryo sigwinch_handler(int signo)
101 1.1 ryo {
102 1.1 ryo nontty = ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
103 1.1 ryo }
104 1.1 ryo
105 1.1 ryo static void
106 1.1 ryo sigalrm_handler(int signo)
107 1.1 ryo {
108 1.1 ryo sigalrm = 1;
109 1.1 ryo }
110 1.1 ryo
111 1.1 ryo struct sample_elm {
112 1.1 ryo struct rb_node node;
113 1.1 ryo uint64_t addr;
114 1.1 ryo const char *name;
115 1.1 ryo uint32_t flags;
116 1.1 ryo #define SAMPLE_ELM_FLAGS_USER 0x00000001
117 1.1 ryo uint32_t num;
118 1.1 ryo uint32_t numcpu[];
119 1.1 ryo };
120 1.1 ryo
121 1.1 ryo static size_t sizeof_sample_elm;
122 1.1 ryo static rb_tree_t rb_tree_sample;
123 1.1 ryo static char *samplebuf;
124 1.1 ryo static u_int sample_nused;
125 1.1 ryo static u_int sample_kern_nsample;
126 1.1 ryo static u_int sample_user_nsample;
127 1.1 ryo static u_int sample_max = 1024 * 512; /* XXX */
128 1.1 ryo static uint32_t *sample_nsample_kern_per_cpu;
129 1.1 ryo static uint32_t *sample_nsample_user_per_cpu;
130 1.1 ryo static uint64_t *sample_nsample_per_event;
131 1.1 ryo static uint64_t *sample_nsample_per_event_cpu;
132 1.1 ryo static uint64_t collect_overflow;
133 1.1 ryo
134 1.1 ryo static int opt_userland = 0;
135 1.1 ryo static int opt_showcounter = 0;
136 1.1 ryo
137 1.1 ryo static int
138 1.1 ryo sample_compare_key(void *ctx, const void *n1, const void *keyp)
139 1.1 ryo {
140 1.1 ryo const struct sample_elm *a1 = n1;
141 1.1 ryo const struct sample_elm *a2 = (const struct sample_elm *)keyp;
142 1.1 ryo return a1->addr - a2->addr;
143 1.1 ryo }
144 1.1 ryo
145 1.1 ryo static signed int
146 1.1 ryo sample_compare_nodes(void *ctx, const void *n1, const void *n2)
147 1.1 ryo {
148 1.1 ryo const struct addr *a2 = n2;
149 1.1 ryo return sample_compare_key(ctx, n1, a2);
150 1.1 ryo }
151 1.1 ryo
152 1.1 ryo static const rb_tree_ops_t sample_ops = {
153 1.1 ryo .rbto_compare_nodes = sample_compare_nodes,
154 1.1 ryo .rbto_compare_key = sample_compare_key
155 1.1 ryo };
156 1.1 ryo
157 1.1 ryo static void
158 1.1 ryo sample_init(void)
159 1.1 ryo {
160 1.1 ryo sizeof_sample_elm = sizeof(struct sample_elm) + sizeof(uint32_t) * ncpu;
161 1.1 ryo }
162 1.1 ryo
163 1.1 ryo static void
164 1.1 ryo sample_reset(void)
165 1.1 ryo {
166 1.1 ryo if (samplebuf == NULL) {
167 1.1 ryo samplebuf = emalloc(sizeof_sample_elm * sample_max);
168 1.1 ryo sample_nsample_kern_per_cpu = emalloc(sizeof(uint32_t) * ncpu);
169 1.1 ryo sample_nsample_user_per_cpu = emalloc(sizeof(uint32_t) * ncpu);
170 1.1 ryo sample_nsample_per_event = emalloc(sizeof(uint64_t) * nevent);
171 1.1 ryo sample_nsample_per_event_cpu =
172 1.1 ryo emalloc(sizeof(uint64_t) * nevent * ncpu);
173 1.1 ryo }
174 1.1 ryo sample_nused = 0;
175 1.1 ryo sample_kern_nsample = 0;
176 1.1 ryo sample_user_nsample = 0;
177 1.1 ryo memset(sample_nsample_kern_per_cpu, 0, sizeof(uint32_t) * ncpu);
178 1.1 ryo memset(sample_nsample_user_per_cpu, 0, sizeof(uint32_t) * ncpu);
179 1.1 ryo memset(sample_nsample_per_event, 0, sizeof(uint64_t) * nevent);
180 1.1 ryo memset(sample_nsample_per_event_cpu, 0,
181 1.1 ryo sizeof(uint64_t) * nevent * ncpu);
182 1.1 ryo
183 1.1 ryo rb_tree_init(&rb_tree_sample, &sample_ops);
184 1.1 ryo }
185 1.1 ryo
186 1.1 ryo static struct sample_elm *
187 1.1 ryo sample_alloc(void)
188 1.1 ryo {
189 1.1 ryo struct sample_elm *e;
190 1.1 ryo
191 1.1 ryo if (sample_nused >= sample_max) {
192 1.1 ryo errx(EXIT_FAILURE, "sample buffer overflow");
193 1.1 ryo return NULL;
194 1.1 ryo }
195 1.1 ryo
196 1.1 ryo e = (struct sample_elm *)(samplebuf + sizeof_sample_elm *
197 1.1 ryo sample_nused++);
198 1.1 ryo memset(e, 0, sizeof_sample_elm);
199 1.1 ryo return e;
200 1.1 ryo }
201 1.1 ryo
202 1.1 ryo static void
203 1.1 ryo sample_takeback(struct sample_elm *e)
204 1.1 ryo {
205 1.1 ryo assert(sample_nused > 0);
206 1.1 ryo sample_nused--;
207 1.1 ryo }
208 1.1 ryo
209 1.1 ryo static int
210 1.1 ryo sample_sortfunc(const void *a, const void *b)
211 1.1 ryo {
212 1.1 ryo const struct sample_elm *ea = a;
213 1.1 ryo const struct sample_elm *eb = b;
214 1.1 ryo return eb->num - ea->num;
215 1.1 ryo }
216 1.1 ryo
217 1.1 ryo static void
218 1.1 ryo sample_sort(void)
219 1.1 ryo {
220 1.1 ryo qsort(samplebuf, sample_nused, sizeof_sample_elm, sample_sortfunc);
221 1.1 ryo }
222 1.1 ryo
223 1.1 ryo static void
224 1.1 ryo sample_collect(tprof_sample_t *s)
225 1.1 ryo {
226 1.1 ryo struct sample_elm *e, *o;
227 1.1 ryo const char *name;
228 1.1 ryo size_t symid;
229 1.1 ryo uint64_t addr, offset;
230 1.1 ryo uint32_t flags = 0;
231 1.1 ryo uint32_t eventid, cpuid;
232 1.1 ryo
233 1.1 ryo eventid = __SHIFTOUT(s->s_flags, TPROF_SAMPLE_COUNTER_MASK);
234 1.1 ryo cpuid = s->s_cpuid;
235 1.1 ryo
236 1.1 ryo sample_nsample_per_event[eventid]++;
237 1.1 ryo sample_nsample_per_event_cpu[nevent * cpuid + eventid]++;
238 1.1 ryo
239 1.1 ryo if ((s->s_flags & TPROF_SAMPLE_INKERNEL) == 0) {
240 1.1 ryo sample_user_nsample++;
241 1.1 ryo sample_nsample_user_per_cpu[cpuid]++;
242 1.1 ryo
243 1.1 ryo name = NULL;
244 1.1 ryo addr = s->s_pid; /* XXX */
245 1.1 ryo flags |= SAMPLE_ELM_FLAGS_USER;
246 1.1 ryo
247 1.1 ryo if (!opt_userland)
248 1.1 ryo return;
249 1.1 ryo } else {
250 1.1 ryo sample_kern_nsample++;
251 1.1 ryo sample_nsample_kern_per_cpu[cpuid]++;
252 1.1 ryo
253 1.1 ryo name = ksymlookup(s->s_pc, &offset, &symid);
254 1.1 ryo if (name != NULL) {
255 1.1 ryo addr = ksyms[symid]->value;
256 1.1 ryo } else {
257 1.1 ryo addr = s->s_pc;
258 1.1 ryo }
259 1.1 ryo }
260 1.1 ryo
261 1.1 ryo e = sample_alloc();
262 1.1 ryo if (e == NULL) {
263 1.1 ryo fprintf(stderr, "cannot allocate sample\n");
264 1.1 ryo collect_overflow++;
265 1.1 ryo return;
266 1.1 ryo }
267 1.1 ryo
268 1.1 ryo e->addr = addr;
269 1.1 ryo e->name = name;
270 1.1 ryo e->flags = flags;
271 1.1 ryo e->num = 1;
272 1.1 ryo e->numcpu[cpuid] = 1;
273 1.1 ryo o = rb_tree_insert_node(&rb_tree_sample, e);
274 1.1 ryo if (o != e) {
275 1.1 ryo /* already exists */
276 1.1 ryo sample_takeback(e);
277 1.1 ryo o->num++;
278 1.1 ryo o->numcpu[cpuid]++;
279 1.1 ryo }
280 1.1 ryo }
281 1.1 ryo
282 1.1 ryo static void
283 1.1 ryo show_tprof_stat(void)
284 1.1 ryo {
285 1.1 ryo static struct tprof_stat tsbuf[2], *ts0, *ts;
286 1.1 ryo static u_int ts_i = 0;
287 1.1 ryo int ret;
288 1.1 ryo
289 1.1 ryo ts0 = &tsbuf[ts_i++ & 1];
290 1.1 ryo ts = &tsbuf[ts_i & 1];
291 1.1 ryo ret = ioctl(devfd, TPROF_IOC_GETSTAT, ts);
292 1.1 ryo if (ret == -1)
293 1.1 ryo err(EXIT_FAILURE, "TPROF_IOC_GETSTAT");
294 1.1 ryo
295 1.1 ryo #define TS_PRINT(label, _m) \
296 1.1 ryo do { \
297 1.1 ryo printf(label "%" PRIu64, ts->_m); \
298 1.1 ryo if (ts->_m != ts0->_m) \
299 1.1 ryo printf("(+%"PRIu64")", \
300 1.1 ryo ts->_m - ts0->_m); \
301 1.1 ryo printf(" "); \
302 1.1 ryo } while (0)
303 1.1 ryo TS_PRINT("tprof sample:", ts_sample);
304 1.1 ryo TS_PRINT(" overflow:", ts_overflow);
305 1.1 ryo TS_PRINT(" buf:", ts_buf);
306 1.1 ryo TS_PRINT(" emptybuf:", ts_emptybuf);
307 1.1 ryo TS_PRINT(" dropbuf:", ts_dropbuf);
308 1.1 ryo TS_PRINT(" dropbuf_sample:", ts_dropbuf_sample);
309 1.1 ryo }
310 1.1 ryo
311 1.1 ryo static void
312 1.1 ryo show_timestamp(void)
313 1.1 ryo {
314 1.1 ryo struct timeval tv;
315 1.1 ryo gettimeofday(&tv, NULL);
316 1.1 ryo cursor_address(win.ws_col - 7, 0);
317 1.1 ryo printf("%-8.8s", &(ctime((time_t *)&tv.tv_sec)[11]));
318 1.1 ryo }
319 1.1 ryo
320 1.1 ryo
321 1.1 ryo static uint64_t *counters; /* counters[2][ncpu][nevent] */
322 1.1 ryo static u_int counters_i;
323 1.1 ryo
324 1.1 ryo static void
325 1.1 ryo show_counters_alloc(void)
326 1.1 ryo {
327 1.1 ryo size_t sz = 2 * ncpu * nevent * sizeof(uint64_t);
328 1.1 ryo counters = emalloc(sz);
329 1.1 ryo memset(counters, 0, sz);
330 1.1 ryo }
331 1.1 ryo
332 1.1 ryo static void
333 1.1 ryo show_counters(void)
334 1.1 ryo {
335 1.1 ryo tprof_counts_t countsbuf;
336 1.1 ryo uint64_t *cn[2], *c0, *c;
337 1.1 ryo u_int i;
338 1.1 ryo int n, ret;
339 1.1 ryo
340 1.1 ryo cn[0] = counters;
341 1.1 ryo cn[1] = counters + ncpu * nevent;
342 1.1 ryo c0 = cn[counters_i++ & 1];
343 1.1 ryo c = cn[counters_i & 1];
344 1.1 ryo
345 1.1 ryo for (n = 0; n < ncpu; n++) {
346 1.1 ryo countsbuf.c_cpu = n;
347 1.1 ryo ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &countsbuf);
348 1.1 ryo if (ret == -1)
349 1.1 ryo err(EXIT_FAILURE, "TPROF_IOC_GETCOUNTS");
350 1.1 ryo
351 1.1 ryo for (i = 0; i < nevent; i++)
352 1.1 ryo c[n * nevent + i] = countsbuf.c_count[i];
353 1.1 ryo }
354 1.1 ryo
355 1.1 ryo printf("%-22s", "Event counter (delta)");
356 1.1 ryo for (n = 0; n < ncpu; n++) {
357 1.1 ryo char cpuname[16];
358 1.1 ryo snprintf(cpuname, sizeof(cpuname), "CPU%u", n);
359 1.1 ryo printf("%11s", cpuname);
360 1.1 ryo }
361 1.1 ryo printf("\n");
362 1.1 ryo
363 1.1 ryo for (i = 0; i < nevent; i++) {
364 1.1 ryo printf("%-22.22s", eventname[i]);
365 1.1 ryo for (n = 0; n < ncpu; n++) {
366 1.1 ryo printf("%11"PRIu64,
367 1.1 ryo c[n * nevent + i] - c0[n * nevent + i]);
368 1.1 ryo }
369 1.1 ryo printf("\n");
370 1.1 ryo }
371 1.1 ryo printf("\n");
372 1.1 ryo }
373 1.1 ryo
374 1.1 ryo static void
375 1.1 ryo show_count_per_event(void)
376 1.1 ryo {
377 1.1 ryo u_int i, nsample_total;
378 1.1 ryo int n;
379 1.1 ryo
380 1.1 ryo nsample_total = sample_kern_nsample + sample_user_nsample;
381 1.1 ryo
382 1.1 ryo for (i = 0; i < nevent; i++) {
383 1.1 ryo if (sample_nsample_per_event[i] >= nsample_total) {
384 1.1 ryo printf("%5.1f%%", sample_nsample_per_event[i] *
385 1.1 ryo 100.00 / nsample_total);
386 1.1 ryo } else {
387 1.1 ryo printf("%5.2f%%", sample_nsample_per_event[i] *
388 1.1 ryo 100.00 / nsample_total);
389 1.1 ryo }
390 1.1 ryo printf("%8lu ", sample_nsample_per_event[i]);
391 1.1 ryo
392 1.1 ryo printf("%-32.32s", eventname[i]);
393 1.1 ryo for (n = 0; n < ncpu; n++) {
394 1.1 ryo printf("%6"PRIu64,
395 1.1 ryo sample_nsample_per_event_cpu[nevent * n + i]);
396 1.1 ryo }
397 1.1 ryo printf("\n");
398 1.1 ryo }
399 1.1 ryo }
400 1.1 ryo
401 1.1 ryo static void
402 1.1 ryo sample_show(void)
403 1.1 ryo {
404 1.1 ryo static u_int nshow;
405 1.1 ryo
406 1.1 ryo struct sample_elm *e;
407 1.1 ryo u_int nsample_total;
408 1.1 ryo int i, n, ndisp;
409 1.1 ryo char namebuf[32];
410 1.1 ryo const char *name;
411 1.1 ryo
412 1.1 ryo int margin_lines = 7;
413 1.1 ryo
414 1.1 ryo margin_lines += 3 + nevent; /* show_counter_per_event() */
415 1.1 ryo
416 1.1 ryo if (opt_showcounter)
417 1.1 ryo margin_lines += 2 + nevent;
418 1.1 ryo if (opt_userland)
419 1.1 ryo margin_lines += 1;
420 1.1 ryo
421 1.1 ryo ndisp = sample_nused;
422 1.1 ryo if (!nontty && ndisp > (win.ws_row - margin_lines))
423 1.1 ryo ndisp = win.ws_row - margin_lines;
424 1.1 ryo
425 1.1 ryo cursor_home();
426 1.1 ryo if (nshow++ == 0)
427 1.1 ryo cls_eos();
428 1.1 ryo
429 1.1 ryo
430 1.1 ryo show_tprof_stat();
431 1.1 ryo cls_eol();
432 1.1 ryo
433 1.1 ryo show_timestamp();
434 1.1 ryo printf("\n");
435 1.1 ryo printf("\n");
436 1.1 ryo
437 1.1 ryo if (opt_showcounter)
438 1.1 ryo show_counters();
439 1.1 ryo
440 1.1 ryo printf(" Rate Sample# Eventname ");
441 1.1 ryo for (n = 0; n < ncpu; n++) {
442 1.1 ryo if (n >= 1000) {
443 1.1 ryo snprintf(namebuf, sizeof(namebuf), "%d", n);
444 1.1 ryo } else if (n >= 100) {
445 1.1 ryo snprintf(namebuf, sizeof(namebuf), "#%d", n);
446 1.1 ryo } else {
447 1.1 ryo snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
448 1.1 ryo }
449 1.1 ryo printf(" %5s", namebuf);
450 1.1 ryo }
451 1.1 ryo printf("\n");
452 1.1 ryo printf("------ ------- --------------------------------");
453 1.1 ryo for (n = 0; n < ncpu; n++) {
454 1.1 ryo printf(" -----");
455 1.1 ryo }
456 1.1 ryo printf("\n");
457 1.1 ryo
458 1.1 ryo show_count_per_event();
459 1.1 ryo printf("\n");
460 1.1 ryo
461 1.1 ryo printf(" Rate Sample# Symbol ");
462 1.1 ryo for (n = 0; n < ncpu; n++) {
463 1.1 ryo if (n >= 1000) {
464 1.1 ryo snprintf(namebuf, sizeof(namebuf), "%d", n);
465 1.1 ryo } else if (n >= 100) {
466 1.1 ryo snprintf(namebuf, sizeof(namebuf), "#%d", n);
467 1.1 ryo } else {
468 1.1 ryo snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
469 1.1 ryo }
470 1.1 ryo printf(" %5s", namebuf);
471 1.1 ryo }
472 1.1 ryo printf("\n");
473 1.1 ryo printf("------ ------- --------------------------------");
474 1.1 ryo for (n = 0; n < ncpu; n++) {
475 1.1 ryo printf(" -----");
476 1.1 ryo }
477 1.1 ryo printf("\n");
478 1.1 ryo
479 1.1 ryo for (i = 0; i < ndisp; i++) {
480 1.1 ryo e = (struct sample_elm *)(samplebuf + sizeof_sample_elm * i);
481 1.1 ryo name = e->name;
482 1.1 ryo if (name == NULL) {
483 1.1 ryo if (e->flags & SAMPLE_ELM_FLAGS_USER) {
484 1.1 ryo snprintf(namebuf, sizeof(namebuf), "<PID:%lu>",
485 1.1 ryo e->addr);
486 1.1 ryo } else {
487 1.1 ryo snprintf(namebuf, sizeof(namebuf), "0x%016lx",
488 1.1 ryo e->addr);
489 1.1 ryo }
490 1.1 ryo name = namebuf;
491 1.1 ryo }
492 1.1 ryo
493 1.1 ryo nsample_total = sample_kern_nsample;
494 1.1 ryo if (opt_userland)
495 1.1 ryo nsample_total += sample_user_nsample;
496 1.1 ryo /*
497 1.1 ryo * even when only kernel mode events are configured,
498 1.1 ryo * interrupts may still occur in the user mode state.
499 1.1 ryo */
500 1.1 ryo if (nsample_total == 0)
501 1.1 ryo nsample_total = 1;
502 1.1 ryo
503 1.1 ryo if (e->num >= nsample_total) {
504 1.1 ryo printf("%5.1f%%", e->num * 100.00 / nsample_total);
505 1.1 ryo } else {
506 1.1 ryo printf("%5.2f%%", e->num * 100.00 / nsample_total);
507 1.1 ryo }
508 1.1 ryo printf("%8u %-32.32s", e->num, name);
509 1.1 ryo
510 1.1 ryo for (n = 0; n < ncpu; n++) {
511 1.1 ryo if (e->numcpu[n] == 0)
512 1.1 ryo printf(" .");
513 1.1 ryo else
514 1.1 ryo printf("%6u", e->numcpu[n]);
515 1.1 ryo }
516 1.1 ryo printf("\n");
517 1.1 ryo }
518 1.1 ryo
519 1.1 ryo if ((u_int)ndisp != sample_nused) {
520 1.1 ryo printf(" : : (more %u symbols omitted)\n",
521 1.1 ryo sample_nused - ndisp);
522 1.1 ryo } else {
523 1.1 ryo for (i = ndisp; i <= win.ws_row - margin_lines; i++) {
524 1.1 ryo printf("~");
525 1.1 ryo cls_eol();
526 1.1 ryo printf("\n");
527 1.1 ryo }
528 1.1 ryo }
529 1.1 ryo
530 1.1 ryo
531 1.1 ryo printf("------ ------- --------------------------------");
532 1.1 ryo for (n = 0; n < ncpu; n++) {
533 1.1 ryo printf(" -----");
534 1.1 ryo }
535 1.1 ryo printf("\n");
536 1.1 ryo
537 1.1 ryo printf("Total %8u %-32.32s", sample_kern_nsample, "in-kernel");
538 1.1 ryo for (n = 0; n < ncpu; n++) {
539 1.1 ryo printf("%6u", sample_nsample_kern_per_cpu[n]);
540 1.1 ryo }
541 1.1 ryo
542 1.1 ryo if (opt_userland) {
543 1.1 ryo printf("\n");
544 1.1 ryo printf(" %8u %-32.32s", sample_user_nsample, "userland");
545 1.1 ryo for (n = 0; n < ncpu; n++) {
546 1.1 ryo printf("%6u", sample_nsample_user_per_cpu[n]);
547 1.1 ryo }
548 1.1 ryo }
549 1.1 ryo
550 1.1 ryo cls_eos();
551 1.1 ryo }
552 1.1 ryo
553 1.1 ryo __dead static void
554 1.1 ryo tprof_top_usage(void)
555 1.1 ryo {
556 1.1 ryo fprintf(stderr, "%s top [-e name[,scale] [-e ...]]"
557 1.1 ryo " [-i interval] [-c] [-u]\n", getprogname());
558 1.1 ryo exit(EXIT_FAILURE);
559 1.1 ryo }
560 1.1 ryo
561 1.1 ryo static int
562 1.1 ryo parse_event_scale(tprof_param_t *param, const char *str)
563 1.1 ryo {
564 1.1 ryo double d;
565 1.1 ryo uint64_t n;
566 1.1 ryo char *p;
567 1.1 ryo
568 1.1 ryo if (str[0] == '=') {
569 1.1 ryo str++;
570 1.1 ryo n = strtoull(str, &p, 0);
571 1.1 ryo if (*p != '\0')
572 1.1 ryo return -1;
573 1.1 ryo param->p_value2 = n;
574 1.1 ryo param->p_flags |= TPROF_PARAM_VALUE2_TRIGGERCOUNT;
575 1.1 ryo } else {
576 1.1 ryo if (strncasecmp("0x", str, 2) == 0)
577 1.1 ryo d = strtol(str, &p, 0);
578 1.1 ryo else
579 1.1 ryo d = strtod(str, &p);
580 1.1 ryo if (*p != '\0')
581 1.1 ryo return -1;
582 1.1 ryo param->p_value2 = 0x100000000ULL / d;
583 1.1 ryo param->p_flags |= TPROF_PARAM_VALUE2_SCALE;
584 1.1 ryo }
585 1.1 ryo return 0;
586 1.1 ryo }
587 1.1 ryo
588 1.1 ryo void
589 1.1 ryo tprof_top(int argc, char **argv)
590 1.1 ryo {
591 1.1 ryo tprof_param_t params[TPROF_MAXCOUNTERS];
592 1.1 ryo struct sigaction sa;
593 1.1 ryo struct itimerval it;
594 1.1 ryo ssize_t bufsize;
595 1.1 ryo u_int i;
596 1.1 ryo int ch, ret;
597 1.1 ryo char *buf, *tokens[2], *p;
598 1.1 ryo
599 1.1 ryo memset(params, 0, sizeof(params));
600 1.1 ryo nevent = 0;
601 1.1 ryo
602 1.1 ryo while ((ch = getopt(argc, argv, "ce:i:u")) != -1) {
603 1.1 ryo switch (ch) {
604 1.1 ryo case 'c':
605 1.1 ryo opt_showcounter = 1;
606 1.1 ryo break;
607 1.1 ryo case 'e':
608 1.1 ryo p = estrdup(optarg);
609 1.1 ryo tokens[0] = strtok(p, ",");
610 1.1 ryo tokens[1] = strtok(NULL, ",");
611 1.1 ryo tprof_event_lookup(tokens[0], ¶ms[nevent]);
612 1.1 ryo if (tokens[1] != NULL) {
613 1.1 ryo if (parse_event_scale(¶ms[nevent], tokens[1]) != 0)
614 1.1 ryo errx(EXIT_FAILURE, "invalid scale: %s", tokens[1]);
615 1.1 ryo }
616 1.1 ryo eventname[nevent] = tokens[0];
617 1.1 ryo nevent++;
618 1.1 ryo if (nevent > __arraycount(params) ||
619 1.1 ryo nevent > ncounters)
620 1.1 ryo errx(EXIT_FAILURE, "Too many events. Only a"
621 1.1 ryo " maximum of %d counters can be used.",
622 1.1 ryo ncounters);
623 1.1 ryo break;
624 1.1 ryo case 'i':
625 1.1 ryo top_interval = strtol(optarg, &p, 10);
626 1.1 ryo if (*p != '\0' || top_interval <= 0)
627 1.1 ryo errx(EXIT_FAILURE, "Bad/invalid interval: %s",
628 1.1 ryo optarg);
629 1.1 ryo break;
630 1.1 ryo case 'u':
631 1.1 ryo opt_userland = 1;
632 1.1 ryo break;
633 1.1 ryo default:
634 1.1 ryo tprof_top_usage();
635 1.1 ryo }
636 1.1 ryo }
637 1.1 ryo argc -= optind;
638 1.1 ryo argv += optind;
639 1.1 ryo
640 1.1 ryo if (argc != 0)
641 1.1 ryo tprof_top_usage();
642 1.1 ryo
643 1.1 ryo bufsize = sizeof(tprof_sample_t) * 8192;
644 1.1 ryo buf = emalloc(bufsize);
645 1.1 ryo
646 1.1 ryo sample_init();
647 1.1 ryo
648 1.1 ryo if (nevent == 0) {
649 1.1 ryo const char *defaultevent;
650 1.1 ryo
651 1.1 ryo switch (tprof_info.ti_ident) {
652 1.1 ryo case TPROF_IDENT_INTEL_GENERIC:
653 1.1 ryo defaultevent = "unhalted-core-cycles";
654 1.1 ryo break;
655 1.1 ryo case TPROF_IDENT_AMD_GENERIC:
656 1.1 ryo defaultevent = "LsNotHaltedCyc";
657 1.1 ryo break;
658 1.1 ryo case TPROF_IDENT_ARMV8_GENERIC:
659 1.1 ryo case TPROF_IDENT_ARMV7_GENERIC:
660 1.1 ryo defaultevent = "CPU_CYCLES";
661 1.1 ryo break;
662 1.1 ryo default:
663 1.1 ryo defaultevent = NULL;
664 1.1 ryo break;
665 1.1 ryo }
666 1.1 ryo if (defaultevent == NULL)
667 1.1 ryo errx(EXIT_FAILURE, "cpu not supported");
668 1.1 ryo
669 1.1 ryo tprof_event_lookup(defaultevent, ¶ms[nevent]);
670 1.1 ryo eventname[nevent] = defaultevent;
671 1.1 ryo nevent++;
672 1.1 ryo }
673 1.1 ryo
674 1.1 ryo show_counters_alloc();
675 1.1 ryo
676 1.1 ryo for (i = 0; i < nevent; i++) {
677 1.1 ryo params[i].p_counter = i;
678 1.1 ryo params[i].p_flags |= TPROF_PARAM_KERN | TPROF_PARAM_PROFILE;
679 1.1 ryo if (opt_userland)
680 1.1 ryo params[i].p_flags |= TPROF_PARAM_USER;
681 1.1 ryo ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, ¶ms[i]);
682 1.1 ryo if (ret == -1)
683 1.1 ryo err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT: %s",
684 1.1 ryo eventname[i]);
685 1.1 ryo }
686 1.1 ryo
687 1.1 ryo tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
688 1.1 ryo ret = ioctl(devfd, TPROF_IOC_START, &mask);
689 1.1 ryo if (ret == -1)
690 1.1 ryo err(EXIT_FAILURE, "TPROF_IOC_START");
691 1.1 ryo
692 1.1 ryo
693 1.1 ryo sigwinch_handler(0);
694 1.1 ryo ksyms = ksymload(&nksyms);
695 1.1 ryo
696 1.1 ryo sigemptyset(&sa.sa_mask);
697 1.1 ryo sa.sa_flags = SA_RESTART;
698 1.1 ryo sa.sa_handler = sigwinch_handler;
699 1.1 ryo sigaction(SIGWINCH, &sa, NULL);
700 1.1 ryo
701 1.1 ryo sigemptyset(&sa.sa_mask);
702 1.1 ryo sa.sa_flags = 0;
703 1.1 ryo sa.sa_handler = sigalrm_handler;
704 1.1 ryo sigaction(SIGALRM, &sa, NULL);
705 1.1 ryo
706 1.1 ryo it.it_interval.tv_sec = it.it_value.tv_sec = top_interval;
707 1.1 ryo it.it_interval.tv_usec = it.it_value.tv_usec = 0;
708 1.1 ryo setitimer(ITIMER_REAL, &it, NULL);
709 1.1 ryo
710 1.1 ryo sample_reset();
711 1.1 ryo printf("collecting samples...");
712 1.1 ryo fflush(stdout);
713 1.1 ryo
714 1.1 ryo do {
715 1.1 ryo /* continue to accumulate tprof_sample until alarm arrives */
716 1.1 ryo while (sigalrm == 0) {
717 1.1 ryo ssize_t len = read(devfd, buf, bufsize);
718 1.1 ryo if (len == -1 && errno != EINTR)
719 1.1 ryo err(EXIT_FAILURE, "read");
720 1.1 ryo if (len > 0) {
721 1.1 ryo tprof_sample_t *s = (tprof_sample_t *)buf;
722 1.1 ryo while (s < (tprof_sample_t *)(buf + len)) {
723 1.1 ryo sample_collect(s);
724 1.1 ryo s++;
725 1.1 ryo }
726 1.1 ryo }
727 1.1 ryo }
728 1.1 ryo sigalrm = 0;
729 1.1 ryo
730 1.1 ryo /* update screen */
731 1.1 ryo sample_sort();
732 1.1 ryo sample_show();
733 1.1 ryo fflush(stdout);
734 1.1 ryo
735 1.1 ryo sample_reset();
736 1.1 ryo } while (!nontty);
737 1.1 ryo
738 1.1 ryo printf("\n");
739 1.1 ryo }
740