iowatcher: Fix buffer overwrite issue
[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\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 cleanup_movie(char *movie_dir)
880 {
881         fprintf(stderr, "Removing movie dir %s\n", movie_dir);
882         snprintf(line, line_len, "rm %s/*", movie_dir);
883         system(line);
884
885         snprintf(line, line_len, "rmdir %s", movie_dir);
886         system(line);
887 }
888
889 static void plot_io_movie(struct plot *plot)
890 {
891         struct trace_file *tf;
892         char *movie_dir = create_movie_temp_dir();
893         int i, pid;
894         struct plot_history *history;
895         int batch_i;
896         int movie_len = 30;
897         int movie_frames_per_sec = 20;
898         int total_frames = movie_len * movie_frames_per_sec;
899         int rows, cols;
900         int batch_count;
901         int graph_width_factor = 5;
902         int orig_y_offset;
903
904         get_graph_size(&cols, &rows);
905         batch_count = cols / total_frames;
906
907         if (batch_count == 0)
908                 batch_count = 1;
909
910         list_for_each_entry(tf, &all_traces, list) {
911                 char label[256];
912                 char *pos;
913
914                 if (!tf->label)
915                         label[0] = 0;
916                 else {
917                         strcpy(label, tf->label);
918                         if (io_per_process)
919                                 strcat(label, " ");
920                 }
921                 pos = label + strlen(label);
922
923                 i = 0;
924                 while (i < cols) {
925                         snprintf(line, line_len, "%s/%010d-%s.svg", movie_dir, i, output_filename);
926                         set_plot_output(plot, line);
927                         set_plot_title(plot, graph_title);
928                         orig_y_offset = plot->start_y_offset;
929
930                         plot->no_legend = 1;
931
932                         set_graph_size(cols / graph_width_factor, rows / 8);
933                         plot->timeline = i / graph_width_factor;
934
935                         plot_tput(plot, tf->min_seconds,
936                                   tf->max_seconds);
937
938                         plot_cpu(plot, tf->max_seconds,
939                                    "CPU System Time", CPU_SYS_GRAPH_INDEX, MPSTAT_SYS);
940
941                         plot->direction = PLOT_ACROSS;
942                         plot_queue_depth(plot, tf->min_seconds, tf->max_seconds);
943
944                         /* movie graph starts here */
945                         plot->start_y_offset = orig_y_offset;
946                         set_graph_size(cols - cols / graph_width_factor, rows);
947                         plot->no_legend = 0;
948                         plot->timeline = 0;
949                         plot->direction = PLOT_DOWN;;
950
951                         if (movie_style == MOVIE_SPINDLE)
952                                 setup_axis_spindle(plot);
953                         else
954                                 setup_axis(plot);
955
956                         svg_alloc_legend(plot, count_io_plot_types() * 2);
957
958                         history = alloc_plot_history(tf);
959                         history->col = i;
960
961                         for (pid = 0; pid < tf->io_plots; pid++) {
962                                 if (tf->gdd_reads[pid]) {
963                                         if (io_per_process)
964                                                 strcpy(pos, tf->gdd_reads[pid]->label);
965                                         svg_add_legend(plot, label, " Reads", tf->gdd_reads[pid]->color);
966                                 }
967                                 if (tf->gdd_writes[pid]) {
968                                         if (io_per_process)
969                                                 strcpy(pos, tf->gdd_writes[pid]->label);
970                                         svg_add_legend(plot, label, " Writes", tf->gdd_writes[pid]->color);
971                                 }
972                         }
973
974                         batch_i = 0;
975                         while (i < cols && batch_i < batch_count) {
976                                 for (pid = 0; pid < tf->io_plots; pid++) {
977                                         if (tf->gdd_reads[pid]) {
978                                                 svg_io_graph_movie(tf->gdd_reads[pid],
979                                                                    history->read_pid_history[pid],
980                                                                    i);
981                                         }
982                                         if (tf->gdd_writes[pid]) {
983                                                 svg_io_graph_movie(tf->gdd_writes[pid],
984                                                                    history->write_pid_history[pid],
985                                                                    i);
986                                         }
987                                 }
988                                 i++;
989                                 batch_i++;
990                         }
991
992                         add_history(history, &movie_history);
993
994                         plot_movie_history(plot, &movie_history);
995
996                         svg_write_legend(plot);
997                         close_plot(plot);
998                         close_plot(plot);
999
1000                         close_plot_file(plot);
1001                 }
1002                 free_all_plot_history(&movie_history);
1003         }
1004         convert_movie_files(movie_dir);
1005         mencode_movie(movie_dir);
1006         cleanup_movie(movie_dir);
1007         free(movie_dir);
1008 }
1009
1010 static void plot_latency(struct plot *plot, int min_seconds, int max_seconds)
1011 {
1012         struct trace_file *tf;
1013         char *units;
1014         char line[128];
1015         u64 max = 0;
1016
1017         if (active_graphs[LATENCY_GRAPH_INDEX] == 0)
1018                 return;
1019
1020         if (num_traces > 1)
1021                 svg_alloc_legend(plot, num_traces);
1022
1023         list_for_each_entry(tf, &all_traces, list) {
1024                 if (tf->latency_gld->max > max)
1025                         max = tf->latency_gld->max;
1026         }
1027
1028         list_for_each_entry(tf, &all_traces, list)
1029                 tf->latency_gld->max = max;
1030
1031         setup_axis(plot);
1032         set_plot_label(plot, "IO Latency");
1033
1034         tf = list_entry(all_traces.next, struct trace_file, list);
1035
1036         scale_line_graph_time(&max, &units);
1037         sprintf(line, "latency (%ss)", units);
1038         set_ylabel(plot, line);
1039         set_yticks(plot, num_yticks, 0, max, "");
1040         set_xticks(plot, num_xticks, min_seconds, max_seconds);
1041
1042         list_for_each_entry(tf, &all_traces, list) {
1043                 svg_line_graph(plot, tf->latency_gld, tf->line_color, 0, 0);
1044                 if (num_traces > 1)
1045                         svg_add_legend(plot, tf->label, "", tf->line_color);
1046         }
1047
1048         if (plot->add_xlabel)
1049                 set_xlabel(plot, "Time (seconds)");
1050         if (num_traces > 1)
1051                 svg_write_legend(plot);
1052         close_plot(plot);
1053         total_graphs_written++;
1054 }
1055
1056 static void plot_iops(struct plot *plot, int min_seconds, int max_seconds)
1057 {
1058         struct trace_file *tf;
1059         char *units;
1060         u64 max = 0;
1061
1062         if (active_graphs[IOPS_GRAPH_INDEX] == 0)
1063                 return;
1064
1065         list_for_each_entry(tf, &all_traces, list) {
1066                 if (tf->iop_gld->max > max)
1067                         max = tf->iop_gld->max;
1068         }
1069
1070         list_for_each_entry(tf, &all_traces, list)
1071                 tf->iop_gld->max = max;
1072
1073         setup_axis(plot);
1074         set_plot_label(plot, "IOPs");
1075         if (num_traces > 1)
1076                 svg_alloc_legend(plot, num_traces);
1077
1078         tf = list_entry(all_traces.next, struct trace_file, list);
1079
1080         scale_line_graph_bytes(&max, &units, 1000);
1081         set_ylabel(plot, "IO/s");
1082
1083         set_yticks(plot, num_yticks, 0, max, units);
1084         set_xticks(plot, num_xticks, min_seconds, max_seconds);
1085
1086         list_for_each_entry(tf, &all_traces, list) {
1087                 svg_line_graph(plot, tf->iop_gld, tf->line_color, 0, 0);
1088                 if (num_traces > 1)
1089                         svg_add_legend(plot, tf->label, "", tf->line_color);
1090         }
1091
1092         if (plot->add_xlabel)
1093                 set_xlabel(plot, "Time (seconds)");
1094         if (num_traces > 1)
1095                 svg_write_legend(plot);
1096
1097         close_plot(plot);
1098         total_graphs_written++;
1099 }
1100
1101 static void check_plot_columns(struct plot *plot, int index)
1102 {
1103         int count;
1104
1105         if (columns > 1 && (total_graphs_written == 0 ||
1106             total_graphs_written % columns != 0)) {
1107                 count = graphs_left(index);
1108                 if (plot->direction == PLOT_DOWN) {
1109                         plot->start_x_offset = 0;
1110                         if (count <= columns)
1111                                 plot->add_xlabel = 1;
1112                 }
1113                 plot->direction = PLOT_ACROSS;
1114
1115         } else {
1116                 plot->direction = PLOT_DOWN;
1117                 if (index == last_active_graph)
1118                         plot->add_xlabel = 1;
1119         }
1120
1121 }
1122
1123 enum {
1124         HELP_LONG_OPT = 1,
1125 };
1126
1127 char *option_string = "T:t:o:l:r:O:N:d:D:p:m::h:w:c:x:y:a:P";
1128 static struct option long_options[] = {
1129         {"columns", required_argument, 0, 'c'},
1130         {"title", required_argument, 0, 'T'},
1131         {"trace", required_argument, 0, 't'},
1132         {"output", required_argument, 0, 'o'},
1133         {"label", required_argument, 0, 'l'},
1134         {"rolling", required_argument, 0, 'r'},
1135         {"no-graph", required_argument, 0, 'N'},
1136         {"only-graph", required_argument, 0, 'O'},
1137         {"device", required_argument, 0, 'd'},
1138         {"blktrace-destination", required_argument, 0, 'D'},
1139         {"prog", required_argument, 0, 'p'},
1140         {"movie", optional_argument, 0, 'm'},
1141         {"width", required_argument, 0, 'w'},
1142         {"height", required_argument, 0, 'h'},
1143         {"xzoom", required_argument, 0, 'x'},
1144         {"yzoom", required_argument, 0, 'y'},
1145         {"io-plot-action", required_argument, 0, 'a'},
1146         {"per-process-io", no_argument, 0, 'P'},
1147         {"help", no_argument, 0, HELP_LONG_OPT},
1148         {0, 0, 0, 0}
1149 };
1150
1151 static void print_usage(void)
1152 {
1153         fprintf(stderr, "iowatcher usage:\n"
1154                 "\t-d (--device): device for blktrace to trace\n"
1155                 "\t-D (--blktrace-destination): destination for blktrace\n"
1156                 "\t-t (--trace): trace file name (more than one allowed)\n"
1157                 "\t-l (--label): trace label in the graph\n"
1158                 "\t-o (--output): output file name (SVG only)\n"
1159                 "\t-p (--prog): program to run while blktrace is run\n"
1160                 "\t-m (--movie [=spindle|rect]): create IO animations\n"
1161                 "\t-r (--rolling): number of seconds in the rolling averge\n"
1162                 "\t-T (--title): graph title\n"
1163                 "\t-N (--no-graph): skip a single graph (io, tput, latency, queue_depth, \n"
1164                 "\t\t\tiops, cpu-sys, cpu-io, cpu-irq cpu-soft cpu-user)\n"
1165                 "\t-O (--only-graph): add a single graph to the output\n"
1166                 "\t-h (--height): set the height of each graph\n"
1167                 "\t-w (--width): set the width of each graph\n"
1168                 "\t-c (--columns): numbers of columns in graph output\n"
1169                 "\t-x (--xzoom): limit processed time to min:max\n"
1170                 "\t-y (--yzoom): limit processed sectors to min:max\n"
1171                 "\t-a (--io-plot-action): plot given action (one of Q,D,C) in IO graph\n"
1172                 "\t-P (--per-process-io): distinguish between processes in IO graph\n"
1173                );
1174         exit(1);
1175 }
1176
1177 static int parse_double_range(char *str, double *min, double *max)
1178 {
1179         char *end;
1180
1181         /* Empty lower bound - leave original value */
1182         if (str[0] != ':') {
1183                 *min = strtod(str, &end);
1184                 if (*min == HUGE_VAL || *min == -HUGE_VAL)
1185                         return -ERANGE;
1186                 if (*end != ':')
1187                         return -EINVAL;
1188         } else
1189                 end = str;
1190         /* Empty upper bound - leave original value */
1191         if (end[1]) {
1192                 *max = strtod(end+1, &end);
1193                 if (*max == HUGE_VAL || *max == -HUGE_VAL)
1194                         return -ERANGE;
1195                 if (*end != 0)
1196                         return -EINVAL;
1197         }
1198         if (*min > *max)
1199                 return -EINVAL;
1200         return 0;
1201 }
1202
1203 static int parse_ull_range(char *str, unsigned long long *min,
1204                            unsigned long long *max)
1205 {
1206         char *end;
1207
1208         /* Empty lower bound - leave original value */
1209         if (str[0] != ':') {
1210                 *min = strtoull(str, &end, 10);
1211                 if (*min == ULLONG_MAX && errno == ERANGE)
1212                         return -ERANGE;
1213                 if (*end != ':')
1214                         return -EINVAL;
1215         } else
1216                 end = str;
1217         /* Empty upper bound - leave original value */
1218         if (end[1]) {
1219                 *max = strtoull(end+1, &end, 10);
1220                 if (*max == ULLONG_MAX && errno == ERANGE)
1221                         return -ERANGE;
1222                 if (*end != 0)
1223                         return -EINVAL;
1224         }
1225         if (*min > *max)
1226                 return -EINVAL;
1227         return 0;
1228 }
1229
1230 static int parse_options(int ac, char **av)
1231 {
1232         int c;
1233         int disabled = 0;
1234
1235         while (1) {
1236                 // int this_option_optind = optind ? optind : 1;
1237                 int option_index = 0;
1238
1239                 c = getopt_long(ac, av, option_string,
1240                                 long_options, &option_index);
1241
1242                 if (c == -1)
1243                         break;
1244
1245                 switch(c) {
1246                 case 'T':
1247                         graph_title = strdup(optarg);
1248                         break;
1249                 case 't':
1250                         add_trace_file(optarg);
1251                         set_blktrace_outfile(optarg);
1252                         break;
1253                 case 'o':
1254                         output_filename = strdup(optarg);
1255                         break;
1256                 case 'l':
1257                         set_trace_label(optarg);
1258                         break;
1259                 case 'r':
1260                         set_rolling_avg(atoi(optarg));
1261                         break;
1262                 case 'O':
1263                         if (!disabled) {
1264                                 disable_all_graphs();
1265                                 disabled = 1;
1266                         }
1267                         enable_one_graph(optarg);
1268                         break;
1269                 case 'N':
1270                         disable_one_graph(optarg);
1271                         break;
1272                 case 'd':
1273                         blktrace_device = strdup(optarg);
1274                         break;
1275                 case 'D':
1276                         blktrace_dest_dir = strdup(optarg);
1277                         if (!strcmp(blktrace_dest_dir, "")) {
1278                                 fprintf(stderr, "Need a directory\n");
1279                                 print_usage();
1280                         }
1281                         break;
1282                 case 'p':
1283                         program_to_run = strdup(optarg);
1284                         break;
1285                 case 'm':
1286                         make_movie = 1;
1287                         if (optarg) {
1288                                 movie_style = lookup_movie_style(optarg);
1289                                 if (movie_style < 0) {
1290                                         fprintf(stderr, "Unknown movie style %s\n", optarg);
1291                                         print_usage();
1292                                 }
1293                         }
1294                         fprintf(stderr, "Using movie style: %s\n",
1295                                 movie_styles[movie_style]);
1296                         break;
1297                 case 'h':
1298                         opt_graph_height = atoi(optarg);
1299                         break;
1300                 case 'w':
1301                         opt_graph_width = atoi(optarg);
1302                         break;
1303                 case 'c':
1304                         columns = atoi(optarg);
1305                         break;
1306                 case 'x':
1307                         if (parse_double_range(optarg, &min_time, &max_time)
1308                             < 0) {
1309                                 fprintf(stderr, "Cannot parse time range %s\n",
1310                                         optarg);
1311                                 exit(1);
1312                         }
1313                         break;
1314                 case 'y':
1315                         if (parse_ull_range(optarg, &min_mb, &max_mb)
1316                             < 0) {
1317                                 fprintf(stderr,
1318                                         "Cannot parse offset range %s\n",
1319                                         optarg);
1320                                 exit(1);
1321                         }
1322                         if (max_mb > ULLONG_MAX >> 20) {
1323                                 fprintf(stderr,
1324                                         "Upper range limit too big."
1325                                         " Maximum is %llu.\n", ULLONG_MAX >> 20);
1326                                 exit(1);
1327                         }
1328                         break;
1329                 case 'a':
1330                         if (strlen(optarg) != 1) {
1331 action_err:
1332                                 fprintf(stderr, "Action must be one of Q, D, C.");
1333                                 exit(1);
1334                         }
1335                         plot_io_action = action_char_to_num(optarg[0]);
1336                         if (plot_io_action < 0)
1337                                 goto action_err;
1338                         break;
1339                 case 'P':
1340                         io_per_process = 1;
1341                         break;
1342                 case '?':
1343                 case HELP_LONG_OPT:
1344                         print_usage();
1345                         break;
1346                 default:
1347                         break;
1348                 }
1349         }
1350         return 0;
1351 }
1352
1353 static void dest_mkdir()
1354 {
1355         int ret;
1356
1357         ret = mkdir(blktrace_dest_dir, 0777);
1358         if (ret && errno != EEXIST) {
1359                 fprintf(stderr, "failed to mkdir error %s\n", strerror(errno));
1360                 exit(errno);
1361         }
1362 }
1363
1364 int main(int ac, char **av)
1365 {
1366         struct plot *plot;
1367         int min_seconds = 0;
1368         int max_seconds = 0;
1369         u64 max_offset = 0;
1370         u64 min_offset = ~(u64)0;
1371         struct trace_file *tf;
1372         int ret;
1373         int rows, cols;
1374
1375         init_io_hash_table();
1376         init_process_hash_table();
1377
1378         enable_all_graphs();
1379
1380         parse_options(ac, av);
1381
1382         last_active_graph = last_graph();
1383         if (make_movie) {
1384                 set_io_graph_scale(256);
1385                 if (movie_style == MOVIE_SPINDLE)
1386                         set_graph_size(750, 550);
1387                 else
1388                         set_graph_size(700, 400);
1389
1390                 /*
1391                  * the plots in the movie don't have a seconds
1392                  * line yet, this makes us skip it
1393                  */
1394                 last_active_graph = TOTAL_GRAPHS + 1;
1395         }
1396         if (opt_graph_height)
1397                 set_graph_height(opt_graph_height);
1398
1399         if (opt_graph_width)
1400                 set_graph_width(opt_graph_width);
1401
1402         if (list_empty(&all_traces)) {
1403                 fprintf(stderr, "No traces found, exiting\n");
1404                 exit(1);
1405         }
1406
1407         if (blktrace_device) {
1408                 char *path;
1409
1410                 dest_mkdir();
1411                 path = join_path(blktrace_dest_dir, blktrace_outfile);
1412
1413                 ret = start_blktrace(blktrace_device, blktrace_outfile,
1414                                      blktrace_dest_dir);
1415                 if (ret) {
1416                         fprintf(stderr, "exiting due to blktrace failure\n");
1417                         exit(1);
1418                 }
1419
1420                 start_mpstat(path);
1421                 if (program_to_run) {
1422                         ret = run_program(program_to_run);
1423                         if (ret) {
1424                                 fprintf(stderr, "failed to run %s\n",
1425                                         program_to_run);
1426                                 exit(1);
1427                         }
1428                         wait_for_tracers();
1429                         blktrace_to_dump(path);
1430                 } else {
1431                         /* no program specified, just wait for
1432                          * blktrace to exit
1433                          */
1434                         wait_for_tracers();
1435                 }
1436                 free(path);
1437         }
1438
1439         /* step one, read all the traces */
1440         read_traces();
1441
1442         /* step two, find the maxes for time and offset */
1443         list_for_each_entry(tf, &all_traces, list)
1444                 compare_minmax_tf(tf, &max_seconds, &min_offset, &max_offset);
1445         min_seconds = min_time;
1446         if (max_seconds > max_time)
1447                 max_seconds = ceil(max_time);
1448         if (min_offset < min_mb << 20)
1449                 min_offset = min_mb << 20;
1450         if (max_offset > max_mb << 20)
1451                 max_offset = max_mb << 20;
1452
1453         /* push the max we found into all the tfs */
1454         set_all_minmax_tf(min_seconds, max_seconds, min_offset, max_offset);
1455
1456         /* alloc graphing structs for all the traces */
1457         setup_trace_file_graphs();
1458
1459         /* run through all the traces and read their events */
1460         read_trace_events();
1461
1462         pick_line_graph_color();
1463
1464         plot = alloc_plot();
1465
1466         if (make_movie) {
1467                 set_legend_width(longest_label + longest_proc_name + 1 + strlen("writes"));
1468                 plot_io_movie(plot);
1469                 exit(0);
1470         }
1471
1472         set_plot_output(plot, output_filename);
1473
1474         if (active_graphs[IO_GRAPH_INDEX] || found_mpstat)
1475                 set_legend_width(longest_label + longest_proc_name + 1 + strlen("writes"));
1476         else if (num_traces > 1)
1477                 set_legend_width(longest_label);
1478         else
1479                 set_legend_width(0);
1480
1481         get_graph_size(&cols, &rows);
1482         if (columns > 1)
1483                 plot->add_xlabel = 1;
1484         set_plot_title(plot, graph_title);
1485
1486         check_plot_columns(plot, IO_GRAPH_INDEX);
1487         plot_io(plot, min_seconds, max_seconds, min_offset, max_offset);
1488         plot->add_xlabel = 0;
1489
1490         if (columns > 1) {
1491                 set_graph_size(cols / columns, rows);
1492                 num_xticks /= columns;
1493                 if (num_xticks < 2)
1494                         num_xticks = 2;
1495         }
1496         if (rows <= 50)
1497                 num_yticks--;
1498
1499         check_plot_columns(plot, TPUT_GRAPH_INDEX);
1500         plot_tput(plot, min_seconds, max_seconds);
1501
1502         check_plot_columns(plot, CPU_IO_GRAPH_INDEX);
1503         plot_cpu(plot, max_seconds, "CPU IO Wait Time",
1504                  CPU_IO_GRAPH_INDEX, MPSTAT_IO);
1505
1506         check_plot_columns(plot, CPU_SYS_GRAPH_INDEX);
1507         plot_cpu(plot, max_seconds, "CPU System Time",
1508                  CPU_SYS_GRAPH_INDEX, MPSTAT_SYS);
1509
1510         check_plot_columns(plot, CPU_IRQ_GRAPH_INDEX);
1511         plot_cpu(plot, max_seconds, "CPU IRQ Time",
1512                  CPU_IRQ_GRAPH_INDEX, MPSTAT_IRQ);
1513
1514         check_plot_columns(plot, CPU_SOFT_GRAPH_INDEX);
1515         plot_cpu(plot, max_seconds, "CPU SoftIRQ Time",
1516                  CPU_SOFT_GRAPH_INDEX, MPSTAT_SOFT);
1517
1518         check_plot_columns(plot, CPU_USER_GRAPH_INDEX);
1519         plot_cpu(plot, max_seconds, "CPU User Time",
1520                  CPU_USER_GRAPH_INDEX, MPSTAT_USER);
1521
1522         check_plot_columns(plot, LATENCY_GRAPH_INDEX);
1523         plot_latency(plot, min_seconds, max_seconds);
1524
1525         check_plot_columns(plot, QUEUE_DEPTH_GRAPH_INDEX);
1526         plot_queue_depth(plot, min_seconds, max_seconds);
1527
1528         check_plot_columns(plot, IOPS_GRAPH_INDEX);
1529         plot_iops(plot, min_seconds, max_seconds);
1530
1531         /* once for all */
1532         close_plot(plot);
1533         close_plot_file(plot);
1534         return 0;
1535 }