2 * Copyright (C) 2012 Fusion-io
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * Parts of this file were imported from Jens Axboe's blktrace sources (also GPL)
19 #include <sys/types.h>
28 #include <asm/types.h>
40 LIST_HEAD(all_traces);
42 static int color_index = 0;
46 "orange", "darkviolet",
53 char *pick_color(void) {
54 char *ret = colors[color_index];
57 ret = colors[color_index];
63 char *pick_cpu_color(void) {
64 char *ret = colors[color_index];
67 ret = colors[color_index];
77 QUEUE_DEPTH_GRAPH_INDEX,
82 static char *graphs_by_name[] = {
90 static int active_graphs[TOTAL_GRAPHS];
91 static int last_active_graph = IOPS_GRAPH_INDEX;
93 static int label_index = 0;
94 static int num_traces = 0;
95 static int longest_label = 0;
98 struct list_head list;
109 struct graph_line_data *tput_gld;
110 struct graph_line_data *iop_gld;
111 struct graph_line_data *latency_gld;
112 struct graph_line_data *queue_depth_gld;
113 struct graph_dot_data *gdd_writes;
114 struct graph_dot_data *gdd_reads;
117 static void enable_all_graphs(void)
120 for (i = 0; i < TOTAL_GRAPHS; i++)
121 active_graphs[i] = 1;
124 static void disable_all_graphs(void)
127 for (i = 0; i < TOTAL_GRAPHS; i++)
128 active_graphs[i] = 0;
131 static int enable_one_graph(char *name)
134 for (i = 0; i < TOTAL_GRAPHS; i++) {
135 if (strcmp(name, graphs_by_name[i]) == 0) {
136 active_graphs[i] = 1;
143 static int disable_one_graph(char *name)
146 for (i = 0; i < TOTAL_GRAPHS; i++) {
147 if (strcmp(name, graphs_by_name[i]) == 0) {
148 active_graphs[i] = 0;
155 static int last_graph(void)
158 for (i = TOTAL_GRAPHS - 1; i >= 0; i--) {
159 if (active_graphs[i]) {
166 static void add_trace_file(char *filename)
168 struct trace_file *tf;
170 tf = calloc(1, sizeof(*tf));
172 fprintf(stderr, "Unable to allocate memory\n");
175 tf->filename = strdup(filename);
176 list_add_tail(&tf->list, &all_traces);
177 tf->read_color = pick_color();
178 tf->write_color = pick_color();
182 static void setup_trace_file_graphs(void)
184 struct trace_file *tf;
186 list_for_each_entry(tf, &all_traces, list) {
187 tf->tput_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
188 tf->latency_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
189 tf->queue_depth_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
190 tf->iop_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
191 tf->gdd_writes = alloc_dot_data(tf->seconds, tf->max_offset, tf->stop_seconds);
192 tf->gdd_reads = alloc_dot_data(tf->seconds, tf->max_offset, tf->stop_seconds);
196 static void read_traces(void)
198 struct trace_file *tf;
204 list_for_each_entry(tf, &all_traces, list) {
205 trace = open_trace(tf->filename);
209 last_time = find_last_time(trace);
211 tf->seconds = SECONDS(last_time);
212 tf->stop_seconds = SECONDS(last_time);
213 tf->max_offset = find_highest_offset(trace);
215 filter_outliers(trace, tf->max_offset, &ymin, &ymax);
216 tf->max_offset = ymax;
220 static void read_trace_events(void)
223 struct trace_file *tf;
227 list_for_each_entry(tf, &all_traces, list) {
232 add_tput(trace, tf->tput_gld);
233 add_iop(trace, tf->iop_gld);
234 add_io(trace, tf->gdd_writes, tf->gdd_reads);
235 add_pending_io(trace, tf->queue_depth_gld);
236 add_completed_io(trace, tf->latency_gld);
237 ret = next_record(trace);
244 static void set_trace_label(char *label)
247 struct trace_file *tf;
248 int len = strlen(label);
250 if (len > longest_label)
253 list_for_each_entry(tf, &all_traces, list) {
254 if (cur == label_index) {
255 tf->label = strdup(label);
263 static char *graph_title = "";
264 static char *output_filename = "trace.svg";
265 static char *blktrace_device = NULL;
266 static char *blktrace_outfile = "trace";
267 static char *blktrace_dest_dir = ".";
268 static char *program_to_run = NULL;
270 static void set_blktrace_outfile(char *arg)
272 char *s = strdup(arg);
273 char *last_dot = strrchr(s, '.');
276 if (strcmp(last_dot, ".dump") == 0)
279 blktrace_outfile = s;
283 char *option_string = "hT:t:o:l:r:O:N:d:p:";
284 static struct option long_options[] = {
285 {"title", required_argument, 0, 'T'},
286 {"trace", required_argument, 0, 't'},
287 {"output", required_argument, 0, 'o'},
288 {"label", required_argument, 0, 'l'},
289 {"rolling", required_argument, 0, 'r'},
290 {"no-graph", required_argument, 0, 'N'},
291 {"only-graph", required_argument, 0, 'O'},
292 {"device", required_argument, 0, 'd'},
293 {"prog", required_argument, 0, 'p'},
294 {"help", required_argument, 0, 'h'},
298 static void print_usage(void)
300 fprintf(stderr, "iowatcher usage:\n"
301 "\t-d (--device): device for blktrace to trace\n"
302 "\t-t (--trace): trace file name (more than one allowed)\n"
303 "\t-l (--label): trace label in the graph\n"
304 "\t-o (--output): output file name (SVG only)\n"
305 "\t-p (--prog): program to run while blktrace is run\n"
306 "\t-r (--rolling): number of seconds in the rolling averge\n"
307 "\t-T (--title): graph title\n"
308 "\t-N (--no-graph): skip a single graph (io, tput, latency, queue_depth, iops)\n"
309 "\t-O (--only-graph): add a single graph (io, tput, latency, queue_depth, iops)\n"
314 static int parse_options(int ac, char **av)
320 // int this_option_optind = optind ? optind : 1;
321 int option_index = 0;
323 c = getopt_long(ac, av, option_string,
324 long_options, &option_index);
334 graph_title = strdup(optarg);
337 add_trace_file(optarg);
338 set_blktrace_outfile(optarg);
341 output_filename = strdup(optarg);
344 set_trace_label(optarg);
347 set_rolling_avg(atoi(optarg));
351 disable_all_graphs();
354 enable_one_graph(optarg);
357 disable_one_graph(optarg);
360 blktrace_device = strdup(optarg);
363 program_to_run = strdup(optarg);
375 static void compare_max_tf(struct trace_file *tf, int *seconds, u64 *max_offset)
377 if (tf->seconds > *seconds)
378 *seconds = tf->seconds;
379 if (tf->max_offset > *max_offset)
380 *max_offset = tf->max_offset;
383 static void set_all_max_tf(int seconds, u64 max_offset)
385 struct trace_file *tf;
387 list_for_each_entry(tf, &all_traces, list) {
388 tf->seconds = seconds;
389 tf->max_offset = max_offset;
393 static void plot_io(struct plot *plot, int seconds, u64 max_offset)
395 struct trace_file *tf;
397 if (active_graphs[IO_GRAPH_INDEX] == 0)
400 plot->add_xlabel = last_active_graph == IO_GRAPH_INDEX;
403 svg_alloc_legend(plot, num_traces * 2);
405 set_plot_label(plot, "Device IO");
406 set_ylabel(plot, "Offset (MB)");
407 set_yticks(plot, 4, 0, max_offset / (1024 * 1024), "");
408 set_xticks(plot, 9, 0, seconds);
410 list_for_each_entry(tf, &all_traces, list) {
411 char *label = tf->label;
415 svg_io_graph(plot, tf->gdd_reads, tf->read_color);
416 if (tf->gdd_reads->total_ios)
417 svg_add_legend(plot, label, " Reads", tf->read_color);
419 svg_io_graph(plot, tf->gdd_writes, tf->write_color);
420 if (tf->gdd_writes->total_ios) {
421 svg_add_legend(plot, label, " Writes", tf->write_color);
424 if (plot->add_xlabel)
425 set_xlabel(plot, "Time (seconds)");
426 svg_write_legend(plot);
430 static void plot_tput(struct plot *plot, int seconds)
432 struct trace_file *tf;
437 if (active_graphs[TPUT_GRAPH_INDEX] == 0)
441 svg_alloc_legend(plot, num_traces);
442 list_for_each_entry(tf, &all_traces, list) {
443 if (tf->tput_gld->max > max)
444 max = tf->tput_gld->max;
446 list_for_each_entry(tf, &all_traces, list)
447 tf->tput_gld->max = max;
449 plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
451 set_plot_label(plot, "Throughput");
453 tf = list_entry(all_traces.next, struct trace_file, list);
455 scale_line_graph_bytes(&max, &units, 1024);
456 sprintf(line, "%sB/s", units);
457 set_ylabel(plot, line);
458 set_yticks(plot, 4, 0, max, "");
459 set_xticks(plot, 9, 0, seconds);
461 list_for_each_entry(tf, &all_traces, list) {
462 svg_line_graph(plot, tf->tput_gld, tf->read_color);
464 svg_add_legend(plot, tf->label, "", tf->read_color);
467 if (plot->add_xlabel)
468 set_xlabel(plot, "Time (seconds)");
470 svg_write_legend(plot);
474 static void plot_latency(struct plot *plot, int seconds)
476 struct trace_file *tf;
481 if (active_graphs[LATENCY_GRAPH_INDEX] == 0)
485 svg_alloc_legend(plot, num_traces);
486 list_for_each_entry(tf, &all_traces, list) {
487 if (tf->latency_gld->max > max)
488 max = tf->latency_gld->max;
490 list_for_each_entry(tf, &all_traces, list)
491 tf->latency_gld->max = max;
493 plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
495 set_plot_label(plot, "IO Latency");
497 tf = list_entry(all_traces.next, struct trace_file, list);
499 scale_line_graph_time(&max, &units);
500 sprintf(line, "latency (%ss)", units);
501 set_ylabel(plot, line);
502 set_yticks(plot, 4, 0, max, "");
503 set_xticks(plot, 9, 0, seconds);
505 list_for_each_entry(tf, &all_traces, list) {
506 svg_line_graph(plot, tf->latency_gld, tf->read_color);
508 svg_add_legend(plot, tf->label, "", tf->read_color);
511 if (plot->add_xlabel)
512 set_xlabel(plot, "Time (seconds)");
514 svg_write_legend(plot);
518 static void plot_queue_depth(struct plot *plot, int seconds)
520 struct trace_file *tf;
522 if (active_graphs[QUEUE_DEPTH_GRAPH_INDEX] == 0)
525 plot->add_xlabel = last_active_graph == QUEUE_DEPTH_GRAPH_INDEX;
528 set_plot_label(plot, "Queue Depth");
530 svg_alloc_legend(plot, num_traces);
532 tf = list_entry(all_traces.next, struct trace_file, list);
533 set_ylabel(plot, "Pending IO");
534 set_yticks(plot, 4, 0, tf->queue_depth_gld->max, "");
535 set_xticks(plot, 9, 0, seconds);
537 list_for_each_entry(tf, &all_traces, list) {
538 svg_line_graph(plot, tf->queue_depth_gld, tf->read_color);
540 svg_add_legend(plot, tf->label, "", tf->read_color);
543 if (plot->add_xlabel)
544 set_xlabel(plot, "Time (seconds)");
546 svg_write_legend(plot);
550 static void plot_iops(struct plot *plot, int seconds)
552 struct trace_file *tf;
556 if (active_graphs[IOPS_GRAPH_INDEX] == 0)
559 list_for_each_entry(tf, &all_traces, list) {
560 if (tf->iop_gld->max > max)
561 max = tf->iop_gld->max;
564 list_for_each_entry(tf, &all_traces, list)
565 tf->iop_gld->max = max;
568 plot->add_xlabel = last_active_graph == IOPS_GRAPH_INDEX;
570 set_plot_label(plot, "IOPs");
572 svg_alloc_legend(plot, num_traces);
574 tf = list_entry(all_traces.next, struct trace_file, list);
576 scale_line_graph_bytes(&max, &units, 1000);
577 set_ylabel(plot, "IO/s");
579 set_yticks(plot, 4, 0, max, units);
580 set_xticks(plot, 9, 0, seconds);
582 list_for_each_entry(tf, &all_traces, list) {
583 svg_line_graph(plot, tf->iop_gld, tf->read_color);
585 svg_add_legend(plot, tf->label, "", tf->read_color);
588 if (plot->add_xlabel)
589 set_xlabel(plot, "Time (seconds)");
591 svg_write_legend(plot);
596 int main(int ac, char **av)
602 struct trace_file *tf;
605 init_io_hash_table();
609 parse_options(ac, av);
611 last_active_graph = last_graph();
613 if (list_empty(&all_traces)) {
614 fprintf(stderr, "No traces found, exiting\n");
618 if (blktrace_device) {
619 ret = start_blktrace(blktrace_device, blktrace_outfile,
622 fprintf(stderr, "exiting due to blktrace failure\n");
625 if (program_to_run) {
626 ret = run_program(program_to_run);
628 fprintf(stderr, "failed to run %s\n",
633 blktrace_to_dump(blktrace_outfile);
635 /* no program specified, just wait for
642 /* step one, read all the traces */
645 /* step two, find the maxes for time and offset */
646 list_for_each_entry(tf, &all_traces, list)
647 compare_max_tf(tf, &seconds, &max_offset);
649 /* push the max we found into all the tfs */
650 set_all_max_tf(seconds, max_offset);
652 /* alloc graphing structs for all the traces */
653 setup_trace_file_graphs();
655 /* run through all the traces and read their events */
658 fd = open(output_filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
660 fprintf(stderr, "Unable to open output file %s %s\n",
661 output_filename, strerror(errno));
665 write_svg_header(fd);
666 plot = alloc_plot(fd);
668 if (active_graphs[IO_GRAPH_INDEX])
669 set_legend_width(longest_label + strlen("writes"));
670 else if (num_traces > 1)
671 set_legend_width(longest_label);
675 set_plot_title(plot, graph_title);
677 plot_io(plot, seconds, max_offset);
678 plot_tput(plot, seconds);
679 plot_latency(plot, seconds);
680 plot_queue_depth(plot, seconds);
681 plot_iops(plot, seconds);