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