Add support for fio bandwith logs (-F logfile)
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
diff --git a/Makefile b/Makefile
index 0e96043..7b5101c 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@
%.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/blkparse.h b/blkparse.h
index d435a87..68becbf 100644
--- a/blkparse.h
+++ b/blkparse.h
@@ -57,6 +57,12 @@
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 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/fio.c b/fio.c
new file mode 100644
index 0000000..110f4a1
--- /dev/null
+++ b/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 <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/fio.h b/fio.h
new file mode 100644
index 0000000..7f49ddd
--- /dev/null
+++ b/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/main.c b/main.c
index fbd8a88..827f57d 100644
--- a/main.c
+++ b/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 @@
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 @@
static char *graphs_by_name[] = {
"io",
"tput",
+ "fio",
"cpu-sys",
"cpu-io",
"cpu-irq",
@@ -135,6 +139,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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);
@@ -436,6 +504,15 @@
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++;
break;
}
cur++;
@@ -468,8 +545,9 @@
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 @@
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 @@
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 @@
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 @@
"\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 @@
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 @@
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 @@
/* 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 @@
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 @@
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/plot.c b/plot.c
index e5af3ad..187789a 100644
--- a/plot.c
+++ b/plot.c
@@ -96,6 +96,19 @@
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/plot.h b/plot.h
index bc85f45..42cbea0 100644
--- a/plot.h
+++ b/plot.h
@@ -133,6 +133,7 @@
};
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 @@
void rewind_spindle_steps(int num);
void setup_axis_spindle(struct plot *plot);
int close_plot_col(struct plot *plot);
+
#endif