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