#include "plot.h"
static int io_graph_scale = 8;
-static int graph_width = 600;
-static int graph_height = 150;
+static int graph_width = 700;
+static int graph_height = 250;
+static int graph_circle_extra = 30;
static int graph_inner_x_margin = 2;
static int graph_inner_y_margin = 2;
static int graph_tick_len = 5;
static int line_len = 1024;
static char line[1024];
-struct graph_line_data *alloc_line_data(int seconds, int stop_seconds)
+static int final_height = 0;
+static int final_width = 0;
+
+struct graph_line_data *alloc_line_data(int min_seconds, int max_seconds, int stop_seconds)
{
int size = sizeof(struct graph_line_data) + (stop_seconds + 1) * sizeof(struct graph_line_pair);
struct graph_line_data *gld;
fprintf(stderr, "Unable to allocate memory for graph data\n");
exit(1);
}
- gld->seconds = seconds;
+ gld->min_seconds = min_seconds;
+ gld->max_seconds = max_seconds;
gld->stop_seconds = stop_seconds;
return gld;
}
free(gld);
}
-struct graph_dot_data *alloc_dot_data(int seconds, u64 max_offset, int stop_seconds)
+struct graph_dot_data *alloc_dot_data(int min_seconds, int max_seconds, u64 min_offset, u64 max_offset, int stop_seconds)
{
int size;
int arr_size;
fprintf(stderr, "Unable to allocate memory for graph data\n");
exit(1);
}
- gdd->seconds = seconds;
+ gdd->min_seconds = min_seconds;
+ gdd->max_seconds = max_seconds;
gdd->stop_seconds = stop_seconds;
gdd->rows = rows;
gdd->cols = cols;
+ gdd->min_offset = min_offset;
gdd->max_offset = max_offset;
return gdd;
}
void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, double bytes, double time)
{
- double bytes_per_row = (double)gdd->max_offset / gdd->rows;
-
- double secs_per_col = (double)gdd->seconds / gdd->cols;
+ double bytes_per_row = (double)(gdd->max_offset - gdd->min_offset + 1) / gdd->rows;
+ double secs_per_col = (double)(gdd->max_seconds - gdd->min_seconds) / gdd->cols;
double col;
double row;
int col_int;
int bit_mod;
double mod = bytes_per_row;
- if (offset > gdd->max_offset)
+ if (offset > gdd->max_offset || offset < gdd->min_offset)
return;
gdd->total_ios++;
time = time / 1000000000.0;
while (bytes > 0) {
- row = (double)offset / bytes_per_row;
- col = time / secs_per_col;
+ row = (double)(offset - gdd->min_offset) / bytes_per_row;
+ col = (time - gdd->min_seconds) / secs_per_col;
col_int = floor(col);
row_int = floor(row);
void write_svg_header(int fd)
{
- char *header = "<svg xmlns=\"http://www.w3.org/2000/svg\"\nxmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
+ char *spaces = " \n";
+ char *header = "<svg xmlns=\"http://www.w3.org/2000/svg\">\n";
char *filter1 ="<filter id=\"shadow\">\n "
"<feOffset result=\"offOut\" in=\"SourceAlpha\" dx=\"4\" dy=\"4\" />\n "
"<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"2\" />\n "
"</filter>\n";
char *defs_start = "<defs>\n";
char *defs_close = "</defs>\n";
+ final_width = 0;
+ final_height = 0;
write(fd, header, strlen(header));
+ /* write a bunch of spaces so we can stuff in the width and height later */
+ write(fd, spaces, strlen(spaces));
+ write(fd, spaces, strlen(spaces));
+ write(fd, spaces, strlen(spaces));
+
write(fd, defs_start, strlen(defs_start));
write(fd, filter1, strlen(filter1));
write(fd, filter2, strlen(filter2));
return (int)axis_x_off_double(x);
}
-
/*
* this draws a backing rectangle for the plot and it
* also creates a new svg element so our offsets can
int len;
int fd = plot->fd;
int bump_height = tick_font_size * 3 + axis_label_font_size;
+ int local_legend_width = legend_width;
+ if (plot->no_legend)
+ local_legend_width = 0;
- plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + legend_width;
+ plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + local_legend_width;
plot->total_height = axis_y() + tick_label_pad + tick_font_size;
if (plot->add_xlabel)
plot->total_height += bump_height;
/* backing rect */
- snprintf(line, line_len, "<rect x=\"0\" y=\"%d\" width=\"%d\" "
+ snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
"height=\"%d\" fill=\"white\" stroke=\"none\"/>",
+ plot->start_x_offset,
plot->start_y_offset, plot->total_width + 40,
plot->total_height + 20);
len = strlen(line);
write(fd, line, len);
- snprintf(line, line_len, "<rect x=\"15\" y=\"%d\" width=\"%d\" "
+
+ snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
"filter=\"url(#shadow)\" "
"height=\"%d\" fill=\"white\" stroke=\"none\"/>",
+ plot->start_x_offset + 15,
plot->start_y_offset, plot->total_width, plot->total_height);
len = strlen(line);
write(fd, line, len);
plot->total_height += 20;
+ plot->total_width += 20;
+ if (plot->total_height + plot->start_y_offset > final_height)
+ final_height = plot->total_height + plot->start_y_offset;
+ if (plot->start_x_offset + plot->total_width + 40 > final_width)
+ final_width = plot->start_x_offset + plot->total_width + 40;
/* create an svg object for all our coords to be relative against */
- snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\" style=\"enable-background:new\">\n", plot->start_x_offset, plot->start_y_offset);
+ snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\">\n", plot->start_x_offset, plot->start_y_offset);
write(fd, line, strlen(line));
snprintf(line, 1024, "<path d=\"M%d %d h %d V %d H %d Z\" stroke=\"black\" stroke-width=\"2\" fill=\"none\"/>\n",
}
}
+/*
+ * this draws a backing rectangle for the plot and it
+ * also creates a new svg element so our offsets can
+ * be relative to this one plot.
+ */
+void setup_axis_spindle(struct plot *plot)
+{
+ int len;
+ int fd = plot->fd;
+ int bump_height = tick_font_size * 3 + axis_label_font_size;
+
+ legend_x_off = -60;
+
+ plot->total_width = axis_x_off(graph_width) + legend_width;
+ plot->total_height = axis_y() + tick_label_pad + tick_font_size;
+
+ if (plot->add_xlabel)
+ plot->total_height += bump_height;
+
+ /* backing rect */
+ snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
+ "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
+ plot->start_x_offset,
+ plot->start_y_offset, plot->total_width + 10,
+ plot->total_height + 20);
+ len = strlen(line);
+ write(fd, line, len);
+
+ snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
+ "filter=\"url(#shadow)\" "
+ "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
+ plot->start_x_offset + 15,
+ plot->start_y_offset, plot->total_width - 30,
+ plot->total_height);
+ len = strlen(line);
+ write(fd, line, len);
+ plot->total_height += 20;
+
+ if (plot->total_height + plot->start_y_offset > final_height)
+ final_height = plot->total_height + plot->start_y_offset;
+ if (plot->start_x_offset + plot->total_width + 40 > final_width)
+ final_width = plot->start_x_offset + plot->total_width + 40;
+
+ /* create an svg object for all our coords to be relative against */
+ snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\">\n", plot->start_x_offset, plot->start_y_offset);
+ write(fd, line, strlen(line));
+
+}
+
/* draw a plot title. This should be done only once,
* and it bumps the plot width/height numbers by
* what it draws.
if (!tick_only) {
snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
"fill=\"black\" style=\"text-anchor: %s\">%d</text>\n",
- tick_x, text_y, font_family, tick_font_size, anchor, step * i);
+ tick_x, text_y, font_family, tick_font_size, anchor,
+ first + step * i);
write(plot->fd, line, strlen(line));
}
tick_x += pixels_per_tick;
"fill=\"black\" style=\"text-anchor: %s\">%d%s</text>\n",
text_x,
axis_y_off(tick_y - tick_font_size / 2),
- font_family, tick_font_size, anchor, step * i, units);
+ font_family, tick_font_size, anchor, first + step * i, units);
write(plot->fd, line, strlen(line));
tick_y += pixels_per_tick;
}
int close_plot(struct plot *plot)
{
close_svg(plot->fd);
- plot->start_y_offset += plot->total_height;
- plot->add_xlabel = 0;
+ if (plot->direction == PLOT_DOWN)
+ plot->start_y_offset += plot->total_height;
+ else if (plot->direction == PLOT_ACROSS)
+ plot->start_x_offset += plot->total_width;
return 0;
}
return plot;
}
+int close_plot_file(struct plot *plot)
+{
+ int ret;
+ ret = lseek(plot->fd, 0, SEEK_SET);
+ if (ret == (off_t)-1) {
+ perror("seek");
+ exit(1);
+ }
+ final_width = ((final_width + 1) / 2) * 2;
+ final_height = ((final_height + 1) / 2) * 2;
+ snprintf(line, line_len, "<svg xmlns=\"http://www.w3.org/2000/svg\" "
+ "width=\"%d\" height=\"%d\">\n",
+ final_width, final_height);
+ write(plot->fd, line, strlen(line));
+ snprintf(line, line_len, "<rect x=\"0\" y=\"0\" width=\"%d\" "
+ "height=\"%d\" fill=\"white\"/>\n", final_width, final_height);
+ write(plot->fd, line, strlen(line));
+ close(plot->fd);
+ plot->fd = 0;
+ return 0;
+}
+
void set_plot_output(struct plot *plot, char *filename)
{
int fd;
if (plot->fd)
- close(plot->fd);
+ close_plot_file(plot);
fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0) {
fprintf(stderr, "Unable to open output file %s err %s\n", filename, strerror(errno));
*max /= div;
}
-int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color)
+int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color, int thresh1, int thresh2)
{
int i;
double val;
+ double avg;
int rolling;
int fd = plot->fd;
char *start = "<path d=\"";
double yscale = ((double)gld->max) / graph_height;
- double xscale = (double)(gld->seconds - 1) / graph_width;
+ double xscale = (double)(gld->max_seconds - gld->min_seconds - 1) / graph_width;
char c = 'M';
double x;
+ int printed_header = 0;
+ int printed_lines = 0;
- if (rolling_avg_secs)
+ if (thresh1 && thresh2)
+ rolling = 0;
+ else if (rolling_avg_secs)
rolling = rolling_avg_secs;
else
- rolling = gld->stop_seconds / 25;
+ rolling = (gld->stop_seconds - gld->min_seconds) / 25;
- write(fd, start, strlen(start));
- for (i = 0; i < gld->stop_seconds; i++) {
- val = rolling_avg(gld->data, i, rolling);
- val = val / yscale;
- x = (double)i / xscale;
- snprintf(line, line_len, "%c %d %d ", c, axis_x_off(x), axis_y_off(val));
+ for (i = gld->min_seconds; i < gld->stop_seconds; i++) {
+ avg = rolling_avg(gld->data, i, rolling);
+ if (yscale == 0)
+ val = 0;
+ else
+ val = avg / yscale;
+
+ if (val > graph_height)
+ val = graph_height;
+ if (val < 0)
+ val = 0;
+
+ x = (double)(i - gld->min_seconds) / xscale;
+ if (!thresh1 && !thresh2) {
+
+ if (!printed_header) {
+ write(fd, start, strlen(start));
+ printed_header = 1;
+ }
+
+ /* in full line mode, everything in the graph is connected */
+ snprintf(line, line_len, "%c %d %d ", c, axis_x_off(x), axis_y_off(val));
+ c = 'L';
+ write(fd, line, strlen(line));
+ printed_lines = 1;
+ } else if (avg > thresh1 || avg > thresh2) {
+ int len = 10;
+ if (!printed_header) {
+ write(fd, start, strlen(start));
+ printed_header = 1;
+ }
+
+ /* otherwise, we just print a bar up there to show this one data point */
+ if (i >= gld->stop_seconds - 2)
+ len = -10;
+
+ /*
+ * we don't use the rolling averages here to show high
+ * points in the data
+ */
+ snprintf(line, line_len, "M %d %d h %d ", axis_x_off(x),
+ axis_y_off(val), len);
+ write(fd, line, strlen(line));
+ printed_lines = 1;
+ }
- c = 'L';
+ }
+ if (printed_lines) {
+ snprintf(line, line_len, "\" fill=\"none\" stroke=\"%s\" stroke-width=\"2\"/>\n", color);
write(fd, line, strlen(line));
}
- snprintf(line, line_len, "\" fill=\"none\" stroke=\"%s\" stroke-width=\"2\"/>\n", color);
- write(fd, line, strlen(line));
+ if (plot->timeline)
+ svg_write_time_line(plot, plot->timeline);
return 0;
}
write(plot->fd, line, strlen(line));
}
-static int svg_add_io(int fd, double row, double col, double width, double height, char *color, float alpha)
+static int svg_add_io(int fd, double row, double col, double width, double height, char *color)
{
float rx = 0;
snprintf(line, line_len, "<rect x=\"%.2f\" y=\"%.2f\" width=\"%.1f\" height=\"%.1f\" "
- "rx=\"%.2f\" style=\"stroke:none;fill:%s;stroke-width:0;opacity:%.2f\"/>\n",
- axis_x_off_double(col), axis_y_off_double(row), width, height, rx, color, alpha);
+ "rx=\"%.2f\" style=\"stroke:none;fill:%s;stroke-width:0\"/>\n",
+ axis_x_off_double(col), axis_y_off_double(row), width, height, rx, color);
return write(fd, line, strlen(line));
}
-int svg_io_graph_movie_array(struct plot *plot, struct plot_history *ph, float alpha)
+int svg_io_graph_movie_array(struct plot *plot, struct plot_history *ph)
{
double cell_index;
double movie_row;
cell_index = ph->history[i];
movie_row = floor(cell_index / graph_width);
movie_col = cell_index - movie_row * graph_width;
- svg_add_io(plot->fd, movie_row, movie_col, 4, 4, ph->color, alpha);
+ svg_add_io(plot->fd, movie_row, movie_col, 4, 4, ph->color);
+ }
+ return 0;
+}
+
+static float spindle_steps = 0;
+
+void rewind_spindle_steps(int num)
+{
+ spindle_steps -= num * 0.01;
+}
+
+int svg_io_graph_movie_array_spindle(struct plot *plot, struct plot_history *ph)
+{
+ double cell_index;
+ int i;
+ int num_circles = 0;
+ double cells_per_circle;
+ double circle_num;
+ double degrees_per_cell;
+ double rot;
+ double center_x;
+ double center_y;
+ double graph_width_extra = graph_width + graph_circle_extra;
+ double graph_height_extra = graph_height + graph_circle_extra;
+ double radius;;
+
+ if (graph_width_extra > graph_height_extra)
+ graph_width_extra = graph_height_extra;
+
+ if (graph_width_extra < graph_height_extra)
+ graph_height_extra = graph_width_extra;
+
+ radius = graph_width_extra;
+
+ center_x = axis_x_off_double(graph_width_extra / 2);
+ center_y = axis_y_off_double(graph_height_extra / 2);
+
+ snprintf(line, line_len, "<g transform=\"rotate(%.4f, %.2f, %.2f)\"> "
+ "<circle cx=\"%.2f\" cy=\"%.2f\" "
+ "stroke=\"black\" stroke-width=\"6\" "
+ "r=\"%.2f\" fill=\"none\"/>\n",
+ -spindle_steps * 1.2, center_x, center_y, center_x, center_y, graph_width_extra / 2);
+ write(plot->fd, line, strlen(line));
+ snprintf(line, line_len, "<circle cx=\"%.2f\" cy=\"%.2f\" "
+ "stroke=\"none\" fill=\"red\" r=\"%.2f\"/>\n</g>\n",
+ axis_x_off_double(graph_width_extra), center_y, 4.5);
+ write(plot->fd, line, strlen(line));
+ spindle_steps += 0.01;
+
+ radius = floor(radius / 2);
+ num_circles = radius / 4 - 3;
+ cells_per_circle = ph->history_max / num_circles;
+ degrees_per_cell = 360 / cells_per_circle;
+
+ for (i = 0; i < ph->num_used; i++) {
+ cell_index = ph->history[i];
+ circle_num = floor(cell_index / cells_per_circle);
+ rot = cell_index - circle_num * cells_per_circle;
+ circle_num = num_circles - circle_num;
+ radius = circle_num * 4;
+
+ rot = rot * degrees_per_cell;
+ rot -= spindle_steps;
+ snprintf(line, line_len, "<path transform=\"rotate(%.4f, %.2f, %.2f)\" "
+ "d=\"M %.2f %.2f a %.2f %.2f 0 0 1 0 5\" "
+ "stroke=\"%s\" stroke-width=\"4\"/>\n",
+ rot, center_x, center_y,
+ axis_x_off_double(graph_width_extra / 2 + radius) + 8, center_y,
+ radius, radius, ph->color);
+
+ write(plot->fd, line, strlen(line));
}
return 0;
}
unsigned char val;
int bit_index;
int bit_mod;
- double blocks_per_row = gdd->max_offset / gdd->rows;
- double movie_blocks_per_cell = gdd->max_offset / (graph_width * graph_height);
+ double blocks_per_row = (gdd->max_offset - gdd->min_offset + 1) / gdd->rows;
+ double movie_blocks_per_cell = (gdd->max_offset - gdd->min_offset + 1) / (graph_width * graph_height);
double cell_index;
int margin_orig = graph_inner_y_margin;
graph_inner_y_margin += 5;
+ ph->history_max = (gdd->max_offset - gdd->min_offset + 1) / movie_blocks_per_cell;
for (row = gdd->rows - 1; row >= 0; row--) {
bit_index = row * gdd->cols + col;
continue;
val = gdd->data[arr_index];
if (val & (1 << bit_mod))
- svg_add_io(fd, floor(row / io_graph_scale), col, 1.5, 1.5, color, 1.0);
+ svg_add_io(fd, floor(row / io_graph_scale), col, 1.5, 1.5, color);
}
}
return 0;