iowatcher: Add mpstat graphing support
[blktrace.git] / iowatcher / main.c
1 /*
2  * Copyright (C) 2012 Fusion-io
3  *
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.
7  *
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.
12  *
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
16  *
17  *  Parts of this file were imported from Jens Axboe's blktrace sources (also GPL)
18  */
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <math.h>
26 #include <inttypes.h>
27 #include <string.h>
28 #include <asm/types.h>
29 #include <errno.h>
30 #include <sys/mman.h>
31 #include <time.h>
32 #include <math.h>
33 #include <getopt.h>
34
35 #include "plot.h"
36 #include "blkparse.h"
37 #include "list.h"
38 #include "tracers.h"
39 #include "mpstat.h"
40
41 LIST_HEAD(all_traces);
42
43 static int found_mpstat = 0;
44 static int cpu_color_index = 0;
45 static int color_index = 0;
46 char *colors[] = {
47         "blue", "darkgreen",
48         "red", "aqua",
49         "orange", "darkviolet",
50         "brown", "#00FF00",
51         "yellow", "coral",
52         "black", "darkred",
53         "fuchsia", "crimson",
54         NULL };
55
56 char *pick_color(void) {
57         char *ret = colors[color_index];
58         if (!ret) {
59                 color_index = 0;
60                 ret = colors[color_index];
61         }
62         color_index++;
63         return ret;
64 }
65
66 char *pick_cpu_color(void) {
67         char *ret = colors[cpu_color_index];
68         if (!ret) {
69                 color_index = 0;
70                 ret = colors[cpu_color_index];
71         }
72         cpu_color_index++;
73         return ret;
74 }
75
76 enum {
77         IO_GRAPH_INDEX = 0,
78         TPUT_GRAPH_INDEX,
79         CPU_SYS_GRAPH_INDEX,
80         CPU_IO_GRAPH_INDEX,
81         CPU_IRQ_GRAPH_INDEX,
82         CPU_SOFT_GRAPH_INDEX,
83         CPU_USER_GRAPH_INDEX,
84         LATENCY_GRAPH_INDEX,
85         QUEUE_DEPTH_GRAPH_INDEX,
86         IOPS_GRAPH_INDEX,
87         TOTAL_GRAPHS
88 };
89
90 enum {
91         MPSTAT_SYS = 0,
92         MPSTAT_IRQ,
93         MPSTAT_IO,
94         MPSTAT_SOFT,
95         MPSTAT_USER,
96         MPSTAT_GRAPHS
97 };
98
99 static char *graphs_by_name[] = {
100         "io",
101         "tput",
102         "cpu-sys",
103         "cpu-io",
104         "cpu-irq",
105         "cpu-soft",
106         "cpu-user",
107         "latency",
108         "queue-depth",
109         "iops",
110 };
111
112 static int active_graphs[TOTAL_GRAPHS];
113 static int last_active_graph = IOPS_GRAPH_INDEX;
114
115 static int label_index = 0;
116 static int num_traces = 0;
117 static int longest_label = 0;
118
119 struct trace_file {
120         struct list_head list;
121         char *filename;
122         char *label;
123         struct trace *trace;
124         int seconds;
125         int stop_seconds;
126         u64 max_offset;
127
128         char *read_color;
129         char *write_color;
130
131         struct graph_line_data *tput_gld;
132         struct graph_line_data *iop_gld;
133         struct graph_line_data *latency_gld;
134         struct graph_line_data *queue_depth_gld;
135         struct graph_dot_data *gdd_writes;
136         struct graph_dot_data *gdd_reads;
137
138         int mpstat_seconds;
139         int mpstat_stop_seconds;
140         struct graph_line_data **mpstat_gld;
141 };
142
143 static void alloc_mpstat_gld(struct trace_file *tf)
144 {
145         struct graph_line_data **ptr;
146
147         if (tf->trace->mpstat_num_cpus == 0)
148                 return;
149
150         ptr = calloc((tf->trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS,
151                      sizeof(struct graph_line_data *));
152         if (!ptr) {
153                 perror("Unable to allocate mpstat arrays\n");
154                 exit(1);
155         }
156         tf->mpstat_gld = ptr;
157 }
158
159 static void enable_all_graphs(void)
160 {
161         int i;
162         for (i = 0; i < TOTAL_GRAPHS; i++)
163                 active_graphs[i] = 1;
164 }
165
166 static void disable_all_graphs(void)
167 {
168         int i;
169         for (i = 0; i < TOTAL_GRAPHS; i++)
170                 active_graphs[i] = 0;
171 }
172
173 static int enable_one_graph(char *name)
174 {
175         int i;
176         for (i = 0; i < TOTAL_GRAPHS; i++) {
177                 if (strcmp(name, graphs_by_name[i]) == 0) {
178                         active_graphs[i] = 1;
179                         return 0;
180                 }
181         }
182         return -ENOENT;
183 }
184
185 static int disable_one_graph(char *name)
186 {
187         int i;
188         for (i = 0; i < TOTAL_GRAPHS; i++) {
189                 if (strcmp(name, graphs_by_name[i]) == 0) {
190                         active_graphs[i] = 0;
191                         return 0;
192                 }
193         }
194         return -ENOENT;
195 }
196
197 static int last_graph(void)
198 {
199         int i;
200         for (i = TOTAL_GRAPHS - 1; i >= 0; i--) {
201                 if (active_graphs[i]) {
202                         return i;
203                 }
204         }
205         return -ENOENT;
206 }
207 static void add_trace_file(char *filename)
208 {
209         struct trace_file *tf;
210
211         tf = calloc(1, sizeof(*tf));
212         if (!tf) {
213                 fprintf(stderr, "Unable to allocate memory\n");
214                 exit(1);
215         }
216         tf->label = "";
217         tf->filename = strdup(filename);
218         list_add_tail(&tf->list, &all_traces);
219         tf->read_color = pick_color();
220         tf->write_color = pick_color();
221         num_traces++;
222 }
223
224 static void setup_trace_file_graphs(void)
225 {
226         struct trace_file *tf;
227         int i;
228
229         list_for_each_entry(tf, &all_traces, list) {
230                 tf->tput_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
231                 tf->latency_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
232                 tf->queue_depth_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
233                 tf->iop_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
234                 tf->gdd_writes = alloc_dot_data(tf->seconds, tf->max_offset, tf->stop_seconds);
235                 tf->gdd_reads = alloc_dot_data(tf->seconds, tf->max_offset, tf->stop_seconds);
236
237                 if (tf->trace->mpstat_num_cpus == 0)
238                         continue;
239
240                 alloc_mpstat_gld(tf);
241                 for (i = 0; i < (tf->trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS; i++) {
242                         tf->mpstat_gld[i] =
243                                 alloc_line_data(tf->mpstat_seconds,
244                                                 tf->mpstat_seconds);
245                         tf->mpstat_gld[i]->max = 100;
246                 }
247         }
248 }
249
250 static void read_traces(void)
251 {
252         struct trace_file *tf;
253         struct trace *trace;
254         u64 last_time;
255         u64 ymin;
256         u64 ymax;
257
258         list_for_each_entry(tf, &all_traces, list) {
259                 trace = open_trace(tf->filename);
260                 if (!trace)
261                         exit(1);
262
263                 last_time = find_last_time(trace);
264                 tf->trace = trace;
265                 tf->seconds = SECONDS(last_time);
266                 tf->stop_seconds = SECONDS(last_time);
267                 tf->max_offset = find_highest_offset(trace);
268
269                 filter_outliers(trace, tf->max_offset, &ymin, &ymax);
270                 tf->max_offset = ymax;
271
272                 read_mpstat(trace, tf->filename);
273                 tf->mpstat_stop_seconds = trace->mpstat_seconds;
274                 tf->mpstat_seconds = trace->mpstat_seconds;
275                 if (tf->mpstat_seconds)
276                         found_mpstat = 1;
277         }
278 }
279
280 static void read_trace_events(void)
281 {
282
283         struct trace_file *tf;
284         struct trace *trace;
285         int ret;
286         int i;
287         int time;
288         double user, sys, iowait, irq, soft;
289         double max_user = 0, max_sys = 0, max_iowait = 0,
290                max_irq = 0, max_soft = 0;
291
292         list_for_each_entry(tf, &all_traces, list) {
293                 trace = tf->trace;
294                 first_record(trace);
295                 while (1) {
296                         check_record(trace);
297                         add_tput(trace, tf->tput_gld);
298                         add_iop(trace, tf->iop_gld);
299                         add_io(trace, tf->gdd_writes, tf->gdd_reads);
300                         add_pending_io(trace, tf->queue_depth_gld);
301                         add_completed_io(trace, tf->latency_gld);
302                         ret = next_record(trace);
303                         if (ret)
304                                 break;
305                 }
306         }
307         list_for_each_entry(tf, &all_traces, list) {
308                 trace = tf->trace;
309
310                 if (trace->mpstat_num_cpus == 0)
311                         continue;
312
313                 first_mpstat(trace);
314
315                 for (time = 0; time < tf->mpstat_stop_seconds; time ++) {
316                         for (i = 0; i < (trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS; i += MPSTAT_GRAPHS) {
317                                 ret = read_mpstat_event(trace, &user, &sys,
318                                                         &iowait, &irq, &soft);
319                                 if (ret)
320                                         goto mpstat_done;
321                                 if (next_mpstat_line(trace))
322                                         goto mpstat_done;
323
324                                 if (sys > max_sys)
325                                         max_sys = sys;
326                                 if (user > max_user)
327                                         max_user = user;
328                                 if (irq > max_irq)
329                                         max_irq = irq;
330                                 if (iowait > max_iowait)
331                                         max_iowait = iowait;
332
333                                 add_mpstat_gld(time, sys, tf->mpstat_gld[i + MPSTAT_SYS]);
334                                 add_mpstat_gld(time, irq, tf->mpstat_gld[i + MPSTAT_IRQ]);
335                                 add_mpstat_gld(time, soft, tf->mpstat_gld[i + MPSTAT_SOFT]);
336                                 add_mpstat_gld(time, user, tf->mpstat_gld[i + MPSTAT_USER]);
337                                 add_mpstat_gld(time, iowait, tf->mpstat_gld[i + MPSTAT_IO]);
338                         }
339                         if (next_mpstat(trace) == NULL)
340                                 break;
341                 }
342         }
343
344 mpstat_done:
345         list_for_each_entry(tf, &all_traces, list) {
346                 trace = tf->trace;
347
348                 if (trace->mpstat_num_cpus == 0)
349                         continue;
350
351                 tf->mpstat_gld[MPSTAT_SYS]->max = max_sys;
352                 tf->mpstat_gld[MPSTAT_IRQ]->max = max_irq;
353                 tf->mpstat_gld[MPSTAT_SOFT]->max = max_soft;
354                 tf->mpstat_gld[MPSTAT_USER]->max = max_user;
355                 tf->mpstat_gld[MPSTAT_IO]->max = max_iowait;;
356         }
357         return;
358 }
359
360 static void set_trace_label(char *label)
361 {
362         int cur = 0;
363         struct trace_file *tf;
364         int len = strlen(label);
365
366         if (len > longest_label)
367                 longest_label = len;
368
369         list_for_each_entry(tf, &all_traces, list) {
370                 if (cur == label_index) {
371                         tf->label = strdup(label);
372                         label_index++;
373                         break;
374                 }
375                 cur++;
376         }
377 }
378
379 static char *graph_title = "";
380 static char *output_filename = "trace.svg";
381 static char *blktrace_device = NULL;
382 static char *blktrace_outfile = "trace";
383 static char *blktrace_dest_dir = ".";
384 static char *program_to_run = NULL;
385
386 static void set_blktrace_outfile(char *arg)
387 {
388         char *s = strdup(arg);
389         char *last_dot = strrchr(s, '.');
390
391         if (last_dot) {
392                 if (strcmp(last_dot, ".dump") == 0)
393                         *last_dot = '\0';
394         }
395         blktrace_outfile = s;
396 }
397
398
399 char *option_string = "hT:t:o:l:r:O:N:d:p:";
400 static struct option long_options[] = {
401         {"title", required_argument, 0, 'T'},
402         {"trace", required_argument, 0, 't'},
403         {"output", required_argument, 0, 'o'},
404         {"label", required_argument, 0, 'l'},
405         {"rolling", required_argument, 0, 'r'},
406         {"no-graph", required_argument, 0, 'N'},
407         {"only-graph", required_argument, 0, 'O'},
408         {"device", required_argument, 0, 'd'},
409         {"prog", required_argument, 0, 'p'},
410         {"help", required_argument, 0, 'h'},
411         {0, 0, 0, 0}
412 };
413
414 static void print_usage(void)
415 {
416         fprintf(stderr, "iowatcher usage:\n"
417                 "\t-d (--device): device for blktrace to trace\n"
418                 "\t-t (--trace): trace file name (more than one allowed)\n"
419                 "\t-l (--label): trace label in the graph\n"
420                 "\t-o (--output): output file name (SVG only)\n"
421                 "\t-p (--prog): program to run while blktrace is run\n"
422                 "\t-r (--rolling): number of seconds in the rolling averge\n"
423                 "\t-T (--title): graph title\n"
424                 "\t-N (--no-graph): skip a single graph (io, tput, latency, queue_depth, iops)\n"
425                 "\t-O (--only-graph): add a single graph (io, tput, latency, queue_depth, iops)\n"
426                );
427         exit(1);
428 }
429
430 static int parse_options(int ac, char **av)
431 {
432         int c;
433         int disabled = 0;
434
435         while (1) {
436                 // int this_option_optind = optind ? optind : 1;
437                 int option_index = 0;
438
439                 c = getopt_long(ac, av, option_string,
440                                 long_options, &option_index);
441
442                 if (c == -1)
443                         break;
444
445                 switch(c) {
446                 case 'h':
447                         print_usage();
448                         break;
449                 case 'T':
450                         graph_title = strdup(optarg);
451                         break;
452                 case 't':
453                         add_trace_file(optarg);
454                         set_blktrace_outfile(optarg);
455                         break;
456                 case 'o':
457                         output_filename = strdup(optarg);
458                         break;
459                 case 'l':
460                         set_trace_label(optarg);
461                         break;
462                 case 'r':
463                         set_rolling_avg(atoi(optarg));
464                         break;
465                 case 'O':
466                         if (!disabled) {
467                                 disable_all_graphs();
468                                 disabled = 1;
469                         }
470                         enable_one_graph(optarg);
471                         break;
472                 case 'N':
473                         disable_one_graph(optarg);
474                         break;
475                 case 'd':
476                         blktrace_device = strdup(optarg);
477                         break;
478                 case 'p':
479                         program_to_run = strdup(optarg);
480                         break;
481                 case '?':
482                         print_usage();
483                         break;
484                 default:
485                         break;
486                 }
487         }
488         return 0;
489 }
490
491 static void compare_max_tf(struct trace_file *tf, int *seconds, u64 *max_offset)
492 {
493         if (tf->seconds > *seconds)
494                 *seconds = tf->seconds;
495         if (tf->max_offset > *max_offset)
496                 *max_offset = tf->max_offset;
497 }
498
499 static void set_all_max_tf(int seconds, u64 max_offset)
500 {
501         struct trace_file *tf;
502
503         list_for_each_entry(tf, &all_traces, list) {
504                 tf->seconds = seconds;
505                 tf->max_offset = max_offset;
506         }
507 }
508
509 static void plot_io(struct plot *plot, int seconds, u64 max_offset)
510 {
511         struct trace_file *tf;
512
513         if (active_graphs[IO_GRAPH_INDEX] == 0)
514                 return;
515
516         plot->add_xlabel = last_active_graph == IO_GRAPH_INDEX;
517         setup_axis(plot);
518
519         svg_alloc_legend(plot, num_traces * 2);
520
521         set_plot_label(plot, "Device IO");
522         set_ylabel(plot, "Offset (MB)");
523         set_yticks(plot, 4, 0, max_offset / (1024 * 1024), "");
524         set_xticks(plot, 9, 0, seconds);
525
526         list_for_each_entry(tf, &all_traces, list) {
527                 char *label = tf->label;
528
529                 if (!label)
530                         label = "";
531                 svg_io_graph(plot, tf->gdd_reads, tf->read_color);
532                 if (tf->gdd_reads->total_ios)
533                         svg_add_legend(plot, label, " Reads", tf->read_color);
534
535                 svg_io_graph(plot, tf->gdd_writes, tf->write_color);
536                 if (tf->gdd_writes->total_ios) {
537                         svg_add_legend(plot, label, " Writes", tf->write_color);
538                 }
539         }
540         if (plot->add_xlabel)
541                 set_xlabel(plot, "Time (seconds)");
542         svg_write_legend(plot);
543         close_plot(plot);
544 }
545
546 static void plot_tput(struct plot *plot, int seconds)
547 {
548         struct trace_file *tf;
549         char *units;
550         char line[128];
551         u64 max = 0;
552
553         if (active_graphs[TPUT_GRAPH_INDEX] == 0)
554                 return;
555
556         if (num_traces > 1)
557                 svg_alloc_legend(plot, num_traces);
558         list_for_each_entry(tf, &all_traces, list) {
559                 if (tf->tput_gld->max > max)
560                         max = tf->tput_gld->max;
561         }
562         list_for_each_entry(tf, &all_traces, list)
563                 tf->tput_gld->max = max;
564
565         plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
566         setup_axis(plot);
567         set_plot_label(plot, "Throughput");
568
569         tf = list_entry(all_traces.next, struct trace_file, list);
570
571         scale_line_graph_bytes(&max, &units, 1024);
572         sprintf(line, "%sB/s", units);
573         set_ylabel(plot, line);
574         set_yticks(plot, 4, 0, max, "");
575         set_xticks(plot, 9, 0, seconds);
576
577         list_for_each_entry(tf, &all_traces, list) {
578                 svg_line_graph(plot, tf->tput_gld, tf->read_color);
579                 if (num_traces > 1)
580                         svg_add_legend(plot, tf->label, "", tf->read_color);
581         }
582
583         if (plot->add_xlabel)
584                 set_xlabel(plot, "Time (seconds)");
585         if (num_traces > 1)
586                 svg_write_legend(plot);
587         close_plot(plot);
588 }
589
590 static void plot_cpu(struct plot *plot, int seconds, char *label,
591                      int active_index, int gld_index)
592 {
593         struct trace_file *tf;
594         char line[128];
595         int max = 0;
596         int i;
597         int gld_i;
598         char *color;
599         double avg = 0;
600         int ymax;
601         int plotted = 0;
602
603         if (active_graphs[active_index] == 0)
604                 return;
605
606         list_for_each_entry(tf, &all_traces, list) {
607                 if (tf->trace->mpstat_num_cpus > max)
608                         max = tf->trace->mpstat_num_cpus;
609         }
610         if (max == 0)
611                 return;
612
613         tf = list_entry(all_traces.next, struct trace_file, list);
614
615         ymax = tf->mpstat_gld[gld_index]->max;
616         if (ymax == 0)
617                 return;
618
619         svg_alloc_legend(plot, num_traces * max);
620
621         plot->add_xlabel = last_active_graph == active_index;
622         setup_axis(plot);
623         set_plot_label(plot, label);
624
625         seconds = tf->mpstat_seconds;
626
627         set_yticks(plot, 4, 0, tf->mpstat_gld[gld_index]->max, "");
628         set_ylabel(plot, "Percent");
629         set_xticks(plot, 9, 0, seconds);
630
631         cpu_color_index = 0;
632         list_for_each_entry(tf, &all_traces, list) {
633                 for (i = 0; i < tf->mpstat_gld[0]->stop_seconds; i++) {
634                         avg += tf->mpstat_gld[gld_index]->data[i].sum;
635                 }
636                 avg /= tf->mpstat_gld[gld_index]->stop_seconds;
637                 color = pick_cpu_color();
638                 svg_line_graph(plot, tf->mpstat_gld[0], color);
639                 svg_add_legend(plot, tf->label, " avg", color);
640
641                 for (i = 1; i < tf->trace->mpstat_num_cpus + 1; i++) {
642                         struct graph_line_data *gld = tf->mpstat_gld[i * MPSTAT_GRAPHS + gld_index];
643                         double this_avg = 0;
644
645                         for (gld_i = 0; gld_i < gld->stop_seconds; gld_i++)
646                                 this_avg += gld->data[i].sum;
647
648                         this_avg /= gld->stop_seconds;
649
650                         for (gld_i = 0; gld_i < gld->stop_seconds; gld_i++) {
651                                 if (this_avg > avg + 30 ||
652                                     gld->data[gld_i].sum > 95) {
653                                         color = pick_cpu_color();
654                                         svg_line_graph(plot, gld, color);
655                                         snprintf(line, 128, " CPU %d\n", i - 1);
656                                         svg_add_legend(plot, tf->label, line, color);
657                                         plotted++;
658                                         break;
659                                 }
660
661                         }
662                 }
663         }
664
665         if (plot->add_xlabel)
666                 set_xlabel(plot, "Time (seconds)");
667
668         if (plot->legend_index <= 8)
669                 svg_write_legend(plot);
670         else
671                 svg_free_legend(plot);
672         close_plot(plot);
673 }
674
675 static void plot_latency(struct plot *plot, int seconds)
676 {
677         struct trace_file *tf;
678         char *units;
679         char line[128];
680         u64 max = 0;
681
682         if (active_graphs[LATENCY_GRAPH_INDEX] == 0)
683                 return;
684
685         if (num_traces > 1)
686                 svg_alloc_legend(plot, num_traces);
687         list_for_each_entry(tf, &all_traces, list) {
688                 if (tf->latency_gld->max > max)
689                         max = tf->latency_gld->max;
690         }
691         list_for_each_entry(tf, &all_traces, list)
692                 tf->latency_gld->max = max;
693
694         plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
695         setup_axis(plot);
696         set_plot_label(plot, "IO Latency");
697
698         tf = list_entry(all_traces.next, struct trace_file, list);
699
700         scale_line_graph_time(&max, &units);
701         sprintf(line, "latency (%ss)", units);
702         set_ylabel(plot, line);
703         set_yticks(plot, 4, 0, max, "");
704         set_xticks(plot, 9, 0, seconds);
705
706         list_for_each_entry(tf, &all_traces, list) {
707                 svg_line_graph(plot, tf->latency_gld, tf->read_color);
708                 if (num_traces > 1)
709                         svg_add_legend(plot, tf->label, "", tf->read_color);
710         }
711
712         if (plot->add_xlabel)
713                 set_xlabel(plot, "Time (seconds)");
714         if (num_traces > 1)
715                 svg_write_legend(plot);
716         close_plot(plot);
717 }
718
719 static void plot_queue_depth(struct plot *plot, int seconds)
720 {
721         struct trace_file *tf;
722
723         if (active_graphs[QUEUE_DEPTH_GRAPH_INDEX] == 0)
724                 return;
725
726         plot->add_xlabel = last_active_graph == QUEUE_DEPTH_GRAPH_INDEX;
727
728         setup_axis(plot);
729         set_plot_label(plot, "Queue Depth");
730         if (num_traces > 1)
731                 svg_alloc_legend(plot, num_traces);
732
733         tf = list_entry(all_traces.next, struct trace_file, list);
734         set_ylabel(plot, "Pending IO");
735         set_yticks(plot, 4, 0, tf->queue_depth_gld->max, "");
736         set_xticks(plot, 9, 0, seconds);
737
738         list_for_each_entry(tf, &all_traces, list) {
739                 svg_line_graph(plot, tf->queue_depth_gld, tf->read_color);
740                 if (num_traces > 1)
741                         svg_add_legend(plot, tf->label, "", tf->read_color);
742         }
743
744         if (plot->add_xlabel)
745                 set_xlabel(plot, "Time (seconds)");
746         if (num_traces > 1)
747                 svg_write_legend(plot);
748         close_plot(plot);
749 }
750
751 static void plot_iops(struct plot *plot, int seconds)
752 {
753         struct trace_file *tf;
754         char *units;
755         u64 max = 0;
756
757         if (active_graphs[IOPS_GRAPH_INDEX] == 0)
758                 return;
759
760         list_for_each_entry(tf, &all_traces, list) {
761                 if (tf->iop_gld->max > max)
762                         max = tf->iop_gld->max;
763         }
764
765         list_for_each_entry(tf, &all_traces, list)
766                 tf->iop_gld->max = max;
767
768
769         plot->add_xlabel = last_active_graph == IOPS_GRAPH_INDEX;
770         setup_axis(plot);
771         set_plot_label(plot, "IOPs");
772         if (num_traces > 1)
773                 svg_alloc_legend(plot, num_traces);
774
775         tf = list_entry(all_traces.next, struct trace_file, list);
776
777         scale_line_graph_bytes(&max, &units, 1000);
778         set_ylabel(plot, "IO/s");
779
780         set_yticks(plot, 4, 0, max, units);
781         set_xticks(plot, 9, 0, seconds);
782
783         list_for_each_entry(tf, &all_traces, list) {
784                 svg_line_graph(plot, tf->iop_gld, tf->read_color);
785                 if (num_traces > 1)
786                         svg_add_legend(plot, tf->label, "", tf->read_color);
787         }
788
789         if (plot->add_xlabel)
790                 set_xlabel(plot, "Time (seconds)");
791         if (num_traces > 1)
792                 svg_write_legend(plot);
793
794         close_plot(plot);
795 }
796
797 int main(int ac, char **av)
798 {
799         struct plot *plot;
800         int seconds = 0;
801         u64 max_offset = 0;
802         int fd;
803         struct trace_file *tf;
804         int ret;
805
806         init_io_hash_table();
807
808         enable_all_graphs();
809
810         parse_options(ac, av);
811
812         last_active_graph = last_graph();
813
814         if (list_empty(&all_traces)) {
815                 fprintf(stderr, "No traces found, exiting\n");
816                 exit(1);
817         }
818
819         if (blktrace_device) {
820                 ret = start_blktrace(blktrace_device, blktrace_outfile,
821                                      blktrace_dest_dir);
822                 if (ret) {
823                         fprintf(stderr, "exiting due to blktrace failure\n");
824                         exit(1);
825                 }
826                 start_mpstat(blktrace_outfile);
827                 if (program_to_run) {
828                         ret = run_program(program_to_run);
829                         if (ret) {
830                                 fprintf(stderr, "failed to run %s\n",
831                                         program_to_run);
832                                 exit(1);
833                         }
834                         wait_for_tracers();
835                         blktrace_to_dump(blktrace_outfile);
836                 } else {
837                         /* no program specified, just wait for
838                          * blktrace to exit
839                          */
840                         wait_for_tracers();
841                 }
842         }
843
844         /* step one, read all the traces */
845         read_traces();
846
847         /* step two, find the maxes for time and offset */
848         list_for_each_entry(tf, &all_traces, list)
849                 compare_max_tf(tf, &seconds, &max_offset);
850
851         /* push the max we found into all the tfs */
852         set_all_max_tf(seconds, max_offset);
853
854         /* alloc graphing structs for all the traces */
855         setup_trace_file_graphs();
856
857         /* run through all the traces and read their events */
858         read_trace_events();
859
860         fd = open(output_filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
861         if (fd < 0) {
862                 fprintf(stderr, "Unable to open output file %s %s\n",
863                         output_filename, strerror(errno));
864                 exit(1);
865         }
866
867         write_svg_header(fd);
868         plot = alloc_plot(fd);
869
870         if (active_graphs[IO_GRAPH_INDEX] || found_mpstat)
871                 set_legend_width(longest_label + strlen("writes"));
872         else if (num_traces > 1)
873                 set_legend_width(longest_label);
874         else
875                 set_legend_width(0);
876
877         set_plot_title(plot, graph_title);
878
879         plot_io(plot, seconds, max_offset);
880         plot_tput(plot, seconds);
881         plot_cpu(plot, seconds, "CPU IO Wait Time",
882                  CPU_IO_GRAPH_INDEX, MPSTAT_IO);
883         plot_cpu(plot, seconds, "CPU System Time",
884                  CPU_SYS_GRAPH_INDEX, MPSTAT_SYS);
885         plot_cpu(plot, seconds, "CPU IRQ Time",
886                  CPU_IRQ_GRAPH_INDEX, MPSTAT_IRQ);
887         plot_cpu(plot, seconds, "CPU SoftIRQ Time",
888                  CPU_SOFT_GRAPH_INDEX, MPSTAT_SOFT);
889         plot_cpu(plot, seconds, "CPU User Time",
890                  CPU_USER_GRAPH_INDEX, MPSTAT_USER);
891
892         plot_latency(plot, seconds);
893         plot_queue_depth(plot, seconds);
894         plot_iops(plot, seconds);
895
896         /* once for all */
897         close_plot(plot);
898         close(fd);
899         return 0;
900 }