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