diff options
author | Chris Mason <chris.mason@fusionio.com> | 2012-08-17 12:18:28 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@fusionio.com> | 2012-08-20 06:23:42 -0400 |
commit | 4b9e525d6b2d5cbeb3141b2a80668537063c6ad7 (patch) | |
tree | ba93251214e5145f21b7ac0c8b1bd6223a059f64 | |
parent | a793c02a3c57bdad9c018ab3bd9fad2b14e31769 (diff) | |
download | iowatcher-4b9e525d6b2d5cbeb3141b2a80668537063c6ad7.tar.gz |
Add movie support
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
-rw-r--r-- | main.c | 434 | ||||
-rw-r--r-- | plot.c | 171 | ||||
-rw-r--r-- | plot.h | 25 |
3 files changed, 504 insertions, 126 deletions
@@ -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; } @@ -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; +} @@ -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 |