From: Jens Axboe Date: Wed, 14 Mar 2012 09:25:44 +0000 (+0100) Subject: gfio: graphing updates X-Git-Tag: gfio-0.1~155 X-Git-Url: https://git.kernel.dk/?p=fio.git;a=commitdiff_plain;h=d8fbeefb67641e9f63088b329de78a26a69fdbae;hp=a217ba7de6e2ea26fbd12db663aaf8a5a9f350a7 gfio: graphing updates - Don't draw tickmark labels for an empty graph - Remove Y-axis label. It was a bit awkward and hard to read. - Ensure that we always put proper units on the Y-axis graph. - Ensure that we always include the top most Y-axis tickmark and make that the ceiling, not the highest Y value. It's hard to visually understand a graph that is drawing somewhere above the last labeled tickmark. - Fix rounding errors in graph.c Signed-off-by: Jens Axboe --- diff --git a/gfio.c b/gfio.c index d0418a45..fc220f5d 100644 --- a/gfio.c +++ b/gfio.c @@ -187,70 +187,36 @@ static void gfio_update_thread_status(struct gui_entry *ge, char *status_message static void gfio_update_thread_status_all(char *status_message, double perc); void report_error(GError *error); -static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten) -{ - switch (power_of_ten) { - case 9: graph_y_title(g, "Billions of IOs / sec"); - break; - case 6: graph_y_title(g, "Millions of IOs / sec"); - break; - case 3: graph_y_title(g, "Thousands of IOs / sec"); - break; - case 0: - default: graph_y_title(g, "IOs / sec"); - break; - } -} - static struct graph *setup_iops_graph(void) { struct graph *g; g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); - graph_title(g, "IOPS"); + graph_title(g, "IOPS (IOs/sec)"); graph_x_title(g, "Time (secs)"); - graph_y_title(g, "IOs / sec"); graph_add_label(g, "Read IOPS"); graph_add_label(g, "Write IOPS"); graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13); graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0); line_graph_set_data_count_limit(g, gfio_graph_limit); - graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change); - graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03); + graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0); return g; } -static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten) -{ - switch (power_of_ten) { - case 9: graph_y_title(g, "Petabytes / sec"); - break; - case 6: graph_y_title(g, "Gigabytes / sec"); - break; - case 3: graph_y_title(g, "Megabytes / sec"); - break; - case 0: - default: graph_y_title(g, "Kilobytes / sec"); - break; - } -} - static struct graph *setup_bandwidth_graph(void) { struct graph *g; g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); - graph_title(g, "Bandwidth"); + graph_title(g, "Bandwidth (bytes/sec)"); graph_x_title(g, "Time (secs)"); - graph_y_title(g, "Kbytes / sec"); graph_add_label(g, "Read Bandwidth"); graph_add_label(g, "Write Bandwidth"); graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13); graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0); + graph_set_base_offset(g, 1); line_graph_set_data_count_limit(g, 100); - graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change); - graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03); - + graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0); return g; } diff --git a/graph.c b/graph.c index aba095ba..5bcefbe4 100644 --- a/graph.c +++ b/graph.c @@ -66,6 +66,7 @@ struct graph { const char *font; graph_axis_unit_change_callback x_axis_unit_change_callback; graph_axis_unit_change_callback y_axis_unit_change_callback; + unsigned int base_offset; double left_extra; double right_extra; double top_extra; @@ -326,7 +327,7 @@ static void graph_draw_common(struct graph *g, cairo_t *cr, static void graph_draw_x_ticks(struct graph *g, cairo_t *cr, double x1, double y1, double x2, double y2, - double minx, double maxx, int nticks) + double minx, double maxx, int nticks, int add_tm_text) { struct tickmark *tm; double tx; @@ -334,13 +335,15 @@ static void graph_draw_x_ticks(struct graph *g, cairo_t *cr, static double dash[] = { 1.0, 2.0 }; nticks = calc_tickmarks(minx, maxx, nticks, &tm, &power_of_ten, - g->x_axis_unit_change_callback == NULL); + g->x_axis_unit_change_callback == NULL, g->base_offset); if (g->x_axis_unit_change_callback) g->x_axis_unit_change_callback(g, power_of_ten); for (i = 0; i < nticks; i++) { tx = (((tm[i].value) - minx) / (maxx - minx)) * (x2 - x1) + x1; - if (tx < x1 || tx > x2) + + /* really tx < yx || tx > x2, but protect against rounding */ + if (x1 - tx > 0.01 || tx - x2 > 0.01) continue; /* Draw tick mark */ @@ -358,6 +361,9 @@ static void graph_draw_x_ticks(struct graph *g, cairo_t *cr, cairo_stroke(cr); cairo_restore(cr); + if (!add_tm_text) + continue; + /* draw tickmark label */ draw_centered_text(g, cr, tx, y2 * 1.04, 12.0, tm[i].string); cairo_stroke(cr); @@ -365,9 +371,9 @@ static void graph_draw_x_ticks(struct graph *g, cairo_t *cr, } } -static void graph_draw_y_ticks(struct graph *g, cairo_t *cr, +static double graph_draw_y_ticks(struct graph *g, cairo_t *cr, double x1, double y1, double x2, double y2, - double miny, double maxy, int nticks) + double miny, double maxy, int nticks, int add_tm_text) { struct tickmark *tm; double ty; @@ -375,14 +381,24 @@ static void graph_draw_y_ticks(struct graph *g, cairo_t *cr, static double dash[] = { 2.0, 2.0 }; nticks = calc_tickmarks(miny, maxy, nticks, &tm, &power_of_ten, - g->y_axis_unit_change_callback == NULL); + g->y_axis_unit_change_callback == NULL, g->base_offset); if (g->y_axis_unit_change_callback) g->y_axis_unit_change_callback(g, power_of_ten); + /* + * Use highest tickmark as top of graph, not highest value. Otherwise + * it's impossible to see what the max value is, if the graph is + * fairly flat. + */ + maxy = tm[nticks - 1].value; + for (i = 0; i < nticks; i++) { ty = y2 - (((tm[i].value) - miny) / (maxy - miny)) * (y2 - y1); - if (ty < y1 || ty > y2) + + /* really ty < y1 || ty > y2, but protect against rounding */ + if (y1 - ty > 0.01 || ty - y2 > 0.01) continue; + /* draw tick mark */ cairo_move_to(cr, x1, ty); cairo_line_to(cr, x1 - (x2 - x1) * 0.02, ty); @@ -397,10 +413,18 @@ static void graph_draw_y_ticks(struct graph *g, cairo_t *cr, cairo_stroke(cr); cairo_restore(cr); + if (!add_tm_text) + continue; + /* draw tickmark label */ draw_right_justified_text(g, cr, x1 - (x2 - x1) * 0.025, ty, 12.0, tm[i].string); cairo_stroke(cr); } + + /* + * Return new max to use + */ + return maxy; } void bar_graph_draw(struct graph *bg, cairo_t *cr) @@ -416,7 +440,7 @@ void bar_graph_draw(struct graph *bg, cairo_t *cr) graph_draw_common(bg, cr, &x1, &y1, &x2, &y2); nlabels = count_labels(bg->labels); - space_per_label = (x2 - x1) / (double) nlabels; + space_per_label = (x2 - x1) / (double) nlabels; mindata = find_min_data(bg->labels); maxdata = find_max_data(bg->labels); @@ -428,7 +452,7 @@ void bar_graph_draw(struct graph *bg, cairo_t *cr) return; } - graph_draw_y_ticks(bg, cr, x1, y1, x2, y2, mindata, maxdata, 10); + graph_draw_y_ticks(bg, cr, x1, y1, x2, y2, mindata, maxdata, 10, 1); i = 0; for (lb = bg->labels; lb; lb = lb->next) { @@ -524,8 +548,8 @@ void line_graph_draw(struct graph *g, cairo_t *cr) gminy = miny - bottom_extra; gmaxy = maxy + top_extra; - graph_draw_x_ticks(g, cr, x1, y1, x2, y2, gminx, gmaxx, 10); - graph_draw_y_ticks(g, cr, x1, y1, x2, y2, gminy, gmaxy, 10); + graph_draw_x_ticks(g, cr, x1, y1, x2, y2, gminx, gmaxx, 10, good_data); + gmaxy = graph_draw_y_ticks(g, cr, x1, y1, x2, y2, gminy, gmaxy, 10, good_data); if (!good_data) goto skip_data; @@ -776,6 +800,16 @@ void graph_add_extra_space(struct graph *g, double left_percent, double right_pe g->bottom_extra = bottom_percent; } +/* + * Normally values are logged in a base unit of 0, but for other purposes + * it makes more sense to log in higher unit. For instance for bandwidth + * purposes, you may want to log in KB/sec (or MB/sec) rather than bytes/sec. + */ +void graph_set_base_offset(struct graph *g, unsigned int base_offset) +{ + g->base_offset = base_offset; +} + int graph_has_tooltips(struct graph *g) { struct graph_label *i; diff --git a/graph.h b/graph.h index cb1e2d54..00995259 100644 --- a/graph.h +++ b/graph.h @@ -85,5 +85,7 @@ extern int graph_has_tooltips(struct graph *g); extern const char *graph_find_tooltip(struct graph *g, int x, int y); extern int graph_contains_xy(struct graph *p, int x, int y); +extern void graph_set_base_offset(struct graph *g, unsigned int base_offset); + #endif diff --git a/tickmarks.c b/tickmarks.c index 2605f721..6a964e31 100644 --- a/tickmarks.c +++ b/tickmarks.c @@ -40,11 +40,11 @@ static double nicenum(double x, int round) } static void shorten(struct tickmark *tm, int nticks, int *power_of_ten, - int use_KMG_symbols) + int use_KMG_symbols, int base_offset) { - int i, l, minshorten; + const char shorten_chr[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 }; + int i, l, minshorten, shorten_idx = 0; char *str; - char shorten_char = '?'; minshorten = 100; for (i = 0; i < nticks; i++) { @@ -55,14 +55,14 @@ static void shorten(struct tickmark *tm, int nticks, int *power_of_ten, continue; if (l > 9 && strcmp(&str[l - 9], "000000000") == 0) { *power_of_ten = 9; - shorten_char = use_KMG_symbols ? 'G' : '\0'; + shorten_idx = 3; } else if (6 < minshorten && l > 6 && strcmp(&str[l - 6], "000000") == 0) { *power_of_ten = 6; - shorten_char = use_KMG_symbols ? 'M' : '\0'; + shorten_idx = 2; } else if (l > 3 && strcmp(&str[l - 3], "000") == 0) { *power_of_ten = 3; - shorten_char = use_KMG_symbols ? 'K': '\0'; + shorten_idx = 1; } else { *power_of_ten = 0; } @@ -73,17 +73,22 @@ static void shorten(struct tickmark *tm, int nticks, int *power_of_ten, if (minshorten == 0) return; + if (!use_KMG_symbols) + shorten_idx = 0; + else if (base_offset) + shorten_idx += base_offset; for (i = 0; i < nticks; i++) { str = tm[i].string; l = strlen(str); - str[l - minshorten] = shorten_char; - str[l - minshorten + 1] = '\0'; + str[l - minshorten] = shorten_chr[shorten_idx]; + if (shorten_idx) + str[l - minshorten + 1] = '\0'; } } int calc_tickmarks(double min, double max, int nticks, struct tickmark **tm, - int *power_of_ten, int use_KMG_symbols) + int *power_of_ten, int use_KMG_symbols, int base_offset) { char str[100]; int nfrac; @@ -109,7 +114,7 @@ int calc_tickmarks(double min, double max, int nticks, struct tickmark **tm, sprintf((*tm)[i].string, str, x); i++; } - shorten(*tm, i, power_of_ten, use_KMG_symbols); + shorten(*tm, i, power_of_ten, use_KMG_symbols, base_offset); return i; } diff --git a/tickmarks.h b/tickmarks.h index a8c1bf9f..1e310dbb 100644 --- a/tickmarks.h +++ b/tickmarks.h @@ -7,6 +7,6 @@ struct tickmark { }; int calc_tickmarks(double min, double max, int nticks, struct tickmark **tm, - int *power_of_ten, int use_KMG_symbols); + int *power_of_ten, int use_KMG_symbols, int base_off); #endif