Add tap(4) to LINT/LINT64.
[dragonfly.git] / usr.bin / evtranalyze / plotter.c
1 #include <assert.h>
2 #include <err.h>
3 #include <errno.h>
4 #include <libprop/proplib.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12
13 #include "trivial.h"
14 #include "plotter.h"
15
16 struct ploticus_plot {
17         unsigned type;
18         prop_dictionary_t params;
19         FILE *fp;
20         char *path;
21 };
22
23 struct ploticus_plotter {
24         int nr_plots;
25         struct ploticus_plot **plots;
26         const char *basepath;
27 };
28
29 const char *plot_prefabs[] = {
30         [PLOT_TYPE_HIST] = "dist",
31         [PLOT_TYPE_LINE] = "lines",
32 };
33
34 static
35 void *
36 ploticus_init(const char *base)
37 {
38         struct ploticus_plotter *ctx;
39
40         if (!(ctx = calloc(1, sizeof(*ctx))))
41                 return ctx;
42         if (!(ctx->basepath = strdup(base)))
43                 goto free_ctx;
44         return ctx;
45 free_ctx:
46         free(ctx);
47         return NULL;
48 }
49
50 static
51 int
52 ploticus_new_plot_hist(struct ploticus_plot *plot)
53 {
54         prop_dictionary_t params = plot->params;
55         prop_string_t str;
56
57         if (!(str = prop_string_create_cstring_nocopy("1")))
58                 return !0;
59         if (!prop_dictionary_set(params, "x", str)) {
60                 prop_object_release(str);
61                 return !0;
62         }
63         if (!(str = prop_string_create_cstring_nocopy("yes")))
64                 return !0;
65         if (!prop_dictionary_set(params, "curve", str)) {
66                 prop_object_release(str);
67                 return !0;
68         }
69         return 0;
70 }
71
72 static
73 int
74 ploticus_new_plot_line(struct ploticus_plot *plot)
75 {
76         prop_dictionary_t params = plot->params;
77         prop_string_t str;
78
79         if (!(str = prop_string_create_cstring_nocopy("1")))
80                 return !0;
81         if (!prop_dictionary_set(params, "x", str)) {
82                 prop_object_release(str);
83                 return !0;
84         }
85         if (!(str = prop_string_create_cstring_nocopy("2")))
86                 return !0;
87         if (!prop_dictionary_set(params, "y", str)) {
88                 prop_object_release(str);
89                 return !0;
90         }
91         return 0;
92 }
93
94 int (*plot_type_initializers[])(struct ploticus_plot *) = {
95         [PLOT_TYPE_HIST] = ploticus_new_plot_hist,
96         [PLOT_TYPE_LINE] = ploticus_new_plot_line,
97 };
98
99 static
100 plotid_t
101 ploticus_new_plot(void *_ctx, enum plot_type type, const char *title)
102 {
103         struct ploticus_plot *plot;
104         prop_dictionary_t params;
105         prop_string_t str;
106         struct ploticus_plotter *ctx = _ctx;
107         struct ploticus_plot **tmp;
108         char *datapath;
109
110         if ((type <= PLOT_TYPE_START) || (type >= PLOT_TYPE_END))
111                 return -1;
112         if (!(tmp = realloc(ctx->plots, sizeof(struct ploticus_plot *) *
113                             (ctx->nr_plots + 1))))
114                 return -1;
115         ctx->plots = tmp;
116
117         if (!(params = prop_dictionary_create()))
118                 return -1;
119         if (!(plot = calloc(1, sizeof(*plot))))
120                 goto free_params;
121         plot->params = params;
122         plot->type = type;
123
124         if (asprintf(&plot->path, "%s-%d-%s", ctx->basepath, type,
125                      title) < 0)
126                 goto free_plot;
127         if (asprintf(&datapath, "%s.data", plot->path) < 0)
128                 goto free_path;
129
130         if (!(str = prop_string_create_cstring(title)))
131                 goto free_datapath;
132         if (!prop_dictionary_set(params, "title", str)) {
133                 prop_object_release(str);
134                 goto free_datapath;
135         }
136         if (!(str = prop_string_create_cstring(datapath)))
137                 goto free_datapath;
138         if (!prop_dictionary_set(params, "data", str)) {
139                 prop_object_release(str);
140                 goto free_datapath;
141         }
142
143         if (plot_type_initializers[type](plot))
144                 goto free_datapath;
145         if (!(plot->fp = fopen(datapath, "w"))) {
146                 goto free_datapath;
147         }
148         free(datapath);
149         ctx->plots[ctx->nr_plots] = plot;
150         return ctx->nr_plots++;
151
152 free_datapath:
153         free(datapath);
154 free_path:
155         free(plot->path);
156 free_plot:
157         free(plot);
158 free_params:
159         prop_object_release(params);
160         return -1;
161 }
162
163 static
164 int
165 ploticus_plot_histogram(void *_ctx, plotid_t id, double val)
166 {
167         struct ploticus_plotter *ctx = _ctx;
168         struct ploticus_plot *plot;
169
170         if ((id < 0) || (id >= ctx->nr_plots))
171                 return ERANGE;
172         plot = ctx->plots[id];
173         assert(plot != NULL);
174
175         fprintf(plot->fp, "%lf\n", val);
176
177         return 0;
178 }
179
180 static
181 int
182 ploticus_plot_line(void *_ctx, plotid_t id, double x, double y)
183 {
184         struct ploticus_plotter *ctx = _ctx;
185         struct ploticus_plot *plot;
186
187         if ((id < 0) || (id >= ctx->nr_plots))
188                 return ERANGE;
189         plot = ctx->plots[id];
190         assert(plot != NULL);
191
192         fprintf(plot->fp, "%lf %lf\n", x, y);
193
194         return 0;
195 }
196
197 extern char **environ;
198
199 static
200 void
201 ploticus_run(struct ploticus_plot *plot)
202 {
203         unsigned nr_params;
204         const char **pl_argv;
205         prop_object_iterator_t it;
206         prop_object_t key, val;
207         const char *keystr;
208         const char *output_format = "-svg";
209         int i;
210
211         printd(PLOT, "ploticus_run\n");
212         nr_params = prop_dictionary_count(plot->params);
213         if (!(pl_argv = calloc(nr_params +
214                                1 +      /* progname */
215                                1 +      /* trailing NULL */
216                                1 +      /* -prefab */
217                                1 +      /* dist */
218                                1 +      /* output format */
219                                1 +      /* -o */
220                                1        /* outpath */
221                                , sizeof(char *))))
222                 err(1, "can't allocate argv");
223         if (!(it = prop_dictionary_iterator(plot->params)))
224                 err(1, "can't allocate dictionary iterator");
225         pl_argv[0] = "ploticus";
226         pl_argv[1] = "-prefab";
227         pl_argv[2] = plot_prefabs[plot->type];
228         pl_argv[2 + nr_params + 1] = output_format;
229         pl_argv[2 + nr_params + 2] = "-o";
230         if (asprintf(__DECONST(char **, &pl_argv[2 + nr_params + 3]),
231                      "%s.svg", plot->path) < 0)
232                 err(1, "Can't allocate args");
233         key = prop_object_iterator_next(it);
234         for (i = 3; key; ++i, key = prop_object_iterator_next(it)) {
235                 keystr = prop_dictionary_keysym_cstring_nocopy(key);
236                 assert(keystr != NULL);
237                 val = prop_dictionary_get_keysym(plot->params, key);
238                 assert(val != NULL);
239                 printd(PLOT, "%s=%s\n", keystr,
240                        prop_string_cstring_nocopy(val));
241                 if (asprintf(__DECONST(char **, &pl_argv[i]), "%s=%s", keystr,
242                              prop_string_cstring_nocopy(val)) < 0)
243                         err(1, "can't allocate exec arguments");
244         }
245         prop_object_iterator_release(it);
246         printd(PLOT, "about to exec with args:\n");
247         for (i = 0; pl_argv[i]; ++i)
248                 printd(PLOT, "%s\n", pl_argv[i]);
249         execve("/usr/pkg/bin/pl", __DECONST(char * const *, pl_argv), environ);
250         err(1, "failed to exec ploticus");
251 }
252
253 static
254 int
255 ploticus_plot_generate(struct ploticus_plot *plot)
256 {
257         pid_t pid;
258         int status;
259
260         fclose(plot->fp);
261
262         switch ((pid = fork())) {
263         case -1:
264                 return -1;
265         case 0: /* child */
266                 ploticus_run(plot);
267                 assert(!"can't get here");
268         }
269         /* parent */
270         if (waitpid(pid, &status, 0) != pid)
271                 err(1, "waitpid() failed");
272         if (!WIFEXITED(status))
273                 warn("ploticus did not exit!");
274         if (WEXITSTATUS(status))
275                 warn("ploticus did not run successfully");
276         return 0;
277 }
278
279 static
280 int
281 ploticus_plot_finish(void *_ctx)
282 {
283         struct ploticus_plotter *ctx = _ctx;
284         int i;
285
286         for (i = 0; i < ctx->nr_plots; ++i) {
287                 if (ploticus_plot_generate(ctx->plots[i]))
288                         return -1;
289         }
290         return 0;
291 }
292
293 static struct plotter ploticus_plotter = {
294         .plot_init = ploticus_init,
295         .plot_new = ploticus_new_plot,
296         .plot_histogram = ploticus_plot_histogram,
297         .plot_line = ploticus_plot_line,
298         .plot_finish = ploticus_plot_finish,
299 };
300
301 static const char *ploticus_path = "/usr/pkg/bin/pl";
302
303 struct plotter *
304 plotter_factory(void)
305 {
306         struct stat st;
307         if ((!stat(ploticus_path, &st)) &&
308             S_ISREG(st.st_mode) &&
309             (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
310                 return &ploticus_plotter;
311         warnx("%s does not exist or is not an executable file", ploticus_path);
312         return NULL;
313 }