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