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