evtranalyze: fix sloppy format strings
[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>
33#include <err.h>
46f0388c 34#include <inttypes.h>
e7c0dbba
AE
35#include <libgen.h>
36#include <math.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include <evtr.h>
43#include "xml.h"
44#include "svg.h"
45
46enum {
47 NR_TOP_THREADS = 5,
48};
49
50struct rows {
51 double row_increment;
52 double row_off;
53};
54
55#define CMD_PROTO(name) \
56 static int cmd_ ## name(int, char **)
57
58CMD_PROTO(show);
59CMD_PROTO(svg);
60
61struct command {
62 const char *name;
63 int (*func)(int argc, char **argv);
64} commands[] = {
65 {
66 .name = "show",
67 .func = &cmd_show,
68 },
69 {
70 .name = "svg",
71 .func = &cmd_svg,
72 },
73 {
74 .name = NULL,
75 },
76};
77
78evtr_t evtr;
79char *opt_infile;
80static int evtranalyze_debug;
81
82#define printd(...) \
83 do { \
84 if (evtranalyze_debug) { \
85 fprintf(stderr, __VA_ARGS__); \
86 } \
87 } while (0)
88
89static
90void
91usage(void)
92{
93 fprintf(stderr, "bad usage :P\n");
94 exit(2);
95}
96
97static
98void
99rows_init(struct rows *rows, int n, double height, double perc)
100{
101 double row_h;
102 rows->row_increment = height / n;
103 /* actual row height */
104 row_h = perc * rows->row_increment;
105 rows->row_off = (rows->row_increment - row_h) / 2.0;
106 assert(!isnan(rows->row_increment));
107 assert(!isnan(rows->row_off));
108}
109
110static
111void
112rows_n(struct rows *rows, int n, double *y, double *height)
113{
114 *y = n * rows->row_increment + rows->row_off;
115 *height = rows->row_increment - 2 * rows->row_off;
116}
117
118/*
119 * Which fontsize to use so that the string fits in the
120 * given rect.
121 */
122static
123double
124fontsize_for_rect(double width, double height, int textlen)
125{
126 double wpc, maxh;
127 /*
128 * We start with a font size equal to the height
129 * of the rectangle and round down so that we only
130 * use a limited number of sizes.
131 *
132 * For a rectangle width comparable to the height,
133 * the text might extend outside of the rectangle.
134 * In that case we need to limit it.
135 */
136 /* available width per character */
137 wpc = width / textlen;
138 /*
139 * Assuming a rough hight/width ratio for characters,
140 * calculate the available height and round it down
141 * just to be on the safe side.
142 */
143#define GLYPH_HIGHT_TO_WIDTH 1.5
144 maxh = GLYPH_HIGHT_TO_WIDTH * wpc * 0.9;
145 if (height > maxh) {
146 height = maxh;
147 } else if (height < 0.01) {
148 height = 0.01;
149 } else {
150 /* rounding (XXX: make cheaper)*/
151 height = log(height);
152 height = round(height);
153 height = exp(height);
154 }
155 return height;
156}
157
158struct pass_hook {
159 void (*pre)(void *);
160 void (*event)(void *, evtr_event_t);
161 void (*post)(void *);
162 void *data;
163 struct evtr_filter *filts;
164 int nfilts;
165};
166
167struct thread_info {
168 uint64_t runtime;
169};
170
171struct td_switch_ctx {
172 svg_document_t svg;
173 struct rows *cpu_rows;
174 struct rows *thread_rows;
175 uint64_t interval_start, interval_end;
176 uint64_t first_ts, last_ts;
177 double width;
178 double xscale; /* scale factor applied to x */
179 svg_rect_t cpu_sw_rect;
180 svg_rect_t thread_rect;
181 svg_rect_t inactive_rect;
182 svg_text_t thread_label;
956f9b36
AE
183 struct cpu_table {
184 struct cpu *cpus;
185 int ncpus;
186 } cputab;
e7c0dbba
AE
187 struct evtr_thread **top_threads;
188 int nr_top_threads;
189 double thread_rows_yoff;
190};
191
192struct cpu {
193 struct evtr_thread *td;
194 int i; /* cpu index */
195 uint64_t ts; /* time cpu switched to td */
196 uint64_t first_ts, last_ts;
956f9b36 197 double freq;
e7c0dbba
AE
198};
199
200static
201void
202do_pass(struct pass_hook *hooks, int nhooks)
203{
204 struct evtr_filter *filts = NULL;
205 int nfilts = 0, i;
206 struct evtr_query *q;
207 struct evtr_event ev;
208
209 for (i = 0; i < nhooks; ++i) {
210 struct pass_hook *h = &hooks[i];
211 if (h->pre)
212 h->pre(h->data);
213 if (h->nfilts > 0) {
214 filts = realloc(filts, (nfilts + h->nfilts) *
215 sizeof(struct evtr_filter));
216 if (!filts)
217 err(1, "Out of memory");
0b33b2e3 218 memcpy(filts + nfilts, h->filts,
e7c0dbba
AE
219 h->nfilts * sizeof(struct evtr_filter));
220 nfilts += h->nfilts;
221 }
222 }
223 q = evtr_query_init(evtr, filts, nfilts);
224 if (!q)
225 err(1, "Can't initialize query\n");
226 while(!evtr_query_next(q, &ev)) {
227 for (i = 0; i < nhooks; ++i) {
228 if (hooks[i].event)
229 hooks[i].event(hooks[i].data, &ev);
230 }
231 }
232 if (evtr_error(evtr)) {
233 err(1, evtr_errmsg(evtr));
234 }
235 evtr_query_destroy(q);
236
237 for (i = 0; i < nhooks; ++i) {
238 if (hooks[i].post)
239 hooks[i].post(hooks[i].data);
240 }
241 if (evtr_rewind(evtr))
242 err(1, "Can't rewind event stream\n");
243}
244
245static
246void
247draw_thread_run(struct td_switch_ctx *ctx, struct cpu *c, evtr_event_t ev, int row)
248{
249 double x, w, y, height;
250 w = (ev->ts - c->ts) * ctx->xscale;
251 x = (ev->ts - ctx->first_ts) * ctx->xscale;
252 rows_n(ctx->thread_rows, row, &y, &height);
253 svg_rect_draw(ctx->svg, ctx->thread_rect, x - w,
254 y + ctx->thread_rows_yoff, w, height);
255}
256
257static
258void
259draw_ctx_switch(struct td_switch_ctx *ctx, struct cpu *c, evtr_event_t ev)
260{
261 struct svg_transform textrot;
262 char comm[100];
263 double x, w, fs, y, height;
264 int textlen;
265
266 assert(ctx->xscale > 0.0);
267 if (!c->ts)
268 return;
269 /* distance to previous context switch */
270 w = (ev->ts - c->ts) * ctx->xscale;
271 x = (ev->ts - ctx->first_ts) * ctx->xscale;
272 if ((x - w) < 0) {
46f0388c
AE
273 fprintf(stderr, "(%ju - %ju) * %.20lf\n",
274 (uintmax_t)ev->ts,
275 (uintmax_t)ctx->first_ts, ctx->xscale);
e7c0dbba
AE
276 abort();
277 }
278
279 rows_n(ctx->cpu_rows, c->i, &y, &height);
280 assert(!isnan(y));
281 assert(!isnan(height));
282
283 svg_rect_draw(ctx->svg, ctx->cpu_sw_rect, x - w, y, w, height);
284
285 /*
286 * Draw the text label describing the thread we
287 * switched out of.
288 */
289 textrot.tx = x - w;
290 textrot.ty = y;
291 textrot.sx = 1.0;
292 textrot.sy = 1.0;
293 textrot.rot = 90.0;
294 textlen = snprintf(comm, sizeof(comm) - 1, "%s (%p)",
295 c->td ? c->td->comm : "unknown",
296 c->td ? c->td->id: NULL);
297 if (textlen > (int)sizeof(comm))
298 textlen = sizeof(comm) - 1;
299 comm[sizeof(comm) - 1] = '\0';
300 /*
301 * Note the width and hight are exchanged because
302 * the bounding rectangle is rotated by 90 degrees.
303 */
304 fs = fontsize_for_rect(height, w, textlen);
305 svg_text_draw(ctx->svg, ctx->thread_label, &textrot, comm,
306 fs);
307}
308
309
310/*
311 * The stats for ntd have changed, update ->top_threads
312 */
313static
314void
315top_threads_update(struct td_switch_ctx *ctx, struct evtr_thread *ntd)
316{
317 struct thread_info *tdi = ntd->userdata;
318 int i, j;
319 for (i = 0; i < ctx->nr_top_threads; ++i) {
320 struct evtr_thread *td = ctx->top_threads[i];
321 if (td == ntd) {
322 /*
323 * ntd is already in top_threads and it is at
324 * the correct ranking
325 */
326 break;
327 }
328 if (!td) {
329 /* empty slot -- just insert our thread */
330 ctx->top_threads[i] = ntd;
331 break;
332 }
333 if (((struct thread_info *)td->userdata)->runtime >=
334 tdi->runtime) {
335 /* this thread ranks higher than we do. Move on */
336 continue;
337 }
338 /*
339 * OK, we've found the first thread that we outrank, so we
340 * need to replace it w/ our thread.
341 */
342 td = ntd; /* td holds the thread we will insert next */
343 for (j = i + 1; j < ctx->nr_top_threads; ++j, ++i) {
344 struct evtr_thread *tmp;
345
346 /* tmp holds the thread we replace */
347 tmp = ctx->top_threads[j];
348 ctx->top_threads[j] = td;
349 if (tmp == ntd) {
350 /*
351 * Our thread was already in the top list,
352 * and we just removed the second instance.
353 * Nothing more to do.
354 */
355 break;
356 }
357 td = tmp;
358 }
359 break;
360 }
361}
362
363static
364void
365ctxsw_prepare_event(void *_ctx, evtr_event_t ev)
366{
367 struct td_switch_ctx *ctx = _ctx;
956f9b36 368 struct cpu *c, *cpus = ctx->cputab.cpus;
e7c0dbba
AE
369 struct thread_info *tdi;
370
371 (void)evtr;
46f0388c
AE
372 printd("test1 (%ju:%ju) : %ju\n",
373 (uintmax_t)ctx->interval_start,
374 (uintmax_t)ctx->interval_end,
375 (uintmax_t)ev->ts);
e7c0dbba
AE
376 if ((ev->ts > ctx->interval_end) ||
377 (ev->ts < ctx->interval_start))
378 return;
0b33b2e3 379 printd("PREPEV on %d\n", ev->cpu);
e7c0dbba
AE
380
381 /* update first/last timestamps */
382 c = &cpus[ev->cpu];
383 if (!c->first_ts) {
384 c->first_ts = ev->ts;
46f0388c
AE
385 printd("setting first_ts (%d) = %ju\n", ev->cpu,
386 (uintmax_t)c->first_ts);
e7c0dbba
AE
387 }
388 c->last_ts = ev->ts;
389 /*
390 * c->td can be null when this is the first ctxsw event we
391 * observe for a cpu
392 */
393 if (c->td) {
394 /* update thread stats */
395 if (!c->td->userdata) {
396 if (!(tdi = malloc(sizeof(struct thread_info))))
397 err(1, "Out of memory");
398 c->td->userdata = tdi;
399 tdi->runtime = 0;
400 }
401 tdi = c->td->userdata;
402 tdi->runtime += ev->ts - c->ts;
e7c0dbba
AE
403 top_threads_update(ctx, c->td);
404 }
405
406 /* Notice that ev->td is the new thread for ctxsw events */
407 c->td = ev->td;
408 c->ts = ev->ts;
409}
410
411static
412void
413ctxsw_prepare_post(void *_ctx)
414{
415 struct td_switch_ctx *ctx = _ctx;
956f9b36 416 struct cpu *cpus = ctx->cputab.cpus;
e7c0dbba
AE
417 int i;
418
419 (void)evtr;
420 ctx->first_ts = -1;
421 ctx->last_ts = 0;
46f0388c 422 printd("first_ts[0] = %ju\n", (uintmax_t)cpus[0].first_ts);
956f9b36 423 for (i = 0; i < ctx->cputab.ncpus; ++i) {
46f0388c
AE
424 printd("first_ts[%d] = %ju\n", i,
425 (uintmax_t)cpus[i].first_ts);
e7c0dbba
AE
426 if (cpus[i].first_ts && (cpus[i].first_ts < ctx->first_ts))
427 ctx->first_ts = cpus[i].first_ts;
428 if (cpus[i].last_ts && (cpus[i].last_ts > ctx->last_ts))
429 ctx->last_ts = cpus[i].last_ts;
430 cpus[i].td = NULL;
431 cpus[i].ts = 0;
432 }
433}
434
435static
436void
437ctxsw_draw_pre(void *_ctx)
438{
439 struct td_switch_ctx *ctx = _ctx;
440 struct svg_transform textrot;
441 char comm[100];
442 double y, height, fs;
443 int i, textlen;
444 struct evtr_thread *td;
445
446 textrot.tx = 0.0 - 0.2; /* XXX */
447 textrot.sx = 1.0;
448 textrot.sy = 1.0;
449 textrot.rot = 270.0;
450
451 for (i = 0; i < ctx->nr_top_threads; ++i) {
452 td = ctx->top_threads[i];
453 rows_n(ctx->thread_rows, i, &y, &height);
454 svg_rect_draw(ctx->svg, ctx->inactive_rect, 0.0,
455 y + ctx->thread_rows_yoff, ctx->width, height);
456 textlen = snprintf(comm, sizeof(comm) - 1, "%s (%p)",
457 td->comm, td->id);
458 if (textlen > (int)sizeof(comm))
459 textlen = sizeof(comm) - 1;
460 comm[sizeof(comm) - 1] = '\0';
461 fs = fontsize_for_rect(height, 100.0, textlen);
462
463 textrot.ty = y + ctx->thread_rows_yoff + height;
464 svg_text_draw(ctx->svg, ctx->thread_label, &textrot,
465 comm, fs);
466 }
467}
468
469static
470void
471ctxsw_draw_event(void *_ctx, evtr_event_t ev)
472{
473 struct td_switch_ctx *ctx = _ctx;
956f9b36 474 struct cpu *c = &ctx->cputab.cpus[ev->cpu];
e7c0dbba
AE
475 int i;
476
477 /*
478 * ctx->last_ts can be 0 if there were no events
479 * in the specified interval, in which case
480 * ctx->first_ts is invalid too.
481 */
482 assert(!ctx->last_ts || (ev->ts >= ctx->first_ts));
46f0388c
AE
483 printd("test2 (%ju:%ju) : %ju\n", (uintmax_t)ctx->interval_start,
484 (uintmax_t)ctx->interval_end, (uintmax_t)ev->ts);
e7c0dbba
AE
485 if ((ev->ts > ctx->interval_end) ||
486 (ev->ts < ctx->interval_start))
487 return;
0b33b2e3 488 printd("DRAWEV %d\n", ev->cpu);
e7c0dbba
AE
489 if (c->td != ev->td) { /* thread switch (or preemption) */
490 draw_ctx_switch(ctx, c, ev);
491 /* XXX: this is silly */
492 for (i = 0; i < ctx->nr_top_threads; ++i) {
493 if (ctx->top_threads[i] == c->td) {
494 draw_thread_run(ctx, c, ev, i);
495 break;
496 }
497 }
498 c->td = ev->td;
499 c->ts = ev->ts;
500 }
501}
502
956f9b36
AE
503static
504void
505cputab_init(struct cpu_table *ct)
506{
507 struct cpu *cpus;
508 double *freqs;
509 int i;
510
511 if ((ct->ncpus = evtr_ncpus(evtr)) <= 0)
512 err(1, "No cpu information!\n");
513 printd("evtranalyze: ncpus %d\n", ct->ncpus);
514 if (!(ct->cpus = malloc(sizeof(struct cpu) * ct->ncpus))) {
515 err(1, "Can't allocate memory\n");
516 }
517 cpus = ct->cpus;
518 if (!(freqs = malloc(sizeof(double) * ct->ncpus))) {
519 err(1, "Can't allocate memory\n");
520 }
521 if ((i = evtr_cpufreqs(evtr, freqs))) {
522 warnc(i, "Can't get cpu frequencies\n");
523 for (i = 0; i < ct->ncpus; ++i) {
524 freqs[i] = -1.0;
525 }
526 }
527
528 /* initialize cpu array */
529 for (i = 0; i < ct->ncpus; ++i) {
530 cpus[i].td = NULL;
531 cpus[i].ts = 0;
532 cpus[i].i = i;
533 cpus[i].first_ts = 0;
534 cpus[i].last_ts = 0;
535 cpus[i].freq = freqs[i];
536 }
537 free(freqs);
538}
539
540
e7c0dbba
AE
541static
542int
543cmd_svg(int argc, char **argv)
544{
545 svg_document_t svg;
956f9b36 546 int ch;
e7c0dbba
AE
547 double height, width;
548 struct rows cpu_rows, thread_rows;
e7c0dbba
AE
549 struct td_switch_ctx td_ctx;
550 struct evtr_filter ctxsw_filts[2] = {
551 {
552 .flags = 0,
553 .cpu = -1,
554 },
555 {
556 .flags = 0,
557 .cpu = -1,
558 },
559 };
560 struct pass_hook ctxsw_prepare = {
561 .pre = NULL,
562 .event = ctxsw_prepare_event,
563 .post = ctxsw_prepare_post,
564 .data = &td_ctx,
565 .filts = ctxsw_filts,
566 .nfilts = sizeof(ctxsw_filts)/sizeof(ctxsw_filts[0]),
567 }, ctxsw_draw = {
568 .pre = ctxsw_draw_pre,
569 .event = ctxsw_draw_event,
570 .post = NULL,
571 .data = &td_ctx,
572 .filts = ctxsw_filts,
573 .nfilts = sizeof(ctxsw_filts)/sizeof(ctxsw_filts[0]),
574 };
575
576 /*
577 * We are interested in thread switch and preemption
578 * events, but we don't use the data directly. Instead
579 * we rely on ev->td.
580 */
0b33b2e3 581 ctxsw_filts[0].fmt = "sw %p > %p";
e7c0dbba
AE
582 ctxsw_filts[1].fmt = "pre %p > %p";
583 td_ctx.interval_start = 0;
584 td_ctx.interval_end = -1; /* i.e. no interval given */
585 td_ctx.nr_top_threads = NR_TOP_THREADS;
586
587 printd("argc: %d, argv[0] = %s\n", argc, argv[0] ? argv[0] : "NULL");
588 optind = 0;
589 optreset = 1;
590 while ((ch = getopt(argc, argv, "i:")) != -1) {
591 switch (ch) {
592 case 'i':
46f0388c
AE
593 if (sscanf(optarg, "%" SCNu64 ":%" SCNu64,
594 &td_ctx.interval_start,
e7c0dbba
AE
595 &td_ctx.interval_end) != 2) {
596 usage();
597 }
598 break;
599 default:
600 usage();
601 }
602
603 }
604 argc -= optind;
605 argv += optind;
606
607 height = 200.0;
608 width = 700.0;
609 td_ctx.width = width;
e7c0dbba 610
956f9b36 611 cputab_init(&td_ctx.cputab);
e7c0dbba
AE
612 if (!(td_ctx.top_threads = calloc(td_ctx.nr_top_threads, sizeof(struct evtr_thread *))))
613 err(1, "Can't allocate memory\n");
614 if (!(svg = svg_document_create("output.svg")))
615 err(1, "Can't open svg document\n");
616
617 /*
618 * Create rectangles to use for output.
619 */
620 if (!(td_ctx.cpu_sw_rect = svg_rect_new("generic")))
621 err(1, "Can't create rectangle\n");
622 if (!(td_ctx.thread_rect = svg_rect_new("thread")))
623 err(1, "Can't create rectangle\n");
624 if (!(td_ctx.inactive_rect = svg_rect_new("inactive")))
625 err(1, "Can't create rectangle\n");
626 /* text for thread names */
627 if (!(td_ctx.thread_label = svg_text_new("generic")))
628 err(1, "Can't create text\n");
956f9b36 629 rows_init(&cpu_rows, td_ctx.cputab.ncpus, height, 0.9);
e7c0dbba
AE
630 td_ctx.svg = svg;
631 td_ctx.xscale = -1.0;
632 td_ctx.cpu_rows = &cpu_rows;
633
634 do_pass(&ctxsw_prepare, 1);
635 td_ctx.thread_rows_yoff = height;
636 td_ctx.thread_rows = &thread_rows;
637 rows_init(td_ctx.thread_rows, td_ctx.nr_top_threads, 300, 0.9);
638 td_ctx.xscale = width / (td_ctx.last_ts - td_ctx.first_ts);
46f0388c
AE
639 printd("first %ju, last %ju, xscale %lf\n",
640 (uintmax_t)td_ctx.first_ts, (uintmax_t)td_ctx.last_ts,
641 td_ctx.xscale);
e7c0dbba
AE
642
643 do_pass(&ctxsw_draw, 1);
644
645 svg_document_close(svg);
646 return 0;
647}
648
649static
650int
651cmd_show(int argc, char **argv)
652{
653 struct evtr_event ev;
654 struct evtr_query *q;
655 struct evtr_filter filt;
956f9b36
AE
656 struct cpu_table cputab;
657 double freq;
e7c0dbba 658 int ch;
956f9b36 659 uint64_t last_ts = 0;
e7c0dbba 660
956f9b36
AE
661 cputab_init(&cputab);
662 /*
663 * Assume all cores run on the same frequency
664 * for now. There's no reason to complicate
665 * things unless we can detect frequency change
666 * events as well.
667 *
668 * Note that the code is very simplistic and will
669 * produce garbage if the kernel doesn't fixup
670 * the timestamps for cores running with different
671 * frequencies.
672 */
673 freq = cputab.cpus[0].freq;
674 freq /= 1000000; /* we want to print out usecs */
675 printd("using freq = %lf\n", freq);
e7c0dbba
AE
676 filt.fmt = NULL;
677 optind = 0;
678 optreset = 1;
679 while ((ch = getopt(argc, argv, "f:")) != -1) {
680 switch (ch) {
681 case 'f':
682 filt.fmt = optarg;
683 break;
684 }
685 }
686 filt.flags = 0;
687 filt.cpu = -1;
688 printd("fmt = %s\n", filt.fmt ? filt.fmt : "NULL");
689 q = evtr_query_init(evtr, &filt, 1);
690 if (!q)
691 err(1, "Can't initialize query\n");
692 while(!evtr_query_next(q, &ev)) {
693 char buf[1024];
956f9b36
AE
694
695 if (!last_ts)
696 last_ts = ev.ts;
697 if (freq < 0.0) {
698 printf("%s\t%llu cycles\t[%.3d]\t%s:%d",
699 ev.td ? ev.td->comm : "unknown",
700 ev.ts - last_ts, ev.cpu,
701 basename(ev.file), ev.line);
702 } else {
703 printf("%s\t%.3lf usecs\t[%.3d]\t%s:%d",
704 ev.td ? ev.td->comm : "unknown",
705 (ev.ts - last_ts) / freq, ev.cpu,
706 basename(ev.file), ev.line);
707 }
e7c0dbba
AE
708 if (ev.fmt) {
709 evtr_event_data(&ev, buf, sizeof(buf));
710 printf(" !\t%s\n", buf);
711 } else {
712 printf("\n");
713 }
956f9b36 714 last_ts = ev.ts;
e7c0dbba
AE
715 }
716 if (evtr_error(evtr)) {
717 err(1, evtr_errmsg(evtr));
718 }
719 evtr_query_destroy(q);
720 return 0;
721}
722
723int
724main(int argc, char **argv)
725{
726 int ch;
727 FILE *inf;
728 struct command *cmd;
729
730 while ((ch = getopt(argc, argv, "f:D:")) != -1) {
731 switch (ch) {
732 case 'f':
733 opt_infile = optarg;
734 break;
735 case 'D':
736 evtranalyze_debug = atoi(optarg);
737 evtr_set_debug(evtranalyze_debug);
738 break;
739 default:
740 usage();
741 }
742 }
743 argc -= optind;
744 argv += optind;
745
746 if (argc == 0) {
747 err(2, "need to specify a command\n");
748 }
749 if (!opt_infile || !strcmp(opt_infile, "-")) {
750 inf = stdin;
751 } else {
752 inf = fopen(opt_infile, "r");
753 if (!inf) {
754 err(2, "Can't open input file\n");
755 }
756 }
757
758 if (!(evtr = evtr_open_read(inf))) {
759 err(1, "Can't open evtr stream\n");
760 }
761
762
763 for (cmd = commands; cmd->name != NULL; ++cmd) {
764 if (strcmp(argv[0], cmd->name))
765 continue;
766 cmd->func(argc, argv);
767 break;
768 }
769 if (!cmd->name) {
770 err(2, "no such command: %s\n", argv[0]);
771 }
772
773 evtr_close(evtr);
774 return 0;
775}