iowatcher: Fix spindle rotation in the movie mode
[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 #include <limits.h>
35 #include <float.h>
36
37 #include "plot.h"
38 #include "blkparse.h"
39 #include "list.h"
40 #include "tracers.h"
41 #include "mpstat.h"
42
43 LIST_HEAD(all_traces);
44
45 static char line[1024];
46 static int line_len = 1024;
47 static int found_mpstat = 0;
48 static int make_movie = 0;
49 static int keep_movie_svgs = 0;
50 static int opt_graph_width = 0;
51 static int opt_graph_height = 0;
52
53 static int columns = 1;
54 static int num_xticks = 7;
55 static int num_yticks = 4;
56
57 static double min_time = 0;
58 static double max_time = DBL_MAX;
59 static unsigned long long min_mb = 0;
60 static unsigned long long max_mb = ULLONG_MAX >> 20;
61
62 int plot_io_action = 0;
63 int io_per_process = 0;
64 unsigned int longest_proc_name = 0;
65
66 /*
67  * this doesn't include the IO graph,
68  * but it counts the other graphs as they go out
69  */
70 static int total_graphs_written = 1;
71
72 enum {
73         IO_GRAPH_INDEX = 0,
74         TPUT_GRAPH_INDEX,
75         CPU_SYS_GRAPH_INDEX,
76         CPU_IO_GRAPH_INDEX,
77         CPU_IRQ_GRAPH_INDEX,
78         CPU_SOFT_GRAPH_INDEX,
79         CPU_USER_GRAPH_INDEX,
80         LATENCY_GRAPH_INDEX,
81         QUEUE_DEPTH_GRAPH_INDEX,
82         IOPS_GRAPH_INDEX,
83         TOTAL_GRAPHS
84 };
85
86 enum {
87         MPSTAT_SYS = 0,
88         MPSTAT_IRQ,
89         MPSTAT_IO,
90         MPSTAT_SOFT,
91         MPSTAT_USER,
92         MPSTAT_GRAPHS
93 };
94
95 static char *graphs_by_name[] = {
96         "io",
97         "tput",
98         "cpu-sys",
99         "cpu-io",
100         "cpu-irq",
101         "cpu-soft",
102         "cpu-user",
103         "latency",
104         "queue-depth",
105         "iops",
106 };
107
108 enum {
109         MOVIE_SPINDLE,
110         MOVIE_RECT,
111         NUM_MOVIE_STYLES,
112 };
113
114 char *movie_styles[] = {
115         "spindle",
116         "rect",
117         NULL
118 };
119
120 static int movie_style = 0;
121
122 static int lookup_movie_style(char *str)
123 {
124         int i;
125
126         for (i = 0; i < NUM_MOVIE_STYLES; i++) {
127                 if (strcmp(str, movie_styles[i]) == 0)
128                         return i;
129         }
130         return -1;
131 }
132
133 static int active_graphs[TOTAL_GRAPHS];
134 static int last_active_graph = IOPS_GRAPH_INDEX;
135
136 static int label_index = 0;
137 static int num_traces = 0;
138 static int longest_label = 0;
139
140 static char *graph_title = "";
141 static char *output_filename = "trace.svg";
142 static char *blktrace_device = NULL;
143 static char *blktrace_outfile = "trace";
144 static char *blktrace_dest_dir = ".";
145 static char *program_to_run = NULL;
146
147 static void alloc_mpstat_gld(struct trace_file *tf)
148 {
149         struct graph_line_data **ptr;
150
151         if (tf->trace->mpstat_num_cpus == 0)
152                 return;
153
154         ptr = calloc((tf->trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS,
155                      sizeof(struct graph_line_data *));
156         if (!ptr) {
157                 perror("Unable to allocate mpstat arrays\n");
158                 exit(1);
159         }
160         tf->mpstat_gld = ptr;
161 }
162
163 static void enable_all_graphs(void)
164 {
165         int i;
166         for (i = 0; i < TOTAL_GRAPHS; i++)
167                 active_graphs[i] = 1;
168 }
169
170 static void disable_all_graphs(void)
171 {
172         int i;
173         for (i = 0; i < TOTAL_GRAPHS; i++)
174                 active_graphs[i] = 0;
175 }
176
177 static int enable_one_graph(char *name)
178 {
179         int i;
180         for (i = 0; i < TOTAL_GRAPHS; i++) {
181                 if (strcmp(name, graphs_by_name[i]) == 0) {
182                         active_graphs[i] = 1;
183                         return 0;
184                 }
185         }
186         return -ENOENT;
187 }
188
189 static int disable_one_graph(char *name)
190 {
191         int i;
192         for (i = 0; i < TOTAL_GRAPHS; i++) {
193                 if (strcmp(name, graphs_by_name[i]) == 0) {
194                         active_graphs[i] = 0;
195                         return 0;
196                 }
197         }
198         return -ENOENT;
199 }
200
201 static int last_graph(void)
202 {
203         int i;
204         for (i = TOTAL_GRAPHS - 1; i >= 0; i--) {
205                 if (active_graphs[i]) {
206                         return i;
207                 }
208         }
209         return -ENOENT;
210 }
211
212 static int graphs_left(int cur)
213 {
214         int i;
215         int left = 0;
216         for (i = cur; i < TOTAL_GRAPHS; i++) {
217                 if (active_graphs[i])
218                         left++;
219         }
220         return left;
221 }
222
223 static char * join_path(char *dest_dir, char *filename)
224 {
225         /* alloc 2 extra bytes for '/' and '\0' */
226         char *path = malloc(strlen(dest_dir) + strlen(filename) + 2);
227         sprintf(path, "%s%s%s", dest_dir, "/", filename);
228
229         return path;
230 }
231
232 static void add_trace_file(char *filename)
233 {
234         struct trace_file *tf;
235
236         tf = calloc(1, sizeof(*tf));
237         if (!tf) {
238                 fprintf(stderr, "Unable to allocate memory\n");
239                 exit(1);
240         }
241         tf->label = "";
242         tf->filename = strdup(filename);
243         list_add_tail(&tf->list, &all_traces);
244         tf->line_color = "black";
245         num_traces++;
246 }
247
248 static void setup_trace_file_graphs(void)
249 {
250         struct trace_file *tf;
251         int i;
252         int alloc_ptrs;
253
254         if (io_per_process)
255                 alloc_ptrs = 16;
256         else
257                 alloc_ptrs = 1;
258         list_for_each_entry(tf, &all_traces, list) {
259                 tf->tput_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds);
260                 tf->latency_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds);
261                 tf->queue_depth_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds);
262                 tf->iop_gld = alloc_line_data(tf->min_seconds, tf->max_seconds, tf->stop_seconds);
263                 tf->gdd_writes = calloc(alloc_ptrs, sizeof(struct graph_dot_data));
264                 tf->gdd_reads = calloc(alloc_ptrs, sizeof(struct graph_dot_data));
265                 tf->io_plots_allocated = alloc_ptrs;
266
267                 if (tf->trace->mpstat_num_cpus == 0)
268                         continue;
269
270                 alloc_mpstat_gld(tf);
271                 for (i = 0; i < (tf->trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS; i++) {
272                         tf->mpstat_gld[i] =
273                                 alloc_line_data(tf->mpstat_min_seconds,
274                                                 tf->mpstat_max_seconds,
275                                                 tf->mpstat_max_seconds);
276                         tf->mpstat_gld[i]->max = 100;
277                 }
278         }
279 }
280
281 static void read_traces(void)
282 {
283         struct trace_file *tf;
284         struct trace *trace;
285         u64 last_time;
286         u64 ymin;
287         u64 ymax;
288         u64 max_bank;
289         u64 max_bank_offset;
290         char *path = NULL;
291
292         list_for_each_entry(tf, &all_traces, list) {
293                 path = join_path(blktrace_dest_dir, tf->filename);
294                 trace = open_trace(path);
295                 if (!trace)
296                         exit(1);
297
298                 last_time = find_last_time(trace);
299                 tf->trace = trace;
300                 tf->max_seconds = SECONDS(last_time) + 1;
301                 tf->stop_seconds = SECONDS(last_time) + 1;
302                 find_extreme_offsets(trace, &tf->min_offset, &tf->max_offset,
303                                     &max_bank, &max_bank_offset);
304                 filter_outliers(trace, tf->min_offset, tf->max_offset, &ymin, &ymax);
305                 tf->min_offset = ymin;
306                 tf->max_offset = ymax;
307
308                 read_mpstat(trace, path);
309                 tf->mpstat_stop_seconds = trace->mpstat_seconds;
310                 tf->mpstat_max_seconds = trace->mpstat_seconds;
311                 if (tf->mpstat_max_seconds)
312                         found_mpstat = 1;
313                 free(path);
314         }
315 }
316
317 static void pick_line_graph_color(void)
318 {
319         struct trace_file *tf;
320         int i;
321
322         list_for_each_entry(tf, &all_traces, list) {
323                 for (i = 0; i < tf->io_plots; i++) {
324                         if (tf->gdd_reads[i]) {
325                                 tf->line_color = tf->gdd_reads[i]->color;
326                                 break;
327                         }
328                         if (tf->gdd_writes[i]) {
329                                 tf->line_color = tf->gdd_writes[i]->color;
330                                 break;
331                         }
332                 }
333         }
334 }
335
336 static void read_trace_events(void)
337 {
338
339         struct trace_file *tf;
340         struct trace *trace;
341         int ret;
342         int i;
343         int time;
344         double user, sys, iowait, irq, soft;
345         double max_user = 0, max_sys = 0, max_iowait = 0,
346                max_irq = 0, max_soft = 0;
347
348         list_for_each_entry(tf, &all_traces, list) {
349                 trace = tf->trace;
350                 first_record(trace);
351                 while (1) {
352                         check_record(trace);
353                         add_tput(trace, tf->tput_gld);
354                         add_iop(trace, tf->iop_gld);
355                         add_io(trace, tf);
356                         add_pending_io(trace, tf->queue_depth_gld);
357                         add_completed_io(trace, tf->latency_gld);
358                         ret = next_record(trace);
359                         if (ret)
360                                 break;
361                 }
362         }
363         list_for_each_entry(tf, &all_traces, list) {
364                 trace = tf->trace;
365
366                 if (trace->mpstat_num_cpus == 0)
367                         continue;
368
369                 first_mpstat(trace);
370
371                 for (time = 0; time < tf->mpstat_stop_seconds; time ++) {
372                         for (i = 0; i < (trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS; i += MPSTAT_GRAPHS) {
373                                 ret = read_mpstat_event(trace, &user, &sys,
374                                                         &iowait, &irq, &soft);
375                                 if (ret)
376                                         goto mpstat_done;
377                                 if (next_mpstat_line(trace))
378                                         goto mpstat_done;
379
380                                 if (sys > max_sys)
381                                         max_sys = sys;
382                                 if (user > max_user)
383                                         max_user = user;
384                                 if (irq > max_irq)
385                                         max_irq = irq;
386                                 if (iowait > max_iowait)
387                                         max_iowait = iowait;
388
389                                 add_mpstat_gld(time, sys, tf->mpstat_gld[i + MPSTAT_SYS]);
390                                 add_mpstat_gld(time, irq, tf->mpstat_gld[i + MPSTAT_IRQ]);
391                                 add_mpstat_gld(time, soft, tf->mpstat_gld[i + MPSTAT_SOFT]);
392                                 add_mpstat_gld(time, user, tf->mpstat_gld[i + MPSTAT_USER]);
393                                 add_mpstat_gld(time, iowait, tf->mpstat_gld[i + MPSTAT_IO]);
394                         }
395                         if (next_mpstat(trace) == NULL)
396                                 break;
397                 }
398         }
399
400 mpstat_done:
401         list_for_each_entry(tf, &all_traces, list) {
402                 trace = tf->trace;
403
404                 if (trace->mpstat_num_cpus == 0)
405                         continue;
406
407                 tf->mpstat_gld[MPSTAT_SYS]->max = max_sys;
408                 tf->mpstat_gld[MPSTAT_IRQ]->max = max_irq;
409                 tf->mpstat_gld[MPSTAT_SOFT]->max = max_soft;
410                 tf->mpstat_gld[MPSTAT_USER]->max = max_user;
411                 tf->mpstat_gld[MPSTAT_IO]->max = max_iowait;;
412         }
413         return;
414 }
415
416 static void set_trace_label(char *label)
417 {
418         int cur = 0;
419         struct trace_file *tf;
420         int len = strlen(label);
421
422         if (len > longest_label)
423                 longest_label = len;
424
425         list_for_each_entry(tf, &all_traces, list) {
426                 if (cur == label_index) {
427                         tf->label = strdup(label);
428                         label_index++;
429                         break;
430                 }
431                 cur++;
432         }
433 }
434
435 static void set_blktrace_outfile(char *arg)
436 {
437         char *s = strdup(arg);
438         char *last_dot = strrchr(s, '.');
439
440         if (last_dot) {
441                 if (strcmp(last_dot, ".dump") == 0)
442                         *last_dot = '\0';
443         }
444         blktrace_outfile = s;
445 }
446
447
448 static void compare_minmax_tf(struct trace_file *tf, int *max_seconds, u64 *min_offset, u64 *max_offset)
449 {
450         if (tf->max_seconds > *max_seconds)
451                 *max_seconds = tf->max_seconds;
452         if (tf->max_offset > *max_offset)
453                 *max_offset = tf->max_offset;
454         if (tf->min_offset < *min_offset)
455                 *min_offset = tf->min_offset;
456 }
457
458 static void set_all_minmax_tf(int min_seconds, int max_seconds, u64 min_offset, u64 max_offset)
459 {
460         struct trace_file *tf;
461
462         list_for_each_entry(tf, &all_traces, list) {
463                 tf->min_seconds = min_seconds;
464                 tf->max_seconds = max_seconds;
465                 if (tf->stop_seconds > max_seconds)
466                         tf->stop_seconds = max_seconds;
467                 if (tf->mpstat_max_seconds) {
468                         tf->mpstat_min_seconds = min_seconds;
469                         tf->mpstat_max_seconds = max_seconds;
470                         if (tf->mpstat_stop_seconds > max_seconds)
471                                 tf->mpstat_stop_seconds = max_seconds;
472                 }
473                 tf->min_offset = min_offset;
474                 tf->max_offset = max_offset;
475         }
476 }
477
478 static char *create_movie_temp_dir(void)
479 {
480         char *ret;
481         char *pattern = strdup("io-movie-XXXXXX");;
482
483         ret = mkdtemp(pattern);
484         if (!ret) {
485                 perror("Unable to create temp directory for movie files");
486                 exit(1);
487         }
488         return ret;
489 }
490
491 static struct pid_plot_history *alloc_pid_plot_history(char *color)
492 {
493         struct pid_plot_history *pph;
494
495         pph = calloc(1, sizeof(struct pid_plot_history));
496         if (!pph) {
497                 perror("memory allocation failed");
498                 exit(1);
499         }
500         pph->history = malloc(sizeof(double) * 4096);
501         if (!pph->history) {
502                 perror("memory allocation failed");
503                 exit(1);
504         }
505         pph->history_len = 4096;
506         pph->color = color;
507
508         return pph;
509 }
510
511 static struct plot_history *alloc_plot_history(struct trace_file *tf)
512 {
513         struct plot_history *ph = calloc(1, sizeof(struct plot_history));
514         int i;
515
516         if (!ph) {
517                 perror("memory allocation failed");
518                 exit(1);
519         }
520         ph->read_pid_history = calloc(tf->io_plots, sizeof(struct pid_plot_history *));
521         if (!ph->read_pid_history) {
522                 perror("memory allocation failed");
523                 exit(1);
524         }
525         ph->write_pid_history = calloc(tf->io_plots, sizeof(struct pid_plot_history *));
526         if (!ph->write_pid_history) {
527                 perror("memory allocation failed");
528                 exit(1);
529         }
530         ph->pid_history_count = tf->io_plots;
531         for (i = 0; i < tf->io_plots; i++) {
532                 if (tf->gdd_reads[i])
533                         ph->read_pid_history[i] = alloc_pid_plot_history(tf->gdd_reads[i]->color);
534                 if (tf->gdd_writes[i])
535                         ph->write_pid_history[i] = alloc_pid_plot_history(tf->gdd_writes[i]->color);
536         }
537         return ph;
538 }
539
540 LIST_HEAD(movie_history);
541 int num_histories = 0;
542
543 static void free_plot_history(struct plot_history *ph)
544 {
545         int pid;
546
547         for (pid = 0; pid < ph->pid_history_count; pid++) {
548                 if (ph->read_pid_history[pid])
549                         free(ph->read_pid_history[pid]);
550                 if (ph->write_pid_history[pid])
551                         free(ph->write_pid_history[pid]);
552         }
553         free(ph->read_pid_history);
554         free(ph->write_pid_history);
555         free(ph);
556 }
557
558 static void add_history(struct plot_history *ph, struct list_head *list)
559 {
560         struct plot_history *entry;
561
562         list_add_tail(&ph->list, list);
563         num_histories++;
564
565         if (num_histories > 12) {
566                 num_histories--;
567                 entry = list_entry(list->next, struct plot_history, list);
568                 list_del(&entry->list);
569                 free_plot_history(entry);
570         }
571 }
572
573 static void plot_movie_history(struct plot *plot, struct list_head *list)
574 {
575         struct plot_history *ph;
576         int pid;
577
578         if (num_histories > 2)
579                 rewind_spindle_steps(num_histories - 1);
580
581         list_for_each_entry(ph, list, list) {
582                 for (pid = 0; pid < ph->pid_history_count; pid++) {
583                         if (ph->read_pid_history[pid]) {
584                                 if (movie_style == MOVIE_SPINDLE) {
585                                         svg_io_graph_movie_array_spindle(plot,
586                                                 ph->read_pid_history[pid]);
587                                 } else {
588                                         svg_io_graph_movie_array(plot,
589                                                 ph->read_pid_history[pid]);
590                                 }
591                         }
592                         if (ph->write_pid_history[pid]) {
593                                 if (movie_style == MOVIE_SPINDLE) {
594                                         svg_io_graph_movie_array_spindle(plot,
595                                                 ph->write_pid_history[pid]);
596                                 } else {
597                                         svg_io_graph_movie_array(plot,
598                                                 ph->write_pid_history[pid]);
599                                 }
600                         }
601                 }
602          }
603 }
604
605 static void free_all_plot_history(struct list_head *head)
606 {
607         struct plot_history *entry;
608
609         while (!list_empty(head)) {
610                 entry = list_entry(head->next, struct plot_history, list);
611                 list_del(&entry->list);
612                 free_plot_history(entry);
613         }
614 }
615
616 static int count_io_plot_types(void)
617 {
618         struct trace_file *tf;
619         int i;
620         int total_io_types = 0;
621
622         list_for_each_entry(tf, &all_traces, list) {
623                 for (i = 0; i < tf->io_plots; i++) {
624                         if (tf->gdd_reads[i])
625                                 total_io_types++;
626                         if (tf->gdd_writes[i])
627                                 total_io_types++;
628                 }
629         }
630         return total_io_types;
631 }
632
633 static void plot_io(struct plot *plot, int min_seconds, int max_seconds, u64 min_offset, u64 max_offset)
634 {
635         struct trace_file *tf;
636         int i;
637
638         if (active_graphs[IO_GRAPH_INDEX] == 0)
639                 return;
640
641         setup_axis(plot);
642
643         svg_alloc_legend(plot, count_io_plot_types() * 2);
644
645         set_plot_label(plot, "Device IO");
646         set_ylabel(plot, "Offset (MB)");
647         set_yticks(plot, num_yticks, min_offset / (1024 * 1024),
648                    max_offset / (1024 * 1024), "");
649         set_xticks(plot, num_xticks, min_seconds, max_seconds);
650
651         list_for_each_entry(tf, &all_traces, list) {
652                 char label[256];
653                 char *pos;
654
655                 if (!tf->label)
656                         label[0] = 0;
657                 else {
658                         strcpy(label, tf->label);
659                         if (io_per_process)
660                                 strcat(label, " ");
661                 }
662                 pos = label + strlen(label);
663
664                 for (i = 0; i < tf->io_plots; i++) {
665                         if (tf->gdd_reads[i]) {
666                                 svg_io_graph(plot, tf->gdd_reads[i]);
667                                 if (io_per_process)
668                                         strcpy(pos, tf->gdd_reads[i]->label);
669                                 svg_add_legend(plot, label, " Reads", tf->gdd_reads[i]->color);
670                         }
671
672                         if (tf->gdd_writes[i]) {
673                                 svg_io_graph(plot, tf->gdd_writes[i]);
674                                 if (io_per_process)
675                                         strcpy(pos, tf->gdd_writes[i]->label);
676                                 svg_add_legend(plot, label, " Writes", tf->gdd_writes[i]->color);
677                         }
678                 }
679         }
680         if (plot->add_xlabel)
681                 set_xlabel(plot, "Time (seconds)");
682         svg_write_legend(plot);
683         close_plot(plot);
684 }
685
686 static void plot_tput(struct plot *plot, int min_seconds, int max_seconds)
687 {
688         struct trace_file *tf;
689         char *units;
690         char line[128];
691         u64 max = 0;
692
693         if (active_graphs[TPUT_GRAPH_INDEX] == 0)
694                 return;
695
696         if (num_traces > 1)
697                 svg_alloc_legend(plot, num_traces);
698         list_for_each_entry(tf, &all_traces, list) {
699                 if (tf->tput_gld->max > max)
700                         max = tf->tput_gld->max;
701         }
702         list_for_each_entry(tf, &all_traces, list)
703                 tf->tput_gld->max = max;
704
705         setup_axis(plot);
706         set_plot_label(plot, "Throughput");
707
708         tf = list_entry(all_traces.next, struct trace_file, list);
709
710         scale_line_graph_bytes(&max, &units, 1024);
711         sprintf(line, "%sB/s", units);
712         set_ylabel(plot, line);
713         set_yticks(plot, num_yticks, 0, max, "");
714         set_xticks(plot, num_xticks, min_seconds, max_seconds);
715
716         list_for_each_entry(tf, &all_traces, list) {
717                 svg_line_graph(plot, tf->tput_gld, tf->line_color, 0, 0);
718                 if (num_traces > 1)
719                         svg_add_legend(plot, tf->label, "", tf->line_color);
720         }
721
722         if (plot->add_xlabel)
723                 set_xlabel(plot, "Time (seconds)");
724         if (num_traces > 1)
725                 svg_write_legend(plot);
726         close_plot(plot);
727         total_graphs_written++;
728 }
729
730 static void plot_cpu(struct plot *plot, int max_seconds, char *label,
731                      int active_index, int gld_index)
732 {
733         struct trace_file *tf;
734         int max = 0;
735         int i;
736         int gld_i;
737         char *color;
738         double avg = 0;
739         int ymax;
740         int plotted = 0;
741
742         if (active_graphs[active_index] == 0)
743                 return;
744
745         list_for_each_entry(tf, &all_traces, list) {
746                 if (tf->trace->mpstat_num_cpus > max)
747                         max = tf->trace->mpstat_num_cpus;
748         }
749         if (max == 0)
750                 return;
751
752         tf = list_entry(all_traces.next, struct trace_file, list);
753
754         ymax = tf->mpstat_gld[gld_index]->max;
755         if (ymax == 0)
756                 return;
757
758         svg_alloc_legend(plot, num_traces * max);
759
760         setup_axis(plot);
761         set_plot_label(plot, label);
762
763         max_seconds = tf->mpstat_max_seconds;
764
765         set_yticks(plot, num_yticks, 0, tf->mpstat_gld[gld_index]->max, "");
766         set_ylabel(plot, "Percent");
767         set_xticks(plot, num_xticks, tf->mpstat_min_seconds, max_seconds);
768
769         reset_cpu_color();
770         list_for_each_entry(tf, &all_traces, list) {
771                 if (tf->mpstat_gld == 0)
772                         break;
773                 for (i = tf->mpstat_gld[0]->min_seconds;
774                      i < tf->mpstat_gld[0]->stop_seconds; i++) {
775                         if (tf->mpstat_gld[gld_index]->data[i].count) {
776                                 avg += (tf->mpstat_gld[gld_index]->data[i].sum /
777                                         tf->mpstat_gld[gld_index]->data[i].count);
778                         }
779                 }
780                 avg /= tf->mpstat_gld[gld_index]->stop_seconds -
781                        tf->mpstat_gld[gld_index]->min_seconds;
782                 color = pick_cpu_color();
783                 svg_line_graph(plot, tf->mpstat_gld[0], color, 0, 0);
784                 svg_add_legend(plot, tf->label, " avg", color);
785
786                 for (i = 1; i < tf->trace->mpstat_num_cpus + 1; i++) {
787                         struct graph_line_data *gld = tf->mpstat_gld[i * MPSTAT_GRAPHS + gld_index];
788                         double this_avg = 0;
789
790                         for (gld_i = gld->min_seconds;
791                              gld_i < gld->stop_seconds; gld_i++) {
792                                 if (gld->data[i].count) {
793                                         this_avg += gld->data[i].sum /
794                                                 gld->data[i].count;
795                                 }
796                         }
797
798                         this_avg /= gld->stop_seconds - gld->min_seconds;
799
800                         for (gld_i = gld->min_seconds;
801                              gld_i < gld->stop_seconds; gld_i++) {
802                                 double val;
803
804                                 if (gld->data[gld_i].count == 0)
805                                         continue;
806                                 val = (double)gld->data[gld_i].sum /
807                                         gld->data[gld_i].count;
808
809                                 if (this_avg > avg + 30 || val > 95) {
810                                         color = pick_cpu_color();
811                                         svg_line_graph(plot, gld, color, avg + 30, 95);
812                                         snprintf(line, line_len, " CPU %d\n", i - 1);
813                                         svg_add_legend(plot, tf->label, line, color);
814                                         plotted++;
815                                         break;
816                                 }
817
818                         }
819                 }
820         }
821
822         if (plot->add_xlabel)
823                 set_xlabel(plot, "Time (seconds)");
824
825         if (!plot->no_legend) {
826                 svg_write_legend(plot);
827                 svg_free_legend(plot);
828         }
829         close_plot(plot);
830         total_graphs_written++;
831 }
832
833 static void plot_queue_depth(struct plot *plot, int min_seconds, int max_seconds)
834 {
835         struct trace_file *tf;
836
837         if (active_graphs[QUEUE_DEPTH_GRAPH_INDEX] == 0)
838                 return;
839
840         setup_axis(plot);
841         set_plot_label(plot, "Queue Depth");
842         if (num_traces > 1)
843                 svg_alloc_legend(plot, num_traces);
844
845         tf = list_entry(all_traces.next, struct trace_file, list);
846         set_ylabel(plot, "Pending IO");
847         set_yticks(plot, num_yticks, 0, tf->queue_depth_gld->max, "");
848         set_xticks(plot, num_xticks, min_seconds, max_seconds);
849
850         list_for_each_entry(tf, &all_traces, list) {
851                 svg_line_graph(plot, tf->queue_depth_gld, tf->line_color, 0, 0);
852                 if (num_traces > 1)
853                         svg_add_legend(plot, tf->label, "", tf->line_color);
854         }
855
856         if (plot->add_xlabel)
857                 set_xlabel(plot, "Time (seconds)");
858         if (num_traces > 1)
859                 svg_write_legend(plot);
860         close_plot(plot);
861         total_graphs_written++;
862 }
863
864 static void convert_movie_files(char *movie_dir)
865 {
866         fprintf(stderr, "Converting svg files in %s\n", movie_dir);
867         snprintf(line, line_len, "find %s -name \\*.svg | xargs -I{} -n 1 -P 8 rsvg-convert -o {}.png {}",
868                  movie_dir);
869         system(line);
870 }
871
872 static void mencode_movie(char *movie_dir)
873 {
874         fprintf(stderr, "Creating movie %s with ffmpeg\n", movie_dir);
875         snprintf(line, line_len, "ffmpeg -r 20 -y -i %s/%%10d-%s.svg.png -b:v 250k "
876                  "-vcodec libx264 %s", movie_dir, output_filename, output_filename);
877         system(line);
878 }
879
880 static void tencode_movie(char *movie_dir)
881 {
882         fprintf(stderr, "Creating movie %s with png2theora\n", movie_dir);
883         snprintf(line, line_len, "png2theora -o %s %s/%%010d-%s.svg.png",
884                         output_filename, movie_dir, output_filename);
885         system(line);
886 }
887
888 static void encode_movie(char *movie_dir)
889 {
890         char *last_dot = strrchr(output_filename, '.');
891
892         if (last_dot &&
893             (!strncmp(last_dot, ".ogg", 4) || !strncmp(last_dot, ".ogv", 4))) {
894                 tencode_movie(movie_dir);
895         } else
896                 mencode_movie(movie_dir);
897 }
898
899 static void cleanup_movie(char *movie_dir)
900 {
901         if (keep_movie_svgs) {
902                 fprintf(stderr, "Keeping movie dir %s\n", movie_dir);
903                 return;
904         }
905         fprintf(stderr, "Removing movie dir %s\n", movie_dir);
906         snprintf(line, line_len, "rm %s/*", movie_dir);
907         system(line);
908
909         snprintf(line, line_len, "rmdir %s", movie_dir);
910         system(line);
911 }
912
913 static void plot_io_movie(struct plot *plot)
914 {
915         struct trace_file *tf;
916         char *movie_dir = create_movie_temp_dir();
917         int i, pid;
918         struct plot_history *history;
919         int batch_i;
920         int movie_len = 30;
921         int movie_frames_per_sec = 20;
922         int total_frames = movie_len * movie_frames_per_sec;
923         int rows, cols;
924         int batch_count;
925         int graph_width_factor = 5;
926         int orig_y_offset;
927
928         get_graph_size(&cols, &rows);
929         batch_count = cols / total_frames;
930
931         if (batch_count == 0)
932                 batch_count = 1;
933
934         list_for_each_entry(tf, &all_traces, list) {
935                 char label[256];
936                 char *pos;
937
938                 if (!tf->label)
939                         label[0] = 0;
940                 else {
941                         strcpy(label, tf->label);
942                         if (io_per_process)
943                                 strcat(label, " ");
944                 }
945                 pos = label + strlen(label);
946
947                 i = 0;
948                 while (i < cols) {
949                         snprintf(line, line_len, "%s/%010d-%s.svg", movie_dir, i, output_filename);
950                         set_plot_output(plot, line);
951                         set_plot_title(plot, graph_title);
952                         orig_y_offset = plot->start_y_offset;
953
954                         plot->no_legend = 1;
955
956                         set_graph_size(cols / graph_width_factor, rows / 8);
957                         plot->timeline = i / graph_width_factor;
958
959                         plot_tput(plot, tf->min_seconds,
960                                   tf->max_seconds);
961
962                         plot_cpu(plot, tf->max_seconds,
963                                    "CPU System Time", CPU_SYS_GRAPH_INDEX, MPSTAT_SYS);
964
965                         plot->direction = PLOT_ACROSS;
966                         plot_queue_depth(plot, tf->min_seconds, tf->max_seconds);
967
968                         /* movie graph starts here */
969                         plot->start_y_offset = orig_y_offset;
970                         set_graph_size(cols - cols / graph_width_factor, rows);
971                         plot->no_legend = 0;
972                         plot->timeline = 0;
973                         plot->direction = PLOT_DOWN;;
974
975                         if (movie_style == MOVIE_SPINDLE)
976                                 setup_axis_spindle(plot);
977                         else
978                                 setup_axis(plot);
979
980                         svg_alloc_legend(plot, count_io_plot_types() * 2);
981
982                         history = alloc_plot_history(tf);
983                         history->col = i;
984
985                         for (pid = 0; pid < tf->io_plots; pid++) {
986                                 if (tf->gdd_reads[pid]) {
987                                         if (io_per_process)
988                                                 strcpy(pos, tf->gdd_reads[pid]->label);
989                                         svg_add_legend(plot, label, " Reads", tf->gdd_reads[pid]->color);
990                                 }
991                                 if (tf->gdd_writes[pid]) {
992                                         if (io_per_process)
993                                                 strcpy(pos, tf->gdd_writes[pid]->label);
994                                         svg_add_legend(plot, label, " Writes", tf->gdd_writes[pid]->color);
995                                 }
996                         }
997
998                         batch_i = 0;
999                         while (i < cols && batch_i < batch_count) {
1000                                 for (pid = 0; pid < tf->io_plots; pid++) {
1001                                         if (tf->gdd_reads[pid]) {
1002                                                 svg_io_graph_movie(tf->gdd_reads[pid],
1003                                                                    history->read_pid_history[pid],
1004                                                                    i);
1005                                         }
1006                                         if (tf->gdd_writes[pid]) {
1007                                                 svg_io_graph_movie(tf->gdd_writes[pid],
1008                                                                    history->write_pid_history[pid],
1009                                                                    i);
1010                                         }
1011                                 }
1012                                 i++;
1013                                 batch_i++;
1014                         }
1015
1016                         add_history(history, &movie_history);
1017
1018                         plot_movie_history(plot, &movie_history);
1019
1020                         svg_write_legend(plot);
1021                         close_plot(plot);
1022                         close_plot(plot);
1023
1024                         close_plot_file(plot);
1025                 }
1026                 free_all_plot_history(&movie_history);
1027         }
1028         convert_movie_files(movie_dir);
1029         encode_movie(movie_dir);
1030         cleanup_movie(movie_dir);
1031         free(movie_dir);
1032 }
1033
1034 static void plot_latency(struct plot *plot, int min_seconds, int max_seconds)
1035 {
1036         struct trace_file *tf;
1037         char *units;
1038         char line[128];
1039         u64 max = 0;
1040
1041         if (active_graphs[LATENCY_GRAPH_INDEX] == 0)
1042                 return;
1043
1044         if (num_traces > 1)
1045                 svg_alloc_legend(plot, num_traces);
1046
1047         list_for_each_entry(tf, &all_traces, list) {
1048                 if (tf->latency_gld->max > max)
1049                         max = tf->latency_gld->max;
1050         }
1051
1052         list_for_each_entry(tf, &all_traces, list)
1053                 tf->latency_gld->max = max;
1054
1055         setup_axis(plot);
1056         set_plot_label(plot, "IO Latency");
1057
1058         tf = list_entry(all_traces.next, struct trace_file, list);
1059
1060         scale_line_graph_time(&max, &units);
1061         sprintf(line, "latency (%ss)", units);
1062         set_ylabel(plot, line);
1063         set_yticks(plot, num_yticks, 0, max, "");
1064         set_xticks(plot, num_xticks, min_seconds, max_seconds);
1065
1066         list_for_each_entry(tf, &all_traces, list) {
1067                 svg_line_graph(plot, tf->latency_gld, tf->line_color, 0, 0);
1068                 if (num_traces > 1)
1069                         svg_add_legend(plot, tf->label, "", tf->line_color);
1070         }
1071
1072         if (plot->add_xlabel)
1073                 set_xlabel(plot, "Time (seconds)");
1074         if (num_traces > 1)
1075                 svg_write_legend(plot);
1076         close_plot(plot);
1077         total_graphs_written++;
1078 }
1079
1080 static void plot_iops(struct plot *plot, int min_seconds, int max_seconds)
1081 {
1082         struct trace_file *tf;
1083         char *units;
1084         u64 max = 0;
1085
1086         if (active_graphs[IOPS_GRAPH_INDEX] == 0)
1087                 return;
1088
1089         list_for_each_entry(tf, &all_traces, list) {
1090                 if (tf->iop_gld->max > max)
1091                         max = tf->iop_gld->max;
1092         }
1093
1094         list_for_each_entry(tf, &all_traces, list)
1095                 tf->iop_gld->max = max;
1096
1097         setup_axis(plot);
1098         set_plot_label(plot, "IOPs");
1099         if (num_traces > 1)
1100                 svg_alloc_legend(plot, num_traces);
1101
1102         tf = list_entry(all_traces.next, struct trace_file, list);
1103
1104         scale_line_graph_bytes(&max, &units, 1000);
1105         set_ylabel(plot, "IO/s");
1106
1107         set_yticks(plot, num_yticks, 0, max, units);
1108         set_xticks(plot, num_xticks, min_seconds, max_seconds);
1109
1110         list_for_each_entry(tf, &all_traces, list) {
1111                 svg_line_graph(plot, tf->iop_gld, tf->line_color, 0, 0);
1112                 if (num_traces > 1)
1113                         svg_add_legend(plot, tf->label, "", tf->line_color);
1114         }
1115
1116         if (plot->add_xlabel)
1117                 set_xlabel(plot, "Time (seconds)");
1118         if (num_traces > 1)
1119                 svg_write_legend(plot);
1120
1121         close_plot(plot);
1122         total_graphs_written++;
1123 }
1124
1125 static void check_plot_columns(struct plot *plot, int index)
1126 {
1127         int count;
1128
1129         if (columns > 1 && (total_graphs_written == 0 ||
1130             total_graphs_written % columns != 0)) {
1131                 count = graphs_left(index);
1132                 if (plot->direction == PLOT_DOWN) {
1133                         plot->start_x_offset = 0;
1134                         if (count <= columns)
1135                                 plot->add_xlabel = 1;
1136                 }
1137                 plot->direction = PLOT_ACROSS;
1138
1139         } else {
1140                 plot->direction = PLOT_DOWN;
1141                 if (index == last_active_graph)
1142                         plot->add_xlabel = 1;
1143         }
1144
1145 }
1146
1147 enum {
1148         HELP_LONG_OPT = 1,
1149 };
1150
1151 char *option_string = "T:t:o:l:r:O:N:d:D:p:m::h:w:c:x:y:a:PK";
1152 static struct option long_options[] = {
1153         {"columns", required_argument, 0, 'c'},
1154         {"title", required_argument, 0, 'T'},
1155         {"trace", required_argument, 0, 't'},
1156         {"output", required_argument, 0, 'o'},
1157         {"label", required_argument, 0, 'l'},
1158         {"rolling", required_argument, 0, 'r'},
1159         {"no-graph", required_argument, 0, 'N'},
1160         {"only-graph", required_argument, 0, 'O'},
1161         {"device", required_argument, 0, 'd'},
1162         {"blktrace-destination", required_argument, 0, 'D'},
1163         {"prog", required_argument, 0, 'p'},
1164         {"movie", optional_argument, 0, 'm'},
1165         {"keep-movie-svgs", no_argument, 0, 'K'},
1166         {"width", required_argument, 0, 'w'},
1167         {"height", required_argument, 0, 'h'},
1168         {"xzoom", required_argument, 0, 'x'},
1169         {"yzoom", required_argument, 0, 'y'},
1170         {"io-plot-action", required_argument, 0, 'a'},
1171         {"per-process-io", no_argument, 0, 'P'},
1172         {"help", no_argument, 0, HELP_LONG_OPT},
1173         {0, 0, 0, 0}
1174 };
1175
1176 static void print_usage(void)
1177 {
1178         fprintf(stderr, "iowatcher usage:\n"
1179                 "\t-d (--device): device for blktrace to trace\n"
1180                 "\t-D (--blktrace-destination): destination for blktrace\n"
1181                 "\t-t (--trace): trace file name (more than one allowed)\n"
1182                 "\t-l (--label): trace label in the graph\n"
1183                 "\t-o (--output): output file name (SVG only)\n"
1184                 "\t-p (--prog): program to run while blktrace is run\n"
1185                 "\t-K (--keep-movie-svgs keep svgs generated for movie mode\n"
1186                 "\t-m (--movie [=spindle|rect]): create IO animations\n"
1187                 "\t-r (--rolling): number of seconds in the rolling averge\n"
1188                 "\t-T (--title): graph title\n"
1189                 "\t-N (--no-graph): skip a single graph (io, tput, latency, queue_depth, \n"
1190                 "\t\t\tiops, cpu-sys, cpu-io, cpu-irq cpu-soft cpu-user)\n"
1191                 "\t-O (--only-graph): add a single graph to the output\n"
1192                 "\t-h (--height): set the height of each graph\n"
1193                 "\t-w (--width): set the width of each graph\n"
1194                 "\t-c (--columns): numbers of columns in graph output\n"
1195                 "\t-x (--xzoom): limit processed time to min:max\n"
1196                 "\t-y (--yzoom): limit processed sectors to min:max\n"
1197                 "\t-a (--io-plot-action): plot given action (one of Q,D,C) in IO graph\n"
1198                 "\t-P (--per-process-io): distinguish between processes in IO graph\n"
1199                );
1200         exit(1);
1201 }
1202
1203 static int parse_double_range(char *str, double *min, double *max)
1204 {
1205         char *end;
1206
1207         /* Empty lower bound - leave original value */
1208         if (str[0] != ':') {
1209                 *min = strtod(str, &end);
1210                 if (*min == HUGE_VAL || *min == -HUGE_VAL)
1211                         return -ERANGE;
1212                 if (*end != ':')
1213                         return -EINVAL;
1214         } else
1215                 end = str;
1216         /* Empty upper bound - leave original value */
1217         if (end[1]) {
1218                 *max = strtod(end+1, &end);
1219                 if (*max == HUGE_VAL || *max == -HUGE_VAL)
1220                         return -ERANGE;
1221                 if (*end != 0)
1222                         return -EINVAL;
1223         }
1224         if (*min > *max)
1225                 return -EINVAL;
1226         return 0;
1227 }
1228
1229 static int parse_ull_range(char *str, unsigned long long *min,
1230                            unsigned long long *max)
1231 {
1232         char *end;
1233
1234         /* Empty lower bound - leave original value */
1235         if (str[0] != ':') {
1236                 *min = strtoull(str, &end, 10);
1237                 if (*min == ULLONG_MAX && errno == ERANGE)
1238                         return -ERANGE;
1239                 if (*end != ':')
1240                         return -EINVAL;
1241         } else
1242                 end = str;
1243         /* Empty upper bound - leave original value */
1244         if (end[1]) {
1245                 *max = strtoull(end+1, &end, 10);
1246                 if (*max == ULLONG_MAX && errno == ERANGE)
1247                         return -ERANGE;
1248                 if (*end != 0)
1249                         return -EINVAL;
1250         }
1251         if (*min > *max)
1252                 return -EINVAL;
1253         return 0;
1254 }
1255
1256 static int parse_options(int ac, char **av)
1257 {
1258         int c;
1259         int disabled = 0;
1260
1261         while (1) {
1262                 // int this_option_optind = optind ? optind : 1;
1263                 int option_index = 0;
1264
1265                 c = getopt_long(ac, av, option_string,
1266                                 long_options, &option_index);
1267
1268                 if (c == -1)
1269                         break;
1270
1271                 switch(c) {
1272                 case 'T':
1273                         graph_title = strdup(optarg);
1274                         break;
1275                 case 't':
1276                         add_trace_file(optarg);
1277                         set_blktrace_outfile(optarg);
1278                         break;
1279                 case 'o':
1280                         output_filename = strdup(optarg);
1281                         break;
1282                 case 'l':
1283                         set_trace_label(optarg);
1284                         break;
1285                 case 'r':
1286                         set_rolling_avg(atoi(optarg));
1287                         break;
1288                 case 'O':
1289                         if (!disabled) {
1290                                 disable_all_graphs();
1291                                 disabled = 1;
1292                         }
1293                         enable_one_graph(optarg);
1294                         break;
1295                 case 'N':
1296                         disable_one_graph(optarg);
1297                         break;
1298                 case 'd':
1299                         blktrace_device = strdup(optarg);
1300                         break;
1301                 case 'D':
1302                         blktrace_dest_dir = strdup(optarg);
1303                         if (!strcmp(blktrace_dest_dir, "")) {
1304                                 fprintf(stderr, "Need a directory\n");
1305                                 print_usage();
1306                         }
1307                         break;
1308                 case 'p':
1309                         program_to_run = strdup(optarg);
1310                         break;
1311                 case 'K':
1312                         keep_movie_svgs = 1;
1313                         break;
1314                 case 'm':
1315                         make_movie = 1;
1316                         if (optarg) {
1317                                 movie_style = lookup_movie_style(optarg);
1318                                 if (movie_style < 0) {
1319                                         fprintf(stderr, "Unknown movie style %s\n", optarg);
1320                                         print_usage();
1321                                 }
1322                         }
1323                         fprintf(stderr, "Using movie style: %s\n",
1324                                 movie_styles[movie_style]);
1325                         break;
1326                 case 'h':
1327                         opt_graph_height = atoi(optarg);
1328                         break;
1329                 case 'w':
1330                         opt_graph_width = atoi(optarg);
1331                         break;
1332                 case 'c':
1333                         columns = atoi(optarg);
1334                         break;
1335                 case 'x':
1336                         if (parse_double_range(optarg, &min_time, &max_time)
1337                             < 0) {
1338                                 fprintf(stderr, "Cannot parse time range %s\n",
1339                                         optarg);
1340                                 exit(1);
1341                         }
1342                         break;
1343                 case 'y':
1344                         if (parse_ull_range(optarg, &min_mb, &max_mb)
1345                             < 0) {
1346                                 fprintf(stderr,
1347                                         "Cannot parse offset range %s\n",
1348                                         optarg);
1349                                 exit(1);
1350                         }
1351                         if (max_mb > ULLONG_MAX >> 20) {
1352                                 fprintf(stderr,
1353                                         "Upper range limit too big."
1354                                         " Maximum is %llu.\n", ULLONG_MAX >> 20);
1355                                 exit(1);
1356                         }
1357                         break;
1358                 case 'a':
1359                         if (strlen(optarg) != 1) {
1360 action_err:
1361                                 fprintf(stderr, "Action must be one of Q, D, C.");
1362                                 exit(1);
1363                         }
1364                         plot_io_action = action_char_to_num(optarg[0]);
1365                         if (plot_io_action < 0)
1366                                 goto action_err;
1367                         break;
1368                 case 'P':
1369                         io_per_process = 1;
1370                         break;
1371                 case '?':
1372                 case HELP_LONG_OPT:
1373                         print_usage();
1374                         break;
1375                 default:
1376                         break;
1377                 }
1378         }
1379         return 0;
1380 }
1381
1382 static void dest_mkdir()
1383 {
1384         int ret;
1385
1386         ret = mkdir(blktrace_dest_dir, 0777);
1387         if (ret && errno != EEXIST) {
1388                 fprintf(stderr, "failed to mkdir error %s\n", strerror(errno));
1389                 exit(errno);
1390         }
1391 }
1392
1393 int main(int ac, char **av)
1394 {
1395         struct plot *plot;
1396         int min_seconds = 0;
1397         int max_seconds = 0;
1398         u64 max_offset = 0;
1399         u64 min_offset = ~(u64)0;
1400         struct trace_file *tf;
1401         int ret;
1402         int rows, cols;
1403
1404         init_io_hash_table();
1405         init_process_hash_table();
1406
1407         enable_all_graphs();
1408
1409         parse_options(ac, av);
1410
1411         last_active_graph = last_graph();
1412         if (make_movie) {
1413                 set_io_graph_scale(256);
1414                 if (movie_style == MOVIE_SPINDLE)
1415                         set_graph_size(750, 550);
1416                 else
1417                         set_graph_size(700, 400);
1418
1419                 /*
1420                  * the plots in the movie don't have a seconds
1421                  * line yet, this makes us skip it
1422                  */
1423                 last_active_graph = TOTAL_GRAPHS + 1;
1424         }
1425         if (opt_graph_height)
1426                 set_graph_height(opt_graph_height);
1427
1428         if (opt_graph_width)
1429                 set_graph_width(opt_graph_width);
1430
1431         if (list_empty(&all_traces)) {
1432                 fprintf(stderr, "No traces found, exiting\n");
1433                 exit(1);
1434         }
1435
1436         if (blktrace_device) {
1437                 char *path;
1438
1439                 dest_mkdir();
1440                 path = join_path(blktrace_dest_dir, blktrace_outfile);
1441
1442                 ret = start_blktrace(blktrace_device, blktrace_outfile,
1443                                      blktrace_dest_dir);
1444                 if (ret) {
1445                         fprintf(stderr, "exiting due to blktrace failure\n");
1446                         exit(1);
1447                 }
1448
1449                 start_mpstat(path);
1450                 if (program_to_run) {
1451                         ret = run_program(program_to_run);
1452                         if (ret) {
1453                                 fprintf(stderr, "failed to run %s\n",
1454                                         program_to_run);
1455                                 exit(1);
1456                         }
1457                         wait_for_tracers();
1458                         blktrace_to_dump(path);
1459                 } else {
1460                         /* no program specified, just wait for
1461                          * blktrace to exit
1462                          */
1463                         wait_for_tracers();
1464                 }
1465                 free(path);
1466         }
1467
1468         /* step one, read all the traces */
1469         read_traces();
1470
1471         /* step two, find the maxes for time and offset */
1472         list_for_each_entry(tf, &all_traces, list)
1473                 compare_minmax_tf(tf, &max_seconds, &min_offset, &max_offset);
1474         min_seconds = min_time;
1475         if (max_seconds > max_time)
1476                 max_seconds = ceil(max_time);
1477         if (min_offset < min_mb << 20)
1478                 min_offset = min_mb << 20;
1479         if (max_offset > max_mb << 20)
1480                 max_offset = max_mb << 20;
1481
1482         /* push the max we found into all the tfs */
1483         set_all_minmax_tf(min_seconds, max_seconds, min_offset, max_offset);
1484
1485         /* alloc graphing structs for all the traces */
1486         setup_trace_file_graphs();
1487
1488         /* run through all the traces and read their events */
1489         read_trace_events();
1490
1491         pick_line_graph_color();
1492
1493         plot = alloc_plot();
1494
1495         if (make_movie) {
1496                 set_legend_width(longest_label + longest_proc_name + 1 + strlen("writes"));
1497                 plot_io_movie(plot);
1498                 exit(0);
1499         }
1500
1501         set_plot_output(plot, output_filename);
1502
1503         if (active_graphs[IO_GRAPH_INDEX] || found_mpstat)
1504                 set_legend_width(longest_label + longest_proc_name + 1 + strlen("writes"));
1505         else if (num_traces > 1)
1506                 set_legend_width(longest_label);
1507         else
1508                 set_legend_width(0);
1509
1510         get_graph_size(&cols, &rows);
1511         if (columns > 1)
1512                 plot->add_xlabel = 1;
1513         set_plot_title(plot, graph_title);
1514
1515         check_plot_columns(plot, IO_GRAPH_INDEX);
1516         plot_io(plot, min_seconds, max_seconds, min_offset, max_offset);
1517         plot->add_xlabel = 0;
1518
1519         if (columns > 1) {
1520                 set_graph_size(cols / columns, rows);
1521                 num_xticks /= columns;
1522                 if (num_xticks < 2)
1523                         num_xticks = 2;
1524         }
1525         if (rows <= 50)
1526                 num_yticks--;
1527
1528         check_plot_columns(plot, TPUT_GRAPH_INDEX);
1529         plot_tput(plot, min_seconds, max_seconds);
1530
1531         check_plot_columns(plot, CPU_IO_GRAPH_INDEX);
1532         plot_cpu(plot, max_seconds, "CPU IO Wait Time",
1533                  CPU_IO_GRAPH_INDEX, MPSTAT_IO);
1534
1535         check_plot_columns(plot, CPU_SYS_GRAPH_INDEX);
1536         plot_cpu(plot, max_seconds, "CPU System Time",
1537                  CPU_SYS_GRAPH_INDEX, MPSTAT_SYS);
1538
1539         check_plot_columns(plot, CPU_IRQ_GRAPH_INDEX);
1540         plot_cpu(plot, max_seconds, "CPU IRQ Time",
1541                  CPU_IRQ_GRAPH_INDEX, MPSTAT_IRQ);
1542
1543         check_plot_columns(plot, CPU_SOFT_GRAPH_INDEX);
1544         plot_cpu(plot, max_seconds, "CPU SoftIRQ Time",
1545                  CPU_SOFT_GRAPH_INDEX, MPSTAT_SOFT);
1546
1547         check_plot_columns(plot, CPU_USER_GRAPH_INDEX);
1548         plot_cpu(plot, max_seconds, "CPU User Time",
1549                  CPU_USER_GRAPH_INDEX, MPSTAT_USER);
1550
1551         check_plot_columns(plot, LATENCY_GRAPH_INDEX);
1552         plot_latency(plot, min_seconds, max_seconds);
1553
1554         check_plot_columns(plot, QUEUE_DEPTH_GRAPH_INDEX);
1555         plot_queue_depth(plot, min_seconds, max_seconds);
1556
1557         check_plot_columns(plot, IOPS_GRAPH_INDEX);
1558         plot_iops(plot, min_seconds, max_seconds);
1559
1560         /* once for all */
1561         close_plot(plot);
1562         close_plot_file(plot);
1563         return 0;
1564 }