iowatcher: Fix filtering of outliers from below
[blktrace.git] / iowatcher / plot.c
CommitLineData
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
ba758825 36static int io_graph_scale = 8;
abf08f96
CM
37static int graph_width = 700;
38static int graph_height = 250;
39static int graph_circle_extra = 30;
ba758825
CM
40static int graph_inner_x_margin = 2;
41static int graph_inner_y_margin = 2;
9e066e23
CM
42static int graph_tick_len = 5;
43static int graph_left_pad = 120;
44static int tick_label_pad = 16;
45static int tick_font_size = 15;
46static char *font_family = "sans-serif";
47
48/* this is the title for the whole page */
49static int plot_title_height = 50;
50static int plot_title_font_size = 25;
51
52/* this is the label at the top of each plot */
53static int plot_label_height = 60;
54static int plot_label_font_size = 20;
55
56/* label for each axis is slightly smaller */
57static int axis_label_font_size = 16;
58
59int legend_x_off = 45;
60int legend_y_off = -10;
61int legend_font_size = 15;
62int legend_width = 80;
63
e199d546
CM
64static int rolling_avg_secs = 0;
65
9e066e23
CM
66static int line_len = 1024;
67static char line[1024];
68
abf08f96
CM
69static int final_height = 0;
70static int final_width = 0;
71
9e066e23
CM
72struct graph_line_data *alloc_line_data(int seconds, int stop_seconds)
73{
74 int size = sizeof(struct graph_line_data) + (stop_seconds + 1) * sizeof(struct graph_line_pair);
75 struct graph_line_data *gld;
76
77 gld = calloc(1, size);
78 if (!gld) {
79 fprintf(stderr, "Unable to allocate memory for graph data\n");
80 exit(1);
81 }
82 gld->seconds = seconds;
83 gld->stop_seconds = stop_seconds;
84 return gld;
85}
86
87void free_line_data(struct graph_line_data *gld)
88{
89 free(gld->label);
90 free(gld);
91}
92
93struct graph_dot_data *alloc_dot_data(int seconds, u64 max_offset, int stop_seconds)
94{
95 int size;
96 int arr_size;
ba758825 97 int rows = graph_height * io_graph_scale;
9e066e23
CM
98 int cols = graph_width;
99 struct graph_dot_data *gdd;
100
101 size = sizeof(struct graph_dot_data);
102
103 /* the number of bits */
104 arr_size = (rows + 1) * cols;
105
106 /* the number of bytes */
107 arr_size /= 8;
108
109 gdd = calloc(1, size + arr_size);
110 if (!gdd) {
111 fprintf(stderr, "Unable to allocate memory for graph data\n");
112 exit(1);
113 }
114 gdd->seconds = seconds;
115 gdd->stop_seconds = stop_seconds;
116 gdd->rows = rows;
117 gdd->cols = cols;
118 gdd->max_offset = max_offset;
119 return gdd;
120}
121
122void free_dot_data(struct graph_dot_data *gdd)
123{
124 free(gdd);
125}
126
ba758825 127void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, double bytes, double time)
9e066e23
CM
128{
129 double bytes_per_row = (double)gdd->max_offset / gdd->rows;
ba758825 130
9e066e23
CM
131 double secs_per_col = (double)gdd->seconds / gdd->cols;
132 double col;
133 double row;
134 int col_int;
135 int row_int;
136 int bit_index;
137 int arr_index;
138 int bit_mod;
ba758825 139 double mod = bytes_per_row;
9e066e23
CM
140
141 if (offset > gdd->max_offset)
142 return;
143
144 gdd->total_ios++;
ba758825 145 time = time / 1000000000.0;
9e066e23 146 while (bytes > 0) {
9e066e23
CM
147 row = (double)offset / bytes_per_row;
148 col = time / secs_per_col;
149
150 col_int = floor(col);
151 row_int = floor(row);
9e066e23
CM
152 bit_index = row_int * gdd->cols + col_int;
153 arr_index = bit_index / 8;
154 bit_mod = bit_index % 8;
155
156 gdd->data[arr_index] |= 1 << bit_mod;
157 offset += mod;
158 bytes -= mod;
159 }
160}
161
162void print_gdd(struct graph_dot_data *gdd)
163{
164 int col = 0;
165 int row = 0;
166 int arr_index;
167 u64 val;
168 int bit_index;
169 int bit_mod;
170
171 for (row = gdd->rows - 1; row >= 0; row--) {
172 for (col = 0; col < gdd->cols; col++) {
173 bit_index = row * gdd->cols + col;
174 arr_index = bit_index / sizeof(unsigned long);
175 bit_mod = bit_index % sizeof(unsigned long);
176
177 val = gdd->data[arr_index];
178 if (val & (1 << bit_mod))
179 printf("*");
180 else
181 printf(" ");
182 }
183 printf("\n");
184 }
185}
186
187static double rolling_avg(struct graph_line_pair *data, int index, int distance)
188{
189 double sum = 0;
190 int start;
191
192 if (distance < 0)
193 distance = 1;
194 if (distance > index) {
195 start = 0;
196 } else {
197 start = index - distance;
198 }
199 distance = 0;
200 while (start <= index) {
201 double avg;
202
203 if (data[start].count)
204 avg = ((double)data[start].sum) / data[start].count;
205 else
206 avg= 0;
207
208 sum += avg;
209 distance++;
210 start++;
211 }
212 return sum / distance;
213}
214
215void write_svg_header(int fd)
216{
abf08f96
CM
217 char *spaces = " \n";
218 char *header = "<svg xmlns=\"http://www.w3.org/2000/svg\">\n";
9e066e23
CM
219 char *filter1 ="<filter id=\"shadow\">\n "
220 "<feOffset result=\"offOut\" in=\"SourceAlpha\" dx=\"4\" dy=\"4\" />\n "
221 "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"2\" />\n "
222 "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n "
223 "</filter>\n";
224 char *filter2 ="<filter id=\"textshadow\" x=\"0\" y=\"0\" width=\"200%\" height=\"200%\">\n "
225 "<feOffset result=\"offOut\" in=\"SourceAlpha\" dx=\"1\" dy=\"1\" />\n "
226 "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"1.5\" />\n "
227 "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n "
228 "</filter>\n";
229 char *filter3 ="<filter id=\"labelshadow\" x=\"0\" y=\"0\" width=\"200%\" height=\"200%\">\n "
230 "<feOffset result=\"offOut\" in=\"SourceGraphic\" dx=\"3\" dy=\"3\" />\n "
231 "<feColorMatrix result=\"matrixOut\" in=\"offOut\" type=\"matrix\" "
232 "values=\"0.2 0 0 0 0 0 0.2 0 0 0 0 0 0.2 0 0 0 0 0 1 0\" /> "
233 "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"2\" />\n "
234 "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n "
235 "</filter>\n";
236 char *defs_start = "<defs>\n";
237 char *defs_close = "</defs>\n";
abf08f96
CM
238 final_width = 0;
239 final_height = 0;
9e066e23
CM
240
241 write(fd, header, strlen(header));
abf08f96
CM
242 /* write a bunch of spaces so we can stuff in the width and height later */
243 write(fd, spaces, strlen(spaces));
8ed9516f
CM
244 write(fd, spaces, strlen(spaces));
245 write(fd, spaces, strlen(spaces));
abf08f96 246
9e066e23
CM
247 write(fd, defs_start, strlen(defs_start));
248 write(fd, filter1, strlen(filter1));
249 write(fd, filter2, strlen(filter2));
250 write(fd, filter3, strlen(filter3));
251 write(fd, defs_close, strlen(defs_close));
252}
253
254void write_drop_shadow(struct plot *plot)
255{
256 snprintf(line, line_len, "<rect x=\"0\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"white\"/>\n",
257 plot->start_y_offset, plot->total_width, 45);
258 write(plot->fd, line, strlen(line));
259
260 snprintf(line, line_len, "<path d=\"M %d %d h %d v %d h %d t %d %d V %d H %d Z\" "
261 "fill=\"white\" filter=\"url(#shadow)\"/>",
262 0, plot->start_y_offset,
263 plot->total_width - graph_left_pad / 2,
264 -plot->total_height, 24, 1, 1,
265 plot->start_y_offset + 10, 0);
266 write(plot->fd, line, strlen(line));
267
268 snprintf(line, line_len, "<path d=\"M %d %d H %d V %d h %d V %d H %d Z\" "
269 "fill=\"white\"/>",
270 0, plot->start_y_offset - 15, /* start */
271 plot->total_width - graph_left_pad / 2 - 10, /* hline over */
272 plot->start_y_offset - plot->total_height, /* vline up */
273 15, /*hline over */
274 plot->start_y_offset, /* vline back down */
275 0);
276 write(plot->fd, line, strlen(line));
277
278 plot->start_y_offset += 45;
279}
280
281/* svg y offset for the traditional 0,0 (bottom left corner) of the plot */
282static int axis_y(void)
283{
ba758825 284 return plot_label_height + graph_height + graph_inner_y_margin;
9e066e23
CM
285}
286
287/* this gives you the correct pixel for a given offset from the bottom left y axis */
ba758825 288static double axis_y_off_double(double y)
9e066e23
CM
289{
290 return plot_label_height + graph_height - y;
291}
292
ba758825
CM
293static int axis_y_off(int y)
294{
295 return axis_y_off_double(y);
296}
297
9e066e23
CM
298/* svg x axis offset from 0 */
299static int axis_x(void)
300{
301 return graph_left_pad;
302}
303
304/* the correct pixel for a given X offset */
ba758825
CM
305static double axis_x_off_double(double x)
306{
307 return graph_left_pad + graph_inner_x_margin + x;
308}
309
9e066e23
CM
310static int axis_x_off(int x)
311{
ba758825 312 return (int)axis_x_off_double(x);
9e066e23
CM
313}
314
315/*
316 * this draws a backing rectangle for the plot and it
317 * also creates a new svg element so our offsets can
318 * be relative to this one plot.
319 */
320void setup_axis(struct plot *plot)
321{
322 int ret;
323 int len;
324 int fd = plot->fd;
325 int bump_height = tick_font_size * 3 + axis_label_font_size;
8ed9516f 326 int local_legend_width = legend_width;
9e066e23 327
8ed9516f
CM
328 if (plot->no_legend)
329 local_legend_width = 0;
330
cc3d54d5 331 plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + local_legend_width;
9e066e23
CM
332 plot->total_height = axis_y() + tick_label_pad + tick_font_size;
333
334 if (plot->add_xlabel)
335 plot->total_height += bump_height;
336
337 /* backing rect */
8ed9516f 338 snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
9e066e23 339 "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
8ed9516f 340 plot->start_x_offset,
9e066e23
CM
341 plot->start_y_offset, plot->total_width + 40,
342 plot->total_height + 20);
343 len = strlen(line);
344 write(fd, line, len);
abf08f96 345
8ed9516f 346 snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
9e066e23
CM
347 "filter=\"url(#shadow)\" "
348 "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
8ed9516f 349 plot->start_x_offset + 15,
9e066e23
CM
350 plot->start_y_offset, plot->total_width, plot->total_height);
351 len = strlen(line);
352 write(fd, line, len);
353 plot->total_height += 20;
8ed9516f 354 plot->total_width += 20;
9e066e23 355
abf08f96
CM
356 if (plot->total_height + plot->start_y_offset > final_height)
357 final_height = plot->total_height + plot->start_y_offset;
8ed9516f
CM
358 if (plot->start_x_offset + plot->total_width + 40 > final_width)
359 final_width = plot->start_x_offset + plot->total_width + 40;
9e066e23
CM
360
361 /* create an svg object for all our coords to be relative against */
abf08f96 362 snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\">\n", plot->start_x_offset, plot->start_y_offset);
9e066e23
CM
363 write(fd, line, strlen(line));
364
365 snprintf(line, 1024, "<path d=\"M%d %d h %d V %d H %d Z\" stroke=\"black\" stroke-width=\"2\" fill=\"none\"/>\n",
366 axis_x(), axis_y(),
ba758825 367 graph_width + graph_inner_x_margin * 2, axis_y_off(graph_height) - graph_inner_y_margin,
9e066e23
CM
368 axis_x());
369 len = strlen(line);
370 ret = write(fd, line, len);
371 if (ret != len) {
372 fprintf(stderr, "failed to write svg axis\n");
373 exit(1);
374 }
375}
376
abf08f96
CM
377/*
378 * this draws a backing rectangle for the plot and it
379 * also creates a new svg element so our offsets can
380 * be relative to this one plot.
381 */
382void setup_axis_spindle(struct plot *plot)
383{
384 int len;
385 int fd = plot->fd;
386 int bump_height = tick_font_size * 3 + axis_label_font_size;
387
8ed9516f
CM
388 legend_x_off = -60;
389
390 plot->total_width = axis_x_off(graph_width) + legend_width;
abf08f96
CM
391 plot->total_height = axis_y() + tick_label_pad + tick_font_size;
392
393 if (plot->add_xlabel)
394 plot->total_height += bump_height;
395
396 /* backing rect */
8ed9516f 397 snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
abf08f96 398 "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
8ed9516f
CM
399 plot->start_x_offset,
400 plot->start_y_offset, plot->total_width + 10,
abf08f96
CM
401 plot->total_height + 20);
402 len = strlen(line);
403 write(fd, line, len);
404
8ed9516f 405 snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
abf08f96
CM
406 "filter=\"url(#shadow)\" "
407 "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
8ed9516f
CM
408 plot->start_x_offset + 15,
409 plot->start_y_offset, plot->total_width - 30,
410 plot->total_height);
abf08f96
CM
411 len = strlen(line);
412 write(fd, line, len);
413 plot->total_height += 20;
414
415 if (plot->total_height + plot->start_y_offset > final_height)
416 final_height = plot->total_height + plot->start_y_offset;
8ed9516f
CM
417 if (plot->start_x_offset + plot->total_width + 40 > final_width)
418 final_width = plot->start_x_offset + plot->total_width + 40;
abf08f96
CM
419
420 /* create an svg object for all our coords to be relative against */
421 snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\">\n", plot->start_x_offset, plot->start_y_offset);
422 write(fd, line, strlen(line));
423
424}
425
9e066e23
CM
426/* draw a plot title. This should be done only once,
427 * and it bumps the plot width/height numbers by
428 * what it draws.
429 *
430 * Call this before setting up the first axis
431 */
432void set_plot_title(struct plot *plot, char *title)
433{
434 int len;
435 int fd = plot->fd;
436
437 plot->total_height = plot_title_height;
438 plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + legend_width;
439
440 /* backing rect */
441 snprintf(line, line_len, "<rect x=\"0\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"white\" stroke=\"none\"/>",
442 plot->start_y_offset, plot->total_width + 40, plot_title_height + 20);
443 len = strlen(line);
444 write(fd, line, len);
445
446 snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
447 "font-weight=\"bold\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n",
448 axis_x_off(graph_width / 2),
449 plot->start_y_offset + plot_title_height / 2,
450 font_family, plot_title_font_size, "middle", title);
451 plot->start_y_offset += plot_title_height;
452 len = strlen(line);
453 write(fd, line, len);
454}
455
456/*
457 * create evenly spread out ticks along the xaxis. if tick only is set
458 * this just makes the ticks, otherwise it labels each tick as it goes
459 */
460void set_xticks(struct plot *plot, int num_ticks, int first, int last)
461{
462 int pixels_per_tick = graph_width / num_ticks;
463 int step = (last - first) / num_ticks;
464 int i;
ba758825 465 int tick_y = axis_y_off(graph_tick_len) + graph_inner_y_margin;
9e066e23
CM
466 int tick_x = axis_x();
467 int tick_only = plot->add_xlabel == 0;
468
469 int text_y = axis_y() + tick_label_pad;
470
471 char *middle = "middle";
472 char *start = "start";
473
474 for (i = 0; i < num_ticks; i++) {
475 char *anchor;
476 if (i != 0) {
477 snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"2\" height=\"%d\" style=\"stroke:none;fill:black;\"/>\n",
478 tick_x, tick_y, graph_tick_len);
479 write(plot->fd, line, strlen(line));
480 anchor = middle;
481 } else {
482 anchor = start;
483 }
484
485 if (!tick_only) {
486 snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
487 "fill=\"black\" style=\"text-anchor: %s\">%d</text>\n",
488 tick_x, text_y, font_family, tick_font_size, anchor, step * i);
489 write(plot->fd, line, strlen(line));
490 }
491 tick_x += pixels_per_tick;
492 }
493
494 if (!tick_only) {
495 snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
496 "fill=\"black\" style=\"text-anchor: middle\">%d</text>\n",
497 axis_x_off(graph_width - 2),
498 text_y, font_family, tick_font_size, last);
499 write(plot->fd, line, strlen(line));
500 }
501}
502
503void set_ylabel(struct plot *plot, char *label)
504{
505 int len;
506 int fd = plot->fd;
507
508 snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" "
509 "transform=\"rotate(-90 %d %d)\" font-weight=\"bold\" "
510 "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n",
511 graph_left_pad / 2 - axis_label_font_size,
512 axis_y_off(graph_height / 2),
513 font_family,
514 graph_left_pad / 2 - axis_label_font_size,
ba758825 515 (int)axis_y_off(graph_height / 2),
9e066e23
CM
516 axis_label_font_size, "middle", label);
517 len = strlen(line);
518 write(fd, line, len);
519}
520
521void set_xlabel(struct plot *plot, char *label)
522{
523 int len;
524 int fd = plot->fd;
525 snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" "
526 "font-weight=\"bold\" "
527 "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n",
528 axis_x_off(graph_width / 2),
529 axis_y() + tick_font_size * 3 + axis_label_font_size / 2,
530 font_family,
531 axis_label_font_size, "middle", label);
532 len = strlen(line);
533 write(fd, line, len);
534
535}
536
537/*
538 * create evenly spread out ticks along the y axis.
539 * The ticks are labeled as it goes
540 */
541void set_yticks(struct plot *plot, int num_ticks, int first, int last, char *units)
542{
543 int pixels_per_tick = graph_height / num_ticks;
544 int step = (last - first) / num_ticks;
545 int i;
546 int tick_y = 0;
547 int text_x = axis_x() - 6;
548 int tick_x = axis_x();
549 char *anchor = "end";
550
551 for (i = 0; i < num_ticks; i++) {
552 if (i != 0) {
553 snprintf(line, line_len, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
554 "style=\"stroke:lightgray;stroke-width:2;stroke-dasharray:9,12;\"/>\n",
555 tick_x, axis_y_off(tick_y),
556 axis_x_off(graph_width), axis_y_off(tick_y));
557 write(plot->fd, line, strlen(line));
558 }
559
560 snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
561 "fill=\"black\" style=\"text-anchor: %s\">%d%s</text>\n",
562 text_x,
563 axis_y_off(tick_y - tick_font_size / 2),
564 font_family, tick_font_size, anchor, step * i, units);
565 write(plot->fd, line, strlen(line));
566 tick_y += pixels_per_tick;
567 }
568 snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
569 "fill=\"black\" style=\"text-anchor: %s\">%d%s</text>\n",
570 text_x, axis_y_off(graph_height), font_family, tick_font_size, anchor, last, units);
571 write(plot->fd, line, strlen(line));
572}
573
574void set_plot_label(struct plot *plot, char *label)
575{
576 int len;
577 int fd = plot->fd;
578
579 snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" "
580 "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n",
581 axis_x() + graph_width / 2,
582 plot_label_height / 2,
583 font_family, plot_label_font_size, "middle", label);
584 len = strlen(line);
585 write(fd, line, len);
586}
587
588static void close_svg(int fd)
589{
590 char *close_line = "</svg>\n";
591
592 write(fd, close_line, strlen(close_line));
593}
594
595int close_plot(struct plot *plot)
596{
597 close_svg(plot->fd);
cc3d54d5
CM
598 if (plot->direction == PLOT_DOWN)
599 plot->start_y_offset += plot->total_height;
600 else if (plot->direction == PLOT_ACROSS)
601 plot->start_x_offset += plot->total_width;
8ed9516f
CM
602 return 0;
603}
604
ba758825 605struct plot *alloc_plot(void)
9e066e23
CM
606{
607 struct plot *plot;
608 plot = calloc(1, sizeof(*plot));
609 if (!plot) {
610 fprintf(stderr, "Unable to allocate memory %s\n", strerror(errno));
611 exit(1);
612 }
ba758825 613 plot->fd = 0;
9e066e23
CM
614 return plot;
615}
616
abf08f96
CM
617int close_plot_file(struct plot *plot)
618{
619 int ret;
620 ret = lseek(plot->fd, 0, SEEK_SET);
621 if (ret == (off_t)-1) {
622 perror("seek");
623 exit(1);
624 }
625 final_width = ((final_width + 1) / 2) * 2;
626 final_height = ((final_height + 1) / 2) * 2;
627 snprintf(line, line_len, "<svg xmlns=\"http://www.w3.org/2000/svg\" "
628 "width=\"%d\" height=\"%d\">\n",
629 final_width, final_height);
630 write(plot->fd, line, strlen(line));
8ed9516f
CM
631 snprintf(line, line_len, "<rect x=\"0\" y=\"0\" width=\"%d\" "
632 "height=\"%d\" fill=\"white\"/>\n", final_width, final_height);
633 write(plot->fd, line, strlen(line));
abf08f96
CM
634 close(plot->fd);
635 plot->fd = 0;
636 return 0;
637}
638
ba758825
CM
639void set_plot_output(struct plot *plot, char *filename)
640{
641 int fd;
642
643 if (plot->fd)
abf08f96 644 close_plot_file(plot);
ba758825
CM
645 fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
646 if (fd < 0) {
647 fprintf(stderr, "Unable to open output file %s err %s\n", filename, strerror(errno));
648 exit(1);
649 }
650 plot->fd = fd;
651 plot->start_y_offset = plot->start_x_offset = 0;
652 write_svg_header(fd);
653}
654
9e066e23
CM
655char *byte_unit_names[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "unobtainium" };
656int MAX_BYTE_UNIT_SCALE = 9;
657
658char *time_unit_names[] = { "n", "u", "m", "s" };
659int MAX_TIME_UNIT_SCALE = 3;
660
661void scale_line_graph_bytes(u64 *max, char **units, u64 factor)
662{
663 int scale = 0;
664 u64 val = *max;
665 u64 div = 1;
666 while (val > factor * 64) {
667 val /= factor;
668 scale++;
669 div *= factor;
670 }
671 *units = byte_unit_names[scale];
672 if (scale == 0)
673 return;
674
675 if (scale > MAX_BYTE_UNIT_SCALE)
676 scale = MAX_BYTE_UNIT_SCALE;
677
678 *max /= div;
679}
680
681void scale_line_graph_time(u64 *max, char **units)
682{
683 int scale = 0;
684 u64 val = *max;
685 u64 div = 1;
686 while (val > 1000 * 10) {
687 val /= 1000;
688 scale++;
689 div *= 1000;
690 if (scale == MAX_TIME_UNIT_SCALE)
691 break;
692 }
693 *units = time_unit_names[scale];
694 if (scale == 0)
695 return;
696
697 *max /= div;
698}
699
bfb0e441 700int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color, int thresh1, int thresh2)
9e066e23
CM
701{
702 int i;
703 double val;
bfb0e441 704 double avg;
9e066e23
CM
705 int rolling;
706 int fd = plot->fd;
707 char *start = "<path d=\"";
708 double yscale = ((double)gld->max) / graph_height;
709 double xscale = (double)(gld->seconds - 1) / graph_width;
710 char c = 'M';
711 double x;
bfb0e441
CM
712 int printed_header = 0;
713 int printed_lines = 0;
9e066e23 714
49d17e16 715 if (thresh1 && thresh2)
bfb0e441
CM
716 rolling = 0;
717 else if (rolling_avg_secs)
9e066e23
CM
718 rolling = rolling_avg_secs;
719 else
720 rolling = gld->stop_seconds / 25;
721
9e066e23 722 for (i = 0; i < gld->stop_seconds; i++) {
bfb0e441 723 avg = rolling_avg(gld->data, i, rolling);
49d17e16
CM
724 if (yscale == 0)
725 val = 0;
726 else
727 val = avg / yscale;
728
729 if (val > graph_height)
730 val = graph_height;
731 if (val < 0)
732 val = 0;
733
9e066e23 734 x = (double)i / xscale;
bfb0e441
CM
735 if (!thresh1 && !thresh2) {
736
737 if (!printed_header) {
738 write(fd, start, strlen(start));
739 printed_header = 1;
740 }
741
742 /* in full line mode, everything in the graph is connected */
743 snprintf(line, line_len, "%c %d %d ", c, axis_x_off(x), axis_y_off(val));
744 c = 'L';
745 write(fd, line, strlen(line));
746 printed_lines = 1;
747 } else if (avg > thresh1 || avg > thresh2) {
748 int len = 10;
749 if (!printed_header) {
750 write(fd, start, strlen(start));
751 printed_header = 1;
752 }
753
754 /* otherwise, we just print a bar up there to show this one data point */
f106953d 755 if (i >= gld->stop_seconds - 2)
bfb0e441
CM
756 len = -10;
757
758 /*
759 * we don't use the rolling averages here to show high
760 * points in the data
761 */
762 snprintf(line, line_len, "M %d %d h %d ", axis_x_off(x),
763 axis_y_off(val), len);
764 write(fd, line, strlen(line));
765 printed_lines = 1;
766 }
9e066e23 767
bfb0e441
CM
768 }
769 if (printed_lines) {
770 snprintf(line, line_len, "\" fill=\"none\" stroke=\"%s\" stroke-width=\"2\"/>\n", color);
9e066e23
CM
771 write(fd, line, strlen(line));
772 }
cc3d54d5
CM
773 if (plot->timeline)
774 svg_write_time_line(plot, plot->timeline);
9e066e23
CM
775
776 return 0;
777}
778
ba758825 779void svg_write_time_line(struct plot *plot, int col)
9e066e23 780{
ba758825
CM
781 snprintf(line, line_len, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
782 "style=\"stroke:black;stroke-width:2;\"/>\n",
783 axis_x_off(col), axis_y_off(0),
784 axis_x_off(col), axis_y_off(graph_height));
785 write(plot->fd, line, strlen(line));
786}
787
abf08f96 788static int svg_add_io(int fd, double row, double col, double width, double height, char *color)
ba758825
CM
789{
790 float rx = 0;
791
792 snprintf(line, line_len, "<rect x=\"%.2f\" y=\"%.2f\" width=\"%.1f\" height=\"%.1f\" "
abf08f96
CM
793 "rx=\"%.2f\" style=\"stroke:none;fill:%s;stroke-width:0\"/>\n",
794 axis_x_off_double(col), axis_y_off_double(row), width, height, rx, color);
9e066e23
CM
795 return write(fd, line, strlen(line));
796}
797
abf08f96 798int svg_io_graph_movie_array(struct plot *plot, struct plot_history *ph)
ba758825
CM
799{
800 double cell_index;
801 double movie_row;
802 double movie_col;
803 int i;
804
805 for (i = 0; i < ph->num_used; i++) {
806 cell_index = ph->history[i];
807 movie_row = floor(cell_index / graph_width);
808 movie_col = cell_index - movie_row * graph_width;
abf08f96
CM
809 svg_add_io(plot->fd, movie_row, movie_col, 4, 4, ph->color);
810 }
811 return 0;
812}
813
814static float spindle_steps = 0;
815
816void rewind_spindle_steps(int num)
817{
818 spindle_steps -= num * 0.01;
819}
820
821int svg_io_graph_movie_array_spindle(struct plot *plot, struct plot_history *ph)
822{
823 double cell_index;
824 int i;
825 int num_circles = 0;
826 double cells_per_circle;
827 double circle_num;
828 double degrees_per_cell;
829 double rot;
830 double center_x;
831 double center_y;
832 double graph_width_extra = graph_width + graph_circle_extra;
833 double graph_height_extra = graph_height + graph_circle_extra;
834 double radius;;
835
836 if (graph_width_extra > graph_height_extra)
837 graph_width_extra = graph_height_extra;
838
839 if (graph_width_extra < graph_height_extra)
840 graph_height_extra = graph_width_extra;
841
842 radius = graph_width_extra;
843
844 center_x = axis_x_off_double(graph_width_extra / 2);
845 center_y = axis_y_off_double(graph_height_extra / 2);
846
847 snprintf(line, line_len, "<g transform=\"rotate(%.4f, %.2f, %.2f)\"> "
848 "<circle cx=\"%.2f\" cy=\"%.2f\" "
849 "stroke=\"black\" stroke-width=\"6\" "
850 "r=\"%.2f\" fill=\"none\"/>\n",
851 -spindle_steps * 1.2, center_x, center_y, center_x, center_y, graph_width_extra / 2);
852 write(plot->fd, line, strlen(line));
853 snprintf(line, line_len, "<circle cx=\"%.2f\" cy=\"%.2f\" "
854 "stroke=\"none\" fill=\"red\" r=\"%.2f\"/>\n</g>\n",
855 axis_x_off_double(graph_width_extra), center_y, 4.5);
856 write(plot->fd, line, strlen(line));
857 spindle_steps += 0.01;
858
859 radius = floor(radius / 2);
860 num_circles = radius / 4 - 3;
861 cells_per_circle = ph->history_max / num_circles;
862 degrees_per_cell = 360 / cells_per_circle;
863
864 for (i = 0; i < ph->num_used; i++) {
865 cell_index = ph->history[i];
866 circle_num = floor(cell_index / cells_per_circle);
867 rot = cell_index - circle_num * cells_per_circle;
868 circle_num = num_circles - circle_num;
869 radius = circle_num * 4;
870
871 rot = rot * degrees_per_cell;
872 rot -= spindle_steps;
873 snprintf(line, line_len, "<path transform=\"rotate(%.4f, %.2f, %.2f)\" "
874 "d=\"M %.2f %.2f a %.2f %.2f 0 0 1 0 5\" "
875 "stroke=\"%s\" stroke-width=\"4\"/>\n",
876 rot, center_x, center_y,
877 axis_x_off_double(graph_width_extra / 2 + radius) + 8, center_y,
878 radius, radius, ph->color);
879
880 write(plot->fd, line, strlen(line));
ba758825
CM
881 }
882 return 0;
883}
884
885static int add_plot_history(struct plot_history *ph, double val)
886{
887 if (ph->num_used == ph->history_len) {
888 ph->history = realloc(ph->history,
889 (ph->history_len + 4096) * sizeof(double));
890 if (!ph->history) {
891 perror("Unable to allocate memory");
892 exit(1);
893 }
894 ph->history_len += 4096;
895 }
896 ph->history[ph->num_used++] = val;
897 return 0;
898}
899
900int svg_io_graph_movie(struct graph_dot_data *gdd, struct plot_history *ph, int col)
901{
902 int row = 0;
903 int arr_index;
904 unsigned char val;
905 int bit_index;
906 int bit_mod;
907 double blocks_per_row = gdd->max_offset / gdd->rows;
908 double movie_blocks_per_cell = gdd->max_offset / (graph_width * graph_height);
909 double cell_index;
910 int margin_orig = graph_inner_y_margin;
911
912 graph_inner_y_margin += 5;
abf08f96 913 ph->history_max = gdd->max_offset / movie_blocks_per_cell;
ba758825
CM
914
915 for (row = gdd->rows - 1; row >= 0; row--) {
916 bit_index = row * gdd->cols + col;
917 arr_index = bit_index / 8;
918 bit_mod = bit_index % 8;
919
920 if (arr_index < 0)
921 continue;
922 val = gdd->data[arr_index];
923 if (val & (1 << bit_mod)) {
924 /* in bytes, linear offset from the start of the drive */
925 cell_index = (double)row * blocks_per_row;
926
927 /* a cell number in the graph */
928 cell_index /= movie_blocks_per_cell;
929
930 add_plot_history(ph, cell_index);
931 }
932 }
933 graph_inner_y_margin = margin_orig;
934 return 0;
935}
936
9e066e23
CM
937int svg_io_graph(struct plot *plot, struct graph_dot_data *gdd, char *color)
938{
939 int fd = plot->fd;;
940 int col = 0;
941 int row = 0;
942 int arr_index;
943 unsigned char val;
944 int bit_index;
945 int bit_mod;
946
947 for (row = gdd->rows - 1; row >= 0; row--) {
948 for (col = 0; col < gdd->cols; col++) {
949 bit_index = row * gdd->cols + col;
950 arr_index = bit_index / 8;
951 bit_mod = bit_index % 8;
952
953 if (arr_index < 0)
954 continue;
955 val = gdd->data[arr_index];
956 if (val & (1 << bit_mod))
abf08f96 957 svg_add_io(fd, floor(row / io_graph_scale), col, 1.5, 1.5, color);
9e066e23
CM
958 }
959 }
960 return 0;
961}
962
963void svg_alloc_legend(struct plot *plot, int num_lines)
964{
965 char **lines = calloc(num_lines, sizeof(char *));
966 plot->legend_index = 0;
967 plot->legend_lines = lines;
968 plot->num_legend_lines = num_lines;
969}
970
e199d546 971void svg_free_legend(struct plot *plot)
9e066e23
CM
972{
973 int i;
e199d546
CM
974 for (i = 0; i < plot->legend_index; i++)
975 free(plot->legend_lines[i]);
976 free(plot->legend_lines);
977 plot->legend_lines = NULL;
978 plot->legend_index = 0;
979}
980
981void svg_write_legend(struct plot *plot)
982{
9e066e23
CM
983 int legend_line_x = axis_x_off(graph_width) + legend_x_off;
984 int legend_line_y = axis_y_off(graph_height) + legend_y_off;
e199d546 985 int i;
9e066e23
CM
986
987 if (plot->legend_index == 0)
988 return;
989
990 snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" "
991 "fill=\"white\" filter=\"url(#shadow)\"/>\n",
992 legend_line_x - 15,
993 legend_line_y - 12,
994 legend_width,
995 plot->legend_index * legend_font_size + legend_font_size / 2 + 12);
996
997 write(plot->fd, line, strlen(line));
998 for (i = 0; i < plot->legend_index; i++) {
999 write(plot->fd, plot->legend_lines[i],
1000 strlen(plot->legend_lines[i]));
1001 free(plot->legend_lines[i]);
1002 }
1003 free(plot->legend_lines);
e199d546 1004 plot->legend_lines = NULL;
9e066e23 1005 plot->legend_index = 0;
9e066e23
CM
1006}
1007
1008void svg_add_legend(struct plot *plot, char *text, char *extra, char *color)
1009{
1010 int legend_line_x = axis_x_off(graph_width) + legend_x_off;
1011 int legend_line_y = axis_y_off(graph_height) + legend_y_off;
1012
1013 if (!text && (!extra || strlen(extra) == 0))
1014 return;
1015
1016 legend_line_y += plot->legend_index * legend_font_size + legend_font_size / 2;
1017 snprintf(line, line_len, "<path d=\"M %d %d h 8\" stroke=\"%s\" stroke-width=\"8\" "
1018 "filter=\"url(#labelshadow)\"/> "
1019 "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
1020 "fill=\"black\" style=\"text-anchor: left\">%s%s</text>\n",
1021 legend_line_x, legend_line_y,
1022 color, legend_line_x + 13,
1023 legend_line_y + 4, font_family, legend_font_size,
1024 text, extra);
1025
1026 plot->legend_lines[plot->legend_index++] = strdup(line);
1027}
1028
1029void set_legend_width(int longest_str)
1030{
1031 if (longest_str)
1032 legend_width = longest_str * (legend_font_size * 3 / 4) + 25;
1033 else
1034 legend_width = 0;
1035}
1036
1037void set_rolling_avg(int rolling)
1038{
1039 rolling_avg_secs = rolling;
1040}
1041
ba758825
CM
1042void set_io_graph_scale(int scale)
1043{
1044 io_graph_scale = scale;
1045}
1046
1047void set_graph_size(int width, int height)
1048{
1049 graph_width = width;
1050 graph_height = height;
1051}
1052
1053void get_graph_size(int *width, int *height)
1054{
1055 *width = graph_width;
1056 *height = graph_height;
1057}
1058
1059void set_graph_height(int h)
1060{
1061 graph_height = h;
1062}
1063void set_graph_width(int w)
1064{
1065 graph_width = w;
1066}