iowatcher: Add movie support
authorChris Mason <chris.mason@fusionio.com>
Fri, 17 Aug 2012 16:18:28 +0000 (12:18 -0400)
committerChris Mason <chris.mason@fusionio.com>
Mon, 20 Aug 2012 10:23:42 +0000 (06:23 -0400)
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
iowatcher/main.c
iowatcher/plot.c
iowatcher/plot.h

index ebc41cbf58dac2fb0d92d1c3f17dbcfaa595830a..c905dd81de2e0447019c6b65e140af30c5e8c531 100644 (file)
 
 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;
 }
index da64d41b1e528a76294efce8c0ecd96b7d35006e..ce8fab86399f5aa8e37faee9d511c89e0674528b 100644 (file)
 
 #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;
+}
index cf52447f86a54cc47ee78bf60ed1f1fa77a1b724..938cc188be44dbdcee510a4f4fabf648d69bb203 100644 (file)
 #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