xref: /dragonfly/usr.bin/evtranalyze/plotter.c (revision 0db87cb7)
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/local/bin/ploticus", __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/local/bin/ploticus";
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 }
314