gfio: graphing updates
authorJens Axboe <axboe@kernel.dk>
Wed, 14 Mar 2012 09:25:44 +0000 (10:25 +0100)
committerJens Axboe <axboe@kernel.dk>
Wed, 14 Mar 2012 09:25:44 +0000 (10:25 +0100)
- 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 <axboe@kernel.dk>
gfio.c
graph.c
graph.h
tickmarks.c
tickmarks.h

diff --git a/gfio.c b/gfio.c
index d0418a4585212939d7a5bee68b227dfceb0fe509..fc220f5da64923f40ec629e224e78562754d0bc6 100644 (file)
--- 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 aba095ba746c5606600ce94dbe4b8ef7d5fa2b79..5bcefbe4fc871645ab975dd7f0f3662d6067f2f0 100644 (file)
--- 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 cb1e2d5473e2ce847fdea4eef9aa5b1491dd6b74..00995259b3a70bcd2041d615f5f3c5e7cf40b58a 100644 (file)
--- 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
 
index 2605f721f0089de5567f0a1195d6d27e0cc71095..6a964e31a2a3715ebb11d6d93f1cf7b414fe927c 100644 (file)
@@ -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;
 }
 
index a8c1bf9f16485a1768875dd4a892bdd10a4e6a95..1e310dbb1857c08596ea1ae6380c0d2ba04934fb 100644 (file)
@@ -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