iowatcher: Add a new movie mode that maps the IOs onto a platter.
authorChris Mason <chris.mason@fusionio.com>
Tue, 21 Aug 2012 19:19:35 +0000 (15:19 -0400)
committerChris Mason <chris.mason@fusionio.com>
Thu, 23 Aug 2012 01:42:17 +0000 (21:42 -0400)
The --movie option defaults to spindle mode now,
but you can choose --movie=rect or --movie=spindle
as well.

Signed-off-by: Chris Mason <chris.mason@fusionio.com>
iowatcher/main.c
iowatcher/plot.c
iowatcher/plot.h

index bc6132ad0e65da23fcd0820dde348ebad04fa5c2..9eb7f3b44c171e3ffb462e8e73f954af974ad08b 100644 (file)
@@ -115,6 +115,31 @@ static char *graphs_by_name[] = {
        "iops",
 };
 
+enum {
+       MOVIE_SPINDLE,
+       MOVIE_RECT,
+       NUM_MOVIE_STYLES,
+};
+
+char *movie_styles[] = {
+       "spindle",
+       "rect",
+       NULL
+};
+
+static int movie_style = 0;
+
+static int lookup_movie_style(char *str)
+{
+       int i;
+
+       for (i = 0; i < NUM_MOVIE_STYLES; i++) {
+               if (strcmp(str, movie_styles[i]) == 0)
+                       return i;
+       }
+       return -1;
+}
+
 static int active_graphs[TOTAL_GRAPHS];
 static int last_active_graph = IOPS_GRAPH_INDEX;
 
@@ -423,7 +448,7 @@ static void set_all_max_tf(int seconds, u64 max_offset)
 static char *create_movie_temp_dir(void)
 {
        char *ret;
-       char *pattern = strdup("btrfs-movie-XXXXXX");;
+       char *pattern = strdup("io-movie-XXXXXX");;
 
        ret = mkdtemp(pattern);
        if (!ret) {
@@ -473,16 +498,16 @@ static void add_history(struct plot_history *ph, struct list_head *list)
 
 static void plot_movie_history(struct plot *plot, struct list_head *list)
 {
-       float alpha = 0.1;
        struct plot_history *ph;
 
+       if (num_histories > 2)
+               rewind_spindle_steps(num_histories - 1);
+
        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;
+               if (movie_style == MOVIE_SPINDLE)
+                       svg_io_graph_movie_array_spindle(plot, ph);
+               else
+                       svg_io_graph_movie_array(plot, ph);
         }
 }
 
@@ -600,7 +625,7 @@ static void convert_movie_files(char *movie_dir)
 static void mencode_movie(char *movie_dir)
 {
        fprintf(stderr, "Creating movie %s\n", movie_dir);
-       snprintf(line, line_len, "ffmpeg -r 25 -y -i %s/%%10d-%s.svg.png -b:v 250k "
+       snprintf(line, line_len, "ffmpeg -r 20 -y -i %s/%%10d-%s.svg.png -b:v 250k "
                 "-vcodec libx264 %s", movie_dir, output_filename, output_filename);
        system(line);
 }
@@ -624,7 +649,7 @@ static void plot_io_movie(struct plot *plot)
        struct plot_history *write_history;
        int batch_i;
        int movie_len = 30;
-       int movie_frames_per_sec = 16;
+       int movie_frames_per_sec = 20;
        int total_frames = movie_len * movie_frames_per_sec;
        int rows, cols;
        int batch_count;
@@ -646,7 +671,11 @@ static void plot_io_movie(struct plot *plot)
                        set_plot_output(plot, line);
 
                        set_plot_title(plot, graph_title);
-                       setup_axis(plot);
+                       if (movie_style == MOVIE_SPINDLE)
+                               setup_axis_spindle(plot);
+                       else
+                               setup_axis(plot);
+
                        svg_alloc_legend(plot, num_traces * 2);
 
                        read_history = alloc_plot_history(tf->read_color);
@@ -661,9 +690,7 @@ static void plot_io_movie(struct plot *plot)
 
                        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++;
@@ -678,7 +705,7 @@ static void plot_io_movie(struct plot *plot)
                        svg_write_legend(plot);
                        close_plot(plot);
 
-                       set_graph_size(cols, rows / 3);
+                       set_graph_size(cols, rows / 7);
                        plot->add_xlabel = 1;
                        __plot_tput(plot, tf->gdd_reads->seconds);
                        svg_write_time_line(plot, i);
@@ -686,6 +713,7 @@ static void plot_io_movie(struct plot *plot)
                        set_graph_size(cols, rows);
 
                        close_plot(plot);
+                       close_plot_file(plot);
                }
                free_all_plot_history(&movie_history_reads);
                free_all_plot_history(&movie_history_writes);
@@ -907,7 +935,7 @@ enum {
        HELP_LONG_OPT = 1,
 };
 
-char *option_string = "T:t:o:l:r:O:N:d:p:mh:w:";
+char *option_string = "T:t:o:l:r:O:N:d:p:m::h:w:";
 static struct option long_options[] = {
        {"title", required_argument, 0, 'T'},
        {"trace", required_argument, 0, 't'},
@@ -918,7 +946,7 @@ static struct option long_options[] = {
        {"only-graph", required_argument, 0, 'O'},
        {"device", required_argument, 0, 'd'},
        {"prog", required_argument, 0, 'p'},
-       {"movie", no_argument, 0, 'm'},
+       {"movie", optional_argument, 0, 'm'},
        {"width", required_argument, 0, 'w'},
        {"height", required_argument, 0, 'h'},
        {"help", no_argument, 0, HELP_LONG_OPT},
@@ -933,7 +961,7 @@ static void print_usage(void)
                "\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-p (--movie [=spindle|rect]): 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"
@@ -993,6 +1021,15 @@ static int parse_options(int ac, char **av)
                        break;
                case 'm':
                        make_movie = 1;
+                       if (optarg) {
+                               movie_style = lookup_movie_style(optarg);
+                               if (movie_style < 0) {
+                                       fprintf(stderr, "Unknown movie style %s\n", optarg);
+                                       print_usage();
+                               }
+                       }
+                       fprintf(stderr, "Using movie style: %s\n",
+                               movie_styles[movie_style]);
                        break;
                case 'h':
                        opt_graph_height = atoi(optarg);
@@ -1029,7 +1066,10 @@ int main(int ac, char **av)
        last_active_graph = last_graph();
        if (make_movie) {
                set_io_graph_scale(256);
-               set_graph_size(700, 250);
+               if (movie_style == MOVIE_SPINDLE)
+                       set_graph_size(550, 550);
+               else
+                       set_graph_size(700, 400);
        }
        if (opt_graph_height)
                set_graph_height(opt_graph_height);
@@ -1119,5 +1159,6 @@ int main(int ac, char **av)
 
        /* once for all */
        close_plot(plot);
+       close_plot_file(plot);
        return 0;
 }
index ce8fab86399f5aa8e37faee9d511c89e0674528b..7f30f77ec1f57d73f0584532b425252881006e22 100644 (file)
@@ -34,8 +34,9 @@
 #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;
@@ -65,6 +66,9 @@ static int rolling_avg_secs = 0;
 static int line_len = 1024;
 static char line[1024];
 
+static int final_height = 0;
+static int final_width = 0;
+
 struct graph_line_data *alloc_line_data(int seconds, int stop_seconds)
 {
        int size = sizeof(struct graph_line_data) + (stop_seconds + 1) * sizeof(struct graph_line_pair);
@@ -210,7 +214,8 @@ static double rolling_avg(struct graph_line_pair *data, int index, int distance)
 
 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 "
@@ -230,8 +235,13 @@ void write_svg_header(int fd)
                "</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, defs_start, strlen(defs_start));
        write(fd, filter1, strlen(filter1));
        write(fd, filter2, strlen(filter2));
@@ -313,7 +323,6 @@ void setup_axis(struct plot *plot)
        int fd = plot->fd;
        int bump_height = tick_font_size * 3 + axis_label_font_size;
 
-
        plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + legend_width;
        plot->total_height = axis_y() + tick_label_pad + tick_font_size;
 
@@ -327,6 +336,7 @@ void setup_axis(struct plot *plot)
                plot->total_height + 20);
        len = strlen(line);
        write(fd, line, len);
+
        snprintf(line, line_len, "<rect x=\"15\" y=\"%d\" width=\"%d\" "
                 "filter=\"url(#shadow)\" "
                 "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
@@ -335,9 +345,13 @@ void setup_axis(struct plot *plot)
        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->total_width + 40 > final_width)
+               final_width = 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",
@@ -352,6 +366,50 @@ void setup_axis(struct plot *plot)
        }
 }
 
