From 24e48e035c6cf14b33408fb2fa899bfdda8bf439 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 18 Jan 2013 11:08:39 -0500 Subject: [PATCH] iowatcher: Add support for fio bandwith logs (-F logfile) Signed-off-by: Chris Mason --- iowatcher/Makefile | 2 +- iowatcher/blkparse.h | 11 +++ iowatcher/fio.c | 217 +++++++++++++++++++++++++++++++++++++++++++ iowatcher/fio.h | 26 ++++++ iowatcher/main.c | 153 +++++++++++++++++++++++++++++- iowatcher/plot.c | 13 +++ iowatcher/plot.h | 2 + 7 files changed, 418 insertions(+), 6 deletions(-) create mode 100644 iowatcher/fio.c create mode 100644 iowatcher/fio.h diff --git a/iowatcher/Makefile b/iowatcher/Makefile index 0e96043..7b5101c 100644 --- a/iowatcher/Makefile +++ b/iowatcher/Makefile @@ -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: diff --git a/iowatcher/blkparse.h b/iowatcher/blkparse.h index d435a87..68becbf 100644 --- a/iowatcher/blkparse.h +++ b/iowatcher/blkparse.h @@ -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 index 0000000..110f4a1 --- /dev/null +++ b/iowatcher/fio.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..7f49ddd --- /dev/null +++ b/iowatcher/fio.h @@ -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 diff --git a/iowatcher/main.c b/iowatcher/main.c index fbd8a88..827f57d 100644 --- a/iowatcher/main.c +++ b/iowatcher/main.c @@ -39,8 +39,10 @@ #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); diff --git a/iowatcher/plot.c b/iowatcher/plot.c index e5af3ad..187789a 100644 --- a/iowatcher/plot.c +++ b/iowatcher/plot.c @@ -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) diff --git a/iowatcher/plot.h b/iowatcher/plot.h index bc85f45..42cbea0 100644 --- a/iowatcher/plot.h +++ b/iowatcher/plot.h @@ -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 -- 2.25.1