evtranalyze: don't try to open stdin by default
[dragonfly.git] / usr.bin / evtranalyze / evtranalyze.c
CommitLineData
e7c0dbba
AE
1/*
2 * Copyright (c) 2009, 2010 Aggelos Economopoulos. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 * 3. Neither the name of The DragonFly Project nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific, prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <assert.h>
5e8d9c2b 33#include <ctype.h>
e7c0dbba 34#include <err.h>
46f0388c 35#include <inttypes.h>
e7c0dbba
AE
36#include <libgen.h>
37#include <math.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42
43#include <evtr.h>
44#include "xml.h"
45#include "svg.h"
4b9ae3c6 46#include "trivial.h"
e7c0dbba
AE
47
48enum {
49 NR_TOP_THREADS = 5,
50};
51
52struct rows {
53 double row_increment;
54 double row_off;
55};
56
57#define CMD_PROTO(name) \
58 static int cmd_ ## name(int, char **)
59
60CMD_PROTO(show);
61CMD_PROTO(svg);
2b130cb3 62CMD_PROTO(stats);
70af04e9 63CMD_PROTO(summary);
e7c0dbba
AE
64
65struct command {
66 const char *name;
67 int (*func)(int argc, char **argv);
68} commands[] = {
69 {
70 .name = "show",
71 .func = &cmd_show,
72 },
73 {
74 .name = "svg",
75 .func = &cmd_svg,
76 },
77 {
2b130cb3
AE
78 .name = "stats",
79 .func = &cmd_stats,
80 },
81 {
70af04e9
AE
82 .name = "summary",
83 .func = &cmd_summary,
84 },
85 {
e7c0dbba
AE
86 .name = NULL,
87 },
88};
89
90evtr_t evtr;
ab91bd09 91static char *opt_infile;
4b9ae3c6 92unsigned evtranalyze_debug;
e7c0dbba
AE
93
94static
95void
5e8d9c2b
AE
96printd_set_flags(const char *str, unsigned int *flags)
97{
98 /*
99 * This is suboptimal as we don't detect
100 * invalid flags.
101 */
102 for (; *str; ++str) {
103 if ('A' == *str) {
104 *flags = -1;
105 return;
106 }
107 if (!islower(*str))
108 err(2, "invalid debug flag %c\n", *str);
9ec5f95f 109 *flags |= 1 << (*str - 'a');
5e8d9c2b
AE
110 }
111}
112
113static
114void
e7c0dbba
AE
115usage(void)
116{
117 fprintf(stderr, "bad usage :P\n");
118 exit(2);
119}
120
121static
122void
123rows_init(struct rows *rows, int n, double height, double perc)
124{
125 double row_h;
126 rows->row_increment = height / n;
127 /* actual row height */
128 row_h = perc * rows->row_increment;
129 rows->row_off = (rows->row_increment - row_h) / 2.0;
130 assert(!isnan(rows->row_increment));
131 assert(!isnan(rows->row_off));
132}
133
134static
135void
136rows_n(struct rows *rows, int n, double *y, double *height)
137{
138 *y = n * rows->row_increment + rows->row_off;
139 *height = rows->row_increment - 2 * rows->row_off;
140}
141
142/*
143 * Which fontsize to use so that the string fits in the
144 * given rect.
145 */
146static
147double
148fontsize_for_rect(double width, double height, int textlen)
149{
150 double wpc, maxh;
151 /*
152 * We start with a font size equal to the height
153 * of the rectangle and round down so that we only
154 * use a limited number of sizes.
155 *
156 * For a rectangle width comparable to the height,
157 * the text might extend outside of the rectangle.
158 * In that case we need to limit it.
159 */
160 /* available width per character */
161 wpc = width / textlen;
162 /*
163 * Assuming a rough hight/width ratio for characters,
164 * calculate the available height and round it down
165 * just to be on the safe side.
166 */
167#define GLYPH_HIGHT_TO_WIDTH 1.5
168 maxh = GLYPH_HIGHT_TO_WIDTH * wpc * 0.9;
169 if (height > maxh) {
170 height = maxh;
171 } else if (height < 0.01) {
172 height = 0.01;
173 } else {
174 /* rounding (XXX: make cheaper)*/
175 height = log(height);
176 height = round(height);
177 height = exp(height);
178 }
179 return height;
180}
181
182struct pass_hook {
183 void (*pre)(void *);
184 void (*event)(void *, evtr_event_t);
185 void (*post)(void *);
186 void *data;
187 struct evtr_filter *filts;
188 int nfilts;
189};
190
191struct thread_info {
192 uint64_t runtime;
193};
194
70af04e9
AE
195struct ts_interval {
196 uint64_t start;
197 uint64_t end;
198};
199
e7c0dbba
AE
200struct td_switch_ctx {
201 svg_document_t svg;
202 struct rows *cpu_rows;
203 struct rows *thread_rows;
70af04e9
AE
204 /* which events the user cares about */
205 struct ts_interval interval;
206 /* first/last event timestamps on any cpu */
207 struct ts_interval firstlast;
e7c0dbba
AE
208 double width;
209 double xscale; /* scale factor applied to x */
210 svg_rect_t cpu_sw_rect;
211 svg_rect_t thread_rect;
212 svg_rect_t inactive_rect;
213 svg_text_t thread_label;
956f9b36
AE
214 struct cpu_table {
215 struct cpu *cpus;
216 int ncpus;
217 } cputab;
e7c0dbba
AE
218 struct evtr_thread **top_threads;
219 int nr_top_threads;
220 double thread_rows_yoff;
221};
222
223struct cpu {
224 struct evtr_thread *td;
225 int i; /* cpu index */
226 uint64_t ts; /* time cpu switched to td */
70af04e9
AE
227 /* timestamp for first/last event on this cpu */
228 struct ts_interval firstlast;
956f9b36 229 double freq;
70af04e9 230 uintmax_t evcnt;
e7c0dbba
AE
231};
232
233static
234void
235do_pass(struct pass_hook *hooks, int nhooks)
236{
237 struct evtr_filter *filts = NULL;
238 int nfilts = 0, i;
239 struct evtr_query *q;
240 struct evtr_event ev;
241
242 for (i = 0; i < nhooks; ++i) {
243 struct pass_hook *h = &hooks[i];
244 if (h->pre)
245 h->pre(h->data);
246 if (h->nfilts > 0) {
247 filts = realloc(filts, (nfilts + h->nfilts) *
248 sizeof(struct evtr_filter));
249 if (!filts)
250 err(1, "Out of memory");
0b33b2e3 251 memcpy(filts + nfilts, h->filts,
e7c0dbba
AE
252 h->nfilts * sizeof(struct evtr_filter));
253 nfilts += h->nfilts;
254 }
255 }
256 q = evtr_query_init(evtr, filts, nfilts);
257 if (!q)
258 err(1, "Can't initialize query\n");
259 while(!evtr_query_next(q, &ev)) {
260 for (i = 0; i < nhooks; ++i) {
261 if (hooks[i].event)
262 hooks[i].event(hooks[i].data, &ev);
263 }
264 }
2b130cb3
AE
265 if (evtr_query_error(q)) {
266 err(1, evtr_query_errmsg(q));
e7c0dbba
AE
267 }
268 evtr_query_destroy(q);
269
270 for (i = 0; i < nhooks; ++i) {
271 if (hooks[i].post)
272 hooks[i].post(hooks[i].data);
273 }
274 if (evtr_rewind(evtr))
275 err(1, "Can't rewind event stream\n");
276}
277
278static
279void
280draw_thread_run(struct td_switch_ctx *ctx, struct cpu *c, evtr_event_t ev, int row)
281{
282 double x, w, y, height;
283 w = (ev->ts - c->ts) * ctx->xscale;
70af04e9 284 x = (ev->ts - ctx->firstlast.start) * ctx->xscale;
e7c0dbba
AE
285 rows_n(ctx->thread_rows, row, &y, &height);
286 svg_rect_draw(ctx->svg, ctx->thread_rect, x - w,
287 y + ctx->thread_rows_yoff, w, height);
288}
289
290static
291void
292draw_ctx_switch(struct td_switch_ctx *ctx, struct cpu *c, evtr_event_t ev)
293{
294 struct svg_transform textrot;
295 char comm[100];
296 double x, w, fs, y, height;
297 int textlen;
298
299 assert(ctx->xscale > 0.0);
300 if (!c->ts)
301 return;
302 /* distance to previous context switch */
303 w = (ev->ts - c->ts) * ctx->xscale;
70af04e9 304 x = (ev->ts - ctx->firstlast.start) * ctx->xscale;
e7c0dbba 305 if ((x - w) < 0) {
46f0388c
AE
306 fprintf(stderr, "(%ju - %ju) * %.20lf\n",
307 (uintmax_t)ev->ts,
70af04e9 308 (uintmax_t)ctx->firstlast.start, ctx->xscale);
e7c0dbba
AE
309 abort();
310 }
311
312 rows_n(ctx->cpu_rows, c->i, &y, &height);
313 assert(!isnan(y));
314 assert(!isnan(height));
315
316 svg_rect_draw(ctx->svg, ctx->cpu_sw_rect, x - w, y, w, height);
317
318 /*
319 * Draw the text label describing the thread we
320 * switched out of.
321 */
322 textrot.tx = x - w;
323 textrot.ty = y;
324 textrot.sx = 1.0;
325 textrot.sy = 1.0;
326 textrot.rot = 90.0;
327 textlen = snprintf(comm, sizeof(comm) - 1, "%s (%p)",
328 c->td ? c->td->comm : "unknown",
329 c->td ? c->td->id: NULL);
330 if (textlen > (int)sizeof(comm))
331 textlen = sizeof(comm) - 1;
332 comm[sizeof(comm) - 1] = '\0';
333 /*
334 * Note the width and hight are exchanged because
335 * the bounding rectangle is rotated by 90 degrees.
336 */
337 fs = fontsize_for_rect(height, w, textlen);
338 svg_text_draw(ctx->svg, ctx->thread_label, &textrot, comm,
339 fs);
340}
341
342
343/*
344 * The stats for ntd have changed, update ->top_threads
345 */
346static
347void
348top_threads_update(struct td_switch_ctx *ctx, struct evtr_thread *ntd)
349{
350 struct thread_info *tdi = ntd->userdata;
351 int i, j;
352 for (i = 0; i < ctx->nr_top_threads; ++i) {
353 struct evtr_thread *td = ctx->top_threads[i];
354 if (td == ntd) {
355 /*
356 * ntd is already in top_threads and it is at
357 * the correct ranking
358 */
359 break;
360 }
361 if (!td) {
362 /* empty slot -- just insert our thread */
363 ctx->top_threads[i] = ntd;
364 break;
365 }
366 if (((struct thread_info *)td->userdata)->runtime >=
367 tdi->runtime) {
368 /* this thread ranks higher than we do. Move on */
369 continue;
370 }
371 /*
372 * OK, we've found the first thread that we outrank, so we
373 * need to replace it w/ our thread.
374 */
375 td = ntd; /* td holds the thread we will insert next */
376 for (j = i + 1; j < ctx->nr_top_threads; ++j, ++i) {
377 struct evtr_thread *tmp;
378
379 /* tmp holds the thread we replace */
380 tmp = ctx->top_threads[j];
381 ctx->top_threads[j] = td;
382 if (tmp == ntd) {
383 /*
384 * Our thread was already in the top list,
385 * and we just removed the second instance.
386 * Nothing more to do.
387 */
388 break;
389 }
390 td = tmp;
391 }
392 break;
393 }
394}
395
396static
397void
398ctxsw_prepare_event(void *_ctx, evtr_event_t ev)
399{
400 struct td_switch_ctx *ctx = _ctx;
956f9b36 401 struct cpu *c, *cpus = ctx->cputab.cpus;
e7c0dbba
AE
402 struct thread_info *tdi;
403
404 (void)evtr;
5e8d9c2b 405 printd(INTV, "test1 (%ju:%ju) : %ju\n",
70af04e9
AE
406 (uintmax_t)ctx->interval.start,
407 (uintmax_t)ctx->interval.end,
46f0388c 408 (uintmax_t)ev->ts);
70af04e9
AE
409 if ((ev->ts > ctx->interval.end) ||
410 (ev->ts < ctx->interval.start))
e7c0dbba 411 return;
5e8d9c2b 412 printd(INTV, "PREPEV on %d\n", ev->cpu);
e7c0dbba
AE
413
414 /* update first/last timestamps */
415 c = &cpus[ev->cpu];
70af04e9
AE
416 if (!c->firstlast.start) {
417 c->firstlast.start = ev->ts;
e7c0dbba 418 }
70af04e9 419 c->firstlast.end = ev->ts;
e7c0dbba
AE
420 /*
421 * c->td can be null when this is the first ctxsw event we
422 * observe for a cpu
423 */
424 if (c->td) {
425 /* update thread stats */
426 if (!c->td->userdata) {
427 if (!(tdi = malloc(sizeof(struct thread_info))))
428 err(1, "Out of memory");
429 c->td->userdata = tdi;
430 tdi->runtime = 0;
431 }
432 tdi = c->td->userdata;
433 tdi->runtime += ev->ts - c->ts;
e7c0dbba
AE
434 top_threads_update(ctx, c->td);
435 }
436
437 /* Notice that ev->td is the new thread for ctxsw events */
438 c->td = ev->td;
439 c->ts = ev->ts;
440}
441
442static
443void
70af04e9 444find_first_last_ts(struct cpu_table *cputab, struct ts_interval *fl)
e7c0dbba 445{
70af04e9 446 struct cpu *cpus = &cputab->cpus[0];
e7c0dbba
AE
447 int i;
448
70af04e9
AE
449 fl->start = -1;
450 fl->end = 0;
451 for (i = 0; i < cputab->ncpus; ++i) {
5e8d9c2b 452 printd(INTV, "cpu%d: (%ju, %ju)\n", i,
70af04e9
AE
453 (uintmax_t)cpus[i].firstlast.start,
454 (uintmax_t)cpus[i].firstlast.end);
455 if (cpus[i].firstlast.start &&
456 (cpus[i].firstlast.start < fl->start))
457 fl->start = cpus[i].firstlast.start;
458 if (cpus[i].firstlast.end &&
459 (cpus[i].firstlast.end > fl->end))
460 fl->end = cpus[i].firstlast.end;
e7c0dbba
AE
461 cpus[i].td = NULL;
462 cpus[i].ts = 0;
463 }
5e8d9c2b 464 printd(INTV, "global (%jd, %jd)\n", (uintmax_t)fl->start, (uintmax_t)fl->end);
70af04e9
AE
465}
466
467static
468void
469ctxsw_prepare_post(void *_ctx)
470{
471 struct td_switch_ctx *ctx = _ctx;
472
473 find_first_last_ts(&ctx->cputab, &ctx->firstlast);
e7c0dbba
AE
474}
475
476static
477void
478ctxsw_draw_pre(void *_ctx)
479{
480 struct td_switch_ctx *ctx = _ctx;
481 struct svg_transform textrot;
482 char comm[100];
483 double y, height, fs;
484 int i, textlen;
485 struct evtr_thread *td;
486
487 textrot.tx = 0.0 - 0.2; /* XXX */
488 textrot.sx = 1.0;
489 textrot.sy = 1.0;
490 textrot.rot = 270.0;
491
492 for (i = 0; i < ctx->nr_top_threads; ++i) {
493 td = ctx->top_threads[i];
70af04e9
AE
494 if (!td)
495 break;
e7c0dbba
AE
496 rows_n(ctx->thread_rows, i, &y, &height);
497 svg_rect_draw(ctx->svg, ctx->inactive_rect, 0.0,
498 y + ctx->thread_rows_yoff, ctx->width, height);
499 textlen = snprintf(comm, sizeof(comm) - 1, "%s (%p)",
500 td->comm, td->id);
501 if (textlen > (int)sizeof(comm))
502 textlen = sizeof(comm) - 1;
503 comm[sizeof(comm) - 1] = '\0';
504 fs = fontsize_for_rect(height, 100.0, textlen);
505
506 textrot.ty = y + ctx->thread_rows_yoff + height;
507 svg_text_draw(ctx->svg, ctx->thread_label, &textrot,
508 comm, fs);
509 }
510}
511
512static
513void
514ctxsw_draw_event(void *_ctx, evtr_event_t ev)
515{
516 struct td_switch_ctx *ctx = _ctx;
956f9b36 517 struct cpu *c = &ctx->cputab.cpus[ev->cpu];
e7c0dbba
AE
518 int i;
519
520 /*
70af04e9 521 * ctx->firstlast.end can be 0 if there were no events
e7c0dbba 522 * in the specified interval, in which case
70af04e9 523 * ctx->firstlast.start is invalid too.
e7c0dbba 524 */
70af04e9 525 assert(!ctx->firstlast.end || (ev->ts >= ctx->firstlast.start));
5e8d9c2b 526 printd(INTV, "test2 (%ju:%ju) : %ju\n", (uintmax_t)ctx->interval.start,
70af04e9
AE
527 (uintmax_t)ctx->interval.end, (uintmax_t)ev->ts);
528 if ((ev->ts > ctx->interval.end) ||
529 (ev->ts < ctx->interval.start))
e7c0dbba 530 return;
5e8d9c2b 531 printd(INTV, "DRAWEV %d\n", ev->cpu);
e7c0dbba
AE
532 if (c->td != ev->td) { /* thread switch (or preemption) */
533 draw_ctx_switch(ctx, c, ev);
534 /* XXX: this is silly */
535 for (i = 0; i < ctx->nr_top_threads; ++i) {
70af04e9
AE
536 if (!ctx->top_threads[i])
537 break;
e7c0dbba
AE
538 if (ctx->top_threads[i] == c->td) {
539 draw_thread_run(ctx, c, ev, i);
540 break;
541 }
542 }
543 c->td = ev->td;
544 c->ts = ev->ts;
545 }
546}
547
548static
956f9b36
AE
549void
550cputab_init(struct cpu_table *ct)
551{
552 struct cpu *cpus;
553 double *freqs;
554 int i;
555
556 if ((ct->ncpus = evtr_ncpus(evtr)) <= 0)
557 err(1, "No cpu information!\n");
5e8d9c2b 558 printd(MISC, "evtranalyze: ncpus %d\n", ct->ncpus);
956f9b36
AE
559 if (!(ct->cpus = malloc(sizeof(struct cpu) * ct->ncpus))) {
560 err(1, "Can't allocate memory\n");
561 }
562 cpus = ct->cpus;
563 if (!(freqs = malloc(sizeof(double) * ct->ncpus))) {
564 err(1, "Can't allocate memory\n");
565 }
566 if ((i = evtr_cpufreqs(evtr, freqs))) {
567 warnc(i, "Can't get cpu frequencies\n");
568 for (i = 0; i < ct->ncpus; ++i) {
569 freqs[i] = -1.0;
570 }
571 }
572
573 /* initialize cpu array */
574 for (i = 0; i < ct->ncpus; ++i) {
575 cpus[i].td = NULL;
576 cpus[i].ts = 0;
577 cpus[i].i = i;
70af04e9
AE
578 cpus[i].firstlast.start = 0;
579 cpus[i].firstlast.end = 0;
580 cpus[i].evcnt = 0;
956f9b36
AE
581 cpus[i].freq = freqs[i];
582 }
583 free(freqs);
584}
585
70af04e9
AE
586static
587void
588parse_interval(const char *_str, struct ts_interval *ts,
589 struct cpu_table *cputab)
590{
591 double s, e, freq;
592 const char *str = _str + 1;
593
594 if ('c' == *_str) { /* cycles */
595 if (sscanf(str, "%" SCNu64 ":%" SCNu64,
596 &ts->start,
597 &ts->end) == 2)
598 return;
599 } else if ('m' == *_str) { /* miliseconds */
600 if (sscanf(str, "%lf:%lf", &s, &e) == 2) {
601 freq = cputab->cpus[0].freq;
602 freq *= 1000.0; /* msecs */
603 if (freq < 0.0) {
604 fprintf(stderr, "No frequency information"
605 " available\n");
606 err(2, "Can't convert time to cycles\n");
607 }
608 ts->start = s * freq;
609 ts->end = e * freq;
610 return;
611 }
612 }
613 fprintf(stderr, "invalid interval format: %s\n", _str);
614 usage();
615}
616
956f9b36
AE
617
618static
e7c0dbba
AE
619int
620cmd_svg(int argc, char **argv)
621{
622 svg_document_t svg;
956f9b36 623 int ch;
e7c0dbba
AE
624 double height, width;
625 struct rows cpu_rows, thread_rows;
e7c0dbba 626 struct td_switch_ctx td_ctx;
70af04e9 627 const char *outpath = "output.svg";
e7c0dbba
AE
628 struct evtr_filter ctxsw_filts[2] = {
629 {
630 .flags = 0,
631 .cpu = -1,
2b130cb3 632 .ev_type = EVTR_TYPE_PROBE,
e7c0dbba
AE
633 },
634 {
635 .flags = 0,
636 .cpu = -1,
2b130cb3 637 .ev_type = EVTR_TYPE_PROBE,
e7c0dbba
AE
638 },
639 };
640 struct pass_hook ctxsw_prepare = {
641 .pre = NULL,
642 .event = ctxsw_prepare_event,
643 .post = ctxsw_prepare_post,
644 .data = &td_ctx,
645 .filts = ctxsw_filts,
646 .nfilts = sizeof(ctxsw_filts)/sizeof(ctxsw_filts[0]),
647 }, ctxsw_draw = {
648 .pre = ctxsw_draw_pre,
649 .event = ctxsw_draw_event,
650 .post = NULL,
651 .data = &td_ctx,
652 .filts = ctxsw_filts,
653 .nfilts = sizeof(ctxsw_filts)/sizeof(ctxsw_filts[0]),
654 };
655
656 /*
657 * We are interested in thread switch and preemption
658 * events, but we don't use the data directly. Instead
659 * we rely on ev->td.
660 */
0b33b2e3 661 ctxsw_filts[0].fmt = "sw %p > %p";
e7c0dbba 662 ctxsw_filts[1].fmt = "pre %p > %p";
70af04e9
AE
663 td_ctx.interval.start = 0;
664 td_ctx.interval.end = -1; /* i.e. no interval given */
e7c0dbba 665 td_ctx.nr_top_threads = NR_TOP_THREADS;
70af04e9 666 cputab_init(&td_ctx.cputab); /* needed for parse_interval() */
e7c0dbba 667
e7c0dbba
AE
668 optind = 0;
669 optreset = 1;
70af04e9 670 while ((ch = getopt(argc, argv, "i:o:")) != -1) {
e7c0dbba
AE
671 switch (ch) {
672 case 'i':
70af04e9
AE
673 parse_interval(optarg, &td_ctx.interval,
674 &td_ctx.cputab);
675 break;
676 case 'o':
677 outpath = optarg;
e7c0dbba
AE
678 break;
679 default:
680 usage();
681 }
682
683 }
684 argc -= optind;
685 argv += optind;
686
687 height = 200.0;
688 width = 700.0;
689 td_ctx.width = width;
e7c0dbba 690
70af04e9
AE
691 if (!(td_ctx.top_threads = calloc(td_ctx.nr_top_threads,
692 sizeof(struct evtr_thread *))))
e7c0dbba 693 err(1, "Can't allocate memory\n");
70af04e9 694 if (!(svg = svg_document_create(outpath)))
e7c0dbba
AE
695 err(1, "Can't open svg document\n");
696
697 /*
698 * Create rectangles to use for output.
699 */
700 if (!(td_ctx.cpu_sw_rect = svg_rect_new("generic")))
701 err(1, "Can't create rectangle\n");
702 if (!(td_ctx.thread_rect = svg_rect_new("thread")))
703 err(1, "Can't create rectangle\n");
704 if (!(td_ctx.inactive_rect = svg_rect_new("inactive")))
705 err(1, "Can't create rectangle\n");
706 /* text for thread names */
707 if (!(td_ctx.thread_label = svg_text_new("generic")))
708 err(1, "Can't create text\n");
956f9b36 709 rows_init(&cpu_rows, td_ctx.cputab.ncpus, height, 0.9);
e7c0dbba
AE
710 td_ctx.svg = svg;
711 td_ctx.xscale = -1.0;
712 td_ctx.cpu_rows = &cpu_rows;
713
714 do_pass(&ctxsw_prepare, 1);
715 td_ctx.thread_rows_yoff = height;
716 td_ctx.thread_rows = &thread_rows;
717 rows_init(td_ctx.thread_rows, td_ctx.nr_top_threads, 300, 0.9);
70af04e9 718 td_ctx.xscale = width / (td_ctx.firstlast.end - td_ctx.firstlast.start);
5e8d9c2b 719 printd(SVG, "first %ju, last %ju, xscale %lf\n",
70af04e9 720 (uintmax_t)td_ctx.firstlast.start, (uintmax_t)td_ctx.firstlast.end,
46f0388c 721 td_ctx.xscale);
e7c0dbba
AE
722
723 do_pass(&ctxsw_draw, 1);
724
725 svg_document_close(svg);
726 return 0;
727}
728
729static
730int
731cmd_show(int argc, char **argv)
732{
733 struct evtr_event ev;
734 struct evtr_query *q;
735 struct evtr_filter filt;
956f9b36
AE
736 struct cpu_table cputab;
737 double freq;
e7c0dbba 738 int ch;
956f9b36 739 uint64_t last_ts = 0;
e7c0dbba 740
956f9b36
AE
741 cputab_init(&cputab);
742 /*
743 * Assume all cores run on the same frequency
744 * for now. There's no reason to complicate
745 * things unless we can detect frequency change
746 * events as well.
747 *
748 * Note that the code is very simplistic and will
749 * produce garbage if the kernel doesn't fixup
750 * the timestamps for cores running with different
751 * frequencies.
752 */
753 freq = cputab.cpus[0].freq;
754 freq /= 1000000; /* we want to print out usecs */
5e8d9c2b 755 printd(MISC, "using freq = %lf\n", freq);
2b130cb3
AE
756 filt.flags = 0;
757 filt.cpu = -1;
758 filt.ev_type = EVTR_TYPE_PROBE;
e7c0dbba
AE
759 filt.fmt = NULL;
760 optind = 0;
761 optreset = 1;
762 while ((ch = getopt(argc, argv, "f:")) != -1) {
763 switch (ch) {
764 case 'f':
765 filt.fmt = optarg;
766 break;
767 }
768 }
e7c0dbba
AE
769 q = evtr_query_init(evtr, &filt, 1);
770 if (!q)
771 err(1, "Can't initialize query\n");
772 while(!evtr_query_next(q, &ev)) {
773 char buf[1024];
956f9b36
AE
774
775 if (!last_ts)
776 last_ts = ev.ts;
777 if (freq < 0.0) {
b51ef1e9 778 printf("%s\t%ju cycles\t[%.3d]\t%s:%d",
956f9b36 779 ev.td ? ev.td->comm : "unknown",
b51ef1e9 780 (uintmax_t)(ev.ts - last_ts), ev.cpu,
956f9b36
AE
781 basename(ev.file), ev.line);
782 } else {
783 printf("%s\t%.3lf usecs\t[%.3d]\t%s:%d",
784 ev.td ? ev.td->comm : "unknown",
785 (ev.ts - last_ts) / freq, ev.cpu,
786 basename(ev.file), ev.line);
787 }
e7c0dbba
AE
788 if (ev.fmt) {
789 evtr_event_data(&ev, buf, sizeof(buf));
790 printf(" !\t%s\n", buf);
791 } else {
792 printf("\n");
793 }
956f9b36 794 last_ts = ev.ts;
e7c0dbba 795 }
2b130cb3
AE
796 if (evtr_query_error(q)) {
797 err(1, evtr_query_errmsg(q));
e7c0dbba
AE
798 }
799 evtr_query_destroy(q);
800 return 0;
801}
802
70af04e9
AE
803static
804int
2b130cb3
AE
805cmd_stats(int argc, char **argv)
806{
807 struct evtr_event ev;
808 struct evtr_query *q;
809 struct evtr_filter filt;
810 struct cpu_table cputab;
811 double freq;
2b130cb3
AE
812 uint64_t last_ts = 0;
813 enum evtr_value_type type = EVTR_VAL_INT;
814 uintmax_t sum, occurences;
815
816 cputab_init(&cputab);
817 /*
818 * Assume all cores run on the same frequency
819 * for now. There's no reason to complicate
820 * things unless we can detect frequency change
821 * events as well.
822 *
823 * Note that the code is very simplistic and will
824 * produce garbage if the kernel doesn't fixup
825 * the timestamps for cores running with different
826 * frequencies.
827 */
828 freq = cputab.cpus[0].freq;
829 freq /= 1000000; /* we want to print out usecs */
830 printd(MISC, "using freq = %lf\n", freq);
831 filt.flags = 0;
832 filt.cpu = -1;
833 filt.ev_type = EVTR_TYPE_STMT;
834 filt.var = NULL;
835 optind = 0;
836 optreset = 1;
b51ef1e9 837
2b130cb3
AE
838 if (argc != 2)
839 err(2, "Need exactly one variable");
840 filt.var = argv[1];
841 q = evtr_query_init(evtr, &filt, 1);
842 if (!q)
843 err(1, "Can't initialize query\n");
844 sum = occurences = 0;
845 while(!evtr_query_next(q, &ev)) {
846
847 if (!last_ts)
848 last_ts = ev.ts;
849
850 assert(ev.type == EVTR_TYPE_STMT);
851 if (type != ev.stmt.val->type) {
852 printf("ignoring assignment of wrong type %d (expected %d)\n",
853 ev.stmt.val->type, type);
854 } else {
5eb01240
AE
855 printf("var \"%s\" = %jx\n",
856 filt.var, ev.stmt.val->num);
2b130cb3
AE
857 sum += ev.stmt.val->num;
858 ++occurences;
859 }
860 last_ts = ev.ts;
861 }
862 if (evtr_query_error(q)) {
863 err(1, evtr_query_errmsg(q));
864 }
865 printf("median for variable %s is %lf\n", argv[1], (double)sum / occurences);
866 evtr_query_destroy(q);
867 return 0;
868}
869
870
871static
872int
70af04e9
AE
873cmd_summary(int argc, char **argv)
874{
875 struct evtr_filter filt;
876 struct evtr_event ev;
877 struct evtr_query *q;
878 double freq;
879 struct cpu_table cputab;
880 struct ts_interval global;
881 uintmax_t global_evcnt;
882 int i;
883
884 (void)argc;
885 (void)argv;
886
887 cputab_init(&cputab);
2b130cb3 888 filt.ev_type = EVTR_TYPE_PROBE;
70af04e9
AE
889 filt.fmt = NULL;
890 filt.flags = 0;
891 filt.cpu = -1;
892
893 q = evtr_query_init(evtr, &filt, 1);
894 if (!q)
895 err(1, "Can't initialize query\n");
896 while(!evtr_query_next(q, &ev)) {
897 struct cpu *c = &cputab.cpus[ev.cpu];
898 if (!c->firstlast.start)
899 c->firstlast.start = ev.ts;
900 ++c->evcnt;
901 c->firstlast.end = ev.ts;
902 }
2b130cb3
AE
903 if (evtr_query_error(q)) {
904 err(1, evtr_query_errmsg(q));
70af04e9
AE
905 }
906 evtr_query_destroy(q);
907
908 find_first_last_ts(&cputab, &global);
909
910 freq = cputab.cpus[0].freq;
911 global_evcnt = 0;
912 for (i = 0; i < cputab.ncpus; ++i) {
913 struct cpu *c = &cputab.cpus[i];
914 printf("CPU %d: %jd events in %.3lf secs\n", i,
915 c->evcnt, (c->firstlast.end - c->firstlast.start)
916 / freq);
917 global_evcnt += c->evcnt;
918 }
919 printf("Total: %jd events on %d cpus in %.3lf secs\n", global_evcnt,
920 cputab.ncpus, (global.end - global.start) / freq);
921 return 0;
922}
923
924
925
e7c0dbba
AE
926int
927main(int argc, char **argv)
928{
929 int ch;
930 FILE *inf;
931 struct command *cmd;
5e8d9c2b 932 char *tmp;
e7c0dbba
AE
933
934 while ((ch = getopt(argc, argv, "f:D:")) != -1) {
935 switch (ch) {
936 case 'f':
937 opt_infile = optarg;
938 break;
939 case 'D':
5e8d9c2b
AE
940 if ((tmp = strchr(optarg, ':'))) {
941 *tmp++ = '\0';
942 evtr_set_debug(tmp);
943 }
944 printd_set_flags(optarg, &evtranalyze_debug);
e7c0dbba
AE
945 break;
946 default:
947 usage();
948 }
949 }
950 argc -= optind;
951 argv += optind;
952
953 if (argc == 0) {
954 err(2, "need to specify a command\n");
955 }
ab91bd09
AE
956 if (!opt_infile) {
957 err(2, "you need to specify an input file\n");
958 } else if (!strcmp(opt_infile, "-")) {
e7c0dbba
AE
959 inf = stdin;
960 } else {
961 inf = fopen(opt_infile, "r");
962 if (!inf) {
963 err(2, "Can't open input file\n");
964 }
965 }
966
967 if (!(evtr = evtr_open_read(inf))) {
968 err(1, "Can't open evtr stream\n");
969 }
970
971
972 for (cmd = commands; cmd->name != NULL; ++cmd) {
973 if (strcmp(argv[0], cmd->name))
974 continue;
975 cmd->func(argc, argv);
976 break;
977 }
978 if (!cmd->name) {
979 err(2, "no such command: %s\n", argv[0]);
980 }
981
982 evtr_close(evtr);
983 return 0;
984}