+/*
+ * 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;
+
+       plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + 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\" "
+                "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
+               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\" "
+                "filter=\"url(#shadow)\" "
+                "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
+               plot->start_y_offset, plot->total_width, 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->total_width + 40 > final_width)
+               final_width = 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.
@@ -541,12 +599,31 @@ struct plot *alloc_plot(void)
        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));
+       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));
@@ -644,17 +721,17 @@ void svg_write_time_line(struct plot *plot, int col)
        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;
@@ -665,7 +742,78 @@ int svg_io_graph_movie_array(struct plot *plot, struct plot_history *ph, float a
                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;
 }
@@ -698,6 +846,7 @@ int svg_io_graph_movie(struct graph_dot_data *gdd, struct plot_history *ph, int
        int margin_orig = graph_inner_y_margin;
 
        graph_inner_y_margin += 5;
+       ph->history_max = gdd->max_offset / movie_blocks_per_cell;
 
        for (row = gdd->rows - 1; row >= 0; row--) {
                bit_index = row * gdd->cols + col;
@@ -741,7 +890,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, 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;
index 938cc188be44dbdcee510a4f4fabf648d69bb203..1d534dd6cf70ad5f6bd779bbc14ef535a4fc983b 100644 (file)
@@ -90,6 +90,7 @@ struct graph_dot_data {
 
 struct plot_history {
        struct list_head list;
+       double history_max;
        int history_len;
        int num_used;
        int col;
@@ -130,8 +131,12 @@ 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);
+int svg_io_graph_movie_array(struct plot *plot, struct plot_history *ph);
 void svg_write_time_line(struct plot *plot, int col);
 void set_graph_height(int h);
 void set_graph_width(int w);
+int close_plot_file(struct plot *plot);
+int svg_io_graph_movie_array_spindle(struct plot *plot, struct plot_history *ph);
+void rewind_spindle_steps(int num);
+void setup_axis_spindle(struct plot *plot);
 #endif