Commit | Line | Data |
---|---|---|
9e066e23 CM |
1 | /* |
2 | * Copyright (C) 2012 Fusion-io | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public | |
6 | * License v2 as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program; if not, write to the Free Software | |
15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
16 | * | |
17 | * Parts of this file were imported from Jens Axboe's blktrace sources (also GPL) | |
18 | */ | |
19 | #include <sys/types.h> | |
20 | #include <sys/stat.h> | |
21 | #include <fcntl.h> | |
22 | #include <unistd.h> | |
23 | #include <stdlib.h> | |
24 | #include <stdio.h> | |
25 | #include <math.h> | |
26 | #include <inttypes.h> | |
27 | #include <string.h> | |
28 | #include <asm/types.h> | |
29 | #include <errno.h> | |
30 | #include <sys/mman.h> | |
31 | #include <time.h> | |
32 | #include <math.h> | |
33 | ||
34 | #include "plot.h" | |
35 | ||
36 | static int graph_width = 600; | |
37 | static int graph_height = 150; | |
38 | static int graph_inner_margin = 2; | |
39 | static int graph_tick_len = 5; | |
40 | static int graph_left_pad = 120; | |
41 | static int tick_label_pad = 16; | |
42 | static int tick_font_size = 15; | |
43 | static char *font_family = "sans-serif"; | |
44 | ||
45 | /* this is the title for the whole page */ | |
46 | static int plot_title_height = 50; | |
47 | static int plot_title_font_size = 25; | |
48 | ||
49 | /* this is the label at the top of each plot */ | |
50 | static int plot_label_height = 60; | |
51 | static int plot_label_font_size = 20; | |
52 | ||
53 | /* label for each axis is slightly smaller */ | |
54 | static int axis_label_font_size = 16; | |
55 | ||
56 | int legend_x_off = 45; | |
57 | int legend_y_off = -10; | |
58 | int legend_font_size = 15; | |
59 | int legend_width = 80; | |
60 | ||
61 | static int line_len = 1024; | |
62 | static char line[1024]; | |
63 | ||
64 | static int rolling_avg_secs = 0; | |
65 | ||
66 | struct graph_line_data *alloc_line_data(int seconds, int stop_seconds) | |
67 | { | |
68 | int size = sizeof(struct graph_line_data) + (stop_seconds + 1) * sizeof(struct graph_line_pair); | |
69 | struct graph_line_data *gld; | |
70 | ||
71 | gld = calloc(1, size); | |
72 | if (!gld) { | |
73 | fprintf(stderr, "Unable to allocate memory for graph data\n"); | |
74 | exit(1); | |
75 | } | |
76 | gld->seconds = seconds; | |
77 | gld->stop_seconds = stop_seconds; | |
78 | return gld; | |
79 | } | |
80 | ||
81 | void free_line_data(struct graph_line_data *gld) | |
82 | { | |
83 | free(gld->label); | |
84 | free(gld); | |
85 | } | |
86 | ||
87 | struct graph_dot_data *alloc_dot_data(int seconds, u64 max_offset, int stop_seconds) | |
88 | { | |
89 | int size; | |
90 | int arr_size; | |
91 | int rows = graph_height; | |
92 | int cols = graph_width; | |
93 | struct graph_dot_data *gdd; | |
94 | ||
95 | size = sizeof(struct graph_dot_data); | |
96 | ||
97 | /* the number of bits */ | |
98 | arr_size = (rows + 1) * cols; | |
99 | ||
100 | /* the number of bytes */ | |
101 | arr_size /= 8; | |
102 | ||
103 | gdd = calloc(1, size + arr_size); | |
104 | if (!gdd) { | |
105 | fprintf(stderr, "Unable to allocate memory for graph data\n"); | |
106 | exit(1); | |
107 | } | |
108 | gdd->seconds = seconds; | |
109 | gdd->stop_seconds = stop_seconds; | |
110 | gdd->rows = rows; | |
111 | gdd->cols = cols; | |
112 | gdd->max_offset = max_offset; | |
113 | return gdd; | |
114 | } | |
115 | ||
116 | void free_dot_data(struct graph_dot_data *gdd) | |
117 | { | |
118 | free(gdd); | |
119 | } | |
120 | ||
121 | void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, int bytes, double time) | |
122 | { | |
123 | double bytes_per_row = (double)gdd->max_offset / gdd->rows; | |
124 | double secs_per_col = (double)gdd->seconds / gdd->cols; | |
125 | double col; | |
126 | double row; | |
127 | int col_int; | |
128 | int row_int; | |
129 | int bit_index; | |
130 | int arr_index; | |
131 | int bit_mod; | |
132 | int mod = (int)bytes_per_row; | |
133 | ||
134 | if (offset > gdd->max_offset) | |
135 | return; | |
136 | ||
137 | gdd->total_ios++; | |
138 | while (bytes > 0) { | |
139 | time = time / 1000000000.0; | |
140 | row = (double)offset / bytes_per_row; | |
141 | col = time / secs_per_col; | |
142 | ||
143 | col_int = floor(col); | |
144 | row_int = floor(row); | |
145 | ||
146 | bit_index = row_int * gdd->cols + col_int; | |
147 | arr_index = bit_index / 8; | |
148 | bit_mod = bit_index % 8; | |
149 | ||
150 | gdd->data[arr_index] |= 1 << bit_mod; | |
151 | offset += mod; | |
152 | bytes -= mod; | |
153 | } | |
154 | } | |
155 | ||
156 | void print_gdd(struct graph_dot_data *gdd) | |
157 | { | |
158 | int col = 0; | |
159 | int row = 0; | |
160 | int arr_index; | |
161 | u64 val; | |
162 | int bit_index; | |
163 | int bit_mod; | |
164 | ||
165 | for (row = gdd->rows - 1; row >= 0; row--) { | |
166 | for (col = 0; col < gdd->cols; col++) { | |
167 | bit_index = row * gdd->cols + col; | |
168 | arr_index = bit_index / sizeof(unsigned long); | |
169 | bit_mod = bit_index % sizeof(unsigned long); | |
170 | ||
171 | val = gdd->data[arr_index]; | |
172 | if (val & (1 << bit_mod)) | |
173 | printf("*"); | |
174 | else | |
175 | printf(" "); | |
176 | } | |
177 | printf("\n"); | |
178 | } | |
179 | } | |
180 | ||
181 | static double rolling_avg(struct graph_line_pair *data, int index, int distance) | |
182 | { | |
183 | double sum = 0; | |
184 | int start; | |
185 | ||
186 | if (distance < 0) | |
187 | distance = 1; | |
188 | if (distance > index) { | |
189 | start = 0; | |
190 | } else { | |
191 | start = index - distance; | |
192 | } | |
193 | distance = 0; | |
194 | while (start <= index) { | |
195 | double avg; | |
196 | ||
197 | if (data[start].count) | |
198 | avg = ((double)data[start].sum) / data[start].count; | |
199 | else | |
200 | avg= 0; | |
201 | ||
202 | sum += avg; | |
203 | distance++; | |
204 | start++; | |
205 | } | |
206 | return sum / distance; | |
207 | } | |
208 | ||
209 | void write_svg_header(int fd) | |
210 | { | |
211 | char *header = "<svg xmlns=\"http://www.w3.org/2000/svg\"\nxmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"; | |
212 | char *filter1 ="<filter id=\"shadow\">\n " | |
213 | "<feOffset result=\"offOut\" in=\"SourceAlpha\" dx=\"4\" dy=\"4\" />\n " | |
214 | "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"2\" />\n " | |
215 | "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n " | |
216 | "</filter>\n"; | |
217 | char *filter2 ="<filter id=\"textshadow\" x=\"0\" y=\"0\" width=\"200%\" height=\"200%\">\n " | |
218 | "<feOffset result=\"offOut\" in=\"SourceAlpha\" dx=\"1\" dy=\"1\" />\n " | |
219 | "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"1.5\" />\n " | |
220 | "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n " | |
221 | "</filter>\n"; | |
222 | char *filter3 ="<filter id=\"labelshadow\" x=\"0\" y=\"0\" width=\"200%\" height=\"200%\">\n " | |
223 | "<feOffset result=\"offOut\" in=\"SourceGraphic\" dx=\"3\" dy=\"3\" />\n " | |
224 | "<feColorMatrix result=\"matrixOut\" in=\"offOut\" type=\"matrix\" " | |
225 | "values=\"0.2 0 0 0 0 0 0.2 0 0 0 0 0 0.2 0 0 0 0 0 1 0\" /> " | |
226 | "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"2\" />\n " | |
227 | "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n " | |
228 | "</filter>\n"; | |
229 | char *defs_start = "<defs>\n"; | |
230 | char *defs_close = "</defs>\n"; | |
231 | ||
232 | write(fd, header, strlen(header)); | |
233 | write(fd, defs_start, strlen(defs_start)); | |
234 | write(fd, filter1, strlen(filter1)); | |
235 | write(fd, filter2, strlen(filter2)); | |
236 | write(fd, filter3, strlen(filter3)); | |
237 | write(fd, defs_close, strlen(defs_close)); | |
238 | } | |
239 | ||
240 | void write_drop_shadow(struct plot *plot) | |
241 | { | |
242 | snprintf(line, line_len, "<rect x=\"0\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"white\"/>\n", | |
243 | plot->start_y_offset, plot->total_width, 45); | |
244 | write(plot->fd, line, strlen(line)); | |
245 | ||
246 | snprintf(line, line_len, "<path d=\"M %d %d h %d v %d h %d t %d %d V %d H %d Z\" " | |
247 | "fill=\"white\" filter=\"url(#shadow)\"/>", | |
248 | 0, plot->start_y_offset, | |
249 | plot->total_width - graph_left_pad / 2, | |
250 | -plot->total_height, 24, 1, 1, | |
251 | plot->start_y_offset + 10, 0); | |
252 | write(plot->fd, line, strlen(line)); | |
253 | ||
254 | snprintf(line, line_len, "<path d=\"M %d %d H %d V %d h %d V %d H %d Z\" " | |
255 | "fill=\"white\"/>", | |
256 | 0, plot->start_y_offset - 15, /* start */ | |
257 | plot->total_width - graph_left_pad / 2 - 10, /* hline over */ | |
258 | plot->start_y_offset - plot->total_height, /* vline up */ | |
259 | 15, /*hline over */ | |
260 | plot->start_y_offset, /* vline back down */ | |
261 | 0); | |
262 | write(plot->fd, line, strlen(line)); | |
263 | ||
264 | plot->start_y_offset += 45; | |
265 | } | |
266 | ||
267 | /* svg y offset for the traditional 0,0 (bottom left corner) of the plot */ | |
268 | static int axis_y(void) | |
269 | { | |
270 | return plot_label_height + graph_height + graph_inner_margin; | |
271 | } | |
272 | ||
273 | /* this gives you the correct pixel for a given offset from the bottom left y axis */ | |
274 | static int axis_y_off(int y) | |
275 | { | |
276 | return plot_label_height + graph_height - y; | |
277 | } | |
278 | ||
279 | /* svg x axis offset from 0 */ | |
280 | static int axis_x(void) | |
281 | { | |
282 | return graph_left_pad; | |
283 | } | |
284 | ||
285 | /* the correct pixel for a given X offset */ | |
286 | static int axis_x_off(int x) | |
287 | { | |
288 | return graph_left_pad + graph_inner_margin + x; | |
289 | } | |
290 | ||
291 | /* | |
292 | * this draws a backing rectangle for the plot and it | |
293 | * also creates a new svg element so our offsets can | |
294 | * be relative to this one plot. | |
295 | */ | |
296 | void setup_axis(struct plot *plot) | |
297 | { | |
298 | int ret; | |
299 | int len; | |
300 | int fd = plot->fd; | |
301 | int bump_height = tick_font_size * 3 + axis_label_font_size; | |
302 | ||
303 | ||
304 | plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + legend_width; | |
305 | plot->total_height = axis_y() + tick_label_pad + tick_font_size; | |
306 | ||
307 | if (plot->add_xlabel) | |
308 | plot->total_height += bump_height; | |
309 | ||
310 | /* backing rect */ | |
311 | snprintf(line, line_len, "<rect x=\"0\" y=\"%d\" width=\"%d\" " | |
312 | "height=\"%d\" fill=\"white\" stroke=\"none\"/>", | |
313 | plot->start_y_offset, plot->total_width + 40, | |
314 | plot->total_height + 20); | |
315 | len = strlen(line); | |
316 | write(fd, line, len); | |
317 | snprintf(line, line_len, "<rect x=\"15\" y=\"%d\" width=\"%d\" " | |
318 | "filter=\"url(#shadow)\" " | |
319 | "height=\"%d\" fill=\"white\" stroke=\"none\"/>", | |
320 | plot->start_y_offset, plot->total_width, plot->total_height); | |
321 | len = strlen(line); | |
322 | write(fd, line, len); | |
323 | plot->total_height += 20; | |
324 | ||
325 | ||
326 | /* create an svg object for all our coords to be relative against */ | |
327 | snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\">\n", plot->start_x_offset, plot->start_y_offset); | |
328 | write(fd, line, strlen(line)); | |
329 | ||
330 | snprintf(line, 1024, "<path d=\"M%d %d h %d V %d H %d Z\" stroke=\"black\" stroke-width=\"2\" fill=\"none\"/>\n", | |
331 | axis_x(), axis_y(), | |
332 | graph_width + graph_inner_margin * 2, axis_y_off(graph_height) - graph_inner_margin, | |
333 | axis_x()); | |
334 | len = strlen(line); | |
335 | ret = write(fd, line, len); | |
336 | if (ret != len) { | |
337 | fprintf(stderr, "failed to write svg axis\n"); | |
338 | exit(1); | |
339 | } | |
340 | } | |
341 | ||
342 | /* draw a plot title. This should be done only once, | |
343 | * and it bumps the plot width/height numbers by | |
344 | * what it draws. | |
345 | * | |
346 | * Call this before setting up the first axis | |
347 | */ | |
348 | void set_plot_title(struct plot *plot, char *title) | |
349 | { | |
350 | int len; | |
351 | int fd = plot->fd; | |
352 | ||
353 | plot->total_height = plot_title_height; | |
354 | plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + legend_width; | |
355 | ||
356 | /* backing rect */ | |
357 | snprintf(line, line_len, "<rect x=\"0\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"white\" stroke=\"none\"/>", | |
358 | plot->start_y_offset, plot->total_width + 40, plot_title_height + 20); | |
359 | len = strlen(line); | |
360 | write(fd, line, len); | |
361 | ||
362 | snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " | |
363 | "font-weight=\"bold\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n", | |
364 | axis_x_off(graph_width / 2), | |
365 | plot->start_y_offset + plot_title_height / 2, | |
366 | font_family, plot_title_font_size, "middle", title); | |
367 | plot->start_y_offset += plot_title_height; | |
368 | len = strlen(line); | |
369 | write(fd, line, len); | |
370 | } | |
371 | ||
372 | /* | |
373 | * create evenly spread out ticks along the xaxis. if tick only is set | |
374 | * this just makes the ticks, otherwise it labels each tick as it goes | |
375 | */ | |
376 | void set_xticks(struct plot *plot, int num_ticks, int first, int last) | |
377 | { | |
378 | int pixels_per_tick = graph_width / num_ticks; | |
379 | int step = (last - first) / num_ticks; | |
380 | int i; | |
381 | int tick_y = axis_y_off(graph_tick_len) + graph_inner_margin; | |
382 | int tick_x = axis_x(); | |
383 | int tick_only = plot->add_xlabel == 0; | |
384 | ||
385 | int text_y = axis_y() + tick_label_pad; | |
386 | ||
387 | char *middle = "middle"; | |
388 | char *start = "start"; | |
389 | ||
390 | for (i = 0; i < num_ticks; i++) { | |
391 | char *anchor; | |
392 | if (i != 0) { | |
393 | snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"2\" height=\"%d\" style=\"stroke:none;fill:black;\"/>\n", | |
394 | tick_x, tick_y, graph_tick_len); | |
395 | write(plot->fd, line, strlen(line)); | |
396 | anchor = middle; | |
397 | } else { | |
398 | anchor = start; | |
399 | } | |
400 | ||
401 | if (!tick_only) { | |
402 | snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " | |
403 | "fill=\"black\" style=\"text-anchor: %s\">%d</text>\n", | |
404 | tick_x, text_y, font_family, tick_font_size, anchor, step * i); | |
405 | write(plot->fd, line, strlen(line)); | |
406 | } | |
407 | tick_x += pixels_per_tick; | |
408 | } | |
409 | ||
410 | if (!tick_only) { | |
411 | snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " | |
412 | "fill=\"black\" style=\"text-anchor: middle\">%d</text>\n", | |
413 | axis_x_off(graph_width - 2), | |
414 | text_y, font_family, tick_font_size, last); | |
415 | write(plot->fd, line, strlen(line)); | |
416 | } | |
417 | } | |
418 | ||
419 | void set_ylabel(struct plot *plot, char *label) | |
420 | { | |
421 | int len; | |
422 | int fd = plot->fd; | |
423 | ||
424 | snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" " | |
425 | "transform=\"rotate(-90 %d %d)\" font-weight=\"bold\" " | |
426 | "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n", | |
427 | graph_left_pad / 2 - axis_label_font_size, | |
428 | axis_y_off(graph_height / 2), | |
429 | font_family, | |
430 | graph_left_pad / 2 - axis_label_font_size, | |
431 | axis_y_off(graph_height / 2), | |
432 | axis_label_font_size, "middle", label); | |
433 | len = strlen(line); | |
434 | write(fd, line, len); | |
435 | } | |
436 | ||
437 | void set_xlabel(struct plot *plot, char *label) | |
438 | { | |
439 | int len; | |
440 | int fd = plot->fd; | |
441 | snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" " | |
442 | "font-weight=\"bold\" " | |
443 | "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n", | |
444 | axis_x_off(graph_width / 2), | |
445 | axis_y() + tick_font_size * 3 + axis_label_font_size / 2, | |
446 | font_family, | |
447 | axis_label_font_size, "middle", label); | |
448 | len = strlen(line); | |
449 | write(fd, line, len); | |
450 | ||
451 | } | |
452 | ||
453 | /* | |
454 | * create evenly spread out ticks along the y axis. | |
455 | * The ticks are labeled as it goes | |
456 | */ | |
457 | void set_yticks(struct plot *plot, int num_ticks, int first, int last, char *units) | |
458 | { | |
459 | int pixels_per_tick = graph_height / num_ticks; | |
460 | int step = (last - first) / num_ticks; | |
461 | int i; | |
462 | int tick_y = 0; | |
463 | int text_x = axis_x() - 6; | |
464 | int tick_x = axis_x(); | |
465 | char *anchor = "end"; | |
466 | ||
467 | for (i = 0; i < num_ticks; i++) { | |
468 | if (i != 0) { | |
469 | snprintf(line, line_len, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" " | |
470 | "style=\"stroke:lightgray;stroke-width:2;stroke-dasharray:9,12;\"/>\n", | |
471 | tick_x, axis_y_off(tick_y), | |
472 | axis_x_off(graph_width), axis_y_off(tick_y)); | |
473 | write(plot->fd, line, strlen(line)); | |
474 | } | |
475 | ||
476 | snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " | |
477 | "fill=\"black\" style=\"text-anchor: %s\">%d%s</text>\n", | |
478 | text_x, | |
479 | axis_y_off(tick_y - tick_font_size / 2), | |
480 | font_family, tick_font_size, anchor, step * i, units); | |
481 | write(plot->fd, line, strlen(line)); | |
482 | tick_y += pixels_per_tick; | |
483 | } | |
484 | snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " | |
485 | "fill=\"black\" style=\"text-anchor: %s\">%d%s</text>\n", | |
486 | text_x, axis_y_off(graph_height), font_family, tick_font_size, anchor, last, units); | |
487 | write(plot->fd, line, strlen(line)); | |
488 | } | |
489 | ||
490 | void set_plot_label(struct plot *plot, char *label) | |
491 | { | |
492 | int len; | |
493 | int fd = plot->fd; | |
494 | ||
495 | snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" " | |
496 | "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n", | |
497 | axis_x() + graph_width / 2, | |
498 | plot_label_height / 2, | |
499 | font_family, plot_label_font_size, "middle", label); | |
500 | len = strlen(line); | |
501 | write(fd, line, len); | |
502 | } | |
503 | ||
504 | static void close_svg(int fd) | |
505 | { | |
506 | char *close_line = "</svg>\n"; | |
507 | ||
508 | write(fd, close_line, strlen(close_line)); | |
509 | } | |
510 | ||
511 | int close_plot(struct plot *plot) | |
512 | { | |
513 | close_svg(plot->fd); | |
514 | plot->start_y_offset += plot->total_height; | |
515 | plot->add_xlabel = 0; | |
516 | return 0; | |
517 | } | |
518 | ||
519 | struct plot *alloc_plot(int fd) | |
520 | { | |
521 | struct plot *plot; | |
522 | plot = calloc(1, sizeof(*plot)); | |
523 | if (!plot) { | |
524 | fprintf(stderr, "Unable to allocate memory %s\n", strerror(errno)); | |
525 | exit(1); | |
526 | } | |
527 | plot->fd = fd; | |
528 | return plot; | |
529 | } | |
530 | ||
531 | char *byte_unit_names[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "unobtainium" }; | |
532 | int MAX_BYTE_UNIT_SCALE = 9; | |
533 | ||
534 | char *time_unit_names[] = { "n", "u", "m", "s" }; | |
535 | int MAX_TIME_UNIT_SCALE = 3; | |
536 | ||
537 | void scale_line_graph_bytes(u64 *max, char **units, u64 factor) | |
538 | { | |
539 | int scale = 0; | |
540 | u64 val = *max; | |
541 | u64 div = 1; | |
542 | while (val > factor * 64) { | |
543 | val /= factor; | |
544 | scale++; | |
545 | div *= factor; | |
546 | } | |
547 | *units = byte_unit_names[scale]; | |
548 | if (scale == 0) | |
549 | return; | |
550 | ||
551 | if (scale > MAX_BYTE_UNIT_SCALE) | |
552 | scale = MAX_BYTE_UNIT_SCALE; | |
553 | ||
554 | *max /= div; | |
555 | } | |
556 | ||
557 | void scale_line_graph_time(u64 *max, char **units) | |
558 | { | |
559 | int scale = 0; | |
560 | u64 val = *max; | |
561 | u64 div = 1; | |
562 | while (val > 1000 * 10) { | |
563 | val /= 1000; | |
564 | scale++; | |
565 | div *= 1000; | |
566 | if (scale == MAX_TIME_UNIT_SCALE) | |
567 | break; | |
568 | } | |
569 | *units = time_unit_names[scale]; | |
570 | if (scale == 0) | |
571 | return; | |
572 | ||
573 | *max /= div; | |
574 | } | |
575 | ||
576 | int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color) | |
577 | { | |
578 | int i; | |
579 | double val; | |
580 | int rolling; | |
581 | int fd = plot->fd; | |
582 | char *start = "<path d=\""; | |
583 | double yscale = ((double)gld->max) / graph_height; | |
584 | double xscale = (double)(gld->seconds - 1) / graph_width; | |
585 | char c = 'M'; | |
586 | double x; | |
587 | ||
588 | if (rolling_avg_secs) | |
589 | rolling = rolling_avg_secs; | |
590 | else | |
591 | rolling = gld->stop_seconds / 25; | |
592 | ||
593 | write(fd, start, strlen(start)); | |
594 | for (i = 0; i < gld->stop_seconds; i++) { | |
595 | val = rolling_avg(gld->data, i, rolling); | |
596 | val = val / yscale; | |
597 | x = (double)i / xscale; | |
598 | snprintf(line, line_len, "%c %d %d ", c, axis_x_off(x), axis_y_off(val)); | |
599 | ||
600 | c = 'L'; | |
601 | write(fd, line, strlen(line)); | |
602 | } | |
603 | snprintf(line, line_len, "\" fill=\"none\" stroke=\"%s\" stroke-width=\"2\"/>\n", color); | |
604 | write(fd, line, strlen(line)); | |
605 | ||
606 | return 0; | |
607 | } | |
608 | ||
609 | static int svg_add_io(int fd, int row, int col, char *color) | |
610 | { | |
611 | snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"1.5\" height=\"1.5\" rx=\"0.5\" style=\"stroke:none;fill:%s\"/>\n", | |
612 | axis_x_off(col), axis_y_off(row), color); | |
613 | return write(fd, line, strlen(line)); | |
614 | } | |
615 | ||
616 | int svg_io_graph(struct plot *plot, struct graph_dot_data *gdd, char *color) | |
617 | { | |
618 | int fd = plot->fd;; | |
619 | int col = 0; | |
620 | int row = 0; | |
621 | int arr_index; | |
622 | unsigned char val; | |
623 | int bit_index; | |
624 | int bit_mod; | |
625 | ||
626 | for (row = gdd->rows - 1; row >= 0; row--) { | |
627 | for (col = 0; col < gdd->cols; col++) { | |
628 | bit_index = row * gdd->cols + col; | |
629 | arr_index = bit_index / 8; | |
630 | bit_mod = bit_index % 8; | |
631 | ||
632 | if (arr_index < 0) | |
633 | continue; | |
634 | val = gdd->data[arr_index]; | |
635 | if (val & (1 << bit_mod)) | |
636 | svg_add_io(fd, row, col, color); | |
637 | } | |
638 | } | |
639 | return 0; | |
640 | } | |
641 | ||
642 | void svg_alloc_legend(struct plot *plot, int num_lines) | |
643 | { | |
644 | char **lines = calloc(num_lines, sizeof(char *)); | |
645 | plot->legend_index = 0; | |
646 | plot->legend_lines = lines; | |
647 | plot->num_legend_lines = num_lines; | |
648 | } | |
649 | ||
650 | void svg_write_legend(struct plot *plot) | |
651 | { | |
652 | int i; | |
653 | int legend_line_x = axis_x_off(graph_width) + legend_x_off; | |
654 | int legend_line_y = axis_y_off(graph_height) + legend_y_off; | |
655 | ||
656 | if (plot->legend_index == 0) | |
657 | return; | |
658 | ||
659 | snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" " | |
660 | "fill=\"white\" filter=\"url(#shadow)\"/>\n", | |
661 | legend_line_x - 15, | |
662 | legend_line_y - 12, | |
663 | legend_width, | |
664 | plot->legend_index * legend_font_size + legend_font_size / 2 + 12); | |
665 | ||
666 | write(plot->fd, line, strlen(line)); | |
667 | for (i = 0; i < plot->legend_index; i++) { | |
668 | write(plot->fd, plot->legend_lines[i], | |
669 | strlen(plot->legend_lines[i])); | |
670 | free(plot->legend_lines[i]); | |
671 | } | |
672 | free(plot->legend_lines); | |
673 | plot->legend_index = 0; | |
674 | plot->legend_lines = 0; | |
675 | } | |
676 | ||
677 | void svg_add_legend(struct plot *plot, char *text, char *extra, char *color) | |
678 | { | |
679 | int legend_line_x = axis_x_off(graph_width) + legend_x_off; | |
680 | int legend_line_y = axis_y_off(graph_height) + legend_y_off; | |
681 | ||
682 | if (!text && (!extra || strlen(extra) == 0)) | |
683 | return; | |
684 | ||
685 | legend_line_y += plot->legend_index * legend_font_size + legend_font_size / 2; | |
686 | snprintf(line, line_len, "<path d=\"M %d %d h 8\" stroke=\"%s\" stroke-width=\"8\" " | |
687 | "filter=\"url(#labelshadow)\"/> " | |
688 | "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " | |
689 | "fill=\"black\" style=\"text-anchor: left\">%s%s</text>\n", | |
690 | legend_line_x, legend_line_y, | |
691 | color, legend_line_x + 13, | |
692 | legend_line_y + 4, font_family, legend_font_size, | |
693 | text, extra); | |
694 | ||
695 | plot->legend_lines[plot->legend_index++] = strdup(line); | |
696 | } | |
697 | ||
698 | void set_legend_width(int longest_str) | |
699 | { | |
700 | if (longest_str) | |
701 | legend_width = longest_str * (legend_font_size * 3 / 4) + 25; | |
702 | else | |
703 | legend_width = 0; | |
704 | } | |
705 | ||
706 | void set_rolling_avg(int rolling) | |
707 | { | |
708 | rolling_avg_secs = rolling; | |
709 | } | |
710 |