tprof_top.c revision 1.3 1 1.3 ryo /* $NetBSD: tprof_top.c,v 1.3 2022/12/09 01:55:46 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.3 ryo __RCSID("$NetBSD: tprof_top.c,v 1.3 2022/12/09 01:55:46 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 <err.h>
41 1.1 ryo #include <errno.h>
42 1.1 ryo #include <fcntl.h>
43 1.1 ryo #include <inttypes.h>
44 1.1 ryo #include <math.h>
45 1.1 ryo #include <signal.h>
46 1.1 ryo #include <stdio.h>
47 1.1 ryo #include <stdlib.h>
48 1.1 ryo #include <string.h>
49 1.1 ryo #include <unistd.h>
50 1.1 ryo #include <util.h>
51 1.1 ryo
52 1.1 ryo #include <dev/tprof/tprof_ioctl.h>
53 1.1 ryo #include "tprof.h"
54 1.1 ryo #include "ksyms.h"
55 1.1 ryo
56 1.3 ryo #define SAMPLE_MODE_ACCUMULATIVE 0
57 1.3 ryo #define SAMPLE_MODE_INSTANTANEOUS 1
58 1.3 ryo #define SAMPLE_MODE_NUM 2
59 1.3 ryo
60 1.3 ryo struct sample_elm {
61 1.3 ryo struct rb_node node;
62 1.3 ryo uint64_t addr;
63 1.3 ryo const char *name;
64 1.3 ryo uint32_t flags;
65 1.3 ryo #define SAMPLE_ELM_FLAGS_USER 0x00000001
66 1.3 ryo uint32_t num[SAMPLE_MODE_NUM];
67 1.3 ryo uint32_t num_cpu[]; /* [SAMPLE_MODE_NUM][ncpu] */
68 1.3 ryo #define SAMPLE_ELM_NUM_CPU(e, k) \
69 1.3 ryo ((e)->num_cpu + (k) * ncpu)
70 1.3 ryo };
71 1.3 ryo
72 1.3 ryo struct ptrarray {
73 1.3 ryo void **pa_ptrs;
74 1.3 ryo size_t pa_allocnum;
75 1.3 ryo size_t pa_inuse;
76 1.3 ryo };
77 1.3 ryo
78 1.3 ryo static int opt_mode = SAMPLE_MODE_INSTANTANEOUS;
79 1.3 ryo static int opt_userland = 0;
80 1.3 ryo static int opt_showcounter = 0;
81 1.3 ryo
82 1.3 ryo /* for display */
83 1.3 ryo static struct winsize win;
84 1.3 ryo static int nontty;
85 1.3 ryo static long top_interval = 1;
86 1.3 ryo
87 1.3 ryo /* for profiling and counting samples */
88 1.3 ryo static sig_atomic_t sigalrm;
89 1.1 ryo static struct sym **ksyms;
90 1.1 ryo static size_t nksyms;
91 1.1 ryo static u_int nevent;
92 1.1 ryo static const char *eventname[TPROF_MAXCOUNTERS];
93 1.3 ryo static size_t sizeof_sample_elm;
94 1.3 ryo static rb_tree_t rb_tree_sample;
95 1.3 ryo struct ptrarray sample_list[SAMPLE_MODE_NUM];
96 1.3 ryo static u_int sample_n_kern[SAMPLE_MODE_NUM];
97 1.3 ryo static u_int sample_n_user[SAMPLE_MODE_NUM];
98 1.3 ryo static uint32_t *sample_n_kern_per_cpu[SAMPLE_MODE_NUM]; /* [ncpu] */
99 1.3 ryo static uint32_t *sample_n_user_per_cpu[SAMPLE_MODE_NUM]; /* [ncpu] */
100 1.3 ryo static uint64_t *sample_n_per_event[SAMPLE_MODE_NUM]; /* [nevent] */
101 1.3 ryo static uint64_t *sample_n_per_event_cpu[SAMPLE_MODE_NUM]; /* [ncpu] */
102 1.3 ryo
103 1.3 ryo /* raw event counter */
104 1.3 ryo static uint64_t *counters; /* counters[2][ncpu][nevent] */
105 1.3 ryo static u_int counters_i;
106 1.3 ryo
107 1.3 ryo static const char *
108 1.3 ryo cycle_event_name(void)
109 1.3 ryo {
110 1.3 ryo const char *cycleevent;
111 1.3 ryo
112 1.3 ryo switch (tprof_info.ti_ident) {
113 1.3 ryo case TPROF_IDENT_INTEL_GENERIC:
114 1.3 ryo cycleevent = "unhalted-core-cycles";
115 1.3 ryo break;
116 1.3 ryo case TPROF_IDENT_AMD_GENERIC:
117 1.3 ryo cycleevent = "LsNotHaltedCyc";
118 1.3 ryo break;
119 1.3 ryo case TPROF_IDENT_ARMV8_GENERIC:
120 1.3 ryo case TPROF_IDENT_ARMV7_GENERIC:
121 1.3 ryo cycleevent = "CPU_CYCLES";
122 1.3 ryo break;
123 1.3 ryo default:
124 1.3 ryo cycleevent = NULL;
125 1.3 ryo break;
126 1.3 ryo }
127 1.3 ryo return cycleevent;
128 1.3 ryo }
129 1.1 ryo
130 1.1 ryo /* XXX: use terminfo or curses */
131 1.1 ryo static void
132 1.1 ryo cursor_address(u_int x, u_int y)
133 1.1 ryo {
134 1.1 ryo if (nontty)
135 1.1 ryo return;
136 1.1 ryo printf("\e[%u;%uH", y - 1, x - 1);
137 1.1 ryo }
138 1.1 ryo
139 1.1 ryo static void
140 1.1 ryo cursor_home(void)
141 1.1 ryo {
142 1.1 ryo if (nontty)
143 1.1 ryo return;
144 1.1 ryo printf("\e[H");
145 1.1 ryo }
146 1.1 ryo
147 1.1 ryo static void
148 1.1 ryo cls_eol(void)
149 1.1 ryo {
150 1.1 ryo if (nontty)
151 1.1 ryo return;
152 1.1 ryo printf("\e[K");
153 1.1 ryo }
154 1.1 ryo
155 1.1 ryo static void
156 1.1 ryo cls_eos(void)
157 1.1 ryo {
158 1.1 ryo if (nontty)
159 1.1 ryo return;
160 1.1 ryo printf("\e[J");
161 1.1 ryo }
162 1.1 ryo
163 1.1 ryo static void
164 1.1 ryo sigwinch_handler(int signo)
165 1.1 ryo {
166 1.1 ryo nontty = ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
167 1.1 ryo }
168 1.1 ryo
169 1.1 ryo static void
170 1.1 ryo sigalrm_handler(int signo)
171 1.1 ryo {
172 1.1 ryo sigalrm = 1;
173 1.1 ryo }
174 1.1 ryo
175 1.3 ryo static void
176 1.3 ryo ptrarray_push(struct ptrarray *ptrarray, void *ptr)
177 1.3 ryo {
178 1.3 ryo int error;
179 1.3 ryo
180 1.3 ryo if (ptrarray->pa_inuse >= ptrarray->pa_allocnum) {
181 1.3 ryo /* increase buffer */
182 1.3 ryo ptrarray->pa_allocnum += 1024;
183 1.3 ryo error = reallocarr(&ptrarray->pa_ptrs, ptrarray->pa_allocnum,
184 1.3 ryo sizeof(*ptrarray->pa_ptrs));
185 1.3 ryo if (error != 0)
186 1.3 ryo errc(EXIT_FAILURE, error, "rellocarr failed");
187 1.3 ryo }
188 1.3 ryo ptrarray->pa_ptrs[ptrarray->pa_inuse++] = ptr;
189 1.3 ryo }
190 1.3 ryo
191 1.3 ryo static void
192 1.3 ryo ptrarray_iterate(struct ptrarray *ptrarray, void (*ifunc)(void *))
193 1.3 ryo {
194 1.3 ryo size_t i;
195 1.1 ryo
196 1.3 ryo for (i = 0; i < ptrarray->pa_inuse; i++) {
197 1.3 ryo (*ifunc)(ptrarray->pa_ptrs[i]);
198 1.3 ryo }
199 1.3 ryo }
200 1.1 ryo
201 1.3 ryo static void
202 1.3 ryo ptrarray_clear(struct ptrarray *ptrarray)
203 1.3 ryo {
204 1.3 ryo ptrarray->pa_inuse = 0;
205 1.3 ryo }
206 1.1 ryo
207 1.1 ryo static int
208 1.1 ryo sample_compare_key(void *ctx, const void *n1, const void *keyp)
209 1.1 ryo {
210 1.1 ryo const struct sample_elm *a1 = n1;
211 1.1 ryo const struct sample_elm *a2 = (const struct sample_elm *)keyp;
212 1.1 ryo return a1->addr - a2->addr;
213 1.1 ryo }
214 1.1 ryo
215 1.1 ryo static signed int
216 1.1 ryo sample_compare_nodes(void *ctx, const void *n1, const void *n2)
217 1.1 ryo {
218 1.1 ryo const struct addr *a2 = n2;
219 1.1 ryo return sample_compare_key(ctx, n1, a2);
220 1.1 ryo }
221 1.1 ryo
222 1.1 ryo static const rb_tree_ops_t sample_ops = {
223 1.1 ryo .rbto_compare_nodes = sample_compare_nodes,
224 1.1 ryo .rbto_compare_key = sample_compare_key
225 1.1 ryo };
226 1.1 ryo
227 1.3 ryo static u_int
228 1.3 ryo n_align(u_int n, u_int align)
229 1.3 ryo {
230 1.3 ryo return (n + align - 1) / align * align;
231 1.3 ryo }
232 1.3 ryo
233 1.1 ryo static void
234 1.1 ryo sample_init(void)
235 1.1 ryo {
236 1.3 ryo const struct sample_elm *e;
237 1.3 ryo int mode;
238 1.3 ryo
239 1.3 ryo u_int size = sizeof(struct sample_elm) +
240 1.3 ryo sizeof(e->num_cpu[0]) * SAMPLE_MODE_NUM * ncpu;
241 1.3 ryo sizeof_sample_elm = n_align(size, __alignof(struct sample_elm));
242 1.3 ryo
243 1.3 ryo for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
244 1.3 ryo sample_n_kern_per_cpu[mode] = ecalloc(1,
245 1.3 ryo sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu);
246 1.3 ryo sample_n_user_per_cpu[mode] = ecalloc(1,
247 1.3 ryo sizeof(typeof(*sample_n_user_per_cpu[mode])) * ncpu);
248 1.3 ryo sample_n_per_event[mode] = ecalloc(1,
249 1.3 ryo sizeof(typeof(*sample_n_per_event[mode])) * nevent);
250 1.3 ryo sample_n_per_event_cpu[mode] = ecalloc(1,
251 1.3 ryo sizeof(typeof(*sample_n_per_event_cpu[mode])) *
252 1.3 ryo nevent * ncpu);
253 1.3 ryo }
254 1.1 ryo }
255 1.1 ryo
256 1.1 ryo static void
257 1.3 ryo sample_clear_instantaneous(void *arg)
258 1.1 ryo {
259 1.3 ryo struct sample_elm *e = (void *)arg;
260 1.1 ryo
261 1.3 ryo e->num[SAMPLE_MODE_INSTANTANEOUS] = 0;
262 1.3 ryo memset(SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_INSTANTANEOUS),
263 1.3 ryo 0, sizeof(e->num_cpu[0]) * ncpu);
264 1.1 ryo }
265 1.1 ryo
266 1.3 ryo static void
267 1.3 ryo sample_reset(bool reset_accumulative)
268 1.1 ryo {
269 1.3 ryo int mode;
270 1.3 ryo
271 1.3 ryo for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
272 1.3 ryo if (mode == SAMPLE_MODE_ACCUMULATIVE && !reset_accumulative)
273 1.3 ryo continue;
274 1.1 ryo
275 1.3 ryo sample_n_kern[mode] = 0;
276 1.3 ryo sample_n_user[mode] = 0;
277 1.3 ryo memset(sample_n_kern_per_cpu[mode], 0,
278 1.3 ryo sizeof(typeof(*sample_n_kern_per_cpu[mode])) * ncpu);
279 1.3 ryo memset(sample_n_user_per_cpu[mode], 0,
280 1.3 ryo sizeof(typeof(*sample_n_user_per_cpu[mode])) * ncpu);
281 1.3 ryo memset(sample_n_per_event[mode], 0,
282 1.3 ryo sizeof(typeof(*sample_n_per_event[mode])) * nevent);
283 1.3 ryo memset(sample_n_per_event_cpu[mode], 0,
284 1.3 ryo sizeof(typeof(*sample_n_per_event_cpu[mode])) *
285 1.3 ryo nevent * ncpu);
286 1.1 ryo }
287 1.1 ryo
288 1.3 ryo if (reset_accumulative) {
289 1.3 ryo rb_tree_init(&rb_tree_sample, &sample_ops);
290 1.3 ryo ptrarray_iterate(&sample_list[SAMPLE_MODE_ACCUMULATIVE], free);
291 1.3 ryo ptrarray_clear(&sample_list[SAMPLE_MODE_ACCUMULATIVE]);
292 1.3 ryo ptrarray_clear(&sample_list[SAMPLE_MODE_INSTANTANEOUS]);
293 1.3 ryo } else {
294 1.3 ryo ptrarray_iterate(&sample_list[SAMPLE_MODE_INSTANTANEOUS],
295 1.3 ryo sample_clear_instantaneous);
296 1.3 ryo ptrarray_clear(&sample_list[SAMPLE_MODE_INSTANTANEOUS]);
297 1.3 ryo }
298 1.1 ryo }
299 1.1 ryo
300 1.3 ryo static int __unused
301 1.3 ryo sample_sortfunc_accumulative(const void *a, const void *b)
302 1.1 ryo {
303 1.3 ryo struct sample_elm * const *ea = a;
304 1.3 ryo struct sample_elm * const *eb = b;
305 1.3 ryo return (*eb)->num[SAMPLE_MODE_ACCUMULATIVE] -
306 1.3 ryo (*ea)->num[SAMPLE_MODE_ACCUMULATIVE];
307 1.1 ryo }
308 1.1 ryo
309 1.1 ryo static int
310 1.3 ryo sample_sortfunc_instantaneous(const void *a, const void *b)
311 1.1 ryo {
312 1.3 ryo struct sample_elm * const *ea = a;
313 1.3 ryo struct sample_elm * const *eb = b;
314 1.3 ryo return (*eb)->num[SAMPLE_MODE_INSTANTANEOUS] -
315 1.3 ryo (*ea)->num[SAMPLE_MODE_INSTANTANEOUS];
316 1.1 ryo }
317 1.1 ryo
318 1.1 ryo static void
319 1.3 ryo sample_sort_accumulative(void)
320 1.1 ryo {
321 1.3 ryo qsort(sample_list[SAMPLE_MODE_ACCUMULATIVE].pa_ptrs,
322 1.3 ryo sample_list[SAMPLE_MODE_ACCUMULATIVE].pa_inuse,
323 1.3 ryo sizeof(struct sample_elm *), sample_sortfunc_accumulative);
324 1.3 ryo }
325 1.3 ryo
326 1.3 ryo static void
327 1.3 ryo sample_sort_instantaneous(void)
328 1.3 ryo {
329 1.3 ryo qsort(sample_list[SAMPLE_MODE_INSTANTANEOUS].pa_ptrs,
330 1.3 ryo sample_list[SAMPLE_MODE_INSTANTANEOUS].pa_inuse,
331 1.3 ryo sizeof(struct sample_elm *), sample_sortfunc_instantaneous);
332 1.1 ryo }
333 1.1 ryo
334 1.1 ryo static void
335 1.1 ryo sample_collect(tprof_sample_t *s)
336 1.1 ryo {
337 1.1 ryo struct sample_elm *e, *o;
338 1.1 ryo const char *name;
339 1.1 ryo size_t symid;
340 1.1 ryo uint64_t addr, offset;
341 1.1 ryo uint32_t flags = 0;
342 1.1 ryo uint32_t eventid, cpuid;
343 1.3 ryo int mode;
344 1.1 ryo
345 1.1 ryo eventid = __SHIFTOUT(s->s_flags, TPROF_SAMPLE_COUNTER_MASK);
346 1.1 ryo cpuid = s->s_cpuid;
347 1.1 ryo
348 1.3 ryo if (eventid >= nevent) /* unknown event from tprof? */
349 1.3 ryo return;
350 1.3 ryo
351 1.3 ryo for (mode = 0; mode < SAMPLE_MODE_NUM; mode++) {
352 1.3 ryo sample_n_per_event[mode][eventid]++;
353 1.3 ryo sample_n_per_event_cpu[mode][nevent * cpuid + eventid]++;
354 1.3 ryo }
355 1.1 ryo
356 1.1 ryo if ((s->s_flags & TPROF_SAMPLE_INKERNEL) == 0) {
357 1.3 ryo sample_n_user[SAMPLE_MODE_ACCUMULATIVE]++;
358 1.3 ryo sample_n_user[SAMPLE_MODE_INSTANTANEOUS]++;
359 1.3 ryo sample_n_user_per_cpu[SAMPLE_MODE_ACCUMULATIVE][cpuid]++;
360 1.3 ryo sample_n_user_per_cpu[SAMPLE_MODE_INSTANTANEOUS][cpuid]++;
361 1.1 ryo
362 1.1 ryo name = NULL;
363 1.1 ryo addr = s->s_pid; /* XXX */
364 1.1 ryo flags |= SAMPLE_ELM_FLAGS_USER;
365 1.1 ryo
366 1.1 ryo if (!opt_userland)
367 1.1 ryo return;
368 1.1 ryo } else {
369 1.3 ryo sample_n_kern[SAMPLE_MODE_ACCUMULATIVE]++;
370 1.3 ryo sample_n_kern[SAMPLE_MODE_INSTANTANEOUS]++;
371 1.3 ryo sample_n_kern_per_cpu[SAMPLE_MODE_ACCUMULATIVE][cpuid]++;
372 1.3 ryo sample_n_kern_per_cpu[SAMPLE_MODE_INSTANTANEOUS][cpuid]++;
373 1.1 ryo
374 1.1 ryo name = ksymlookup(s->s_pc, &offset, &symid);
375 1.1 ryo if (name != NULL) {
376 1.1 ryo addr = ksyms[symid]->value;
377 1.1 ryo } else {
378 1.1 ryo addr = s->s_pc;
379 1.1 ryo }
380 1.1 ryo }
381 1.1 ryo
382 1.3 ryo e = ecalloc(1, sizeof_sample_elm);
383 1.1 ryo e->addr = addr;
384 1.1 ryo e->name = name;
385 1.1 ryo e->flags = flags;
386 1.3 ryo e->num[SAMPLE_MODE_ACCUMULATIVE] = 1;
387 1.3 ryo e->num[SAMPLE_MODE_INSTANTANEOUS] = 1;
388 1.3 ryo SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_ACCUMULATIVE)[cpuid] = 1;
389 1.3 ryo SAMPLE_ELM_NUM_CPU(e, SAMPLE_MODE_INSTANTANEOUS)[cpuid] = 1;
390 1.1 ryo o = rb_tree_insert_node(&rb_tree_sample, e);
391 1.3 ryo if (o == e) {
392 1.3 ryo /* new symbol. add to list for sort */
393 1.3 ryo ptrarray_push(&sample_list[SAMPLE_MODE_ACCUMULATIVE], o);
394 1.3 ryo ptrarray_push(&sample_list[SAMPLE_MODE_INSTANTANEOUS], o);
395 1.3 ryo } else {
396 1.1 ryo /* already exists */
397 1.3 ryo free(e);
398 1.3 ryo
399 1.3 ryo o->num[SAMPLE_MODE_ACCUMULATIVE]++;
400 1.3 ryo if (o->num[SAMPLE_MODE_INSTANTANEOUS]++ == 0) {
401 1.3 ryo /* new instantaneous symbols. add to list for sort */
402 1.3 ryo ptrarray_push(&sample_list[SAMPLE_MODE_INSTANTANEOUS],
403 1.3 ryo o);
404 1.3 ryo }
405 1.3 ryo SAMPLE_ELM_NUM_CPU(o, SAMPLE_MODE_ACCUMULATIVE)[cpuid]++;
406 1.3 ryo SAMPLE_ELM_NUM_CPU(o, SAMPLE_MODE_INSTANTANEOUS)[cpuid]++;
407 1.1 ryo }
408 1.1 ryo }
409 1.1 ryo
410 1.1 ryo static void
411 1.1 ryo show_tprof_stat(void)
412 1.1 ryo {
413 1.1 ryo static struct tprof_stat tsbuf[2], *ts0, *ts;
414 1.1 ryo static u_int ts_i = 0;
415 1.1 ryo int ret;
416 1.1 ryo
417 1.1 ryo ts0 = &tsbuf[ts_i++ & 1];
418 1.1 ryo ts = &tsbuf[ts_i & 1];
419 1.1 ryo ret = ioctl(devfd, TPROF_IOC_GETSTAT, ts);
420 1.1 ryo if (ret == -1)
421 1.1 ryo err(EXIT_FAILURE, "TPROF_IOC_GETSTAT");
422 1.1 ryo
423 1.1 ryo #define TS_PRINT(label, _m) \
424 1.1 ryo do { \
425 1.1 ryo printf(label "%" PRIu64, ts->_m); \
426 1.1 ryo if (ts->_m != ts0->_m) \
427 1.1 ryo printf("(+%"PRIu64")", \
428 1.1 ryo ts->_m - ts0->_m); \
429 1.1 ryo printf(" "); \
430 1.1 ryo } while (0)
431 1.1 ryo TS_PRINT("tprof sample:", ts_sample);
432 1.1 ryo TS_PRINT(" overflow:", ts_overflow);
433 1.1 ryo TS_PRINT(" buf:", ts_buf);
434 1.1 ryo TS_PRINT(" emptybuf:", ts_emptybuf);
435 1.1 ryo TS_PRINT(" dropbuf:", ts_dropbuf);
436 1.1 ryo TS_PRINT(" dropbuf_sample:", ts_dropbuf_sample);
437 1.1 ryo }
438 1.1 ryo
439 1.1 ryo static void
440 1.1 ryo show_timestamp(void)
441 1.1 ryo {
442 1.1 ryo struct timeval tv;
443 1.1 ryo gettimeofday(&tv, NULL);
444 1.1 ryo cursor_address(win.ws_col - 7, 0);
445 1.1 ryo printf("%-8.8s", &(ctime((time_t *)&tv.tv_sec)[11]));
446 1.1 ryo }
447 1.1 ryo
448 1.1 ryo static void
449 1.1 ryo show_counters_alloc(void)
450 1.1 ryo {
451 1.3 ryo size_t sz = 2 * ncpu * nevent * sizeof(*counters);
452 1.3 ryo counters = ecalloc(1, sz);
453 1.1 ryo }
454 1.1 ryo
455 1.1 ryo static void
456 1.1 ryo show_counters(void)
457 1.1 ryo {
458 1.1 ryo tprof_counts_t countsbuf;
459 1.1 ryo uint64_t *cn[2], *c0, *c;
460 1.1 ryo u_int i;
461 1.1 ryo int n, ret;
462 1.1 ryo
463 1.1 ryo cn[0] = counters;
464 1.1 ryo cn[1] = counters + ncpu * nevent;
465 1.1 ryo c0 = cn[counters_i++ & 1];
466 1.1 ryo c = cn[counters_i & 1];
467 1.1 ryo
468 1.1 ryo for (n = 0; n < ncpu; n++) {
469 1.1 ryo countsbuf.c_cpu = n;
470 1.1 ryo ret = ioctl(devfd, TPROF_IOC_GETCOUNTS, &countsbuf);
471 1.1 ryo if (ret == -1)
472 1.1 ryo err(EXIT_FAILURE, "TPROF_IOC_GETCOUNTS");
473 1.1 ryo
474 1.1 ryo for (i = 0; i < nevent; i++)
475 1.1 ryo c[n * nevent + i] = countsbuf.c_count[i];
476 1.1 ryo }
477 1.1 ryo
478 1.1 ryo printf("%-22s", "Event counter (delta)");
479 1.1 ryo for (n = 0; n < ncpu; n++) {
480 1.1 ryo char cpuname[16];
481 1.1 ryo snprintf(cpuname, sizeof(cpuname), "CPU%u", n);
482 1.1 ryo printf("%11s", cpuname);
483 1.1 ryo }
484 1.1 ryo printf("\n");
485 1.1 ryo
486 1.1 ryo for (i = 0; i < nevent; i++) {
487 1.1 ryo printf("%-22.22s", eventname[i]);
488 1.1 ryo for (n = 0; n < ncpu; n++) {
489 1.1 ryo printf("%11"PRIu64,
490 1.1 ryo c[n * nevent + i] - c0[n * nevent + i]);
491 1.1 ryo }
492 1.1 ryo printf("\n");
493 1.1 ryo }
494 1.1 ryo printf("\n");
495 1.1 ryo }
496 1.1 ryo
497 1.1 ryo static void
498 1.1 ryo show_count_per_event(void)
499 1.1 ryo {
500 1.1 ryo u_int i, nsample_total;
501 1.1 ryo int n;
502 1.1 ryo
503 1.3 ryo nsample_total = sample_n_kern[opt_mode] + sample_n_user[opt_mode];
504 1.1 ryo
505 1.1 ryo for (i = 0; i < nevent; i++) {
506 1.3 ryo if (sample_n_per_event[opt_mode][i] >= nsample_total) {
507 1.3 ryo printf("%5.1f%%", sample_n_per_event[opt_mode][i] *
508 1.1 ryo 100.00 / nsample_total);
509 1.1 ryo } else {
510 1.3 ryo printf("%5.2f%%", sample_n_per_event[opt_mode][i] *
511 1.1 ryo 100.00 / nsample_total);
512 1.1 ryo }
513 1.3 ryo printf("%8"PRIu64" ", sample_n_per_event[opt_mode][i]);
514 1.1 ryo
515 1.1 ryo printf("%-32.32s", eventname[i]);
516 1.1 ryo for (n = 0; n < ncpu; n++) {
517 1.1 ryo printf("%6"PRIu64,
518 1.3 ryo sample_n_per_event_cpu[opt_mode][nevent * n + i]);
519 1.1 ryo }
520 1.1 ryo printf("\n");
521 1.1 ryo }
522 1.1 ryo }
523 1.1 ryo
524 1.1 ryo static void
525 1.1 ryo sample_show(void)
526 1.1 ryo {
527 1.1 ryo static u_int nshow;
528 1.1 ryo
529 1.1 ryo struct sample_elm *e;
530 1.3 ryo struct ptrarray *samples;
531 1.1 ryo u_int nsample_total;
532 1.1 ryo int i, n, ndisp;
533 1.1 ryo char namebuf[32];
534 1.1 ryo const char *name;
535 1.1 ryo
536 1.1 ryo int margin_lines = 7;
537 1.1 ryo
538 1.1 ryo margin_lines += 3 + nevent; /* show_counter_per_event() */
539 1.1 ryo
540 1.3 ryo if (opt_mode == SAMPLE_MODE_INSTANTANEOUS)
541 1.3 ryo sample_sort_instantaneous();
542 1.3 ryo else
543 1.3 ryo sample_sort_accumulative();
544 1.3 ryo samples = &sample_list[opt_mode];
545 1.3 ryo
546 1.1 ryo if (opt_showcounter)
547 1.1 ryo margin_lines += 2 + nevent;
548 1.1 ryo if (opt_userland)
549 1.1 ryo margin_lines += 1;
550 1.1 ryo
551 1.3 ryo ndisp = samples->pa_inuse;
552 1.1 ryo if (!nontty && ndisp > (win.ws_row - margin_lines))
553 1.1 ryo ndisp = win.ws_row - margin_lines;
554 1.1 ryo
555 1.1 ryo cursor_home();
556 1.1 ryo if (nshow++ == 0)
557 1.1 ryo cls_eos();
558 1.1 ryo
559 1.1 ryo
560 1.3 ryo if (opt_mode == SAMPLE_MODE_ACCUMULATIVE)
561 1.3 ryo printf("[Accumulative mode] ");
562 1.3 ryo
563 1.1 ryo show_tprof_stat();
564 1.1 ryo cls_eol();
565 1.1 ryo
566 1.1 ryo show_timestamp();
567 1.1 ryo printf("\n");
568 1.1 ryo printf("\n");
569 1.1 ryo
570 1.1 ryo if (opt_showcounter)
571 1.1 ryo show_counters();
572 1.1 ryo
573 1.1 ryo printf(" Rate Sample# Eventname ");
574 1.1 ryo for (n = 0; n < ncpu; n++) {
575 1.1 ryo if (n >= 1000) {
576 1.1 ryo snprintf(namebuf, sizeof(namebuf), "%d", n);
577 1.1 ryo } else if (n >= 100) {
578 1.1 ryo snprintf(namebuf, sizeof(namebuf), "#%d", n);
579 1.1 ryo } else {
580 1.1 ryo snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
581 1.1 ryo }
582 1.1 ryo printf(" %5s", namebuf);
583 1.1 ryo }
584 1.1 ryo printf("\n");
585 1.1 ryo printf("------ ------- --------------------------------");
586 1.1 ryo for (n = 0; n < ncpu; n++) {
587 1.1 ryo printf(" -----");
588 1.1 ryo }
589 1.1 ryo printf("\n");
590 1.1 ryo
591 1.1 ryo show_count_per_event();
592 1.1 ryo printf("\n");
593 1.1 ryo
594 1.1 ryo printf(" Rate Sample# Symbol ");
595 1.1 ryo for (n = 0; n < ncpu; n++) {
596 1.1 ryo if (n >= 1000) {
597 1.1 ryo snprintf(namebuf, sizeof(namebuf), "%d", n);
598 1.1 ryo } else if (n >= 100) {
599 1.1 ryo snprintf(namebuf, sizeof(namebuf), "#%d", n);
600 1.1 ryo } else {
601 1.1 ryo snprintf(namebuf, sizeof(namebuf), "CPU%d", n);
602 1.1 ryo }
603 1.1 ryo printf(" %5s", namebuf);
604 1.1 ryo }
605 1.1 ryo printf("\n");
606 1.1 ryo printf("------ ------- --------------------------------");
607 1.1 ryo for (n = 0; n < ncpu; n++) {
608 1.1 ryo printf(" -----");
609 1.1 ryo }
610 1.1 ryo printf("\n");
611 1.1 ryo
612 1.1 ryo for (i = 0; i < ndisp; i++) {
613 1.3 ryo e = (struct sample_elm *)samples->pa_ptrs[i];
614 1.1 ryo name = e->name;
615 1.1 ryo if (name == NULL) {
616 1.1 ryo if (e->flags & SAMPLE_ELM_FLAGS_USER) {
617 1.3 ryo snprintf(namebuf, sizeof(namebuf),
618 1.3 ryo "<PID:%"PRIu64">", e->addr);
619 1.1 ryo } else {
620 1.3 ryo snprintf(namebuf, sizeof(namebuf),
621 1.3 ryo "0x%016"PRIx64, e->addr);
622 1.1 ryo }
623 1.1 ryo name = namebuf;
624 1.1 ryo }
625 1.1 ryo
626 1.3 ryo nsample_total = sample_n_kern[opt_mode];
627 1.1 ryo if (opt_userland)
628 1.3 ryo nsample_total += sample_n_user[opt_mode];
629 1.1 ryo /*
630 1.1 ryo * even when only kernel mode events are configured,
631 1.1 ryo * interrupts may still occur in the user mode state.
632 1.1 ryo */
633 1.1 ryo if (nsample_total == 0)
634 1.1 ryo nsample_total = 1;
635 1.1 ryo
636 1.3 ryo if (e->num[opt_mode] >= nsample_total) {
637 1.3 ryo printf("%5.1f%%",
638 1.3 ryo e->num[opt_mode] * 100.00 / nsample_total);
639 1.1 ryo } else {
640 1.3 ryo printf("%5.2f%%",
641 1.3 ryo e->num[opt_mode] * 100.00 / nsample_total);
642 1.1 ryo }
643 1.3 ryo printf("%8u %-32.32s", e->num[opt_mode], name);
644 1.1 ryo
645 1.1 ryo for (n = 0; n < ncpu; n++) {
646 1.3 ryo if (SAMPLE_ELM_NUM_CPU(e, opt_mode)[n] == 0) {
647 1.1 ryo printf(" .");
648 1.3 ryo } else {
649 1.3 ryo printf("%6u",
650 1.3 ryo SAMPLE_ELM_NUM_CPU(e, opt_mode)[n]);
651 1.3 ryo }
652 1.1 ryo }
653 1.1 ryo printf("\n");
654 1.1 ryo }
655 1.1 ryo
656 1.3 ryo if ((u_int)ndisp != samples->pa_inuse) {
657 1.3 ryo printf(" : : (more %zu symbols omitted)\n",
658 1.3 ryo samples->pa_inuse - ndisp);
659 1.1 ryo } else {
660 1.1 ryo for (i = ndisp; i <= win.ws_row - margin_lines; i++) {
661 1.1 ryo printf("~");
662 1.1 ryo cls_eol();
663 1.1 ryo printf("\n");
664 1.1 ryo }
665 1.1 ryo }
666 1.1 ryo
667 1.1 ryo
668 1.1 ryo printf("------ ------- --------------------------------");
669 1.1 ryo for (n = 0; n < ncpu; n++) {
670 1.1 ryo printf(" -----");
671 1.1 ryo }
672 1.1 ryo printf("\n");
673 1.1 ryo
674 1.3 ryo printf("Total %8u %-32.32s", sample_n_kern[opt_mode], "in-kernel");
675 1.1 ryo for (n = 0; n < ncpu; n++) {
676 1.3 ryo printf("%6u", sample_n_kern_per_cpu[opt_mode][n]);
677 1.1 ryo }
678 1.1 ryo
679 1.1 ryo if (opt_userland) {
680 1.1 ryo printf("\n");
681 1.3 ryo printf(" %8u %-32.32s",
682 1.3 ryo sample_n_user[opt_mode], "userland");
683 1.1 ryo for (n = 0; n < ncpu; n++) {
684 1.3 ryo printf("%6u", sample_n_user_per_cpu[opt_mode][n]);
685 1.1 ryo }
686 1.1 ryo }
687 1.1 ryo
688 1.1 ryo cls_eos();
689 1.1 ryo }
690 1.1 ryo
691 1.1 ryo __dead static void
692 1.1 ryo tprof_top_usage(void)
693 1.1 ryo {
694 1.3 ryo fprintf(stderr, "%s top [-acu] [-e name[,scale] [-e ...]]"
695 1.3 ryo " [-i interval]\n", getprogname());
696 1.1 ryo exit(EXIT_FAILURE);
697 1.1 ryo }
698 1.1 ryo
699 1.1 ryo static int
700 1.1 ryo parse_event_scale(tprof_param_t *param, const char *str)
701 1.1 ryo {
702 1.1 ryo double d;
703 1.1 ryo uint64_t n;
704 1.1 ryo char *p;
705 1.1 ryo
706 1.1 ryo if (str[0] == '=') {
707 1.1 ryo str++;
708 1.1 ryo n = strtoull(str, &p, 0);
709 1.1 ryo if (*p != '\0')
710 1.1 ryo return -1;
711 1.1 ryo param->p_value2 = n;
712 1.1 ryo param->p_flags |= TPROF_PARAM_VALUE2_TRIGGERCOUNT;
713 1.1 ryo } else {
714 1.1 ryo if (strncasecmp("0x", str, 2) == 0)
715 1.1 ryo d = strtol(str, &p, 0);
716 1.1 ryo else
717 1.1 ryo d = strtod(str, &p);
718 1.1 ryo if (*p != '\0')
719 1.1 ryo return -1;
720 1.1 ryo param->p_value2 = 0x100000000ULL / d;
721 1.1 ryo param->p_flags |= TPROF_PARAM_VALUE2_SCALE;
722 1.1 ryo }
723 1.1 ryo return 0;
724 1.1 ryo }
725 1.1 ryo
726 1.1 ryo void
727 1.1 ryo tprof_top(int argc, char **argv)
728 1.1 ryo {
729 1.1 ryo tprof_param_t params[TPROF_MAXCOUNTERS];
730 1.1 ryo struct sigaction sa;
731 1.1 ryo struct itimerval it;
732 1.3 ryo ssize_t tprof_bufsize;
733 1.1 ryo u_int i;
734 1.1 ryo int ch, ret;
735 1.3 ryo char *tprof_buf, *tokens[2], *p;
736 1.1 ryo
737 1.1 ryo memset(params, 0, sizeof(params));
738 1.1 ryo nevent = 0;
739 1.1 ryo
740 1.3 ryo while ((ch = getopt(argc, argv, "ace:i:u")) != -1) {
741 1.1 ryo switch (ch) {
742 1.3 ryo case 'a':
743 1.3 ryo opt_mode = SAMPLE_MODE_ACCUMULATIVE;
744 1.3 ryo break;
745 1.1 ryo case 'c':
746 1.1 ryo opt_showcounter = 1;
747 1.1 ryo break;
748 1.1 ryo case 'e':
749 1.1 ryo p = estrdup(optarg);
750 1.1 ryo tokens[0] = strtok(p, ",");
751 1.1 ryo tokens[1] = strtok(NULL, ",");
752 1.1 ryo tprof_event_lookup(tokens[0], ¶ms[nevent]);
753 1.1 ryo if (tokens[1] != NULL) {
754 1.3 ryo if (parse_event_scale(¶ms[nevent],
755 1.3 ryo tokens[1]) != 0) {
756 1.3 ryo errx(EXIT_FAILURE, "invalid scale: %s",
757 1.3 ryo tokens[1]);
758 1.3 ryo }
759 1.1 ryo }
760 1.1 ryo eventname[nevent] = tokens[0];
761 1.1 ryo nevent++;
762 1.1 ryo if (nevent > __arraycount(params) ||
763 1.1 ryo nevent > ncounters)
764 1.1 ryo errx(EXIT_FAILURE, "Too many events. Only a"
765 1.1 ryo " maximum of %d counters can be used.",
766 1.1 ryo ncounters);
767 1.1 ryo break;
768 1.1 ryo case 'i':
769 1.1 ryo top_interval = strtol(optarg, &p, 10);
770 1.1 ryo if (*p != '\0' || top_interval <= 0)
771 1.1 ryo errx(EXIT_FAILURE, "Bad/invalid interval: %s",
772 1.1 ryo optarg);
773 1.1 ryo break;
774 1.1 ryo case 'u':
775 1.1 ryo opt_userland = 1;
776 1.1 ryo break;
777 1.1 ryo default:
778 1.1 ryo tprof_top_usage();
779 1.1 ryo }
780 1.1 ryo }
781 1.1 ryo argc -= optind;
782 1.1 ryo argv += optind;
783 1.1 ryo
784 1.1 ryo if (argc != 0)
785 1.1 ryo tprof_top_usage();
786 1.1 ryo
787 1.1 ryo if (nevent == 0) {
788 1.3 ryo const char *defaultevent = cycle_event_name();
789 1.1 ryo if (defaultevent == NULL)
790 1.1 ryo errx(EXIT_FAILURE, "cpu not supported");
791 1.1 ryo
792 1.1 ryo tprof_event_lookup(defaultevent, ¶ms[nevent]);
793 1.1 ryo eventname[nevent] = defaultevent;
794 1.1 ryo nevent++;
795 1.1 ryo }
796 1.1 ryo
797 1.3 ryo sample_init();
798 1.1 ryo show_counters_alloc();
799 1.1 ryo
800 1.1 ryo for (i = 0; i < nevent; i++) {
801 1.1 ryo params[i].p_counter = i;
802 1.1 ryo params[i].p_flags |= TPROF_PARAM_KERN | TPROF_PARAM_PROFILE;
803 1.1 ryo if (opt_userland)
804 1.1 ryo params[i].p_flags |= TPROF_PARAM_USER;
805 1.1 ryo ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, ¶ms[i]);
806 1.1 ryo if (ret == -1)
807 1.1 ryo err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT: %s",
808 1.1 ryo eventname[i]);
809 1.1 ryo }
810 1.1 ryo
811 1.1 ryo tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
812 1.1 ryo ret = ioctl(devfd, TPROF_IOC_START, &mask);
813 1.1 ryo if (ret == -1)
814 1.1 ryo err(EXIT_FAILURE, "TPROF_IOC_START");
815 1.1 ryo
816 1.1 ryo
817 1.1 ryo sigwinch_handler(0);
818 1.1 ryo ksyms = ksymload(&nksyms);
819 1.1 ryo
820 1.1 ryo sigemptyset(&sa.sa_mask);
821 1.1 ryo sa.sa_flags = SA_RESTART;
822 1.1 ryo sa.sa_handler = sigwinch_handler;
823 1.1 ryo sigaction(SIGWINCH, &sa, NULL);
824 1.1 ryo
825 1.1 ryo sigemptyset(&sa.sa_mask);
826 1.1 ryo sa.sa_flags = 0;
827 1.1 ryo sa.sa_handler = sigalrm_handler;
828 1.1 ryo sigaction(SIGALRM, &sa, NULL);
829 1.1 ryo
830 1.1 ryo it.it_interval.tv_sec = it.it_value.tv_sec = top_interval;
831 1.1 ryo it.it_interval.tv_usec = it.it_value.tv_usec = 0;
832 1.1 ryo setitimer(ITIMER_REAL, &it, NULL);
833 1.1 ryo
834 1.3 ryo sample_reset(true);
835 1.1 ryo printf("collecting samples...");
836 1.1 ryo fflush(stdout);
837 1.1 ryo
838 1.3 ryo tprof_bufsize = sizeof(tprof_sample_t) * 8192;
839 1.3 ryo tprof_buf = emalloc(tprof_bufsize);
840 1.1 ryo do {
841 1.1 ryo /* continue to accumulate tprof_sample until alarm arrives */
842 1.1 ryo while (sigalrm == 0) {
843 1.3 ryo ssize_t len = read(devfd, tprof_buf, tprof_bufsize);
844 1.1 ryo if (len == -1 && errno != EINTR)
845 1.1 ryo err(EXIT_FAILURE, "read");
846 1.1 ryo if (len > 0) {
847 1.3 ryo tprof_sample_t *s = (tprof_sample_t *)tprof_buf;
848 1.3 ryo while (s < (tprof_sample_t *)(tprof_buf + len))
849 1.3 ryo sample_collect(s++);
850 1.1 ryo }
851 1.1 ryo }
852 1.1 ryo sigalrm = 0;
853 1.1 ryo
854 1.1 ryo /* update screen */
855 1.1 ryo sample_show();
856 1.1 ryo fflush(stdout);
857 1.1 ryo
858 1.3 ryo sample_reset(false);
859 1.1 ryo } while (!nontty);
860 1.1 ryo
861 1.1 ryo printf("\n");
862 1.1 ryo }
863