From 807a01c2ff0a00ff4f0a723e773a8a2bfca4a246 Mon Sep 17 00:00:00 2001 From: Aggelos Economopoulos Date: Sat, 12 Jun 2010 05:01:27 +0300 Subject: [PATCH] evtranalyze: plotting capability We can now generate histograms and (x, y) line plots via ploticus (if available) --- usr.bin/evtranalyze/Makefile | 4 +- usr.bin/evtranalyze/evtranalyze.c | 134 ++++++++++--- usr.bin/evtranalyze/plotter.c | 313 ++++++++++++++++++++++++++++++ usr.bin/evtranalyze/plotter.h | 22 +++ usr.bin/evtranalyze/trivial.h | 1 + 5 files changed, 447 insertions(+), 27 deletions(-) create mode 100644 usr.bin/evtranalyze/plotter.c create mode 100644 usr.bin/evtranalyze/plotter.h diff --git a/usr.bin/evtranalyze/Makefile b/usr.bin/evtranalyze/Makefile index 3099607e83..1632f43064 100644 --- a/usr.bin/evtranalyze/Makefile +++ b/usr.bin/evtranalyze/Makefile @@ -1,6 +1,6 @@ PROG= evtranalyze -SRCS= xml.c svg.c evtranalyze.c +SRCS= xml.c svg.c evtranalyze.c plotter.c DPADD= ${LIBEVTR} -LDADD= -levtr -lm +LDADD= -levtr -lm -lprop .include diff --git a/usr.bin/evtranalyze/evtranalyze.c b/usr.bin/evtranalyze/evtranalyze.c index b654ce04a2..c0148eb4a1 100644 --- a/usr.bin/evtranalyze/evtranalyze.c +++ b/usr.bin/evtranalyze/evtranalyze.c @@ -43,6 +43,7 @@ #include #include "xml.h" #include "svg.h" +#include "plotter.h" #include "trivial.h" enum { @@ -1061,29 +1062,69 @@ cmd_show(int argc, char **argv) struct stats_ops { const char *statscmd; - void *(*prepare)(int, char **); + void *(*prepare)(int, char **, struct evtr_filter *); void (*each_event)(void *, evtr_event_t); void (*report)(void *); }; struct stats_integer_ctx { const char *varname; + struct { + int plot; + const char *path; + } opts; + void *plotter_ctx; + struct plotter *plotter; + plotid_t time_plot; uintmax_t sum; uintmax_t occurences; }; static void * -stats_integer_prepare(int argc, char **argv) +stats_integer_prepare(int argc, char **argv, struct evtr_filter *filt) { struct stats_integer_ctx *ctx; + int ch; - if (argc != 2) - err(2, "Need exactly one variable"); - if (!(ctx = malloc(sizeof(*ctx)))) + if (!(ctx = calloc(1, sizeof(*ctx)))) return ctx; - ctx->varname = argv[1]; + + optind = 0; + optreset = 1; + while ((ch = getopt(argc, argv, "p:")) != -1) { + switch (ch) { + case 'p': + ctx->opts.plot = !0; + ctx->opts.path = optarg; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + err(2, "Need exactly one variable"); + ctx->varname = argv[0]; ctx->sum = ctx->occurences = 0; + filt->flags = 0; + filt->cpu = -1; + filt->ev_type = EVTR_TYPE_STMT; + filt->var = ctx->varname; + if (!ctx->opts.plot) + return ctx; + + if (!(ctx->plotter = plotter_factory())) + err(1, "can't allocate plotter"); + if (!(ctx->plotter_ctx = ctx->plotter->plot_init(ctx->opts.path))) + err(1, "can't allocate plotter context"); + + if ((ctx->time_plot = ctx->plotter->plot_new(ctx->plotter_ctx, + PLOT_TYPE_LINE, + ctx->varname)) < 0) + err(1, "can't create histogram"); return ctx; } @@ -1098,6 +1139,9 @@ stats_integer_each(void *_ctx, evtr_event_t ev) ctx->varname); return; } + if (ctx->plotter) + ctx->plotter->plot_line(ctx->plotter_ctx, ctx->time_plot, + (double)ev->ts, (double)ev->stmt.val->num); ctx->sum += ev->stmt.val->num; ++ctx->occurences; } @@ -1109,10 +1153,20 @@ stats_integer_report(void *_ctx) struct stats_integer_ctx *ctx = _ctx; printf("median for variable %s is %lf\n", ctx->varname, (double)ctx->sum / ctx->occurences); + if (ctx->plotter) + ctx->plotter->plot_finish(ctx->plotter_ctx); + free(ctx); } struct stats_completion_ctx { + struct stats_completion_options { + int plot; + const char *path; + } opts; + struct plotter *plotter; + void *plotter_ctx; + plotid_t durations_plot; const char *varname; const char *ctor; const char *dtor; @@ -1145,23 +1199,57 @@ ctor_data_new(evtr_event_t ev) static void * -stats_completion_prepare(int argc, char **argv) +stats_completion_prepare(int argc, char **argv, struct evtr_filter *filt) { struct stats_completion_ctx *ctx; + int ch; - if (argc != 4) - err(2, "need a variable, a constructor and a destructor"); if (!(ctx = calloc(1, sizeof(*ctx)))) return ctx; + + optind = 0; + optreset = 1; + while ((ch = getopt(argc, argv, "p:")) != -1) { + switch (ch) { + case 'p': + ctx->opts.plot = !0; + ctx->opts.path = optarg; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc != 3) + err(2, "need a variable, a constructor and a destructor"); if (!(ctx->ctors = ehash_new())) goto free_ctx; ctx->ctors->hashfunc = &hashfunc_ctor; ctx->ctors->cmpfunc = &cmpfunc_ctor; if (!(ctx->durations = vector_new())) goto free_ctors; - ctx->varname = argv[1]; - ctx->ctor = argv[2]; - ctx->dtor = argv[3]; + ctx->varname = argv[0]; + ctx->ctor = argv[1]; + ctx->dtor = argv[2]; + + filt->flags = 0; + filt->cpu = -1; + filt->ev_type = EVTR_TYPE_STMT; + filt->var = ctx->varname; + + if (!ctx->opts.plot) + return ctx; + + if (!(ctx->plotter = plotter_factory())) + err(1, "can't allocate plotter"); + if (!(ctx->plotter_ctx = ctx->plotter->plot_init(ctx->opts.path))) + err(1, "can't allocate plotter context"); + + if ((ctx->durations_plot = ctx->plotter->plot_new(ctx->plotter_ctx, + PLOT_TYPE_HIST, + ctx->varname)) < 0) + err(1, "can't create histogram"); return ctx; free_ctors: ; /* XXX */ @@ -1213,6 +1301,10 @@ stats_completion_each(void *_ctx, evtr_event_t ev) ev->stmt.val->ctor.name = tmp; return; } + if (ctx->plotter) + ctx->plotter->plot_histogram(ctx->plotter_ctx, + ctx->durations_plot, + (double)(ev->ts - cd->ts)); vector_push(ctx->durations, ev->ts - cd->ts); ++ctx->completed_events; ctx->completed_duration_sum += ev->ts - cd->ts; @@ -1241,7 +1333,9 @@ stats_completion_report(void *_ctx) avg = (double)ctx->completed_duration_sum / ctx->completed_events; printf("Average event duration:\t%lf (stddev %lf)\n", avg, stddev(ctx->durations, avg)); - + + if (ctx->plotter) + ctx->plotter->plot_finish(ctx->plotter_ctx); vector_destroy(ctx->durations); /* XXX: hash */ free(ctx); @@ -1302,21 +1396,12 @@ cmd_stats(int argc, char **argv) freq = cputab.cpus[0].freq; freq /= 1000000; /* we want to print out usecs */ printd(MISC, "using freq = %lf\n", freq); - filt.flags = 0; - filt.cpu = -1; - filt.ev_type = EVTR_TYPE_STMT; - filt.var = NULL; - optind = 0; - optreset = 1; - if (argc < 2) - err(2, "Need a variable"); - filt.var = argv[1]; + if (!(statctx = statsops->prepare(argc, argv, &filt))) + err(1, "Can't allocate stats context"); q = evtr_query_init(evtr, &filt, 1); if (!q) err(1, "Can't initialize query"); - if (!(statctx = statsops->prepare(argc, argv))) - err(1, "Can't allocate stats context"); while(!evtr_query_next(q, &ev)) { if (!last_ts) @@ -1388,7 +1473,6 @@ cmd_summary(int argc, char **argv) return 0; } - int main(int argc, char **argv) diff --git a/usr.bin/evtranalyze/plotter.c b/usr.bin/evtranalyze/plotter.c new file mode 100644 index 0000000000..a0988e4c0a --- /dev/null +++ b/usr.bin/evtranalyze/plotter.c @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "trivial.h" +#include "plotter.h" + +struct ploticus_plot { + unsigned type; + prop_dictionary_t params; + FILE *fp; + char *path; +}; + +struct ploticus_plotter { + int nr_plots; + struct ploticus_plot **plots; + const char *basepath; +}; + +const char *plot_prefabs[] = { + [PLOT_TYPE_HIST] = "dist", + [PLOT_TYPE_LINE] = "lines", +}; + +static +void * +ploticus_init(const char *base) +{ + struct ploticus_plotter *ctx; + + if (!(ctx = calloc(1, sizeof(*ctx)))) + return ctx; + if (!(ctx->basepath = strdup(base))) + goto free_ctx; + return ctx; +free_ctx: + free(ctx); + return NULL; +} + +static +int +ploticus_new_plot_hist(struct ploticus_plot *plot) +{ + prop_dictionary_t params = plot->params; + prop_string_t str; + + if (!(str = prop_string_create_cstring_nocopy("1"))) + return !0; + if (!prop_dictionary_set(params, "x", str)) { + prop_object_release(str); + return !0; + } + if (!(str = prop_string_create_cstring_nocopy("yes"))) + return !0; + if (!prop_dictionary_set(params, "curve", str)) { + prop_object_release(str); + return !0; + } + return 0; +} + +static +int +ploticus_new_plot_line(struct ploticus_plot *plot) +{ + prop_dictionary_t params = plot->params; + prop_string_t str; + + if (!(str = prop_string_create_cstring_nocopy("1"))) + return !0; + if (!prop_dictionary_set(params, "x", str)) { + prop_object_release(str); + return !0; + } + if (!(str = prop_string_create_cstring_nocopy("2"))) + return !0; + if (!prop_dictionary_set(params, "y", str)) { + prop_object_release(str); + return !0; + } + return 0; +} + +int (*plot_type_initializers[])(struct ploticus_plot *) = { + [PLOT_TYPE_HIST] = ploticus_new_plot_hist, + [PLOT_TYPE_LINE] = ploticus_new_plot_line, +}; + +static +plotid_t +ploticus_new_plot(void *_ctx, enum plot_type type, const char *title) +{ + struct ploticus_plot *plot; + prop_dictionary_t params; + prop_string_t str; + struct ploticus_plotter *ctx = _ctx; + struct ploticus_plot **tmp; + char *datapath; + + if ((type <= PLOT_TYPE_START) || (type >= PLOT_TYPE_END)) + return -1; + if (!(tmp = realloc(ctx->plots, sizeof(struct ploticus_plot *) * + (ctx->nr_plots + 1)))) + return -1; + ctx->plots = tmp; + + if (!(params = prop_dictionary_create())) + return -1; + if (!(plot = calloc(1, sizeof(*plot)))) + goto free_params; + plot->params = params; + plot->type = type; + + if (asprintf(&plot->path, "%s-%d-%s", ctx->basepath, type, + title) < 0) + goto free_plot; + if (asprintf(&datapath, "%s.data", plot->path) < 0) + goto free_path; + + if (!(str = prop_string_create_cstring(title))) + goto free_datapath; + if (!prop_dictionary_set(params, "title", str)) { + prop_object_release(str); + goto free_datapath; + } + if (!(str = prop_string_create_cstring(datapath))) + goto free_datapath; + if (!prop_dictionary_set(params, "data", str)) { + prop_object_release(str); + goto free_datapath; + } + + if (plot_type_initializers[type](plot)) + goto free_datapath; + if (!(plot->fp = fopen(datapath, "w"))) { + goto free_datapath; + } + free(datapath); + ctx->plots[ctx->nr_plots] = plot; + return ctx->nr_plots++; + +free_datapath: + free(datapath); +free_path: + free(plot->path); +free_plot: + free(plot); +free_params: + prop_object_release(params); + return -1; +} + +static +int +ploticus_plot_histogram(void *_ctx, plotid_t id, double val) +{ + struct ploticus_plotter *ctx = _ctx; + struct ploticus_plot *plot; + + if ((id < 0) || (id >= ctx->nr_plots)) + return ERANGE; + plot = ctx->plots[id]; + assert(plot != NULL); + + fprintf(plot->fp, "%lf\n", val); + + return 0; +} + +static +int +ploticus_plot_line(void *_ctx, plotid_t id, double x, double y) +{ + struct ploticus_plotter *ctx = _ctx; + struct ploticus_plot *plot; + + if ((id < 0) || (id >= ctx->nr_plots)) + return ERANGE; + plot = ctx->plots[id]; + assert(plot != NULL); + + fprintf(plot->fp, "%lf %lf\n", x, y); + + return 0; +} + +extern char **environ; + +static +void +ploticus_run(struct ploticus_plot *plot) +{ + unsigned nr_params; + const char **pl_argv; + prop_object_iterator_t it; + prop_object_t key, val; + const char *keystr; + const char *output_format = "-svg"; + int i; + + printd(PLOT, "ploticus_run\n"); + nr_params = prop_dictionary_count(plot->params); + if (!(pl_argv = calloc(nr_params + + 1 + /* progname */ + 1 + /* trailing NULL */ + 1 + /* -prefab */ + 1 + /* dist */ + 1 + /* output format */ + 1 + /* -o */ + 1 /* outpath */ + , sizeof(char *)))) + err(1, "can't allocate argv"); + if (!(it = prop_dictionary_iterator(plot->params))) + err(1, "can't allocate dictionary iterator"); + pl_argv[0] = "ploticus"; + pl_argv[1] = "-prefab"; + pl_argv[2] = plot_prefabs[plot->type]; + pl_argv[2 + nr_params + 1] = output_format; + pl_argv[2 + nr_params + 2] = "-o"; + if (asprintf(__DECONST(char **, &pl_argv[2 + nr_params + 3]), + "%s.svg", plot->path) < 0) + err(1, "Can't allocate args"); + key = prop_object_iterator_next(it); + for (i = 3; key; ++i, key = prop_object_iterator_next(it)) { + keystr = prop_dictionary_keysym_cstring_nocopy(key); + assert(keystr != NULL); + val = prop_dictionary_get_keysym(plot->params, key); + assert(val != NULL); + printd(PLOT, "%s=%s\n", keystr, + prop_string_cstring_nocopy(val)); + if (asprintf(__DECONST(char **, &pl_argv[i]), "%s=%s", keystr, + prop_string_cstring_nocopy(val)) < 0) + err(1, "can't allocate exec arguments"); + } + prop_object_iterator_release(it); + printd(PLOT, "about to exec with args:\n"); + for (i = 0; pl_argv[i]; ++i) + printd(PLOT, "%s\n", pl_argv[i]); + execve("/usr/pkg/bin/pl", __DECONST(char * const *, pl_argv), environ); + err(1, "failed to exec ploticus"); +} + +static +int +ploticus_plot_generate(struct ploticus_plot *plot) +{ + pid_t pid; + int status; + + fclose(plot->fp); + + switch ((pid = fork())) { + case -1: + return -1; + case 0: /* child */ + ploticus_run(plot); + assert(!"can't get here"); + } + /* parent */ + if (waitpid(pid, &status, 0) != pid) + err(1, "waitpid() failed"); + if (!WIFEXITED(status)) + warn("ploticus did not exit!"); + if (WEXITSTATUS(status)) + warn("ploticus did not run successfully"); + return 0; +} + +static +int +ploticus_plot_finish(void *_ctx) +{ + struct ploticus_plotter *ctx = _ctx; + int i; + + for (i = 0; i < ctx->nr_plots; ++i) { + if (ploticus_plot_generate(ctx->plots[i])) + return -1; + } + return 0; +} + +static struct plotter ploticus_plotter = { + .plot_init = ploticus_init, + .plot_new = ploticus_new_plot, + .plot_histogram = ploticus_plot_histogram, + .plot_line = ploticus_plot_line, + .plot_finish = ploticus_plot_finish, +}; + +static const char *ploticus_path = "/usr/pkg/bin/pl"; + +struct plotter * +plotter_factory(void) +{ + struct stat st; + if ((!stat(ploticus_path, &st)) && + S_ISREG(st.st_mode) && + (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) + return &ploticus_plotter; + warnx("%s does not exist or is not an executable file", ploticus_path); + return NULL; +} diff --git a/usr.bin/evtranalyze/plotter.h b/usr.bin/evtranalyze/plotter.h new file mode 100644 index 0000000000..702ffc101e --- /dev/null +++ b/usr.bin/evtranalyze/plotter.h @@ -0,0 +1,22 @@ +#ifndef _PLOTTER_H_ +#define _PLOTTER_H_ + +typedef int plotid_t; +enum plot_type { + PLOT_TYPE_START, + PLOT_TYPE_HIST, + PLOT_TYPE_LINE, + PLOT_TYPE_END +}; + +struct plotter { + void *(*plot_init)(const char *); + plotid_t (*plot_new)(void *, enum plot_type, const char *); + int (*plot_histogram)(void *, plotid_t, double); + int (*plot_line)(void *, plotid_t, double, double); + int (*plot_finish)(void *); +}; + +struct plotter *plotter_factory(void); + +#endif /* _PLOTTER_H_ */ diff --git a/usr.bin/evtranalyze/trivial.h b/usr.bin/evtranalyze/trivial.h index 7cd000b7e8..50d3f9df29 100644 --- a/usr.bin/evtranalyze/trivial.h +++ b/usr.bin/evtranalyze/trivial.h @@ -8,6 +8,7 @@ enum debug_flags { DEFINE_DEBUG_FLAG(INTV, 'i'), DEFINE_DEBUG_FLAG(SVG, 's'), DEFINE_DEBUG_FLAG(MISC, 'm'), + DEFINE_DEBUG_FLAG(PLOT, 'p'), }; #define printd(subsys, ...) \ -- 2.41.0