aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Mason <chris.mason@fusionio.com>2012-09-10 21:01:02 -0400
committerChris Mason <chris.mason@oracle.com>2012-09-10 21:01:02 -0400
commita78b574c36dbcf35ad0f950f02b8b5729edeba68 (patch)
treec3cf94d85233a2198d60e562e91a1a89fc6c2555
parent1e1e3f04296420c0e3cb46ffad56f247f6a17963 (diff)
parentf2e40ddd2b405fb22bd93ca61160e3505921d59e (diff)
downloadblktrace-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.c92
-rw-r--r--iowatcher/blkparse.h7
-rw-r--r--iowatcher/main.c254
-rw-r--r--iowatcher/plot.c97
-rw-r--r--iowatcher/plot.h19
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);