evtranalyze: plotting capability
[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"
807a01c2 46#include "plotter.h"
4b9ae3c6 47#include "trivial.h"
e7c0dbba
AE
48
49enum {
50 NR_TOP_THREADS = 5,
36b35b4a 51 NR_BUCKETS = 1021,
e7c0dbba
AE
52};
53
54struct rows {
55 double row_increment;
56 double row_off;
57};
58
59#define CMD_PROTO(name) \
60 static int cmd_ ## name(int, char **)
61
62CMD_PROTO(show);
63CMD_PROTO(svg);
2b130cb3 64CMD_PROTO(stats);
70af04e9 65CMD_PROTO(summary);
e7c0dbba
AE
66
67struct command {
68 const char *name;
69 int (*func)(int argc, char **argv);
70} commands[] = {
71 {
72 .name = "show",
73 .func = &cmd_show,
74 },
75 {
76 .name = "svg",
77 .func = &cmd_svg,
78 },
2b130cb3
AE
79 {
80 .name = "stats",
81 .func = &cmd_stats,
82 },
70af04e9
AE
83 {
84 .name = "summary",
85 .func = &cmd_summary,
86 },
e7c0dbba
AE
87 {
88 .name = NULL,
89 },
90};
91
92evtr_t evtr;
ab91bd09 93static char *opt_infile;
4b9ae3c6 94unsigned evtranalyze_debug;
e7c0dbba 95
5e8d9c2b
AE
96static
97void
98printd_set_flags(const char *str, unsigned int *flags)
99{
100 /*
101 * This is suboptimal as we don't detect
102 * invalid flags.
103 */
104 for (; *str; ++str) {
105 if ('A' == *str) {
106 *flags = -1;
107 return;
108 }
109 if (!islower(*str))
110 err(2, "invalid debug flag %c\n", *str);
9ec5f95f 111 *flags |= 1 << (*str - 'a');
5e8d9c2b
AE
112 }
113}
114
36b35b4a
AE
115struct hashentry {
116 uintptr_t key;
117 uintptr_t val;
118 struct hashentry *next;
119};
120
121struct hashtab {
122 struct hashentry *buckets[NR_BUCKETS];
123 uintptr_t (*hashfunc)(uintptr_t);
124 uintptr_t (*cmpfunc)(uintptr_t, uintptr_t);
125};
126
da620666 127static int
36b35b4a
AE
128ehash_find(const struct hashtab *tab, uintptr_t key, uintptr_t *val)
129{
130 struct hashentry *ent;
131
132 for(ent = tab->buckets[tab->hashfunc(key)];
133 ent && tab->cmpfunc(ent->key, key);
134 ent = ent->next);
135
136 if (!ent)
137 return !0;
138 *val = ent->val;
139 return 0;
140}
141
da620666 142static struct hashentry *
36b35b4a
AE
143ehash_insert(struct hashtab *tab, uintptr_t key, uintptr_t val)
144{
145 struct hashentry *ent;
146 int hsh;
147
148 if (!(ent = malloc(sizeof(*ent)))) {
149 fprintf(stderr, "out of memory\n");
150 return NULL;
151 }
152 hsh = tab->hashfunc(key);
153 ent->next = tab->buckets[hsh];
154 ent->key = key;
155 ent->val = val;
156 tab->buckets[hsh] = ent;
157 return ent;
158}
159static
160int
161ehash_delete(struct hashtab *tab, uintptr_t key)
162{
163 struct hashentry *ent, *prev;
164
165 prev = NULL;
166 for(ent = tab->buckets[tab->hashfunc(key)];
167 ent && tab->cmpfunc(ent->key, key);
168 prev = ent, ent = ent->next);
169 if (!ent)
170 return !0;
171 if (prev)
172 prev->next = ent->next;
173 else
174 tab->buckets[tab->hashfunc(key)] = ent->next;
175 free(ent);
176 return 0;
177}
178
179static
180uintptr_t
181cmpfunc_pointer(uintptr_t a, uintptr_t b)
182{
183 return b - a;
184}
185
186static
187uintptr_t
188hashfunc_pointer(uintptr_t p)
189{
190 return p % NR_BUCKETS;
191}
192
193static
194uintptr_t
195hashfunc_string(uintptr_t p)
196{
197 const char *str = (char *)p;
198 unsigned long hash = 5381;
199 int c;
200
201 while ((c = *str++))
202 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
203 return hash % NR_BUCKETS;
204}
205
da620666 206static struct hashtab *
36b35b4a
AE
207ehash_new(void)
208{
209 struct hashtab *tab;
210 if (!(tab = calloc(sizeof(struct hashtab), 1)))
211 return tab;
212 tab->hashfunc = &hashfunc_pointer;
213 tab->cmpfunc = &cmpfunc_pointer;
214 return tab;
215}
216
217/* returns 0 if equal */
218static
219int
220cmp_vals(evtr_variable_value_t a, evtr_variable_value_t b)
221{
222 if (a->type != b->type)
223 return !0;
224 switch (a->type) {
225 case EVTR_VAL_NIL:
226 return 0;
227 case EVTR_VAL_INT:
228 return !(a->num == b->num);
229 case EVTR_VAL_STR:
230 return strcmp(a->str, b->str);
231 case EVTR_VAL_HASH:
232 return !0; /* come on! */
233 case EVTR_VAL_CTOR:
234 err(3, "not implemented");
235 }
236 err(3, "can't get here");
237}
238
239static
240uintptr_t
241cmpfunc_ctor(uintptr_t _a, uintptr_t _b)
242{
243 evtr_variable_value_t vala, valb;
244 vala = (evtr_variable_value_t)_a;
245 valb = (evtr_variable_value_t)_b;
246 if (strcmp(vala->ctor.name, valb->ctor.name))
247 return !0;
248 vala = TAILQ_FIRST(&vala->ctor.args);
249 valb = TAILQ_FIRST(&valb->ctor.args);
250 for (;;) {
251 if (!vala && !valb)
252 return 0;
253 if ((vala && !valb) || (valb && !vala))
254 return !0;
255 if (cmp_vals(vala, valb))
256 return !0;
257 vala = TAILQ_NEXT(vala, link);
258 valb = TAILQ_NEXT(valb, link);
259 }
260}
261
262static
263uintptr_t
264hashfunc_ctor(uintptr_t _p)
265{
266 evtr_variable_value_t val, ctor_val = (evtr_variable_value_t)_p;
267 char buf[1024], *p = &buf[0];
268 size_t len;
269
270 p = buf;
271 assert(ctor_val->type == EVTR_VAL_CTOR);
272 len = strlcpy(buf, ctor_val->ctor.name, sizeof(buf));
273 if (len >= sizeof(buf))
274 goto done;
275
276 TAILQ_FOREACH(val, &ctor_val->ctor.args, link) {
277 switch (val->type) {
278 case EVTR_VAL_NIL:
279 assert(!"can't happen");
280 break;
281 case EVTR_VAL_INT:
282 len += snprintf(p + len, sizeof(buf) - len - 1,
283 "%jd", val->num);
284 break;
285 case EVTR_VAL_STR:
286 len = strlcat(p, val->str, sizeof(buf));
287 break;
288 case EVTR_VAL_HASH:
289 break; /* come on! */
290 case EVTR_VAL_CTOR:
291 err(3, "not implemented");
292 }
293 if (len >= (sizeof(buf) - 1))
294 break;
295 }
296done:
297 buf[sizeof(buf) - 1] = '\0';
298 return hashfunc_string((uintptr_t)buf);
299}
300
301typedef struct vector {
302 uintmax_t *vals;
303 int used;
304 int allocated;
305} *vector_t;
306
da620666 307static vector_t
36b35b4a
AE
308vector_new(void)
309{
310 vector_t v;
311 if (!(v = malloc(sizeof(*v))))
312 return v;
313 v->allocated = 2;
314 if (!(v->vals = malloc(v->allocated * sizeof(v->vals[0])))) {
315 free(v);
316 return NULL;
317 }
318 v->allocated =
319 v->used = 0;
320 return v;
321}
322
323static
324void
325vector_push(vector_t v, uintmax_t val)
326{
327 uintmax_t *tmp;
328 if (v->used == v->allocated) {
329 tmp = realloc(v->vals, 2 * v->allocated * sizeof(v->vals[0]));
330 if (!tmp)
331 err(1, "out of memory");
332 v->vals = tmp;
333 v->allocated *= 2;
334 }
335 v->vals[v->used++] = val;
336}
337
338static
339void
340vector_destroy(vector_t v)
341{
342 free(v->vals);
343 free(v);
344}
345
346static
347int
348vector_nelems(vector_t v)
349{
350 return v->used;
351}
352
353#define vector_foreach(v, val, i) \
354 for (i = 0, val = v->vals[0]; i < v->used; val = v->vals[++i])
355
356static
357double
358stddev(vector_t v, double avg)
359{
360 uintmax_t val;
361 int i;
362 double diff, sqr_sum = 0.0;
363
364 if (vector_nelems(v) < 2)
365 return 1 / 0.0;
366 vector_foreach(v, val, i) {
367 diff = val - avg;
368 sqr_sum += diff * diff;
369 }
370 return sqrt(sqr_sum / (vector_nelems(v) - 1));
371}
372
e7c0dbba
AE
373static
374void
375usage(void)
376{
377 fprintf(stderr, "bad usage :P\n");
378 exit(2);
379}
380
381static
382void
383rows_init(struct rows *rows, int n, double height, double perc)
384{
385 double row_h;
386 rows->row_increment = height / n;
387 /* actual row height */
388 row_h = perc * rows->row_increment;
389 rows->row_off = (rows->row_increment - row_h) / 2.0;
390 assert(!isnan(rows->row_increment));
391 assert(!isnan(rows->row_off));
392}
393
394static
395void
396rows_n(struct rows *rows, int n, double *y, double *height)
397{
398 *y = n * rows->row_increment + rows->row_off;
399 *height = rows->row_increment - 2 * rows->row_off;
400}
401
402/*
403 * Which fontsize to use so that the string fits in the
404 * given rect.
405 */
406static
407double
408fontsize_for_rect(double width, double height, int textlen)
409{
410 double wpc, maxh;
411 /*
412 * We start with a font size equal to the height
413 * of the rectangle and round down so that we only
414 * use a limited number of sizes.
415 *
416 * For a rectangle width comparable to the height,
417 * the text might extend outside of the rectangle.
418 * In that case we need to limit it.
419 */
420 /* available width per character */
421 wpc = width / textlen;
422 /*
423 * Assuming a rough hight/width ratio for characters,
424 * calculate the available height and round it down
425 * just to be on the safe side.
426 */
427#define GLYPH_HIGHT_TO_WIDTH 1.5
428 maxh = GLYPH_HIGHT_TO_WIDTH * wpc * 0.9;
429 if (height > maxh) {
430 height = maxh;
431 } else if (height < 0.01) {
432 height = 0.01;
433 } else {
434 /* rounding (XXX: make cheaper)*/
435 height = log(height);
436 height = round(height);
437 height = exp(height);
438 }
439 return height;
440}
441
442struct pass_hook {
443 void (*pre)(void *);
444 void (*event)(void *, evtr_event_t);
445 void (*post)(void *);
446 void *data;
447 struct evtr_filter *filts;
448 int nfilts;
449};
450
451struct thread_info {
452 uint64_t runtime;
453};
454
70af04e9
AE
455struct ts_interval {
456 uint64_t start;
457 uint64_t end;
458};
459
e7c0dbba
AE
460struct td_switch_ctx {
461 svg_document_t svg;
462 struct rows *cpu_rows;
463 struct rows *thread_rows;
70af04e9
AE
464 /* which events the user cares about */
465 struct ts_interval interval;
466 /* first/last event timestamps on any cpu */
467 struct ts_interval firstlast;
e7c0dbba
AE
468 double width;
469 double xscale; /* scale factor applied to x */
470 svg_rect_t cpu_sw_rect;
471 svg_rect_t thread_rect;
472 svg_rect_t inactive_rect;
473 svg_text_t thread_label;
956f9b36
AE
474 struct cpu_table {
475 struct cpu *cpus;
476 int ncpus;
477 } cputab;
e7c0dbba
AE
478 struct evtr_thread **top_threads;
479 int nr_top_threads;
480 double thread_rows_yoff;
481};
482
483struct cpu {
484 struct evtr_thread *td;
485 int i; /* cpu index */
486 uint64_t ts; /* time cpu switched to td */
70af04e9
AE
487 /* timestamp for first/last event on this cpu */
488 struct ts_interval firstlast;
956f9b36 489 double freq;
70af04e9 490 uintmax_t evcnt;
e7c0dbba
AE
491};
492
493static
494void
495do_pass(struct pass_hook *hooks, int nhooks)
496{
497 struct evtr_filter *filts = NULL;
498 int nfilts = 0, i;
499 struct evtr_query *q;
500 struct evtr_event ev;
501
502 for (i = 0; i < nhooks; ++i) {
503 struct pass_hook *h = &hooks[i];
504 if (h->pre)
505 h->pre(h->data);
506 if (h->nfilts > 0) {
507 filts = realloc(filts, (nfilts + h->nfilts) *
508 sizeof(struct evtr_filter));
509 if (!filts)
510 err(1, "Out of memory");
0b33b2e3 511 memcpy(filts + nfilts, h->filts,
e7c0dbba
AE
512 h->nfilts * sizeof(struct evtr_filter));
513 nfilts += h->nfilts;
514 }
515 }
516 q = evtr_query_init(evtr, filts, nfilts);
517 if (!q)
518 err(1, "Can't initialize query\n");
519 while(!evtr_query_next(q, &ev)) {
520 for (i = 0; i < nhooks; ++i) {
521 if (hooks[i].event)
522 hooks[i].event(hooks[i].data, &ev);
523 }
524 }
2b130cb3
AE
525 if (evtr_query_error(q)) {
526 err(1, evtr_query_errmsg(q));
e7c0dbba
AE
527 }
528 evtr_query_destroy(q);
529
530 for (i = 0; i < nhooks; ++i) {
531 if (hooks[i].post)
532 hooks[i].post(hooks[i].data);
533 }
534 if (evtr_rewind(evtr))
535 err(1, "Can't rewind event stream\n");
536}
537
538static
539void
540draw_thread_run(struct td_switch_ctx *ctx, struct cpu *c, evtr_event_t ev, int row)
541{
542 double x, w, y, height;
543 w = (ev->ts - c->ts) * ctx->xscale;
70af04e9 544 x = (ev->ts - ctx->firstlast.start) * ctx->xscale;
e7c0dbba
AE
545 rows_n(ctx->thread_rows, row, &y, &height);
546 svg_rect_draw(ctx->svg, ctx->thread_rect, x - w,
547 y + ctx->thread_rows_yoff, w, height);
548}
549
550static
551void
552draw_ctx_switch(struct td_switch_ctx *ctx, struct cpu *c, evtr_event_t ev)
553{
554 struct svg_transform textrot;
555 char comm[100];
556 double x, w, fs, y, height;
557 int textlen;
558
559 assert(ctx->xscale > 0.0);
560 if (!c->ts)
561 return;
562 /* distance to previous context switch */
563 w = (ev->ts - c->ts) * ctx->xscale;
70af04e9 564 x = (ev->ts - ctx->firstlast.start) * ctx->xscale;
e7c0dbba 565 if ((x - w) < 0) {
46f0388c
AE
566 fprintf(stderr, "(%ju - %ju) * %.20lf\n",
567 (uintmax_t)ev->ts,
70af04e9 568 (uintmax_t)ctx->firstlast.start, ctx->xscale);
e7c0dbba
AE
569 abort();
570 }
571
572 rows_n(ctx->cpu_rows, c->i, &y, &height);
573 assert(!isnan(y));
574 assert(!isnan(height));
575
576 svg_rect_draw(ctx->svg, ctx->cpu_sw_rect, x - w, y, w, height);
577
578 /*
579 * Draw the text label describing the thread we
580 * switched out of.
581 */
582 textrot.tx = x - w;
583 textrot.ty = y;
584 textrot.sx = 1.0;
585 textrot.sy = 1.0;
586 textrot.rot = 90.0;
587 textlen = snprintf(comm, sizeof(comm) - 1, "%s (%p)",
588 c->td ? c->td->comm : "unknown",
589 c->td ? c->td->id: NULL);
590 if (textlen > (int)sizeof(comm))
591 textlen = sizeof(comm) - 1;
592 comm[sizeof(comm) - 1] = '\0';
593 /*
594 * Note the width and hight are exchanged because
595 * the bounding rectangle is rotated by 90 degrees.
596 */
597 fs = fontsize_for_rect(height, w, textlen);
598 svg_text_draw(ctx->svg, ctx->thread_label, &textrot, comm,
599 fs);
600}
601
602
603/*
604 * The stats for ntd have changed, update ->top_threads
605 */
606static
607void
608top_threads_update(struct td_switch_ctx *ctx, struct evtr_thread *ntd)
609{
610 struct thread_info *tdi = ntd->userdata;
611 int i, j;
612 for (i = 0; i < ctx->nr_top_threads; ++i) {
613 struct evtr_thread *td = ctx->top_threads[i];
614 if (td == ntd) {
615 /*
616 * ntd is already in top_threads and it is at
617 * the correct ranking
618 */
619 break;
620 }
621 if (!td) {
622 /* empty slot -- just insert our thread */
623 ctx->top_threads[i] = ntd;
624 break;
625 }
626 if (((struct thread_info *)td->userdata)->runtime >=
627 tdi->runtime) {
628 /* this thread ranks higher than we do. Move on */
629 continue;
630 }
631 /*
632 * OK, we've found the first thread that we outrank, so we
633 * need to replace it w/ our thread.
634 */
635 td = ntd; /* td holds the thread we will insert next */
636 for (j = i + 1; j < ctx->nr_top_threads; ++j, ++i) {
637 struct evtr_thread *tmp;
638
639 /* tmp holds the thread we replace */
640 tmp = ctx->top_threads[j];
641 ctx->top_threads[j] = td;
642 if (tmp == ntd) {
643 /*
644 * Our thread was already in the top list,
645 * and we just removed the second instance.
646 * Nothing more to do.
647 */
648 break;
649 }
650 td = tmp;
651 }
652 break;
653 }
654}
655
656static
657void
658ctxsw_prepare_event(void *_ctx, evtr_event_t ev)
659{
660 struct td_switch_ctx *ctx = _ctx;
956f9b36 661 struct cpu *c, *cpus = ctx->cputab.cpus;
e7c0dbba
AE
662 struct thread_info *tdi;
663
664 (void)evtr;
5e8d9c2b 665 printd(INTV, "test1 (%ju:%ju) : %ju\n",
70af04e9
AE
666 (uintmax_t)ctx->interval.start,
667 (uintmax_t)ctx->interval.end,
46f0388c 668 (uintmax_t)ev->ts);
70af04e9
AE
669 if ((ev->ts > ctx->interval.end) ||
670 (ev->ts < ctx->interval.start))
e7c0dbba 671 return;
5e8d9c2b 672 printd(INTV, "PREPEV on %d\n", ev->cpu);
e7c0dbba
AE
673
674 /* update first/last timestamps */
675 c = &cpus[ev->cpu];
70af04e9
AE
676 if (!c->firstlast.start) {
677 c->firstlast.start = ev->ts;
e7c0dbba 678 }
70af04e9 679 c->firstlast.end = ev->ts;
e7c0dbba
AE
680 /*
681 * c->td can be null when this is the first ctxsw event we
682 * observe for a cpu
683 */
684 if (c->td) {
685 /* update thread stats */
686 if (!c->td->userdata) {
687 if (!(tdi = malloc(sizeof(struct thread_info))))
688 err(1, "Out of memory");
689 c->td->userdata = tdi;
690 tdi->runtime = 0;
691 }
692 tdi = c->td->userdata;
693 tdi->runtime += ev->ts - c->ts;
e7c0dbba
AE
694 top_threads_update(ctx, c->td);
695 }
696
697 /* Notice that ev->td is the new thread for ctxsw events */
698 c->td = ev->td;
699 c->ts = ev->ts;
700}
701
702static
703void
70af04e9 704find_first_last_ts(struct cpu_table *cputab, struct ts_interval *fl)
e7c0dbba 705{
70af04e9 706 struct cpu *cpus = &cputab->cpus[0];
e7c0dbba
AE
707 int i;
708
70af04e9
AE
709 fl->start = -1;
710 fl->end = 0;
711 for (i = 0; i < cputab->ncpus; ++i) {
5e8d9c2b 712 printd(INTV, "cpu%d: (%ju, %ju)\n", i,
70af04e9
AE
713 (uintmax_t)cpus[i].firstlast.start,
714 (uintmax_t)cpus[i].firstlast.end);
715 if (cpus[i].firstlast.start &&
716 (cpus[i].firstlast.start < fl->start))
717 fl->start = cpus[i].firstlast.start;
718 if (cpus[i].firstlast.end &&
719 (cpus[i].firstlast.end > fl->end))
720 fl->end = cpus[i].firstlast.end;
e7c0dbba
AE
721 cpus[i].td = NULL;
722 cpus[i].ts = 0;
723 }
5e8d9c2b 724 printd(INTV, "global (%jd, %jd)\n", (uintmax_t)fl->start, (uintmax_t)fl->end);
70af04e9
AE
725}
726
727static
728void
729ctxsw_prepare_post(void *_ctx)
730{
731 struct td_switch_ctx *ctx = _ctx;
732
733 find_first_last_ts(&ctx->cputab, &ctx->firstlast);
e7c0dbba
AE
734}
735
736static
737void
738ctxsw_draw_pre(void *_ctx)
739{
740 struct td_switch_ctx *ctx = _ctx;
741 struct svg_transform textrot;
742 char comm[100];
743 double y, height, fs;
744 int i, textlen;
745 struct evtr_thread *td;
746
747 textrot.tx = 0.0 - 0.2; /* XXX */
748 textrot.sx = 1.0;
749 textrot.sy = 1.0;
750 textrot.rot = 270.0;
751
752 for (i = 0; i < ctx->nr_top_threads; ++i) {
753 td = ctx->top_threads[i];
70af04e9
AE
754 if (!td)
755 break;
e7c0dbba
AE
756 rows_n(ctx->thread_rows, i, &y, &height);
757 svg_rect_draw(ctx->svg, ctx->inactive_rect, 0.0,
758 y + ctx->thread_rows_yoff, ctx->width, height);
759 textlen = snprintf(comm, sizeof(comm) - 1, "%s (%p)",
760 td->comm, td->id);
761 if (textlen > (int)sizeof(comm))
762 textlen = sizeof(comm) - 1;
763 comm[sizeof(comm) - 1] = '\0';
764 fs = fontsize_for_rect(height, 100.0, textlen);
765
766 textrot.ty = y + ctx->thread_rows_yoff + height;
767 svg_text_draw(ctx->svg, ctx->thread_label, &textrot,
768 comm, fs);
769 }
770}
771
772static
773void
774ctxsw_draw_event(void *_ctx, evtr_event_t ev)
775{
776 struct td_switch_ctx *ctx = _ctx;
956f9b36 777 struct cpu *c = &ctx->cputab.cpus[ev->cpu];
e7c0dbba
AE
778 int i;
779
780 /*
70af04e9 781 * ctx->firstlast.end can be 0 if there were no events
e7c0dbba 782 * in the specified interval, in which case
70af04e9 783 * ctx->firstlast.start is invalid too.
e7c0dbba 784 */
70af04e9 785 assert(!ctx->firstlast.end || (ev->ts >= ctx->firstlast.start));
5e8d9c2b 786 printd(INTV, "test2 (%ju:%ju) : %ju\n", (uintmax_t)ctx->interval.start,
70af04e9
AE
787 (uintmax_t)ctx->interval.end, (uintmax_t)ev->ts);
788 if ((ev->ts > ctx->interval.end) ||
789 (ev->ts < ctx->interval.start))
e7c0dbba 790 return;
5e8d9c2b 791 printd(INTV, "DRAWEV %d\n", ev->cpu);
e7c0dbba
AE
792 if (c->td != ev->td) { /* thread switch (or preemption) */
793 draw_ctx_switch(ctx, c, ev);
794 /* XXX: this is silly */
795 for (i = 0; i < ctx->nr_top_threads; ++i) {
70af04e9
AE
796 if (!ctx->top_threads[i])
797 break;
e7c0dbba
AE
798 if (ctx->top_threads[i] == c->td) {
799 draw_thread_run(ctx, c, ev, i);
800 break;
801 }
802 }
803 c->td = ev->td;
804 c->ts = ev->ts;
805 }
806}
807
956f9b36
AE
808static
809void
810cputab_init(struct cpu_table *ct)
811{
812 struct cpu *cpus;
813 double *freqs;
814 int i;
815
816 if ((ct->ncpus = evtr_ncpus(evtr)) <= 0)
817 err(1, "No cpu information!\n");
5e8d9c2b 818 printd(MISC, "evtranalyze: ncpus %d\n", ct->ncpus);
956f9b36
AE
819 if (!(ct->cpus = malloc(sizeof(struct cpu) * ct->ncpus))) {
820 err(1, "Can't allocate memory\n");
821 }
822 cpus = ct->cpus;
823 if (!(freqs = malloc(sizeof(double) * ct->ncpus))) {
824 err(1, "Can't allocate memory\n");
825 }
826 if ((i = evtr_cpufreqs(evtr, freqs))) {
827 warnc(i, "Can't get cpu frequencies\n");
828 for (i = 0; i < ct->ncpus; ++i) {
829 freqs[i] = -1.0;
830 }
831 }
832
833 /* initialize cpu array */
834 for (i = 0; i < ct->ncpus; ++i) {
835 cpus[i].td = NULL;
836 cpus[i].ts = 0;
837 cpus[i].i = i;
70af04e9
AE
838 cpus[i].firstlast.start = 0;
839 cpus[i].firstlast.end = 0;
840 cpus[i].evcnt = 0;
956f9b36
AE
841 cpus[i].freq = freqs[i];
842 }
843 free(freqs);
844}
845
70af04e9
AE
846static
847void
848parse_interval(const char *_str, struct ts_interval *ts,
849 struct cpu_table *cputab)
850{
851 double s, e, freq;
852 const char *str = _str + 1;
853
854 if ('c' == *_str) { /* cycles */
855 if (sscanf(str, "%" SCNu64 ":%" SCNu64,
856 &ts->start,
857 &ts->end) == 2)
858 return;
859 } else if ('m' == *_str) { /* miliseconds */
860 if (sscanf(str, "%lf:%lf", &s, &e) == 2) {
861 freq = cputab->cpus[0].freq;
862 freq *= 1000.0; /* msecs */
863 if (freq < 0.0) {
864 fprintf(stderr, "No frequency information"
865 " available\n");
866 err(2, "Can't convert time to cycles\n");
867 }
868 ts->start = s * freq;
869 ts->end = e * freq;
870 return;
871 }
872 }
873 fprintf(stderr, "invalid interval format: %s\n", _str);
874 usage();
875}
876
956f9b36 877
e7c0dbba
AE
878static
879int
880cmd_svg(int argc, char **argv)
881{
882 svg_document_t svg;
956f9b36 883 int ch;
e7c0dbba
AE
884 double height, width;
885 struct rows cpu_rows, thread_rows;
e7c0dbba 886 struct td_switch_ctx td_ctx;
70af04e9 887 const char *outpath = "output.svg";
e7c0dbba
AE
888 struct evtr_filter ctxsw_filts[2] = {
889 {
890 .flags = 0,
891 .cpu = -1,
2b130cb3 892 .ev_type = EVTR_TYPE_PROBE,
e7c0dbba
AE
893 },
894 {
895 .flags = 0,
896 .cpu = -1,
2b130cb3 897 .ev_type = EVTR_TYPE_PROBE,
e7c0dbba
AE
898 },
899 };
900 struct pass_hook ctxsw_prepare = {
901 .pre = NULL,
902 .event = ctxsw_prepare_event,
903 .post = ctxsw_prepare_post,
904 .data = &td_ctx,
905 .filts = ctxsw_filts,
906 .nfilts = sizeof(ctxsw_filts)/sizeof(ctxsw_filts[0]),
907 }, ctxsw_draw = {
908 .pre = ctxsw_draw_pre,
909 .event = ctxsw_draw_event,
910 .post = NULL,
911 .data = &td_ctx,
912 .filts = ctxsw_filts,
913 .nfilts = sizeof(ctxsw_filts)/sizeof(ctxsw_filts[0]),
914 };
915
916 /*
917 * We are interested in thread switch and preemption
918 * events, but we don't use the data directly. Instead
919 * we rely on ev->td.
920 */
0b33b2e3 921 ctxsw_filts[0].fmt = "sw %p > %p";
e7c0dbba 922 ctxsw_filts[1].fmt = "pre %p > %p";
70af04e9
AE
923 td_ctx.interval.start = 0;
924 td_ctx.interval.end = -1; /* i.e. no interval given */
e7c0dbba 925 td_ctx.nr_top_threads = NR_TOP_THREADS;
70af04e9 926 cputab_init(&td_ctx.cputab); /* needed for parse_interval() */
e7c0dbba 927
e7c0dbba
AE
928 optind = 0;
929 optreset = 1;
70af04e9 930 while ((ch = getopt(argc, argv, "i:o:")) != -1) {
e7c0dbba
AE
931 switch (ch) {
932 case 'i':
70af04e9
AE
933 parse_interval(optarg, &td_ctx.interval,
934 &td_ctx.cputab);
935 break;
936 case 'o':
937 outpath = optarg;
e7c0dbba
AE
938 break;
939 default:
940 usage();
941 }
942
943 }
944 argc -= optind;
945 argv += optind;
946
947 height = 200.0;
948 width = 700.0;
949 td_ctx.width = width;
e7c0dbba 950
70af04e9
AE
951 if (!(td_ctx.top_threads = calloc(td_ctx.nr_top_threads,
952 sizeof(struct evtr_thread *))))
e7c0dbba 953 err(1, "Can't allocate memory\n");
70af04e9 954 if (!(svg = svg_document_create(outpath)))
e7c0dbba
AE
955 err(1, "Can't open svg document\n");
956
957 /*
958 * Create rectangles to use for output.
959 */
960 if (!(td_ctx.cpu_sw_rect = svg_rect_new("generic")))
961 err(1, "Can't create rectangle\n");
962 if (!(td_ctx.thread_rect = svg_rect_new("thread")))
963 err(1, "Can't create rectangle\n");
964 if (!(td_ctx.inactive_rect = svg_rect_new("inactive")))
965 err(1, "Can't create rectangle\n");
966 /* text for thread names */
967 if (!(td_ctx.thread_label = svg_text_new("generic")))
968 err(1, "Can't create text\n");
956f9b36 969 rows_init(&cpu_rows, td_ctx.cputab.ncpus, height, 0.9);
e7c0dbba
AE
970 td_ctx.svg = svg;
971 td_ctx.xscale = -1.0;
972 td_ctx.cpu_rows = &cpu_rows;
973
974 do_pass(&ctxsw_prepare, 1);
975 td_ctx.thread_rows_yoff = height;
976 td_ctx.thread_rows = &thread_rows;
977 rows_init(td_ctx.thread_rows, td_ctx.nr_top_threads, 300, 0.9);
70af04e9 978 td_ctx.xscale = width / (td_ctx.firstlast.end - td_ctx.firstlast.start);
5e8d9c2b 979 printd(SVG, "first %ju, last %ju, xscale %lf\n",
70af04e9 980 (uintmax_t)td_ctx.firstlast.start, (uintmax_t)td_ctx.firstlast.end,
46f0388c 981 td_ctx.xscale);
e7c0dbba
AE
982
983 do_pass(&ctxsw_draw, 1);
984
985 svg_document_close(svg);
986 return 0;
987}
988
989static
990int
991cmd_show(int argc, char **argv)
992{
993 struct evtr_event ev;
994 struct evtr_query *q;
995 struct evtr_filter filt;
956f9b36
AE
996 struct cpu_table cputab;
997 double freq;
e7c0dbba 998 int ch;
956f9b36 999 uint64_t last_ts = 0;
e7c0dbba 1000
956f9b36
AE
1001 cputab_init(&cputab);
1002 /*
1003 * Assume all cores run on the same frequency
1004 * for now. There's no reason to complicate
1005 * things unless we can detect frequency change
1006 * events as well.
1007 *
1008 * Note that the code is very simplistic and will
1009 * produce garbage if the kernel doesn't fixup
1010 * the timestamps for cores running with different
1011 * frequencies.
1012 */
1013 freq = cputab.cpus[0].freq;
1014 freq /= 1000000; /* we want to print out usecs */
5e8d9c2b 1015 printd(MISC, "using freq = %lf\n", freq);
2b130cb3
AE
1016 filt.flags = 0;
1017 filt.cpu = -1;
1018 filt.ev_type = EVTR_TYPE_PROBE;
e7c0dbba
AE
1019 filt.fmt = NULL;
1020 optind = 0;
1021 optreset = 1;
1022 while ((ch = getopt(argc, argv, "f:")) != -1) {
1023 switch (ch) {
1024 case 'f':
1025 filt.fmt = optarg;
1026 break;
1027 }
1028 }
e7c0dbba
AE
1029 q = evtr_query_init(evtr, &filt, 1);
1030 if (!q)
1031 err(1, "Can't initialize query\n");
1032 while(!evtr_query_next(q, &ev)) {
1033 char buf[1024];
956f9b36
AE
1034
1035 if (!last_ts)
1036 last_ts = ev.ts;
1037 if (freq < 0.0) {
b51ef1e9 1038 printf("%s\t%ju cycles\t[%.3d]\t%s:%d",
956f9b36 1039 ev.td ? ev.td->comm : "unknown",
b51ef1e9 1040 (uintmax_t)(ev.ts - last_ts), ev.cpu,
956f9b36
AE
1041 basename(ev.file), ev.line);
1042 } else {
1043 printf("%s\t%.3lf usecs\t[%.3d]\t%s:%d",
1044 ev.td ? ev.td->comm : "unknown",
1045 (ev.ts - last_ts) / freq, ev.cpu,
1046 basename(ev.file), ev.line);
1047 }
e7c0dbba
AE
1048 if (ev.fmt) {
1049 evtr_event_data(&ev, buf, sizeof(buf));
1050 printf(" !\t%s\n", buf);
1051 } else {
1052 printf("\n");
1053 }
956f9b36 1054 last_ts = ev.ts;
e7c0dbba 1055 }
2b130cb3
AE
1056 if (evtr_query_error(q)) {
1057 err(1, evtr_query_errmsg(q));
e7c0dbba
AE
1058 }
1059 evtr_query_destroy(q);
1060 return 0;
1061}
1062
36b35b4a
AE
1063struct stats_ops {
1064 const char *statscmd;
807a01c2 1065 void *(*prepare)(int, char **, struct evtr_filter *);
36b35b4a
AE
1066 void (*each_event)(void *, evtr_event_t);
1067 void (*report)(void *);
1068};
1069
1070struct stats_integer_ctx {
1071 const char *varname;
807a01c2
AE
1072 struct {
1073 int plot;
1074 const char *path;
1075 } opts;
1076 void *plotter_ctx;
1077 struct plotter *plotter;
1078 plotid_t time_plot;
36b35b4a
AE
1079 uintmax_t sum;
1080 uintmax_t occurences;
1081};
1082
1083static
1084void *
807a01c2 1085stats_integer_prepare(int argc, char **argv, struct evtr_filter *filt)
36b35b4a
AE
1086{
1087 struct stats_integer_ctx *ctx;
807a01c2 1088 int ch;
36b35b4a 1089
807a01c2 1090 if (!(ctx = calloc(1, sizeof(*ctx))))
36b35b4a 1091 return ctx;
807a01c2
AE
1092
1093 optind = 0;
1094 optreset = 1;
1095 while ((ch = getopt(argc, argv, "p:")) != -1) {
1096 switch (ch) {
1097 case 'p':
1098 ctx->opts.plot = !0;
1099 ctx->opts.path = optarg;
1100 break;
1101 default:
1102 usage();
1103 }
1104 }
1105 argc -= optind;
1106 argv += optind;
1107
1108 if (argc != 1)
1109 err(2, "Need exactly one variable");
1110 ctx->varname = argv[0];
36b35b4a 1111 ctx->sum = ctx->occurences = 0;
807a01c2
AE
1112 filt->flags = 0;
1113 filt->cpu = -1;
1114 filt->ev_type = EVTR_TYPE_STMT;
1115 filt->var = ctx->varname;
1116 if (!ctx->opts.plot)
1117 return ctx;
1118
1119 if (!(ctx->plotter = plotter_factory()))
1120 err(1, "can't allocate plotter");
1121 if (!(ctx->plotter_ctx = ctx->plotter->plot_init(ctx->opts.path)))
1122 err(1, "can't allocate plotter context");
1123
1124 if ((ctx->time_plot = ctx->plotter->plot_new(ctx->plotter_ctx,
1125 PLOT_TYPE_LINE,
1126 ctx->varname)) < 0)
1127 err(1, "can't create histogram");
36b35b4a
AE
1128 return ctx;
1129}
1130
1131static
1132void
1133stats_integer_each(void *_ctx, evtr_event_t ev)
1134{
1135 struct stats_integer_ctx *ctx = _ctx;
1136 if (EVTR_VAL_INT != ev->stmt.val->type) {
1137 fprintf(stderr, "event at %jd (cpu %d) does not treat %s as an"
1138 "integer variable; ignored\n", ev->ts, ev->cpu,
1139 ctx->varname);
1140 return;
1141 }
807a01c2
AE
1142 if (ctx->plotter)
1143 ctx->plotter->plot_line(ctx->plotter_ctx, ctx->time_plot,
1144 (double)ev->ts, (double)ev->stmt.val->num);
36b35b4a
AE
1145 ctx->sum += ev->stmt.val->num;
1146 ++ctx->occurences;
1147}
1148
1149static
1150void
1151stats_integer_report(void *_ctx)
1152{
1153 struct stats_integer_ctx *ctx = _ctx;
1154 printf("median for variable %s is %lf\n", ctx->varname,
1155 (double)ctx->sum / ctx->occurences);
807a01c2
AE
1156 if (ctx->plotter)
1157 ctx->plotter->plot_finish(ctx->plotter_ctx);
1158
36b35b4a
AE
1159 free(ctx);
1160}
1161
1162struct stats_completion_ctx {
807a01c2
AE
1163 struct stats_completion_options {
1164 int plot;
1165 const char *path;
1166 } opts;
1167 struct plotter *plotter;
1168 void *plotter_ctx;
1169 plotid_t durations_plot;
36b35b4a
AE
1170 const char *varname;
1171 const char *ctor;
1172 const char *dtor;
1173 struct hashtab *ctors;
1174 uintmax_t historical_dtors;
1175 uintmax_t uncompleted_events;
1176 uintmax_t begun_events;
1177 uintmax_t completed_events;
1178 uintmax_t completed_duration_sum;
1179 vector_t durations;
1180};
1181
1182struct ctor_data {
1183 evtr_variable_value_t val;
1184 uintmax_t ts;
1185};
1186
1187static
1188struct ctor_data *
1189ctor_data_new(evtr_event_t ev)
1190{
1191 struct ctor_data *cd;
1192
1193 if (!(cd = malloc(sizeof(*cd))))
1194 return cd;
1195 cd->val = ev->stmt.val;
1196 cd->ts = ev->ts;
1197 return cd;
1198}
1199
1200static
1201void *
807a01c2 1202stats_completion_prepare(int argc, char **argv, struct evtr_filter *filt)
36b35b4a
AE
1203{
1204 struct stats_completion_ctx *ctx;
807a01c2 1205 int ch;
36b35b4a 1206
36b35b4a
AE
1207 if (!(ctx = calloc(1, sizeof(*ctx))))
1208 return ctx;
807a01c2
AE
1209
1210 optind = 0;
1211 optreset = 1;
1212 while ((ch = getopt(argc, argv, "p:")) != -1) {
1213 switch (ch) {
1214 case 'p':
1215 ctx->opts.plot = !0;
1216 ctx->opts.path = optarg;
1217 break;
1218 default:
1219 usage();
1220 }
1221 }
1222 argc -= optind;
1223 argv += optind;
1224 if (argc != 3)
1225 err(2, "need a variable, a constructor and a destructor");
36b35b4a
AE
1226 if (!(ctx->ctors = ehash_new()))
1227 goto free_ctx;
1228 ctx->ctors->hashfunc = &hashfunc_ctor;
1229 ctx->ctors->cmpfunc = &cmpfunc_ctor;
1230 if (!(ctx->durations = vector_new()))
1231 goto free_ctors;
807a01c2
AE
1232 ctx->varname = argv[0];
1233 ctx->ctor = argv[1];
1234 ctx->dtor = argv[2];
1235
1236 filt->flags = 0;
1237 filt->cpu = -1;
1238 filt->ev_type = EVTR_TYPE_STMT;
1239 filt->var = ctx->varname;
1240
1241 if (!ctx->opts.plot)
1242 return ctx;
1243
1244 if (!(ctx->plotter = plotter_factory()))
1245 err(1, "can't allocate plotter");
1246 if (!(ctx->plotter_ctx = ctx->plotter->plot_init(ctx->opts.path)))
1247 err(1, "can't allocate plotter context");
1248
1249 if ((ctx->durations_plot = ctx->plotter->plot_new(ctx->plotter_ctx,
1250 PLOT_TYPE_HIST,
1251 ctx->varname)) < 0)
1252 err(1, "can't create histogram");
36b35b4a
AE
1253 return ctx;
1254free_ctors:
1255 ; /* XXX */
1256free_ctx:
1257 free(ctx);
1258 return NULL;
1259}
1260
1261static
1262void
1263stats_completion_each(void *_ctx, evtr_event_t ev)
1264{
1265 struct stats_completion_ctx *ctx = _ctx;
1266 struct ctor_data *cd;
1267
1268 if (ev->stmt.val->type != EVTR_VAL_CTOR) {
1269 fprintf(stderr, "event at %jd (cpu %d) does not assign to %s "
1270 "with a data constructor; ignored\n", ev->ts, ev->cpu,
1271 ctx->varname);
1272 return;
1273 }
1274 if (!strcmp(ev->stmt.val->ctor.name, ctx->ctor)) {
1275 uintptr_t v;
1276 if (!ehash_find(ctx->ctors, (uintptr_t)ev->stmt.val, &v)) {
1277 /* XXX:better diagnostic */
1278 fprintf(stderr, "duplicate ctor\n");
1279 err(3, "giving up");
1280 }
1281 if (!(cd = ctor_data_new(ev)))
1282 err(1, "out of memory");
1283 v = (uintptr_t)cd;
1284 if (!ehash_insert(ctx->ctors, (uintptr_t)ev->stmt.val, v))
1285 err(1, "out of memory");
1286 ++ctx->begun_events;
1287 } else if (!strcmp(ev->stmt.val->ctor.name, ctx->dtor)) {
1288 uintptr_t v;
1289 const char *tmp = ev->stmt.val->ctor.name;
1290 ev->stmt.val->ctor.name = ctx->ctor;
1291 if (ehash_find(ctx->ctors, (uintptr_t)ev->stmt.val, &v)) {
1292 ++ctx->historical_dtors;
1293 ev->stmt.val->ctor.name = tmp;
1294 return;
1295 }
1296 cd = (struct ctor_data *)v;
1297 if (cd->ts >= ev->ts) {
1298 /* XXX:better diagnostic */
1299 fprintf(stderr, "destructor preceds constructor;"
1300 " ignored\n");
1301 ev->stmt.val->ctor.name = tmp;
1302 return;
1303 }
807a01c2
AE
1304 if (ctx->plotter)
1305 ctx->plotter->plot_histogram(ctx->plotter_ctx,
1306 ctx->durations_plot,
1307 (double)(ev->ts - cd->ts));
36b35b4a
AE
1308 vector_push(ctx->durations, ev->ts - cd->ts);
1309 ++ctx->completed_events;
1310 ctx->completed_duration_sum += ev->ts - cd->ts;
1311 if (ehash_delete(ctx->ctors, (uintptr_t)ev->stmt.val))
1312 err(3, "ctor disappeared from hash!");
1313 ev->stmt.val->ctor.name = tmp;
1314 } else {
1315 fprintf(stderr, "event at %jd (cpu %d) assigns to %s "
1316 "with an unexpected data constructor; ignored\n",
1317 ev->ts, ev->cpu, ctx->varname);
1318 return;
1319 }
1320}
1321
1322static
1323void
1324stats_completion_report(void *_ctx)
1325{
1326 struct stats_completion_ctx *ctx = _ctx;
1327 double avg;
1328
1329 printf("Events completed without having started:\t%jd\n",
1330 ctx->historical_dtors);
1331 printf("Events started but didn't complete:\t%jd\n",
1332 ctx->begun_events - ctx->completed_events);
1333 avg = (double)ctx->completed_duration_sum / ctx->completed_events;
1334 printf("Average event duration:\t%lf (stddev %lf)\n", avg,
1335 stddev(ctx->durations, avg));
807a01c2
AE
1336
1337 if (ctx->plotter)
1338 ctx->plotter->plot_finish(ctx->plotter_ctx);
36b35b4a
AE
1339 vector_destroy(ctx->durations);
1340 /* XXX: hash */
1341 free(ctx);
1342}
1343
1344static struct stats_ops cmd_stat_ops[] = {
1345 {
1346 .statscmd = "integer",
1347 .prepare = &stats_integer_prepare,
1348 .each_event = &stats_integer_each,
1349 .report = &stats_integer_report,
1350 },
1351 {
1352 .statscmd = "completion",
1353 .prepare = &stats_completion_prepare,
1354 .each_event = &stats_completion_each,
1355 .report = &stats_completion_report,
1356 },
1357 {
1358 .statscmd = NULL,
1359 },
1360};
1361
2b130cb3
AE
1362static
1363int
1364cmd_stats(int argc, char **argv)
1365{
1366 struct evtr_event ev;
1367 struct evtr_query *q;
1368 struct evtr_filter filt;
1369 struct cpu_table cputab;
1370 double freq;
2b130cb3 1371 uint64_t last_ts = 0;
36b35b4a
AE
1372 struct stats_ops *statsops = &cmd_stat_ops[0];
1373 void *statctx;
1374
1375 for (; statsops->statscmd; ++statsops) {
1376 if (!strcmp(statsops->statscmd, argv[1]))
1377 break;
1378 }
1379 if (!statsops->statscmd)
1380 err(2, "No such stats type: %s", argv[1]);
2b130cb3 1381
36b35b4a
AE
1382 --argc;
1383 ++argv;
2b130cb3
AE
1384 cputab_init(&cputab);
1385 /*
1386 * Assume all cores run on the same frequency
1387 * for now. There's no reason to complicate
1388 * things unless we can detect frequency change
1389 * events as well.
1390 *
1391 * Note that the code is very simplistic and will
1392 * produce garbage if the kernel doesn't fixup
1393 * the timestamps for cores running with different
1394 * frequencies.
1395 */
1396 freq = cputab.cpus[0].freq;
1397 freq /= 1000000; /* we want to print out usecs */
1398 printd(MISC, "using freq = %lf\n", freq);
b51ef1e9 1399
807a01c2
AE
1400 if (!(statctx = statsops->prepare(argc, argv, &filt)))
1401 err(1, "Can't allocate stats context");
2b130cb3
AE
1402 q = evtr_query_init(evtr, &filt, 1);
1403 if (!q)
36b35b4a 1404 err(1, "Can't initialize query");
2b130cb3
AE
1405 while(!evtr_query_next(q, &ev)) {
1406
1407 if (!last_ts)
1408 last_ts = ev.ts;
1409
1410 assert(ev.type == EVTR_TYPE_STMT);
36b35b4a 1411 statsops->each_event(statctx, &ev);
2b130cb3
AE
1412 last_ts = ev.ts;
1413 }
1414 if (evtr_query_error(q)) {
1415 err(1, evtr_query_errmsg(q));
1416 }
2b130cb3 1417 evtr_query_destroy(q);
36b35b4a 1418 statsops->report(statctx);
2b130cb3
AE
1419 return 0;
1420}
1421
1422
70af04e9
AE
1423static
1424int
1425cmd_summary(int argc, char **argv)
1426{
1427 struct evtr_filter filt;
1428 struct evtr_event ev;
1429 struct evtr_query *q;
1430 double freq;
1431 struct cpu_table cputab;
1432 struct ts_interval global;
1433 uintmax_t global_evcnt;
1434 int i;
1435
1436 (void)argc;
1437 (void)argv;
1438
1439 cputab_init(&cputab);
2b130cb3 1440 filt.ev_type = EVTR_TYPE_PROBE;
70af04e9
AE
1441 filt.fmt = NULL;
1442 filt.flags = 0;
1443 filt.cpu = -1;
1444
1445 q = evtr_query_init(evtr, &filt, 1);
1446 if (!q)
1447 err(1, "Can't initialize query\n");
1448 while(!evtr_query_next(q, &ev)) {
1449 struct cpu *c = &cputab.cpus[ev.cpu];
1450 if (!c->firstlast.start)
1451 c->firstlast.start = ev.ts;
1452 ++c->evcnt;
1453 c->firstlast.end = ev.ts;
1454 }
2b130cb3
AE
1455 if (evtr_query_error(q)) {
1456 err(1, evtr_query_errmsg(q));
70af04e9
AE
1457 }
1458 evtr_query_destroy(q);
1459
1460 find_first_last_ts(&cputab, &global);
1461
1462 freq = cputab.cpus[0].freq;
1463 global_evcnt = 0;
1464 for (i = 0; i < cputab.ncpus; ++i) {
1465 struct cpu *c = &cputab.cpus[i];
1466 printf("CPU %d: %jd events in %.3lf secs\n", i,
1467 c->evcnt, (c->firstlast.end - c->firstlast.start)
1468 / freq);
1469 global_evcnt += c->evcnt;
1470 }
1471 printf("Total: %jd events on %d cpus in %.3lf secs\n", global_evcnt,
1472 cputab.ncpus, (global.end - global.start) / freq);
1473 return 0;
1474}
1475
70af04e9 1476
e7c0dbba
AE
1477int
1478main(int argc, char **argv)
1479{
1480 int ch;
1481 FILE *inf;
1482 struct command *cmd;
5e8d9c2b 1483 char *tmp;
e7c0dbba
AE
1484
1485 while ((ch = getopt(argc, argv, "f:D:")) != -1) {
1486 switch (ch) {
1487 case 'f':
1488 opt_infile = optarg;
1489 break;
1490 case 'D':
5e8d9c2b
AE
1491 if ((tmp = strchr(optarg, ':'))) {
1492 *tmp++ = '\0';
1493 evtr_set_debug(tmp);
1494 }
1495 printd_set_flags(optarg, &evtranalyze_debug);
e7c0dbba
AE
1496 break;
1497 default:
1498 usage();
1499 }
1500 }
1501 argc -= optind;
1502 argv += optind;
1503
1504 if (argc == 0) {
1505 err(2, "need to specify a command\n");
1506 }
ab91bd09
AE
1507 if (!opt_infile) {
1508 err(2, "you need to specify an input file\n");
1509 } else if (!strcmp(opt_infile, "-")) {
e7c0dbba
AE
1510 inf = stdin;
1511 } else {
1512 inf = fopen(opt_infile, "r");
1513 if (!inf) {
1514 err(2, "Can't open input file\n");
1515 }
1516 }
1517
1518 if (!(evtr = evtr_open_read(inf))) {
1519 err(1, "Can't open evtr stream\n");
1520 }
1521
1522
1523 for (cmd = commands; cmd->name != NULL; ++cmd) {
1524 if (strcmp(argv[0], cmd->name))
1525 continue;
1526 cmd->func(argc, argv);
1527 break;
1528 }
1529 if (!cmd->name) {
1530 err(2, "no such command: %s\n", argv[0]);
1531 }
1532
1533 evtr_close(evtr);
1534 return 0;
1535}