iowatcher: Add support for fio bandwith logs (-F logfile)
authorChris Mason <chris.mason@fusionio.com>
Fri, 18 Jan 2013 16:08:39 +0000 (11:08 -0500)
committerChris Mason <clm@fb.com>
Wed, 24 Sep 2014 19:02:08 +0000 (12:02 -0700)
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
iowatcher/Makefile
iowatcher/blkparse.h
iowatcher/fio.c [new file with mode: 0644]
iowatcher/fio.h [new file with mode: 0644]
iowatcher/main.c
iowatcher/plot.c
iowatcher/plot.h

index 0e960437def8d322e7c4eba7b21fad0ac2942d1c..7b5101c35159747f353bc2b440b8b15fa48cea6d 100644 (file)
@@ -18,7 +18,7 @@ all: $(ALL)
 %.o: %.c
        $(CC) -o $*.o -c $(ALL_CFLAGS) $<
 
-iowatcher: blkparse.o plot.o main.o tracers.o mpstat.o
+iowatcher: blkparse.o plot.o main.o tracers.o mpstat.o fio.o
        $(CC) $(ALL_CFLAGS) -o $@ $(filter %.o,$^) -lm
 
 depend:
index d435a87911e25d02b0e8f2c8a3a3efe272db55b5..68becbf164126cfbebd8e7e992b635dc11559258 100644 (file)
@@ -57,6 +57,12 @@ struct trace {
        int mpstat_fd;
        int mpstat_seconds;
        int mpstat_num_cpus;
+
+       char *fio_start;
+       char *fio_cur;
+       u64 fio_len;
+       int fio_fd;
+       int fio_seconds;
        int num_devices;
        struct dev_info devices[MAX_DEVICES_PER_TRACE];
 };
