aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Mason <chris.mason@fusionio.com>2012-08-17 12:18:28 -0400
committerChris Mason <chris.mason@fusionio.com>2012-08-20 06:23:42 -0400
commit4b9e525d6b2d5cbeb3141b2a80668537063c6ad7 (patch)
treeba93251214e5145f21b7ac0c8b1bd6223a059f64
parenta793c02a3c57bdad9c018ab3bd9fad2b14e31769 (diff)
downloadiowatcher-4b9e525d6b2d5cbeb3141b2a80668537063c6ad7.tar.gz
Add movie support
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
-rw-r--r--main.c434
-rw-r--r--plot.c171
-rw-r--r--plot.h25
3 files changed, 504 insertions, 126 deletions
diff --git a/main.c b/main.c
index ebc41cb..c905dd8 100644
--- a/main.c
+++ b/main.c
@@ -40,9 +40,15 @@
LIST_HEAD(all_traces);
+static char line[1024];
+static int line_len = 1024;
static int found_mpstat = 0;
static int cpu_color_index = 0;
static int color_index = 0;
+static int make_movie = 0;
+static int opt_graph_width = 0;
+static int opt_graph_height = 0;
+
char *colors[] = {
"blue", "darkgreen",
"red", "aqua",
@@ -396,124 +402,108 @@ static void set_blktrace_outfile(char *arg)
}
-char *option_string = "hT:t:o:l:r:O:N:d:p:";
-static struct option long_options[] = {
- {"title", required_argument, 0, 'T'},
- {"trace", required_argument, 0, 't'},
- {"output", required_argument, 0, 'o'},
- {"label", required_argument, 0, 'l'},
- {"rolling", required_argument, 0, 'r'},
- {"no-graph", required_argument, 0, 'N'},
- {"only-graph", required_argument, 0, 'O'},
- {"device", required_argument, 0, 'd'},
- {"prog", required_argument, 0, 'p'},
- {"help", required_argument, 0, 'h'},
- {0, 0, 0, 0}
-};
-
-static void print_usage(void)
+static void compare_max_tf(struct trace_file *tf, int *seconds, u64 *max_offset)
{
- fprintf(stderr, "iowatcher usage:\n"
- "\t-d (--device): device for blktrace to trace\n"
- "\t-t (--trace): trace file name (more than one allowed)\n"
- "\t-l (--label): trace label in the graph\n"
- "\t-o (--output): output file name (SVG only)\n"
- "\t-p (--prog): program to run while blktrace is run\n"
- "\t-r (--rolling): number of seconds in the rolling averge\n"
- "\t-T (--title): graph title\n"
- "\t-N (--no-graph): skip a single graph (io, tput, latency, queue_depth, iops)\n"
- "\t-O (--only-graph): add a single graph (io, tput, latency, queue_depth, iops)\n"
- );
- exit(1);
+ if (tf->seconds > *seconds)
+ *seconds = tf->seconds;
+ if (tf->max_offset > *max_offset)
+ *max_offset = tf->max_offset;
}
-static int parse_options(int ac, char **av)
+static void set_all_max_tf(int seconds, u64 max_offset)
{
- int c;
- int disabled = 0;
+ struct trace_file *tf;
- while (1) {
- // int this_option_optind = optind ? optind : 1;
- int option_index = 0;
+ list_for_each_entry(tf, &all_traces, list) {
+ tf->seconds = seconds;
+ tf->max_offset = max_offset;
+ }
+}
- c = getopt_long(ac, av, option_string,
- long_options, &option_index);
+static char *create_movie_temp_dir(void)
+{
+ char *ret;
+ char *pattern = strdup("btrfs-movie-XXXXXX");;
- if (c == -1)
- break;
+ ret = mkdtemp(pattern);
+ if (!ret) {
+ perror("Unable to create temp directory for movie files");
+ exit(1);
+ }
+ return ret;
+}
- switch(c) {
- case 'h':
- print_usage();
- break;
- case 'T':
- graph_title = strdup(optarg);
- break;
- case 't':
- add_trace_file(optarg);
- set_blktrace_outfile(optarg);
- break;
- case 'o':
- output_filename = strdup(optarg);
- break;
- case 'l':
- set_trace_label(optarg);
- break;
- case 'r':
- set_rolling_avg(atoi(optarg));
- break;
- case 'O':
- if (!disabled) {
- disable_all_graphs();
- disabled = 1;
- }
- enable_one_graph(optarg);
- break;
- case 'N':
- disable_one_graph(optarg);
- break;
- case 'd':
- blktrace_device = strdup(optarg);
- break;
- case 'p':
- program_to_run = strdup(optarg);
- break;
- case '?':
- print_usage();
- break;
- default:
- break;
- }
+static struct plot_history *alloc_plot_history(char *color)
+{
+ struct plot_history *ph = calloc(1, sizeof(struct plot_history));
+
+ if (!ph) {
+ perror("memory allocation failed");
+ exit(1);
}
- return 0;
+ ph->history = calloc(4096, sizeof(double));
+ if (!ph->history) {
+ perror("memory allocation failed");
+ exit(1);
+ }
+ ph->history_len = 4096;
+ ph->color = color;
+ return ph;
}
-static void compare_max_tf(struct trace_file *tf, int *seconds, u64 *max_offset)
+LIST_HEAD(movie_history_writes);
+LIST_HEAD(movie_history_reads);
+int num_histories = 0;
+
+static void add_history(struct plot_history *ph, struct list_head *list)
{
- if (tf->seconds > *seconds)
- *seconds = tf->seconds;
- if (tf->max_offset > *max_offset)
- *max_offset = tf->max_offset;
+ struct plot_history *entry;
+
+ list_add_tail(&ph->list, list);
+ num_histories++;
+
+ if (num_histories > 12) {
+ num_histories--;
+ entry = list_entry(list->next, struct plot_history, list);
+ list_del(&entry->list);
+ free(entry->history);
+ free(entry);
+ }
}
-static void set_all_max_tf(int seconds, u64 max_offset)
+static void plot_movie_history(struct plot *plot, struct list_head *list)
{
- struct trace_file *tf;
+ float alpha = 0.1;
+ struct plot_history *ph;
+
+ list_for_each_entry(ph, list, list) {
+ if (ph->list.next == list)
+ alpha = 1;
+ svg_io_graph_movie_array(plot, ph, 1);
+ alpha += 0.2;
+ if (alpha > 1)
+ alpha = 0.8;
+ }
+}
- list_for_each_entry(tf, &all_traces, list) {
- tf->seconds = seconds;
- tf->max_offset = max_offset;
+static void free_all_plot_history(struct list_head *head)
+{
+ struct plot_history *entry;
+ while (!list_empty(head)) {
+ entry = list_entry(head->next, struct plot_history, list);
+ list_del(&entry->list);
+ free(entry->history);
+ free(entry);
}
}
-static void plot_io(struct plot *plot, int seconds, u64 max_offset)
+static void __plot_io(struct plot *plot, int seconds, u64 max_offset)
{
struct trace_file *tf;
if (active_graphs[IO_GRAPH_INDEX] == 0)
return;
- plot->add_xlabel = last_active_graph == IO_GRAPH_INDEX;
setup_axis(plot);
svg_alloc_legend(plot, num_traces * 2);
@@ -540,10 +530,17 @@ static void plot_io(struct plot *plot, int seconds, u64 max_offset)
if (plot->add_xlabel)
set_xlabel(plot, "Time (seconds)");
svg_write_legend(plot);
+}
+
+static void plot_io(struct plot *plot, int seconds, u64 max_offset)
+{
+ plot->add_xlabel = last_active_graph == IO_GRAPH_INDEX;
+
+ __plot_io(plot, seconds, max_offset);
close_plot(plot);
}
-static void plot_tput(struct plot *plot, int seconds)
+static void __plot_tput(struct plot *plot, int seconds)
{
struct trace_file *tf;
char *units;
@@ -562,7 +559,6 @@ static void plot_tput(struct plot *plot, int seconds)
list_for_each_entry(tf, &all_traces, list)
tf->tput_gld->max = max;
- plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
setup_axis(plot);
set_plot_label(plot, "Throughput");
@@ -584,9 +580,123 @@ static void plot_tput(struct plot *plot, int seconds)
set_xlabel(plot, "Time (seconds)");
if (num_traces > 1)
svg_write_legend(plot);
+}
+
+static void plot_tput(struct plot *plot, int seconds)
+{
+ plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
+ __plot_tput(plot, seconds);
close_plot(plot);
}
+static void convert_movie_files(char *movie_dir)
+{
+ fprintf(stderr, "Converting svg files in %s\n", movie_dir);
+ snprintf(line, line_len, "find %s -name \\*.svg | xargs -I{} -n 1 -P 8 rsvg-convert -o {}.png {}",
+ movie_dir);
+ system(line);
+}
+
+static void mencode_movie(char *movie_dir)
+{
+ fprintf(stderr, "Creating movie %s\n", movie_dir);
+ snprintf(line, line_len, "mencoder mf://%s/*.png -mf type=png:fps=16 -of lavf "
+ "-ovc x264 -oac copy -o %s",
+ movie_dir, output_filename);
+ system(line);
+}
+
+static void cleanup_movie(char *movie_dir)
+{
+ fprintf(stderr, "Removing movie dir %s\n", movie_dir);
+ snprintf(line, line_len, "rm %s/*", movie_dir);
+ system(line);
+
+ snprintf(line, line_len, "rmdir %s", movie_dir);
+ system(line);
+}
+
+static void plot_io_movie(struct plot *plot)
+{
+ struct trace_file *tf;
+ char *movie_dir = create_movie_temp_dir();
+ int i;
+ struct plot_history *read_history;
+ struct plot_history *write_history;
+ int batch_i;
+ int movie_len = 30;
+ int movie_frames_per_sec = 16;
+ int total_frames = movie_len * movie_frames_per_sec;
+ int rows, cols;
+ int batch_count;
+
+ get_graph_size(&cols, &rows);
+ batch_count = cols / total_frames;
+
+ if (batch_count == 0)
+ batch_count = 1;
+
+ list_for_each_entry(tf, &all_traces, list) {
+ char *label = tf->label;
+ if (!label)
+ label = "";
+
+ i = 0;
+ while (i < cols) {
+ snprintf(line, line_len, "%s/%010d-%s.svg", movie_dir, i, output_filename);
+ set_plot_output(plot, line);
+
+ set_plot_title(plot, graph_title);
+ setup_axis(plot);
+ svg_alloc_legend(plot, num_traces * 2);
+
+ read_history = alloc_plot_history(tf->read_color);
+ write_history = alloc_plot_history(tf->write_color);
+ read_history->col = i;
+ write_history->col = i;
+
+ if (tf->gdd_reads->total_ios)
+ svg_add_legend(plot, label, " Reads", tf->read_color);
+ if (tf->gdd_writes->total_ios)
+ svg_add_legend(plot, label, " Writes", tf->write_color);
+
+ batch_i = 0;
+ while (i < cols && batch_i < batch_count) {
+ /* print just this column */
+ svg_io_graph_movie(tf->gdd_reads, read_history, i);
+
+ svg_io_graph_movie(tf->gdd_writes, write_history, i);
+ i++;
+ batch_i++;
+ }
+
+ add_history(read_history, &movie_history_reads);
+ add_history(write_history, &movie_history_writes);
+
+ plot_movie_history(plot, &movie_history_reads);
+ plot_movie_history(plot, &movie_history_writes);
+
+ svg_write_legend(plot);
+ close_plot(plot);
+
+ set_graph_size(cols, rows / 3);
+ plot->add_xlabel = 1;
+ __plot_tput(plot, tf->gdd_reads->seconds);
+ svg_write_time_line(plot, i);
+ close_plot(plot);
+ set_graph_size(cols, rows);
+
+ close_plot(plot);
+ }
+ free_all_plot_history(&movie_history_reads);
+ free_all_plot_history(&movie_history_writes);
+ }
+ convert_movie_files(movie_dir);
+ mencode_movie(movie_dir);
+ cleanup_movie(movie_dir);
+ free(movie_dir);
+}
+
static void plot_cpu(struct plot *plot, int seconds, char *label,
int active_index, int gld_index)
{
@@ -794,12 +904,120 @@ static void plot_iops(struct plot *plot, int seconds)
close_plot(plot);
}
+enum {
+ HELP_LONG_OPT = 1,
+};
+
+char *option_string = "T:t:o:l:r:O:N:d:p:mh:w:";
+static struct option long_options[] = {
+ {"title", required_argument, 0, 'T'},
+ {"trace", required_argument, 0, 't'},
+ {"output", required_argument, 0, 'o'},
+ {"label", required_argument, 0, 'l'},
+ {"rolling", required_argument, 0, 'r'},
+ {"no-graph", required_argument, 0, 'N'},
+ {"only-graph", required_argument, 0, 'O'},
+ {"device", required_argument, 0, 'd'},
+ {"prog", required_argument, 0, 'p'},
+ {"movie", no_argument, 0, 'm'},
+ {"width", required_argument, 0, 'w'},
+ {"height", required_argument, 0, 'h'},
+ {"help", required_argument, 0, HELP_LONG_OPT},
+ {0, 0, 0, 0}
+};
+
+static void print_usage(void)
+{
+ fprintf(stderr, "iowatcher usage:\n"
+ "\t-d (--device): device for blktrace to trace\n"
+ "\t-t (--trace): trace file name (more than one allowed)\n"
+ "\t-l (--label): trace label in the graph\n"
+ "\t-o (--output): output file name (SVG only)\n"
+ "\t-p (--prog): program to run while blktrace is run\n"
+ "\t-p (--movie): create IO animations\n"
+ "\t-r (--rolling): number of seconds in the rolling averge\n"
+ "\t-T (--title): graph title\n"
+ "\t-N (--no-graph): skip a single graph (io, tput, latency, queue_depth, iops)\n"
+ "\t-h (--height): set the height of each graph\n"
+ "\t-w (--width): set the width of each graph\n"
+ );
+ exit(1);
+}
+
+static int parse_options(int ac, char **av)
+{
+ int c;
+ int disabled = 0;
+
+ while (1) {
+ // int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+
+ c = getopt_long(ac, av, option_string,
+ long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch(c) {
+ case 'T':
+ graph_title = strdup(optarg);
+ break;
+ case 't':
+ add_trace_file(optarg);
+ set_blktrace_outfile(optarg);
+ break;
+ case 'o':
+ output_filename = strdup(optarg);
+ break;
+ case 'l':
+ set_trace_label(optarg);
+ break;
+ case 'r':
+ set_rolling_avg(atoi(optarg));
+ break;
+ case 'O':
+ if (!disabled) {
+ disable_all_graphs();
+ disabled = 1;
+ }
+ enable_one_graph(optarg);
+ break;
+ case 'N':
+ disable_one_graph(optarg);
+ break;
+ case 'd':
+ blktrace_device = strdup(optarg);
+ break;
+ case 'p':
+ program_to_run = strdup(optarg);
+ break;
+ case 'm':
+ make_movie = 1;
+ break;
+ case 'h':
+ opt_graph_height = atoi(optarg);
+ break;
+ case 'w':
+ opt_graph_width = atoi(optarg);
+ break;
+ case '?':
+ case HELP_LONG_OPT:
+ print_usage();
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+
int main(int ac, char **av)
{
struct plot *plot;
int seconds = 0;
u64 max_offset = 0;
- int fd;
struct trace_file *tf;
int ret;
@@ -810,6 +1028,15 @@ int main(int ac, char **av)
parse_options(ac, av);
last_active_graph = last_graph();
+ if (make_movie) {
+ set_io_graph_scale(256);
+ set_graph_size(700, 250);
+ }
+ if (opt_graph_height)
+ set_graph_height(opt_graph_height);
+
+ if (opt_graph_width)
+ set_graph_width(opt_graph_height);
if (list_empty(&all_traces)) {
fprintf(stderr, "No traces found, exiting\n");
@@ -857,15 +1084,14 @@ int main(int ac, char **av)
/* run through all the traces and read their events */
read_trace_events();
- fd = open(output_filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (fd < 0) {
- fprintf(stderr, "Unable to open output file %s %s\n",
- output_filename, strerror(errno));
- exit(1);
+ plot = alloc_plot();
+
+ if (make_movie) {
+ plot_io_movie(plot);
+ exit(0);
}
- write_svg_header(fd);
- plot = alloc_plot(fd);
+ set_plot_output(plot, output_filename);
if (active_graphs[IO_GRAPH_INDEX] || found_mpstat)
set_legend_width(longest_label + strlen("writes"));
@@ -874,7 +1100,6 @@ int main(int ac, char **av)
else
set_legend_width(0);
- set_plot_title(plot, graph_title);
plot_io(plot, seconds, max_offset);
plot_tput(plot, seconds);
@@ -895,6 +1120,5 @@ int main(int ac, char **av)
/* once for all */
close_plot(plot);
- close(fd);
return 0;
}
diff --git a/plot.c b/plot.c
index da64d41..ce8fab8 100644
--- a/plot.c
+++ b/plot.c
@@ -33,9 +33,11 @@
#include "plot.h"
+static int io_graph_scale = 8;
static int graph_width = 600;
static int graph_height = 150;
-static int graph_inner_margin = 2;
+static int graph_inner_x_margin = 2;
+static int graph_inner_y_margin = 2;
static int graph_tick_len = 5;
static int graph_left_pad = 120;
static int tick_label_pad = 16;
@@ -88,7 +90,7 @@ struct graph_dot_data *alloc_dot_data(int seconds, u64 max_offset, int stop_seco
{
int size;
int arr_size;
- int rows = graph_height;
+ int rows = graph_height * io_graph_scale;
int cols = graph_width;
struct graph_dot_data *gdd;
@@ -118,9 +120,10 @@ void free_dot_data(struct graph_dot_data *gdd)
free(gdd);
}
-void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, int bytes, double time)
+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 col;
double row;
@@ -129,20 +132,19 @@ void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, int bytes, double time)
int bit_index;
int arr_index;
int bit_mod;
- int mod = (int)bytes_per_row;
+ double mod = bytes_per_row;
if (offset > gdd->max_offset)
return;
gdd->total_ios++;
+ time = time / 1000000000.0;
while (bytes > 0) {
- time = time / 1000000000.0;
row = (double)offset / bytes_per_row;
col = time / secs_per_col;
col_int = floor(col);
row_int = floor(row);
-
bit_index = row_int * gdd->cols + col_int;
arr_index = bit_index / 8;
bit_mod = bit_index % 8;
@@ -267,15 +269,20 @@ void write_drop_shadow(struct plot *plot)
/* svg y offset for the traditional 0,0 (bottom left corner) of the plot */
static int axis_y(void)
{
- return plot_label_height + graph_height + graph_inner_margin;
+ return plot_label_height + graph_height + graph_inner_y_margin;
}
/* this gives you the correct pixel for a given offset from the bottom left y axis */
-static int axis_y_off(int y)
+static double axis_y_off_double(double y)
{
return plot_label_height + graph_height - y;
}
+static int axis_y_off(int y)
+{
+ return axis_y_off_double(y);
+}
+
/* svg x axis offset from 0 */
static int axis_x(void)
{
@@ -283,11 +290,17 @@ static int axis_x(void)
}
/* the correct pixel for a given X offset */
+static double axis_x_off_double(double x)
+{
+ return graph_left_pad + graph_inner_x_margin + x;
+}
+
static int axis_x_off(int x)
{
- return graph_left_pad + graph_inner_margin + x;
+ 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
@@ -324,12 +337,12 @@ void setup_axis(struct plot *plot)
/* 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);
+ snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\" style=\"enable-background:new\">\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",
axis_x(), axis_y(),
- graph_width + graph_inner_margin * 2, axis_y_off(graph_height) - graph_inner_margin,
+ graph_width + graph_inner_x_margin * 2, axis_y_off(graph_height) - graph_inner_y_margin,
axis_x());
len = strlen(line);
ret = write(fd, line, len);
@@ -378,7 +391,7 @@ void set_xticks(struct plot *plot, int num_ticks, int first, int last)
int pixels_per_tick = graph_width / num_ticks;
int step = (last - first) / num_ticks;
int i;
- int tick_y = axis_y_off(graph_tick_len) + graph_inner_margin;
+ int tick_y = axis_y_off(graph_tick_len) + graph_inner_y_margin;
int tick_x = axis_x();
int tick_only = plot->add_xlabel == 0;
@@ -428,7 +441,7 @@ void set_ylabel(struct plot *plot, char *label)
axis_y_off(graph_height / 2),
font_family,
graph_left_pad / 2 - axis_label_font_size,
- axis_y_off(graph_height / 2),
+ (int)axis_y_off(graph_height / 2),
axis_label_font_size, "middle", label);
len = strlen(line);
write(fd, line, len);
@@ -516,7 +529,7 @@ int close_plot(struct plot *plot)
return 0;
}
-struct plot *alloc_plot(int fd)
+struct plot *alloc_plot(void)
{
struct plot *plot;
plot = calloc(1, sizeof(*plot));
@@ -524,10 +537,26 @@ struct plot *alloc_plot(int fd)
fprintf(stderr, "Unable to allocate memory %s\n", strerror(errno));
exit(1);
}
- plot->fd = fd;
+ plot->fd = 0;
return plot;
}
+void set_plot_output(struct plot *plot, char *filename)
+{
+ int fd;
+
+ if (plot->fd)
+ close(plot->fd);
+ 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));
+ exit(1);
+ }
+ plot->fd = fd;
+ plot->start_y_offset = plot->start_x_offset = 0;
+ write_svg_header(fd);
+}
+
char *byte_unit_names[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "unobtainium" };
int MAX_BYTE_UNIT_SCALE = 9;
@@ -606,13 +635,92 @@ int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color)
return 0;
}
-static int svg_add_io(int fd, int row, int col, char *color)
+void svg_write_time_line(struct plot *plot, int col)
{
- snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"1.5\" height=\"1.5\" rx=\"0.5\" style=\"stroke:none;fill:%s\"/>\n",
- axis_x_off(col), axis_y_off(row), color);
+ snprintf(line, line_len, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
+ "style=\"stroke:black;stroke-width:2;\"/>\n",
+ axis_x_off(col), axis_y_off(0),
+ axis_x_off(col), axis_y_off(graph_height));
+ 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)
+{
+ 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);
return write(fd, line, strlen(line));
}
+int svg_io_graph_movie_array(struct plot *plot, struct plot_history *ph, float alpha)
+{
+ double cell_index;
+ double movie_row;
+ double movie_col;
+ int i;
+
+ for (i = 0; i < ph->num_used; i++) {
+ 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);
+ }
+ return 0;
+}
+
+static int add_plot_history(struct plot_history *ph, double val)
+{
+ if (ph->num_used == ph->history_len) {
+ ph->history = realloc(ph->history,
+ (ph->history_len + 4096) * sizeof(double));
+ if (!ph->history) {
+ perror("Unable to allocate memory");
+ exit(1);
+ }
+ ph->history_len += 4096;
+ }
+ ph->history[ph->num_used++] = val;
+ return 0;
+}
+
+int svg_io_graph_movie(struct graph_dot_data *gdd, struct plot_history *ph, int col)
+{
+ int row = 0;
+ int arr_index;
+ 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 cell_index;
+ int margin_orig = graph_inner_y_margin;
+
+ graph_inner_y_margin += 5;
+
+ for (row = gdd->rows - 1; row >= 0; row--) {
+ bit_index = row * gdd->cols + col;
+ arr_index = bit_index / 8;
+ bit_mod = bit_index % 8;
+
+ if (arr_index < 0)
+ continue;
+ val = gdd->data[arr_index];
+ if (val & (1 << bit_mod)) {
+ /* in bytes, linear offset from the start of the drive */
+ cell_index = (double)row * blocks_per_row;
+
+ /* a cell number in the graph */
+ cell_index /= movie_blocks_per_cell;
+
+ add_plot_history(ph, cell_index);
+ }
+ }
+ graph_inner_y_margin = margin_orig;
+ return 0;
+}
+
int svg_io_graph(struct plot *plot, struct graph_dot_data *gdd, char *color)
{
int fd = plot->fd;;
@@ -633,7 +741,7 @@ int svg_io_graph(struct plot *plot, struct graph_dot_data *gdd, char *color)
continue;
val = gdd->data[arr_index];
if (val & (1 << bit_mod))
- svg_add_io(fd, row, col, color);
+ svg_add_io(fd, floor(row / io_graph_scale), col, 1.5, 1.5, color, 1.0);
}
}
return 0;
@@ -718,3 +826,28 @@ void set_rolling_avg(int rolling)
rolling_avg_secs = rolling;
}
+void set_io_graph_scale(int scale)
+{
+ io_graph_scale = scale;
+}
+
+void set_graph_size(int width, int height)
+{
+ graph_width = width;
+ graph_height = height;
+}
+
+void get_graph_size(int *width, int *height)
+{
+ *width = graph_width;
+ *height = graph_height;
+}
+
+void set_graph_height(int h)
+{
+ graph_height = h;
+}
+void set_graph_width(int w)
+{
+ graph_width = w;
+}
diff --git a/plot.h b/plot.h
index cf52447..938cc18 100644
--- a/plot.h
+++ b/plot.h
@@ -19,10 +19,13 @@
#define __IOWATCH_PLOT__
#define MAX_TICKS 10
+#include "list.h"
+
typedef __u64 u64;
typedef __u32 u32;
typedef __u16 u16;
+
struct plot {
int fd;
@@ -85,16 +88,25 @@ struct graph_dot_data {
unsigned char data[];
};
+struct plot_history {
+ struct list_head list;
+ int history_len;
+ int num_used;
+ int col;
+ char *color;
+ double *history;
+};
+
int svg_io_graph(struct plot *plot, struct graph_dot_data *gdd, char *color);
int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color);
struct graph_line_data *alloc_line_data(int seconds, int stop_seconds);
void free_line_data(struct graph_line_data *gld);
struct graph_dot_data *alloc_dot_data(int seconds, u64 max_offset, int stop_seconds);
void free_dot_data(struct graph_dot_data *gdd);
-void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, int bytes, double time);
+void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, double bytes, double time);
void print_gdd(struct graph_dot_data *gdd);
void write_svg_header(int fd);
-struct plot *alloc_plot(int fd);
+struct plot *alloc_plot(void);
int close_plot(struct plot *plot);
void setup_axis(struct plot *plot);
void set_xticks(struct plot *plot, int num_ticks, int first, int last);
@@ -113,4 +125,13 @@ void svg_alloc_legend(struct plot *plot, int num_lines);
void set_legend_width(int longest_str);
void set_rolling_avg(int rolling);
void svg_free_legend(struct plot *plot);
+void set_io_graph_scale(int scale);
+void set_plot_output(struct plot *plot, char *filename);
+void set_graph_size(int width, int height);
+void get_graph_size(int *width, int *height);
+int svg_io_graph_movie(struct graph_dot_data *gdd, struct plot_history *ph, int col);
+int svg_io_graph_movie_array(struct plot *plot, struct plot_history *ph, float alpha);
+void svg_write_time_line(struct plot *plot, int col);
+void set_graph_height(int h);
+void set_graph_width(int w);
#endif