diff options
author | Chris Mason <chris.mason@fusionio.com> | 2012-09-10 21:01:02 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2012-09-10 21:01:02 -0400 |
commit | a78b574c36dbcf35ad0f950f02b8b5729edeba68 (patch) | |
tree | c3cf94d85233a2198d60e562e91a1a89fc6c2555 | |
parent | 1e1e3f04296420c0e3cb46ffad56f247f6a17963 (diff) | |
parent | f2e40ddd2b405fb22bd93ca61160e3505921d59e (diff) | |
download | blktrace-a78b574c36dbcf35ad0f950f02b8b5729edeba68.tar.gz |
iowatcher: Merge branch 'jan'
Jan Kara's updates for xzoom and yzoom
Conflicts:
main.c
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
-rw-r--r-- | iowatcher/blkparse.c | 92 | ||||
-rw-r--r-- | iowatcher/blkparse.h | 7 | ||||
-rw-r--r-- | iowatcher/main.c | 254 | ||||
-rw-r--r-- | iowatcher/plot.c | 97 | ||||
-rw-r--r-- | iowatcher/plot.h | 19 |
5 files changed, 339 insertions, 130 deletions
diff --git a/iowatcher/blkparse.c b/iowatcher/blkparse.c index abdacb5..a0fb8c5 100644 --- a/iowatcher/blkparse.c +++ b/iowatcher/blkparse.c @@ -41,6 +41,7 @@ static struct list_head io_hash_table[IO_HASH_TABLE_SIZE]; static u64 ios_in_flight = 0; +extern int plot_io_action; /* * Trace categories @@ -93,6 +94,8 @@ enum { __BLK_TA_DRV_DATA, /* binary driver data */ }; +#define BLK_TA_MASK ((1 << BLK_TC_SHIFT) - 1) + /* * Notify events. */ @@ -411,11 +414,11 @@ out: return -1; } -void find_highest_offset(struct trace *trace, u64 *max_ret, u64 *max_bank_ret, - u64 *max_offset_ret) +void find_extreme_offsets(struct trace *trace, u64 *min_ret, u64 *max_ret, u64 *max_bank_ret, + u64 *max_offset_ret) { u64 found = 0; - u64 max = 0; + u64 max = 0, min = ~(u64)0; u64 max_bank = 0; u64 max_bank_offset = 0; u64 num_banks = 0; @@ -423,11 +426,12 @@ void find_highest_offset(struct trace *trace, u64 *max_ret, u64 *max_bank_ret, while (1) { if (!(trace->io->action & BLK_TC_ACT(BLK_TC_NOTIFY))) { found = trace->io->sector << 9; - found += trace->io->bytes; + if (found < min) + min = found; - if (max < found) { + found += trace->io->bytes; + if (max < found) max = found; - } } else { u64 bank; u64 offset; @@ -443,30 +447,41 @@ void find_highest_offset(struct trace *trace, u64 *max_ret, u64 *max_bank_ret, break; } first_record(trace); + *min_ret = min; *max_ret = max; *max_bank_ret = max_bank; *max_offset_ret = max_bank_offset; } -int filter_outliers(struct trace *trace, u64 max_offset, +int filter_outliers(struct trace *trace, u64 min_offset, u64 max_offset, u64 *yzoom_min, u64 *yzoom_max) { int hits[11]; u64 max_per_bucket[11]; - u64 bytes_per_bucket = max_offset / 10; + u64 min_per_bucket[11]; + u64 bytes_per_bucket = (max_offset - min_offset + 1) / 10; int slot; int fat_count = 0; memset(hits, 0, sizeof(int) * 11); memset(max_per_bucket, 0, sizeof(u64) * 11); + memset(min_per_bucket, 0xff, sizeof(u64) * 11); first_record(trace); while (1) { - if (!(trace->io->action & BLK_TC_ACT(BLK_TC_NOTIFY))) { - u64 top = (trace->io->sector << 9) + trace->io->bytes; - slot = (int)(top / bytes_per_bucket); + if (!(trace->io->action & BLK_TC_ACT(BLK_TC_NOTIFY)) && + (trace->io->action & BLK_TA_MASK) == __BLK_TA_QUEUE) { + u64 off = (trace->io->sector << 9) - min_offset; + + slot = (int)(off / bytes_per_bucket); hits[slot]++; - if (top > max_per_bucket[slot]) - max_per_bucket[slot] = top; + if (off < min_per_bucket[slot]) + min_per_bucket[slot] = off; + + off += trace->io->bytes; + slot = (int)(off / bytes_per_bucket); + hits[slot]++; + if (off > max_per_bucket[slot]) + max_per_bucket[slot] = off; } if (next_record(trace)) break; @@ -483,17 +498,17 @@ int filter_outliers(struct trace *trace, u64 max_offset, double d = hits[slot]; if (d >= (double)fat_count * .05) { - *yzoom_max = max_per_bucket[slot]; + *yzoom_max = max_per_bucket[slot] + min_offset; break; } } - *yzoom_min = 0; + *yzoom_min = min_offset; for (slot = 0; slot < 10; slot++) { double d = hits[slot]; if (d >= (double)fat_count * .05) { - *yzoom_min = slot * bytes_per_bucket; + *yzoom_min = min_per_bucket[slot] + min_offset; break; } } @@ -599,8 +614,23 @@ static inline int tput_event(struct trace *trace) return __BLK_TA_COMPLETE; } +int action_char_to_num(char action) +{ + switch (action) { + case 'Q': + return __BLK_TA_QUEUE; + case 'D': + return __BLK_TA_ISSUE; + case 'C': + return __BLK_TA_COMPLETE; + } + return -1; +} + static inline int io_event(struct trace *trace) { + if (plot_io_action) + return plot_io_action; if (trace->found_queue) return __BLK_TA_QUEUE; if (trace->found_issue) @@ -614,7 +644,7 @@ static inline int io_event(struct trace *trace) void add_tput(struct trace *trace, struct graph_line_data *gld) { struct blk_io_trace *io = trace->io; - int action = io->action & 0xffff; + int action = io->action & BLK_TA_MASK; int seconds; if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY)) @@ -624,10 +654,8 @@ void add_tput(struct trace *trace, struct graph_line_data *gld) return; seconds = SECONDS(io->time); - if (seconds > gld->seconds) { - fprintf(stderr, "Bad record %d %d %d\n", seconds, gld->seconds, action); - abort(); - } + if (seconds > gld->max_seconds) + return; gld->data[seconds].sum += io->bytes; gld->data[seconds].count = 1; @@ -639,7 +667,7 @@ void add_io(struct trace *trace, struct graph_dot_data *gdd_writes, struct graph_dot_data *gdd_reads) { struct blk_io_trace *io = trace->io; - int action = io->action & 0xffff; + int action = io->action & BLK_TA_MASK; u64 offset; if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY)) @@ -661,7 +689,7 @@ void add_pending_io(struct trace *trace, struct graph_line_data *gld) int ret; int seconds; struct blk_io_trace *io = trace->io; - int action = io->action & 0xffff; + int action = io->action & BLK_TA_MASK; double avg; if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY)) @@ -671,10 +699,8 @@ void add_pending_io(struct trace *trace, struct graph_line_data *gld) return; seconds = SECONDS(io->time); - if (seconds > gld->seconds) { - fprintf(stderr, "Bad record %d %d\n", seconds, gld->seconds); - abort(); - } + if (seconds > gld->max_seconds) + return; ret = hash_dispatched_io(trace->io); if (ret) @@ -696,7 +722,7 @@ void add_completed_io(struct trace *trace, { struct blk_io_trace *io = trace->io; int seconds; - int action = io->action & 0xffff; + int action = io->action & BLK_TA_MASK; struct pending_io *pio; double avg; u64 latency; @@ -734,7 +760,7 @@ void add_completed_io(struct trace *trace, void add_iop(struct trace *trace, struct graph_line_data *gld) { struct blk_io_trace *io = trace->io; - int action = io->action & 0xffff; + int action = io->action & BLK_TA_MASK; int seconds; if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY)) @@ -745,10 +771,8 @@ void add_iop(struct trace *trace, struct graph_line_data *gld) return; seconds = SECONDS(io->time); - if (seconds > gld->seconds) { - fprintf(stderr, "Bad record %d %d\n", seconds, gld->seconds); - abort(); - } + if (seconds > gld->max_seconds) + return; gld->data[seconds].sum += 1; gld->data[seconds].count = 1; @@ -759,7 +783,7 @@ void add_iop(struct trace *trace, struct graph_line_data *gld) void check_record(struct trace *trace) { struct blk_io_trace *io = trace->io; - int action = io->action & 0xffff; + int action = io->action & BLK_TA_MASK; if (!(io->action & BLK_TC_ACT(BLK_TC_NOTIFY))) { switch (action) { diff --git a/iowatcher/blkparse.h b/iowatcher/blkparse.h index a78a8b7..6300ced 100644 --- a/iowatcher/blkparse.h +++ b/iowatcher/blkparse.h @@ -63,10 +63,11 @@ static inline unsigned int MINOR(unsigned int dev) void init_io_hash_table(void); struct trace *open_trace(char *filename); u64 find_last_time(struct trace *trace); -void find_highest_offset(struct trace *trace, u64 *max_ret, u64 *max_bank_ret, - u64 *max_offset_ret); -int filter_outliers(struct trace *trace, u64 max_offset, +void find_extreme_offsets(struct trace *trace, u64 *min_ret, u64 *max_ret, + u64 *max_bank_ret, u64 *max_offset_ret); +int filter_outliers(struct trace *trace, u64 min_offset, u64 max_offset, u64 *yzoom_min, u64 *yzoom_max); +int action_char_to_num(char action); void add_iop(struct trace *trace, struct graph_line_data *gld); void check_record(struct trace *trace); void add_completed_io(struct trace *trace, diff --git a/iowatcher/main.c b/iowatcher/main.c index a477f04..e076f67 100644 --- a/iowatcher/main.c +++ b/iowatcher/main.c @@ -31,6 +31,8 @@ #include <time.h> #include <math.h> #include <getopt.h> +#include <limits.h> +#include <float.h> #include "plot.h" #include "blkparse.h" @@ -50,9 +52,16 @@ static int opt_graph_width = 0; static int opt_graph_height = 0; static int columns = 1; -static int num_xticks = 9; +static int num_xticks = 7; static int num_yticks = 4; +static double min_time = 0; +static double max_time = DBL_MAX; +static unsigned long long min_mb = 0; +static unsigned long long max_mb = ULLONG_MAX >> 20; + +int plot_io_action = 0; + /* * this doesn't include the IO graph, * but it counts the other graphs as they go out @@ -162,8 +171,10 @@ struct trace_file { char *filename; char *label; struct trace *trace; - int seconds; - int stop_seconds; + int stop_seconds; /* Time when trace stops */ + int min_seconds; /* Beginning of the interval we should plot */ + int max_seconds; /* End of the interval we should plot */ + u64 min_offset; u64 max_offset; char *read_color; @@ -176,7 +187,8 @@ struct trace_file { struct graph_dot_data *gdd_writes; struct graph_dot_data *gdd_reads; - int mpstat_seconds; + int mpstat_min_seconds; + int mpstat_max_seconds; int mpstat_stop_seconds; struct graph_line_data **mpstat_gld; }; @@ -280,12 +292,12 @@ static void setup_trace_file_graphs(void) int i; list_for_each_entry(tf, &all_traces, list) { - tf->tput_gld = alloc_line_data(tf->seconds, tf->stop_seconds); - tf->latency_gld = alloc_line_data(tf->seconds, tf->stop_seconds); - tf->queue_depth_gld = alloc_line_data(tf->seconds, tf->stop_seconds); - tf->iop_gld = alloc_line_data(tf->seconds, tf->stop_seconds); - tf->gdd_writes = alloc_dot_data(tf->seconds, tf->max_offset, tf->stop_seconds); - tf->gdd_reads = alloc_dot_data(tf->seconds, tf->max_offset, tf->stop_seconds); + tf->tput_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds); + tf->latency_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds); + tf->queue_depth_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds); + tf->iop_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds); + tf->gdd_writes = alloc_dot_data(tf->min_seconds, tf->max_seconds, tf->min_offset, tf->max_offset, tf->stop_seconds); + tf->gdd_reads = alloc_dot_data(tf->min_seconds, tf->max_seconds, tf->min_offset, tf->max_offset, tf->stop_seconds); if (tf->trace->mpstat_num_cpus == 0) continue; @@ -293,8 +305,9 @@ static void setup_trace_file_graphs(void) alloc_mpstat_gld(tf); for (i = 0; i < (tf->trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS; i++) { tf->mpstat_gld[i] = - alloc_line_data(tf->mpstat_seconds, - tf->mpstat_seconds); + alloc_line_data(tf->mpstat_min_seconds, + tf->mpstat_max_seconds, + tf->mpstat_max_seconds); tf->mpstat_gld[i]->max = 100; } } @@ -317,17 +330,18 @@ static void read_traces(void) last_time = find_last_time(trace); tf->trace = trace; - tf->seconds = SECONDS(last_time); + tf->max_seconds = SECONDS(last_time); tf->stop_seconds = SECONDS(last_time); - find_highest_offset(trace, &tf->max_offset, &max_bank, - &max_bank_offset); - filter_outliers(trace, tf->max_offset, &ymin, &ymax); + find_extreme_offsets(trace, &tf->min_offset, &tf->max_offset, + &max_bank, &max_bank_offset); + filter_outliers(trace, tf->min_offset, tf->max_offset, &ymin, &ymax); + tf->min_offset = ymin; tf->max_offset = ymax; read_mpstat(trace, tf->filename); tf->mpstat_stop_seconds = trace->mpstat_seconds; - tf->mpstat_seconds = trace->mpstat_seconds; - if (tf->mpstat_seconds) + tf->mpstat_max_seconds = trace->mpstat_seconds; + if (tf->mpstat_max_seconds) found_mpstat = 1; } } @@ -451,20 +465,32 @@ static void set_blktrace_outfile(char *arg) } -static void compare_max_tf(struct trace_file *tf, int *seconds, u64 *max_offset) +static void compare_minmax_tf(struct trace_file *tf, int *max_seconds, u64 *min_offset, u64 *max_offset) { - if (tf->seconds > *seconds) - *seconds = tf->seconds; + if (tf->max_seconds > *max_seconds) + *max_seconds = tf->max_seconds; if (tf->max_offset > *max_offset) *max_offset = tf->max_offset; + if (tf->min_offset < *min_offset) + *min_offset = tf->min_offset; } -static void set_all_max_tf(int seconds, u64 max_offset) +static void set_all_minmax_tf(int min_seconds, int max_seconds, u64 min_offset, u64 max_offset) { struct trace_file *tf; list_for_each_entry(tf, &all_traces, list) { - tf->seconds = seconds; + tf->min_seconds = min_seconds; + tf->max_seconds = max_seconds; + if (tf->stop_seconds > max_seconds) + tf->stop_seconds = max_seconds; + if (tf->mpstat_max_seconds) { + tf->mpstat_min_seconds = min_seconds; + tf->mpstat_max_seconds = max_seconds; + if (tf->mpstat_stop_seconds > max_seconds) + tf->mpstat_stop_seconds = max_seconds; + } + tf->min_offset = min_offset; tf->max_offset = max_offset; } } @@ -546,7 +572,7 @@ static void free_all_plot_history(struct list_head *head) } } -static void plot_io(struct plot *plot, int seconds, u64 max_offset) +static void plot_io(struct plot *plot, int min_seconds, int max_seconds, u64 min_offset, u64 max_offset) { struct trace_file *tf; @@ -559,8 +585,9 @@ static void plot_io(struct plot *plot, int seconds, u64 max_offset) set_plot_label(plot, "Device IO"); set_ylabel(plot, "Offset (MB)"); - set_yticks(plot, num_yticks, 0, max_offset / (1024 * 1024), ""); - set_xticks(plot, num_xticks, 0, seconds); + set_yticks(plot, num_yticks, min_offset / (1024 * 1024), + max_offset / (1024 * 1024), ""); + set_xticks(plot, num_xticks, min_seconds, max_seconds); list_for_each_entry(tf, &all_traces, list) { char *label = tf->label; @@ -582,7 +609,7 @@ static void plot_io(struct plot *plot, int seconds, u64 max_offset) close_plot(plot); } -static void plot_tput(struct plot *plot, int seconds) +static void plot_tput(struct plot *plot, int min_seconds, int max_seconds) { struct trace_file *tf; char *units; @@ -610,7 +637,7 @@ static void plot_tput(struct plot *plot, int seconds) sprintf(line, "%sB/s", units); set_ylabel(plot, line); set_yticks(plot, num_yticks, 0, max, ""); - set_xticks(plot, num_xticks, 0, seconds); + set_xticks(plot, num_xticks, min_seconds, max_seconds); list_for_each_entry(tf, &all_traces, list) { svg_line_graph(plot, tf->tput_gld, tf->read_color, 0, 0); @@ -626,7 +653,7 @@ static void plot_tput(struct plot *plot, int seconds) total_graphs_written++; } -static void plot_cpu(struct plot *plot, int seconds, char *label, +static void plot_cpu(struct plot *plot, int max_seconds, char *label, int active_index, int gld_index) { struct trace_file *tf; @@ -659,23 +686,25 @@ static void plot_cpu(struct plot *plot, int seconds, char *label, setup_axis(plot); set_plot_label(plot, label); - seconds = tf->mpstat_seconds; + max_seconds = tf->mpstat_max_seconds; set_yticks(plot, num_yticks, 0, tf->mpstat_gld[gld_index]->max, ""); set_ylabel(plot, "Percent"); - set_xticks(plot, num_xticks, 0, seconds); + set_xticks(plot, num_xticks, tf->mpstat_min_seconds, max_seconds); cpu_color_index = 0; list_for_each_entry(tf, &all_traces, list) { if (tf->mpstat_gld == 0) break; - for (i = 0; i < tf->mpstat_gld[0]->stop_seconds; i++) { + for (i = tf->mpstat_gld[0]->min_seconds; + i < tf->mpstat_gld[0]->stop_seconds; i++) { if (tf->mpstat_gld[gld_index]->data[i].count) { avg += (tf->mpstat_gld[gld_index]->data[i].sum / tf->mpstat_gld[gld_index]->data[i].count); } } - avg /= tf->mpstat_gld[gld_index]->stop_seconds; + avg /= tf->mpstat_gld[gld_index]->stop_seconds - + tf->mpstat_gld[gld_index]->min_seconds; color = pick_cpu_color(); svg_line_graph(plot, tf->mpstat_gld[0], color, 0, 0); svg_add_legend(plot, tf->label, " avg", color); @@ -684,16 +713,18 @@ static void plot_cpu(struct plot *plot, int seconds, char *label, struct graph_line_data *gld = tf->mpstat_gld[i * MPSTAT_GRAPHS + gld_index]; double this_avg = 0; - for (gld_i = 0; gld_i < gld->stop_seconds; gld_i++) { + for (gld_i = gld->min_seconds; + gld_i < gld->stop_seconds; gld_i++) { if (gld->data[i].count) { this_avg += gld->data[i].sum / gld->data[i].count; } } - this_avg /= gld->stop_seconds; + this_avg /= gld->stop_seconds - gld->min_seconds; - for (gld_i = 0; gld_i < gld->stop_seconds; gld_i++) { + for (gld_i = gld->min_seconds; + gld_i < gld->stop_seconds; gld_i++) { double val; if (gld->data[gld_i].count == 0) @@ -725,7 +756,7 @@ static void plot_cpu(struct plot *plot, int seconds, char *label, total_graphs_written++; } -static void plot_queue_depth(struct plot *plot, int seconds) +static void plot_queue_depth(struct plot *plot, int min_seconds, int max_seconds) { struct trace_file *tf; @@ -740,7 +771,7 @@ static void plot_queue_depth(struct plot *plot, int seconds) tf = list_entry(all_traces.next, struct trace_file, list); set_ylabel(plot, "Pending IO"); set_yticks(plot, num_yticks, 0, tf->queue_depth_gld->max, ""); - set_xticks(plot, num_xticks, 0, seconds); + set_xticks(plot, num_xticks, min_seconds, max_seconds); list_for_each_entry(tf, &all_traces, list) { svg_line_graph(plot, tf->queue_depth_gld, tf->read_color, 0, 0); @@ -821,13 +852,15 @@ static void plot_io_movie(struct plot *plot) set_graph_size(cols / graph_width_factor, rows / 8); plot->timeline = i / graph_width_factor; - plot_tput(plot, tf->gdd_reads->seconds); + plot_tput(plot, tf->gdd_reads->min_seconds, + tf->gdd_reads->max_seconds); - plot_cpu(plot, tf->gdd_reads->seconds, + plot_cpu(plot, tf->gdd_reads->max_seconds, "CPU System Time", CPU_SYS_GRAPH_INDEX, MPSTAT_SYS); plot->direction = PLOT_ACROSS; - plot_queue_depth(plot, tf->gdd_reads->seconds); + plot_queue_depth(plot, tf->gdd_reads->min_seconds, + tf->gdd_reads->max_seconds); /* movie graph starts here */ plot->start_y_offset = orig_y_offset; @@ -882,7 +915,7 @@ static void plot_io_movie(struct plot *plot) free(movie_dir); } -static void plot_latency(struct plot *plot, int seconds) +static void plot_latency(struct plot *plot, int min_seconds, int max_seconds) { struct trace_file *tf; char *units; @@ -912,7 +945,7 @@ static void plot_latency(struct plot *plot, int seconds) sprintf(line, "latency (%ss)", units); set_ylabel(plot, line); set_yticks(plot, num_yticks, 0, max, ""); - set_xticks(plot, num_xticks, 0, seconds); + set_xticks(plot, num_xticks, min_seconds, max_seconds); list_for_each_entry(tf, &all_traces, list) { svg_line_graph(plot, tf->latency_gld, tf->read_color, 0, 0); @@ -928,7 +961,7 @@ static void plot_latency(struct plot *plot, int seconds) total_graphs_written++; } -static void plot_iops(struct plot *plot, int seconds) +static void plot_iops(struct plot *plot, int min_seconds, int max_seconds) { struct trace_file *tf; char *units; @@ -956,7 +989,7 @@ static void plot_iops(struct plot *plot, int seconds) set_ylabel(plot, "IO/s"); set_yticks(plot, num_yticks, 0, max, units); - set_xticks(plot, num_xticks, 0, seconds); + set_xticks(plot, num_xticks, min_seconds, max_seconds); list_for_each_entry(tf, &all_traces, list) { svg_line_graph(plot, tf->iop_gld, tf->read_color, 0, 0); @@ -999,7 +1032,7 @@ enum { HELP_LONG_OPT = 1, }; -char *option_string = "T:t:o:l:r:O:N:d:p:m::h:w:c:"; +char *option_string = "T:t:o:l:r:O:N:d:p:m::h:w:c:x:y:a:"; static struct option long_options[] = { {"columns", required_argument, 0, 'c'}, {"title", required_argument, 0, 'T'}, @@ -1014,6 +1047,9 @@ static struct option long_options[] = { {"movie", optional_argument, 0, 'm'}, {"width", required_argument, 0, 'w'}, {"height", required_argument, 0, 'h'}, + {"xzoom", required_argument, 0, 'x'}, + {"yzoom", required_argument, 0, 'y'}, + {"io-plot-action", required_argument, 0, 'a'}, {"help", no_argument, 0, HELP_LONG_OPT}, {0, 0, 0, 0} }; @@ -1026,7 +1062,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 [=spindle|rect]): create IO animations\n" + "\t-m (--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, \n" @@ -1035,10 +1071,66 @@ static void print_usage(void) "\t-h (--height): set the height of each graph\n" "\t-w (--width): set the width of each graph\n" "\t-c (--columns): numbers of columns in graph output\n" + "\t-x (--xzoom): limit processed time to min:max\n" + "\t-y (--yzoom): limit processed sectors to min:max\n" + "\t-a (--io-plot-action): plot given action (one of Q,D,C) in IO graph\n" ); exit(1); } +static int parse_double_range(char *str, double *min, double *max) +{ + char *end; + + /* Empty lower bound - leave original value */ + if (str[0] != ':') { + *min = strtod(str, &end); + if (*min == HUGE_VAL || *min == -HUGE_VAL) + return -ERANGE; + if (*end != ':') + return -EINVAL; + } else + end = str; + /* Empty upper bound - leave original value */ + if (end[1]) { + *max = strtod(end+1, &end); + if (*max == HUGE_VAL || *max == -HUGE_VAL) + return -ERANGE; + if (*end != 0) + return -EINVAL; + } + if (*min > *max) + return -EINVAL; + return 0; +} + +static int parse_ull_range(char *str, unsigned long long *min, + unsigned long long *max) +{ + char *end; + + /* Empty lower bound - leave original value */ + if (str[0] != ':') { + *min = strtoull(str, &end, 10); + if (*min == ULLONG_MAX && errno == ERANGE) + return -ERANGE; + if (*end != ':') + return -EINVAL; + } else + end = str; + /* Empty upper bound - leave original value */ + if (end[1]) { + *max = strtoull(end+1, &end, 10); + if (*max == ULLONG_MAX && errno == ERANGE) + return -ERANGE; + if (*end != 0) + return -EINVAL; + } + if (*min > *max) + return -EINVAL; + return 0; +} + static int parse_options(int ac, char **av) { int c; @@ -1108,6 +1200,39 @@ static int parse_options(int ac, char **av) case 'c': columns = atoi(optarg); break; + case 'x': + if (parse_double_range(optarg, &min_time, &max_time) + < 0) { + fprintf(stderr, "Cannot parse time range %s\n", + optarg); + exit(1); + } + break; + case 'y': + if (parse_ull_range(optarg, &min_mb, &max_mb) + < 0) { + fprintf(stderr, + "Cannot parse offset range %s\n", + optarg); + exit(1); + } + if (max_mb > ULLONG_MAX >> 20) { + fprintf(stderr, + "Upper range limit too big." + " Maximum is %llu.\n", ULLONG_MAX >> 20); + exit(1); + } + break; + case 'a': + if (strlen(optarg) != 1) { +action_err: + fprintf(stderr, "Action must be one of Q, D, C."); + exit(1); + } + plot_io_action = action_char_to_num(optarg[0]); + if (plot_io_action < 0) + goto action_err; + break; case '?': case HELP_LONG_OPT: print_usage(); @@ -1123,8 +1248,10 @@ static int parse_options(int ac, char **av) int main(int ac, char **av) { struct plot *plot; - int seconds = 0; + int min_seconds = 0; + int max_seconds = 0; u64 max_offset = 0; + u64 min_offset = ~(u64)0; struct trace_file *tf; int ret; int rows, cols; @@ -1190,10 +1317,17 @@ int main(int ac, char **av) /* step two, find the maxes for time and offset */ list_for_each_entry(tf, &all_traces, list) - compare_max_tf(tf, &seconds, &max_offset); + compare_minmax_tf(tf, &max_seconds, &min_offset, &max_offset); + min_seconds = min_time; + if (max_seconds > max_time) + max_seconds = ceil(max_time); + if (min_offset < min_mb << 20) + min_offset = min_mb << 20; + if (max_offset > max_mb << 20) + max_offset = max_mb << 20; /* push the max we found into all the tfs */ - set_all_max_tf(seconds, max_offset); + set_all_minmax_tf(min_seconds, max_seconds, min_offset, max_offset); /* alloc graphing structs for all the traces */ setup_trace_file_graphs(); @@ -1222,7 +1356,7 @@ int main(int ac, char **av) plot->add_xlabel = 1; set_plot_title(plot, graph_title); - plot_io(plot, seconds, max_offset); + plot_io(plot, min_seconds, max_seconds, min_offset, max_offset); plot->add_xlabel = 0; if (columns > 1) { @@ -1235,36 +1369,36 @@ int main(int ac, char **av) num_yticks--; check_plot_columns(plot, TPUT_GRAPH_INDEX); - plot_tput(plot, seconds); + plot_tput(plot, min_seconds, max_seconds); check_plot_columns(plot, CPU_IO_GRAPH_INDEX); - plot_cpu(plot, seconds, "CPU IO Wait Time", + plot_cpu(plot, max_seconds, "CPU IO Wait Time", CPU_IO_GRAPH_INDEX, MPSTAT_IO); check_plot_columns(plot, CPU_SYS_GRAPH_INDEX); - plot_cpu(plot, seconds, "CPU System Time", + plot_cpu(plot, max_seconds, "CPU System Time", CPU_SYS_GRAPH_INDEX, MPSTAT_SYS); check_plot_columns(plot, CPU_IRQ_GRAPH_INDEX); - plot_cpu(plot, seconds, "CPU IRQ Time", + plot_cpu(plot, max_seconds, "CPU IRQ Time", CPU_IRQ_GRAPH_INDEX, MPSTAT_IRQ); check_plot_columns(plot, CPU_SOFT_GRAPH_INDEX); - plot_cpu(plot, seconds, "CPU SoftIRQ Time", + plot_cpu(plot, max_seconds, "CPU SoftIRQ Time", CPU_SOFT_GRAPH_INDEX, MPSTAT_SOFT); check_plot_columns(plot, CPU_USER_GRAPH_INDEX); - plot_cpu(plot, seconds, "CPU User Time", + plot_cpu(plot, max_seconds, "CPU User Time", CPU_USER_GRAPH_INDEX, MPSTAT_USER); check_plot_columns(plot, LATENCY_GRAPH_INDEX); - plot_latency(plot, seconds); + plot_latency(plot, min_seconds, max_seconds); check_plot_columns(plot, QUEUE_DEPTH_GRAPH_INDEX); - plot_queue_depth(plot, seconds); + plot_queue_depth(plot, min_seconds, max_seconds); check_plot_columns(plot, IOPS_GRAPH_INDEX); - plot_iops(plot, seconds); + plot_iops(plot, min_seconds, max_seconds); /* once for all */ close_plot(plot); diff --git a/iowatcher/plot.c b/iowatcher/plot.c index 74bcb59..d3505e8 100644 --- a/iowatcher/plot.c +++ b/iowatcher/plot.c @@ -69,7 +69,7 @@ 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) +struct graph_line_data *alloc_line_data(int min_seconds, int max_seconds, int stop_seconds) { int size = sizeof(struct graph_line_data) + (stop_seconds + 1) * sizeof(struct graph_line_pair); struct graph_line_data *gld; @@ -79,7 +79,8 @@ struct graph_line_data *alloc_line_data(int seconds, int stop_seconds) fprintf(stderr, "Unable to allocate memory for graph data\n"); exit(1); } - gld->seconds = seconds; + gld->min_seconds = min_seconds; + gld->max_seconds = max_seconds; gld->stop_seconds = stop_seconds; return gld; } @@ -90,7 +91,7 @@ void free_line_data(struct graph_line_data *gld) free(gld); } -struct graph_dot_data *alloc_dot_data(int seconds, u64 max_offset, int stop_seconds) +struct graph_dot_data *alloc_dot_data(int min_seconds, int max_seconds, u64 min_offset, u64 max_offset, int stop_seconds) { int size; int arr_size; @@ -111,10 +112,12 @@ struct graph_dot_data *alloc_dot_data(int seconds, u64 max_offset, int stop_seco fprintf(stderr, "Unable to allocate memory for graph data\n"); exit(1); } - gdd->seconds = seconds; + gdd->min_seconds = min_seconds; + gdd->max_seconds = max_seconds; gdd->stop_seconds = stop_seconds; gdd->rows = rows; gdd->cols = cols; + gdd->min_offset = min_offset; gdd->max_offset = max_offset; return gdd; } @@ -126,9 +129,8 @@ void free_dot_data(struct graph_dot_data *gdd) void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, double bytes, double time) { - double bytes_per_row = (double)gdd->max_offset / gdd->rows; - - double secs_per_col = (double)gdd->seconds / gdd->cols; + double bytes_per_row = (double)(gdd->max_offset - gdd->min_offset + 1) / gdd->rows; + double secs_per_col = (double)(gdd->max_seconds - gdd->min_seconds) / gdd->cols; double col; double row; int col_int; @@ -138,14 +140,14 @@ void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, double bytes, double ti int bit_mod; double mod = bytes_per_row; - if (offset > gdd->max_offset) + if (offset > gdd->max_offset || offset < gdd->min_offset) return; gdd->total_ios++; time = time / 1000000000.0; while (bytes > 0) { - row = (double)offset / bytes_per_row; - col = time / secs_per_col; + row = (double)(offset - gdd->min_offset) / bytes_per_row; + col = (time - gdd->min_seconds) / secs_per_col; col_int = floor(col); row_int = floor(row); @@ -453,14 +455,34 @@ void set_plot_title(struct plot *plot, char *title) write(fd, line, len); } +#define TICK_MINI_STEPS 3 + +static double find_step(double first, double last, int num_ticks) +{ + int mini_step[TICK_MINI_STEPS] = { 1, 2, 5 }; + int cur_mini_step = 0; + double step = (last - first) / num_ticks; + double log10 = log(10); + + /* Round to power of 10 */ + step = exp(floor(log(step) / log10) * log10); + /* Scale down step to provide enough ticks */ + while ((last - first) / (step * mini_step[cur_mini_step]) > num_ticks + && cur_mini_step < TICK_MINI_STEPS) + cur_mini_step++; + step *= mini_step[cur_mini_step - 1]; + + return step; +} + /* * create evenly spread out ticks along the xaxis. if tick only is set * this just makes the ticks, otherwise it labels each tick as it goes */ 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 pixels_per_tick; + double step; int i; int tick_y = axis_y_off(graph_tick_len) + graph_inner_y_margin; int tick_x = axis_x(); @@ -471,6 +493,14 @@ void set_xticks(struct plot *plot, int num_ticks, int first, int last) char *middle = "middle"; char *start = "start"; + step = find_step(first, last, num_ticks); + /* + * We don't want last two ticks to be too close together so subtract + * 20% of the step from the interval + */ + num_ticks = (double)(last - first - step / 5) / step + 1; + pixels_per_tick = graph_width * step / (double)(last - first); + for (i = 0; i < num_ticks; i++) { char *anchor; if (i != 0) { @@ -483,19 +513,32 @@ void set_xticks(struct plot *plot, int num_ticks, int first, int last) } if (!tick_only) { - snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " - "fill=\"black\" style=\"text-anchor: %s\">%d</text>\n", - tick_x, text_y, font_family, tick_font_size, anchor, step * i); + if (step >= 1) + snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " + "fill=\"black\" style=\"text-anchor: %s\">%d</text>\n", + tick_x, text_y, font_family, tick_font_size, anchor, + (int)(first + step * i)); + else + snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " + "fill=\"black\" style=\"text-anchor: %s\">%.2f</text>\n", + tick_x, text_y, font_family, tick_font_size, anchor, + first + step * i); write(plot->fd, line, strlen(line)); } tick_x += pixels_per_tick; } if (!tick_only) { - snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " - "fill=\"black\" style=\"text-anchor: middle\">%d</text>\n", - axis_x_off(graph_width - 2), - text_y, font_family, tick_font_size, last); + if (step >= 1) + snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " + "fill=\"black\" style=\"text-anchor: middle\">%d</text>\n", + axis_x_off(graph_width - 2), + text_y, font_family, tick_font_size, last); + else + snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" " + "fill=\"black\" style=\"text-anchor: middle\">%.2f</text>\n", + axis_x_off(graph_width - 2), + text_y, font_family, tick_font_size, (double)last); write(plot->fd, line, strlen(line)); } } @@ -561,7 +604,7 @@ void set_yticks(struct plot *plot, int num_ticks, int first, int last, char *uni "fill=\"black\" style=\"text-anchor: %s\">%d%s</text>\n", text_x, axis_y_off(tick_y - tick_font_size / 2), - font_family, tick_font_size, anchor, step * i, units); + font_family, tick_font_size, anchor, first + step * i, units); write(plot->fd, line, strlen(line)); tick_y += pixels_per_tick; } @@ -706,7 +749,7 @@ int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color, int fd = plot->fd; char *start = "<path d=\""; double yscale = ((double)gld->max) / graph_height; - double xscale = (double)(gld->seconds - 1) / graph_width; + double xscale = (double)(gld->max_seconds - gld->min_seconds - 1) / graph_width; char c = 'M'; double x; int printed_header = 0; @@ -717,9 +760,9 @@ int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color, else if (rolling_avg_secs) rolling = rolling_avg_secs; else - rolling = gld->stop_seconds / 25; + rolling = (gld->stop_seconds - gld->min_seconds) / 25; - for (i = 0; i < gld->stop_seconds; i++) { + for (i = gld->min_seconds; i < gld->stop_seconds; i++) { avg = rolling_avg(gld->data, i, rolling); if (yscale == 0) val = 0; @@ -731,7 +774,7 @@ int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color, if (val < 0) val = 0; - x = (double)i / xscale; + x = (double)(i - gld->min_seconds) / xscale; if (!thresh1 && !thresh2) { if (!printed_header) { @@ -904,13 +947,13 @@ int svg_io_graph_movie(struct graph_dot_data *gdd, struct plot_history *ph, int unsigned char val; int bit_index; int bit_mod; - double blocks_per_row = gdd->max_offset / gdd->rows; - double movie_blocks_per_cell = gdd->max_offset / (graph_width * graph_height); + double blocks_per_row = (gdd->max_offset - gdd->min_offset + 1) / gdd->rows; + double movie_blocks_per_cell = (gdd->max_offset - gdd->min_offset + 1) / (graph_width * graph_height); double cell_index; int margin_orig = graph_inner_y_margin; graph_inner_y_margin += 5; - ph->history_max = gdd->max_offset / movie_blocks_per_cell; + ph->history_max = (gdd->max_offset - gdd->min_offset + 1) / movie_blocks_per_cell; for (row = gdd->rows - 1; row >= 0; row--) { bit_index = row * gdd->cols + col; diff --git a/iowatcher/plot.h b/iowatcher/plot.h index 2d9d891..fb9c63b 100644 --- a/iowatcher/plot.h +++ b/iowatcher/plot.h @@ -68,8 +68,11 @@ struct graph_line_pair { }; struct graph_line_data { - /* total number of seconds in this graph */ - int seconds; + /* beginning of an interval displayed by this graph */ + int min_seconds; + + /* end of an interval displayed by this graph */ + int max_seconds; int stop_seconds; @@ -82,6 +85,7 @@ struct graph_line_data { }; struct graph_dot_data { + u64 min_offset; u64 max_offset; u64 max_bank; u64 max_bank_offset; @@ -95,8 +99,11 @@ struct graph_dot_data { /* in pixels, number of cols in our bitmap */ int cols; - /* total number of seconds in this graph */ - int seconds; + /* beginning of an interval displayed by this graph */ + int min_seconds; + + /* end of an interval displayed by this graph */ + int max_seconds; int stop_seconds; /* label for the legend */ @@ -118,9 +125,9 @@ struct plot_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, int thresh1, int thresh2); -struct graph_line_data *alloc_line_data(int seconds, int stop_seconds); +struct graph_line_data *alloc_line_data(int min_seconds, int max_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); +struct graph_dot_data *alloc_dot_data(int min_seconds, int max_seconds, u64 min_offset, 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, double bytes, double time); void print_gdd(struct graph_dot_data *gdd); |