@@ -81,8 +87,13 @@ struct trace_file {
        struct graph_line_data *iop_gld;
        struct graph_line_data *latency_gld;
        struct graph_line_data *queue_depth_gld;
+
+       int fio_trace;
+       struct graph_line_data *fio_gld;
+
        /* Number of entries in gdd_writes / gdd_reads */
        int io_plots;
+
        /* Allocated array size for gdd_writes / gdd_reads */
        int io_plots_allocated;
        struct graph_dot_data **gdd_writes;
diff --git a/iowatcher/fio.c b/iowatcher/fio.c
new file mode 100644 (file)
index 0000000..110f4a1
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2013 Fusion-io
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public
+ *  License v2 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <inttypes.h>
+#include <string.h>
+#include <asm/types.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <math.h>
+
+#include "plot.h"
+#include "blkparse.h"
+#include "list.h"
+#include "tracers.h"
+#include "fio.h"
+
+static int past_eof(struct trace *trace, char *cur)
+{
+       if (cur >= trace->fio_start + trace->fio_len)
+               return 1;
+       return 0;
+}
+
+static int parse_fio_line(struct trace *trace, int *time, int *rate, int *dir, int *bs)
+{
+       char *cur = trace->fio_cur;
+       char *p;
+       int *res[] = { time, rate, dir, bs, NULL };
+       int val;
+       int i = 0;
+       int *t;
+       char *end = index(cur, '\n');
+       char *tmp;
+
+       if (!end)
+               return 1;
+
+       tmp = strndup(cur, end - cur);
+       if (!tmp)
+               return 1;
+       p = strtok(tmp, ",");
+       while (p && *res) {
+               val = atoi(p);
+               t = res[i++];
+               *t = val;
+               p = strtok(NULL, ",");
+       }
+
+       free(tmp);
+
+       if (i < 3)
+               return 1;
+       return 0;
+}
+
+int next_fio_line(struct trace *trace)
+{
+       char *next;
+       char *cur = trace->fio_cur;
+
+       next = strchr(cur, '\n');
+       if (!next)
+               return 1;
+       next++;
+       if (past_eof(trace, next))
+               return 1;
+       trace->fio_cur = next;
+       return 0;
+}
+
+char *first_fio(struct trace *trace)
+{
+       trace->fio_cur = trace->fio_start;
+       return trace->fio_cur;
+}
+
+static void find_last_fio_time(struct trace *trace)
+{
+       double d;
+       int time, rate, dir, bs;
+       int ret;
+       int last_time = 0;
+
+       if (trace->fio_len == 0)
+               return;
+
+       first_fio(trace);
+       while (1) {
+               ret = parse_fio_line(trace, &time, &rate, &dir, &bs);
+               if (ret)
+                       break;
+               if (dir <= 1 && time > last_time)
+                       last_time = time;
+               ret = next_fio_line(trace);
+               if (ret)
+                       break;
+       }
+       d = (double)time / 1000;
+       trace->fio_seconds = ceil(d);
+       return;
+}
+
+int read_fio(struct trace *trace, char *trace_name)
+{
+       int fd;
+       struct stat st;
+       int ret;
+       char *p;
+
+       fd = open(trace_name, O_RDONLY);
+       if (fd < 0)
+               return 1;
+
+       ret = fstat(fd, &st);
+       if (ret < 0) {
+               fprintf(stderr, "stat failed on %s err %s\n",
+                       trace_name, strerror(errno));
+               goto fail_fd;
+       }
+       p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (p == MAP_FAILED) {
+               fprintf(stderr, "Unable to mmap trace file %s, err %s\n",
+                       trace_name, strerror(errno));
+               goto fail_fd;
+       }
+       trace->fio_start = p;
+       trace->fio_len = st.st_size;
+       trace->fio_cur = p;
+       trace->fio_fd = fd;
+       find_last_fio_time(trace);
+       first_fio(trace);
+       return 0;
+
+fail_fd:
+       close(fd);
+       return 1;
+}
+
+struct trace *open_fio_trace(char *path)
+{
+       int ret;
+       struct trace *trace;
+
+       trace = calloc(1, sizeof(*trace));
+       if (!trace) {
+               fprintf(stderr, "unable to allocate memory for trace\n");
+               exit(1);
+       }
+
+       ret = read_fio(trace, path);
+       if (ret) {
+               free(trace);
+               return NULL;
+       }
+
+       return trace;
+}
+
+int read_fio_event(struct trace *trace, int *time_ret, u64 *bw_ret, int *dir_ret)
+{
+       char *cur = trace->fio_cur;
+       int time, rate, dir, bs;
+       int ret;
+
+       if (past_eof(trace, cur))
+               return 1;
+
+       ret = parse_fio_line(trace, &time, &rate, &dir, &bs);
+       if (ret)
+               return 1;
+
+       time = floor((double)time / 1000);
+       *time_ret = time;
+       *bw_ret = (u64)rate * 1024;
+
+       *dir_ret = dir;
+       return 0;
+}
+
+int add_fio_gld(int time, u64 bw, struct graph_line_data *gld)
+{
+       double val;
+
+       if (time > gld->max_seconds)
+               return 0;
+
+       gld->data[time].sum += bw;
+       gld->data[time].count++;
+
+       val = ((double)gld->data[time].sum) / gld->data[time].count;
+
+       if (val > gld->max)
+               gld->max = ceil(val);
+       return 0;
+
+}
diff --git a/iowatcher/fio.h b/iowatcher/fio.h
new file mode 100644 (file)
index 0000000..7f49ddd
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 Fusion-io
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public
+ *  License v2 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __FIO__
+#define __FIO__
+
+int read_fio_event(struct trace *trace, int *time, u64 *bw, int *dir);
+int add_fio_gld(int time, u64 bw, struct graph_line_data *gld);
+int next_fio_line(struct trace *trace);
+struct trace *open_fio_trace(char *path);
+char *first_fio(struct trace *trace);
+
+#endif
index fbd8a8856fee437a8752afd338da1ee7f4d16d10..827f57d861155430971959eb3ab9fef542a6efdf 100644 (file)
 #include "list.h"
 #include "tracers.h"
 #include "mpstat.h"
+#include "fio.h"
 
 LIST_HEAD(all_traces);
+LIST_HEAD(fio_traces);
 
 static char line[1024];
 static int line_len = 1024;
@@ -72,6 +74,7 @@ static int total_graphs_written = 1;
 enum {
        IO_GRAPH_INDEX = 0,
        TPUT_GRAPH_INDEX,
+       FIO_GRAPH_INDEX,
        CPU_SYS_GRAPH_INDEX,
        CPU_IO_GRAPH_INDEX,
        CPU_IRQ_GRAPH_INDEX,
@@ -95,6 +98,7 @@ enum {
 static char *graphs_by_name[] = {
        "io",
        "tput",
+       "fio",
        "cpu-sys",
        "cpu-io",
        "cpu-irq",
@@ -135,6 +139,7 @@ static int last_active_graph = IOPS_GRAPH_INDEX;
 
 static int label_index = 0;
 static int num_traces = 0;
+static int num_fio_traces = 0;
 static int longest_label = 0;
 
 static char *graph_title = "";
@@ -247,6 +252,23 @@ static void add_trace_file(char *filename)
        num_traces++;
 }
 
+static void add_fio_trace_file(char *filename)
+{
+       struct trace_file *tf;
+
+       tf = calloc(1, sizeof(*tf));
+       if (!tf) {
+               fprintf(stderr, "Unable to allocate memory\n");
+               exit(1);
+       }
+       tf->label = "";
+       tf->filename = strdup(filename);
+       list_add_tail(&tf->list, &fio_traces);
+       tf->line_color = pick_fio_color();
+       tf->fio_trace = 1;
+       num_fio_traces++;
+}
+
 static void setup_trace_file_graphs(void)
 {
        struct trace_file *tf;
@@ -257,11 +279,13 @@ static void setup_trace_file_graphs(void)
                alloc_ptrs = 16;
        else
                alloc_ptrs = 1;
+
        list_for_each_entry(tf, &all_traces, list) {
                tf->tput_reads_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds);
                tf->tput_writes_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 = calloc(alloc_ptrs, sizeof(struct graph_dot_data));
                tf->gdd_reads = calloc(alloc_ptrs, sizeof(struct graph_dot_data));
@@ -279,6 +303,15 @@ static void setup_trace_file_graphs(void)
                        tf->mpstat_gld[i]->max = 100;
                }
        }
+
+       list_for_each_entry(tf, &fio_traces, list) {
+               if (tf->trace->fio_seconds > 0) {
+                       tf->fio_gld = alloc_line_data(tf->min_seconds,
+                                                     tf->max_seconds,
+                                                     tf->stop_seconds);
+               }
+       }
+
 }
 
 static void read_traces(void)
@@ -294,6 +327,7 @@ static void read_traces(void)
 
        list_for_each_entry(tf, &all_traces, list) {
                path = join_path(blktrace_dest_dir, tf->filename);
+
                trace = open_trace(path);
                if (!trace)
                        exit(1);
@@ -314,8 +348,18 @@ static void read_traces(void)
                tf->mpstat_max_seconds = trace->mpstat_seconds;
                if (tf->mpstat_max_seconds)
                        found_mpstat = 1;
+
                free(path);
        }
+
+       list_for_each_entry(tf, &fio_traces, list) {
+               trace = open_fio_trace(tf->filename);
+               if (!trace)
+                       exit(1);
+               tf->trace = trace;
+               tf->max_seconds = tf->trace->fio_seconds;
+               tf->stop_seconds = tf->trace->fio_seconds;
+       }
 }
 
 static void pick_line_graph_color(void)
@@ -343,6 +387,26 @@ static void pick_line_graph_color(void)
        }
 }
 
+static void read_fio_events(struct trace_file *tf)
+{
+       u64 bw = 0;
+       int time = 0;
+       int dir = 0;
+       int ret;
+
+       first_fio(tf->trace);
+       while (1) {
+               ret = read_fio_event(tf->trace, &time, &bw, &dir);
+               if (ret)
+                       break;
+
+               if (dir <= 1)
+                       add_fio_gld(time, bw, tf->fio_gld);
+               if (next_fio_line(tf->trace))
+                       break;
+       }
+}
+
 static void read_trace_events(void)
 {
 
@@ -355,8 +419,12 @@ static void read_trace_events(void)
        double max_user = 0, max_sys = 0, max_iowait = 0,
               max_irq = 0, max_soft = 0;
 
+       list_for_each_entry(tf, &fio_traces, list)
+               read_fio_events(tf);
+
        list_for_each_entry(tf, &all_traces, list) {
                trace = tf->trace;
+
                first_record(trace);
                while (1) {
                        check_record(trace);
@@ -433,6 +501,15 @@ static void set_trace_label(char *label)
                longest_label = len;
 
        list_for_each_entry(tf, &all_traces, list) {
+               if (cur == label_index) {
+                       tf->label = strdup(label);
+                       label_index++;
+                       return;
+                       break;
+               }
+               cur++;
+       }
+       list_for_each_entry(tf, &fio_traces, list) {
                if (cur == label_index) {
                        tf->label = strdup(label);
                        label_index++;
@@ -468,8 +545,9 @@ static void compare_minmax_tf(struct trace_file *tf, int *max_seconds, u64 *min_
 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) {
+       struct list_head *traces = &all_traces;
+again:
+       list_for_each_entry(tf, traces, list) {
                tf->min_seconds = min_seconds;
                tf->max_seconds = max_seconds;
                if (tf->stop_seconds > max_seconds)
@@ -483,6 +561,10 @@ static void set_all_minmax_tf(int min_seconds, int max_seconds, u64 min_offset,
                tf->min_offset = min_offset;
                tf->max_offset = max_offset;
        }
+       if (traces == &all_traces) {
+               traces = &fio_traces;
+               goto again;
+       }
 }
 
 static char *create_movie_temp_dir(void)
@@ -748,6 +830,57 @@ static void plot_tput(struct plot *plot, int min_seconds, int max_seconds)
        total_graphs_written++;
 }
 
+static void plot_fio_tput(struct plot *plot, int min_seconds, int max_seconds)
+{
+       struct trace_file *tf;
+       char *units;
+       char line[128];
+       u64 max = 0;
+
+       if (num_fio_traces == 0 || active_graphs[FIO_GRAPH_INDEX] == 0)
+               return;
+
+       if (num_fio_traces > 1)
+               svg_alloc_legend(plot, num_fio_traces);
+
+       list_for_each_entry(tf, &fio_traces, list) {
+               if (tf->fio_gld->max > max)
+                       max = tf->fio_gld->max;
+       }
+
+       list_for_each_entry(tf, &fio_traces, list) {
+               if (tf->fio_gld->max > 0)
+                       tf->fio_gld->max = max;
+       }
+
+       setup_axis(plot);
+       set_plot_label(plot, "Fio Throughput");
+
+       tf = list_entry(all_traces.next, struct trace_file, list);
+
+       scale_line_graph_bytes(&max, &units, 1024);
+       sprintf(line, "%sB/s", units);
+       set_ylabel(plot, line);
+       set_yticks(plot, num_yticks, 0, max, "");
+
+       set_xticks(plot, num_xticks, min_seconds, max_seconds);
+       list_for_each_entry(tf, &fio_traces, list) {
+               if (tf->fio_gld->max > 0) {
+                       svg_line_graph(plot, tf->fio_gld, tf->line_color, 0, 0);
+                       if (num_fio_traces > 1)
+                               svg_add_legend(plot, tf->label, "", tf->line_color);
+               }
+       }
+
+       if (plot->add_xlabel)
+               set_xlabel(plot, "Time (seconds)");
+
+       if (num_fio_traces > 1)
+               svg_write_legend(plot);
+       close_plot(plot);
+       total_graphs_written++;
+}
+
 static void plot_cpu(struct plot *plot, int max_seconds, char *label,
                     int active_index, int gld_index)
 {
@@ -1170,9 +1303,10 @@ enum {
        HELP_LONG_OPT = 1,
 };
 
-char *option_string = "T:t:o:l:r:O:N:d:D:p:m::h:w:c:x:y:a:C:PK";
+char *option_string = "F:T:t:o:l:r:O:N:d:D:p:m::h:w:c:x:y:a:C:PK";
 static struct option long_options[] = {
        {"columns", required_argument, 0, 'c'},
+       {"fio-trace", required_argument, 0, 'F'},
        {"title", required_argument, 0, 'T'},
        {"trace", required_argument, 0, 't'},
        {"output", required_argument, 0, 'o'},
@@ -1202,6 +1336,7 @@ static void print_usage(void)
                "\t-d (--device): device for blktrace to trace\n"
                "\t-D (--blktrace-destination): destination for blktrace\n"
                "\t-t (--trace): trace file name (more than one allowed)\n"
+               "\t-F (--fio-trace): fio bandwidth trace (more than one allowed)\n"
                "\t-l (--label): trace label in the graph\n"
                "\t-o (--output): output file name (SVG only)\n"
                "\t-p (--prog): program to run while blktrace is run\n"
@@ -1300,6 +1435,9 @@ static int parse_options(int ac, char **av)
                        add_trace_file(optarg);
                        set_blktrace_outfile(optarg);
                        break;
+               case 'F':
+                       add_fio_trace_file(optarg);
+                       break;
                case 'o':
                        output_filename = strdup(optarg);
                        break;
@@ -1459,7 +1597,7 @@ int main(int ac, char **av)
        if (opt_graph_width)
                set_graph_width(opt_graph_width);
 
-       if (list_empty(&all_traces)) {
+       if (list_empty(&all_traces) && list_empty(&fio_traces)) {
                fprintf(stderr, "No traces found, exiting\n");
                exit(1);
        }
@@ -1510,6 +1648,8 @@ int main(int ac, char **av)
        /* step two, find the maxes for time and offset */
        list_for_each_entry(tf, &all_traces, list)
                compare_minmax_tf(tf, &max_seconds, &min_offset, &max_offset);
+       list_for_each_entry(tf, &fio_traces, list)
+               compare_minmax_tf(tf, &max_seconds, &min_offset, &max_offset);
        min_seconds = min_time;
        if (max_seconds > max_time)
                max_seconds = ceil(max_time);
@@ -1541,7 +1681,7 @@ int main(int ac, char **av)
 
        if (active_graphs[IO_GRAPH_INDEX] || found_mpstat)
                set_legend_width(longest_label + longest_proc_name + 1 + strlen("writes"));
-       else if (num_traces > 1)
+       else if (num_traces >= 1 || num_fio_traces >= 1)
                set_legend_width(longest_label);
        else
                set_legend_width(0);
@@ -1567,6 +1707,9 @@ int main(int ac, char **av)
        check_plot_columns(plot, TPUT_GRAPH_INDEX);
        plot_tput(plot, min_seconds, max_seconds);
 
+       check_plot_columns(plot, FIO_GRAPH_INDEX);
+       plot_fio_tput(plot, min_seconds, max_seconds);
+
        check_plot_columns(plot, CPU_IO_GRAPH_INDEX);
        plot_cpu(plot, max_seconds, "CPU IO Wait Time",
                 CPU_IO_GRAPH_INDEX, MPSTAT_IO);
index e5af3ad82bf9aed0d176b85b9cb83e4192085020..187789a2b94285080819743a987bf0ac1c957ef2 100644 (file)
@@ -96,6 +96,19 @@ char *pick_color(void)
        return ret;
 }
 
+char *pick_fio_color(void)
+{
+       static int fio_color_index;
+       char *ret = colors[fio_color_index];
+
+       if (!ret) {
+               fio_color_index = 0;
+               ret = colors[fio_color_index];
+       }
+       fio_color_index += 2;
+       return ret;
+}
+
 static int cpu_color_index;
 
 char *pick_cpu_color(void)
index bc85f457f502d765cab6be492ef000d2dea284a3..42cbea0d9f97234f73a3bbc6e5854c8b3f96641a 100644 (file)
@@ -133,6 +133,7 @@ struct plot_history {
 };
 
 char *pick_color(void);
+char *pick_fio_color(void);
 char *pick_cpu_color(void);
 void reset_cpu_color(void);
 int svg_io_graph(struct plot *plot, struct graph_dot_data *gdd);
@@ -178,4 +179,5 @@ int svg_io_graph_movie_array_spindle(struct plot *plot, struct pid_plot_history
 void rewind_spindle_steps(int num);
 void setup_axis_spindle(struct plot *plot);
 int close_plot_col(struct plot *plot);
+
 #endif