iowatcher: Add movie support
[blktrace.git] / iowatcher / main.c
1 /*
2  * Copyright (C) 2012 Fusion-io
3  *
4  *  This program is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU General Public
6  *  License v2 as published by the Free Software Foundation.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  *
17  *  Parts of this file were imported from Jens Axboe's blktrace sources (also GPL)
18  */
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <math.h>
26 #include <inttypes.h>
27 #include <string.h>
28 #include <asm/types.h>
29 #include <errno.h>
30 #include <sys/mman.h>
31 #include <time.h>
32 #include <math.h>
33 #include <getopt.h>
34
35 #include "plot.h"
36 #include "blkparse.h"
37 #include "list.h"
38 #include "tracers.h"
39 #include "mpstat.h"
40
41 LIST_HEAD(all_traces);
42
43 static char line[1024];
44 static int line_len = 1024;
45 static int found_mpstat = 0;
46 static int cpu_color_index = 0;
47 static int color_index = 0;
48 static int make_movie = 0;
49 static int opt_graph_width = 0;
50 static int opt_graph_height = 0;
51
52 char *colors[] = {
53         "blue", "darkgreen",
54         "red", "aqua",
55         "orange", "darkviolet",
56         "brown", "#00FF00",
57         "yellow", "coral",
58         "black", "darkred",
59         "fuchsia", "crimson",
60         NULL };
61
62 char *pick_color(void) {
63         char *ret = colors[color_index];
64         if (!ret) {
65                 color_index = 0;
66                 ret = colors[color_index];
67         }
68         color_index++;
69         return ret;
70 }
71
72 char *pick_cpu_color(void) {
73         char *ret = colors[cpu_color_index];
74         if (!ret) {
75                 color_index = 0;
76                 ret = colors[cpu_color_index];
77         }
78         cpu_color_index++;
79         return ret;
80 }
81
82 enum {
83         IO_GRAPH_INDEX = 0,
84         TPUT_GRAPH_INDEX,
85         CPU_SYS_GRAPH_INDEX,
86         CPU_IO_GRAPH_INDEX,
87         CPU_IRQ_GRAPH_INDEX,
88         CPU_SOFT_GRAPH_INDEX,
89         CPU_USER_GRAPH_INDEX,
90         LATENCY_GRAPH_INDEX,
91         QUEUE_DEPTH_GRAPH_INDEX,
92         IOPS_GRAPH_INDEX,
93         TOTAL_GRAPHS
94 };
95
96 enum {
97         MPSTAT_SYS = 0,
98         MPSTAT_IRQ,
99         MPSTAT_IO,
100         MPSTAT_SOFT,
101         MPSTAT_USER,
102         MPSTAT_GRAPHS
103 };
104
105 static char *graphs_by_name[] = {
106         "io",
107         "tput",
108         "cpu-sys",
109         "cpu-io",
110         "cpu-irq",
111         "cpu-soft",
112         "cpu-user",
113         "latency",
114         "queue-depth",
115         "iops",
116 };
117
118 static int active_graphs[TOTAL_GRAPHS];
119 static int last_active_graph = IOPS_GRAPH_INDEX;
120
121 static int label_index = 0;
122 static int num_traces = 0;
123 static int longest_label = 0;
124
125 struct trace_file {
126         struct list_head list;
127         char *filename;
128         char *label;
129         struct trace *trace;
130         int seconds;
131         int stop_seconds;
132         u64 max_offset;
133
134         char *read_color;
135         char *write_color;
136
137         struct graph_line_data *tput_gld;
138         struct graph_line_data *iop_gld;
139         struct graph_line_data *latency_gld;
140         struct graph_line_data *queue_depth_gld;
141         struct graph_dot_data *gdd_writes;
142         struct graph_dot_data *gdd_reads;
143
144         int mpstat_seconds;
145         int mpstat_stop_seconds;
146         struct graph_line_data **mpstat_gld;
147 };
148
149 static void alloc_mpstat_gld(struct trace_file *tf)
150 {
151         struct graph_line_data **ptr;
152
153         if (tf->trace->mpstat_num_cpus == 0)
154                 return;
155
156         ptr = calloc((tf->trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS,
157                      sizeof(struct graph_line_data *));
158         if (!ptr) {
159                 perror("Unable to allocate mpstat arrays\n");
160                 exit(1);
161         }
162         tf->mpstat_gld = ptr;
163 }
164
165 static void enable_all_graphs(void)
166 {
167         int i;
168         for (i = 0; i < TOTAL_GRAPHS; i++)
169                 active_graphs[i] = 1;
170 }
171
172 static void disable_all_graphs(void)
173 {
174         int i;
175         for (i = 0; i < TOTAL_GRAPHS; i++)
176                 active_graphs[i] = 0;
177 }
178
179 static int enable_one_graph(char *name)
180 {
181         int i;
182         for (i = 0; i < TOTAL_GRAPHS; i++) {
183                 if (strcmp(name, graphs_by_name[i]) == 0) {
184                         active_graphs[i] = 1;
185                         return 0;
186                 }
187         }
188         return -ENOENT;
189 }
190
191 static int disable_one_graph(char *name)
192 {
193         int i;
194         for (i = 0; i < TOTAL_GRAPHS; i++) {
195                 if (strcmp(name, graphs_by_name[i]) == 0) {
196                         active_graphs[i] = 0;
197                         return 0;
198                 }
199         }
200         return -ENOENT;
201 }
202
203 static int last_graph(void)
204 {
205         int i;
206         for (i = TOTAL_GRAPHS - 1; i >= 0; i--) {
207                 if (active_graphs[i]) {
208                         return i;
209                 }
210         }
211         return -ENOENT;
212 }
213 static void add_trace_file(char *filename)
214 {
215         struct trace_file *tf;
216
217         tf = calloc(1, sizeof(*tf));
218         if (!tf) {
219                 fprintf(stderr, "Unable to allocate memory\n");
220                 exit(1);
221         }
222         tf->label = "";
223         tf->filename = strdup(filename);
224         list_add_tail(&tf->list, &all_traces);
225         tf->read_color = pick_color();
226         tf->write_color = pick_color();
227         num_traces++;
228 }
229
230 static void setup_trace_file_graphs(void)
231 {
232         struct trace_file *tf;
233         int i;
234
235         list_for_each_entry(tf, &all_traces, list) {
236                 tf->tput_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
237                 tf->latency_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
238                 tf->queue_depth_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
239                 tf->iop_gld = alloc_line_data(tf->seconds, tf->stop_seconds);
240                 tf->gdd_writes = alloc_dot_data(tf->seconds, tf->max_offset, tf->stop_seconds);
241                 tf->gdd_reads = alloc_dot_data(tf->seconds, tf->max_offset, tf->stop_seconds);
242
243                 if (tf->trace->mpstat_num_cpus == 0)
244                         continue;
245
246                 alloc_mpstat_gld(tf);
247                 for (i = 0; i < (tf->trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS; i++) {
248                         tf->mpstat_gld[i] =
249                                 alloc_line_data(tf->mpstat_seconds,
250                                                 tf->mpstat_seconds);
251                         tf->mpstat_gld[i]->max = 100;
252                 }
253         }
254 }
255
256 static void read_traces(void)
257 {
258         struct trace_file *tf;
259         struct trace *trace;
260         u64 last_time;
261         u64 ymin;
262         u64 ymax;
263
264         list_for_each_entry(tf, &all_traces, list) {
265                 trace = open_trace(tf->filename);
266                 if (!trace)
267                         exit(1);
268
269                 last_time = find_last_time(trace);
270                 tf->trace = trace;
271                 tf->seconds = SECONDS(last_time);
272                 tf->stop_seconds = SECONDS(last_time);
273                 tf->max_offset = find_highest_offset(trace);
274
275                 filter_outliers(trace, tf->max_offset, &ymin, &ymax);
276                 tf->max_offset = ymax;
277
278                 read_mpstat(trace, tf->filename);
279                 tf->mpstat_stop_seconds = trace->mpstat_seconds;
280                 tf->mpstat_seconds = trace->mpstat_seconds;
281                 if (tf->mpstat_seconds)
282                         found_mpstat = 1;
283         }
284 }
285
286 static void read_trace_events(void)
287 {
288
289         struct trace_file *tf;
290         struct trace *trace;
291         int ret;
292         int i;
293         int time;
294         double user, sys, iowait, irq, soft;
295         double max_user = 0, max_sys = 0, max_iowait = 0,
296                max_irq = 0, max_soft = 0;
297
298         list_for_each_entry(tf, &all_traces, list) {
299                 trace = tf->trace;
300                 first_record(trace);
301                 while (1) {
302                         check_record(trace);
303                         add_tput(trace, tf->tput_gld);
304                         add_iop(trace, tf->iop_gld);
305                         add_io(trace, tf->gdd_writes, tf->gdd_reads);
306                         add_pending_io(trace, tf->queue_depth_gld);
307                         add_completed_io(trace, tf->latency_gld);
308                         ret = next_record(trace);
309                         if (ret)
310                                 break;
311                 }
312         }
313         list_for_each_entry(tf, &all_traces, list) {
314                 trace = tf->trace;
315
316                 if (trace->mpstat_num_cpus == 0)
317                         continue;
318
319                 first_mpstat(trace);
320
321                 for (time = 0; time < tf->mpstat_stop_seconds; time ++) {
322                         for (i = 0; i < (trace->mpstat_num_cpus + 1) * MPSTAT_GRAPHS; i += MPSTAT_GRAPHS) {
323                                 ret = read_mpstat_event(trace, &user, &sys,
324                                                         &iowait, &irq, &soft);
325                                 if (ret)
326                                         goto mpstat_done;
327                                 if (next_mpstat_line(trace))
328                                         goto mpstat_done;
329
330                                 if (sys > max_sys)
331                                         max_sys = sys;
332                                 if (user > max_user)
333                                         max_user = user;
334                                 if (irq > max_irq)
335                                         max_irq = irq;
336                                 if (iowait > max_iowait)
337                                         max_iowait = iowait;
338
339                                 add_mpstat_gld(time, sys, tf->mpstat_gld[i + MPSTAT_SYS]);
340                                 add_mpstat_gld(time, irq, tf->mpstat_gld[i + MPSTAT_IRQ]);
341                                 add_mpstat_gld(time, soft, tf->mpstat_gld[i + MPSTAT_SOFT]);
342                                 add_mpstat_gld(time, user, tf->mpstat_gld[i + MPSTAT_USER]);
343                                 add_mpstat_gld(time, iowait, tf->mpstat_gld[i + MPSTAT_IO]);
344                         }
345                         if (next_mpstat(trace) == NULL)
346                                 break;
347                 }
348         }
349
350 mpstat_done:
351         list_for_each_entry(tf, &all_traces, list) {
352                 trace = tf->trace;
353
354                 if (trace->mpstat_num_cpus == 0)
355                         continue;
356
357                 tf->mpstat_gld[MPSTAT_SYS]->max = max_sys;
358                 tf->mpstat_gld[MPSTAT_IRQ]->max = max_irq;
359                 tf->mpstat_gld[MPSTAT_SOFT]->max = max_soft;
360                 tf->mpstat_gld[MPSTAT_USER]->max = max_user;
361                 tf->mpstat_gld[MPSTAT_IO]->max = max_iowait;;
362         }
363         return;
364 }
365
366 static void set_trace_label(char *label)
367 {
368         int cur = 0;
369         struct trace_file *tf;
370         int len = strlen(label);
371
372         if (len > longest_label)
373                 longest_label = len;
374
375         list_for_each_entry(tf, &all_traces, list) {
376                 if (cur == label_index) {
377                         tf->label = strdup(label);
378                         label_index++;
379                         break;
380                 }
381                 cur++;
382         }
383 }
384
385 static char *graph_title = "";
386 static char *output_filename = "trace.svg";
387 static char *blktrace_device = NULL;
388 static char *blktrace_outfile = "trace";
389 static char *blktrace_dest_dir = ".";
390 static char *program_to_run = NULL;
391
392 static void set_blktrace_outfile(char *arg)
393 {
394         char *s = strdup(arg);
395         char *last_dot = strrchr(s, '.');
396
397         if (last_dot) {
398                 if (strcmp(last_dot, ".dump") == 0)
399                         *last_dot = '\0';
400         }
401         blktrace_outfile = s;
402 }
403
404
405 static void compare_max_tf(struct trace_file *tf, int *seconds, u64 *max_offset)
406 {
407         if (tf->seconds > *seconds)
408                 *seconds = tf->seconds;
409         if (tf->max_offset > *max_offset)
410                 *max_offset = tf->max_offset;
411 }
412
413 static void set_all_max_tf(int seconds, u64 max_offset)
414 {
415         struct trace_file *tf;
416
417         list_for_each_entry(tf, &all_traces, list) {
418                 tf->seconds = seconds;
419                 tf->max_offset = max_offset;
420         }
421 }
422
423 static char *create_movie_temp_dir(void)
424 {
425         char *ret;
426         char *pattern = strdup("btrfs-movie-XXXXXX");;
427
428         ret = mkdtemp(pattern);
429         if (!ret) {
430                 perror("Unable to create temp directory for movie files");
431                 exit(1);
432         }
433         return ret;
434 }
435
436 static struct plot_history *alloc_plot_history(char *color)
437 {
438         struct plot_history *ph = calloc(1, sizeof(struct plot_history));
439
440         if (!ph) {
441                 perror("memory allocation failed");
442                 exit(1);
443         }
444         ph->history = calloc(4096, sizeof(double));
445         if (!ph->history) {
446                 perror("memory allocation failed");
447                 exit(1);
448         }
449         ph->history_len = 4096;
450         ph->color = color;
451         return ph;
452 }
453
454 LIST_HEAD(movie_history_writes);
455 LIST_HEAD(movie_history_reads);
456 int num_histories = 0;
457
458 static void add_history(struct plot_history *ph, struct list_head *list)
459 {
460         struct plot_history *entry;
461
462         list_add_tail(&ph->list, list);
463         num_histories++;
464
465         if (num_histories > 12) {
466                 num_histories--;
467                 entry = list_entry(list->next, struct plot_history, list);
468                 list_del(&entry->list);
469                 free(entry->history);
470                 free(entry);
471         }
472 }
473
474 static void plot_movie_history(struct plot *plot, struct list_head *list)
475 {
476         float alpha = 0.1;
477         struct plot_history *ph;
478
479         list_for_each_entry(ph, list, list) {
480                 if (ph->list.next == list)
481                         alpha = 1;
482                 svg_io_graph_movie_array(plot, ph, 1);
483                 alpha += 0.2;
484                 if (alpha > 1)
485                         alpha = 0.8;
486          }
487 }
488
489 static void free_all_plot_history(struct list_head *head)
490 {
491         struct plot_history *entry;
492         while (!list_empty(head)) {
493                 entry = list_entry(head->next, struct plot_history, list);
494                 list_del(&entry->list);
495                 free(entry->history);
496                 free(entry);
497         }
498 }
499
500 static void __plot_io(struct plot *plot, int seconds, u64 max_offset)
501 {
502         struct trace_file *tf;
503
504         if (active_graphs[IO_GRAPH_INDEX] == 0)
505                 return;
506
507         setup_axis(plot);
508
509         svg_alloc_legend(plot, num_traces * 2);
510
511         set_plot_label(plot, "Device IO");
512         set_ylabel(plot, "Offset (MB)");
513         set_yticks(plot, 4, 0, max_offset / (1024 * 1024), "");
514         set_xticks(plot, 9, 0, seconds);
515
516         list_for_each_entry(tf, &all_traces, list) {
517                 char *label = tf->label;
518
519                 if (!label)
520                         label = "";
521                 svg_io_graph(plot, tf->gdd_reads, tf->read_color);
522                 if (tf->gdd_reads->total_ios)
523                         svg_add_legend(plot, label, " Reads", tf->read_color);
524
525                 svg_io_graph(plot, tf->gdd_writes, tf->write_color);
526                 if (tf->gdd_writes->total_ios) {
527                         svg_add_legend(plot, label, " Writes", tf->write_color);
528                 }
529         }
530         if (plot->add_xlabel)
531                 set_xlabel(plot, "Time (seconds)");
532         svg_write_legend(plot);
533 }
534
535 static void plot_io(struct plot *plot, int seconds, u64 max_offset)
536 {
537         plot->add_xlabel = last_active_graph == IO_GRAPH_INDEX;
538
539         __plot_io(plot, seconds, max_offset);
540         close_plot(plot);
541 }
542
543 static void __plot_tput(struct plot *plot, int seconds)
544 {
545         struct trace_file *tf;
546         char *units;
547         char line[128];
548         u64 max = 0;
549
550         if (active_graphs[TPUT_GRAPH_INDEX] == 0)
551                 return;
552
553         if (num_traces > 1)
554                 svg_alloc_legend(plot, num_traces);
555         list_for_each_entry(tf, &all_traces, list) {
556                 if (tf->tput_gld->max > max)
557                         max = tf->tput_gld->max;
558         }
559         list_for_each_entry(tf, &all_traces, list)
560                 tf->tput_gld->max = max;
561
562         setup_axis(plot);
563         set_plot_label(plot, "Throughput");
564
565         tf = list_entry(all_traces.next, struct trace_file, list);
566
567         scale_line_graph_bytes(&max, &units, 1024);
568         sprintf(line, "%sB/s", units);
569         set_ylabel(plot, line);
570         set_yticks(plot, 4, 0, max, "");
571         set_xticks(plot, 9, 0, seconds);
572
573         list_for_each_entry(tf, &all_traces, list) {
574                 svg_line_graph(plot, tf->tput_gld, tf->read_color);
575                 if (num_traces > 1)
576                         svg_add_legend(plot, tf->label, "", tf->read_color);
577         }
578
579         if (plot->add_xlabel)
580                 set_xlabel(plot, "Time (seconds)");
581         if (num_traces > 1)
582                 svg_write_legend(plot);
583 }
584
585 static void plot_tput(struct plot *plot, int seconds)
586 {
587         plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
588         __plot_tput(plot, seconds);
589         close_plot(plot);
590 }
591
592 static void convert_movie_files(char *movie_dir)
593 {
594         fprintf(stderr, "Converting svg files in %s\n", movie_dir);
595         snprintf(line, line_len, "find %s -name \\*.svg | xargs -I{} -n 1 -P 8 rsvg-convert -o {}.png {}",
596                  movie_dir);
597         system(line);
598 }
599
600 static void mencode_movie(char *movie_dir)
601 {
602         fprintf(stderr, "Creating movie %s\n", movie_dir);
603         snprintf(line, line_len, "mencoder mf://%s/*.png -mf type=png:fps=16 -of lavf "
604                  "-ovc x264 -oac copy -o %s",
605                  movie_dir, output_filename);
606         system(line);
607 }
608
609 static void cleanup_movie(char *movie_dir)
610 {
611         fprintf(stderr, "Removing movie dir %s\n", movie_dir);
612         snprintf(line, line_len, "rm %s/*", movie_dir);
613         system(line);
614
615         snprintf(line, line_len, "rmdir %s", movie_dir);
616         system(line);
617 }
618
619 static void plot_io_movie(struct plot *plot)
620 {
621         struct trace_file *tf;
622         char *movie_dir = create_movie_temp_dir();
623         int i;
624         struct plot_history *read_history;
625         struct plot_history *write_history;
626         int batch_i;
627         int movie_len = 30;
628         int movie_frames_per_sec = 16;
629         int total_frames = movie_len * movie_frames_per_sec;
630         int rows, cols;
631         int batch_count;
632
633         get_graph_size(&cols, &rows);
634         batch_count = cols / total_frames;
635
636         if (batch_count == 0)
637                 batch_count = 1;
638
639         list_for_each_entry(tf, &all_traces, list) {
640                 char *label = tf->label;
641                 if (!label)
642                         label = "";
643
644                 i = 0;
645                 while (i < cols) {
646                         snprintf(line, line_len, "%s/%010d-%s.svg", movie_dir, i, output_filename);
647                         set_plot_output(plot, line);
648
649                         set_plot_title(plot, graph_title);
650                         setup_axis(plot);
651                         svg_alloc_legend(plot, num_traces * 2);
652
653                         read_history = alloc_plot_history(tf->read_color);
654                         write_history = alloc_plot_history(tf->write_color);
655                         read_history->col = i;
656                         write_history->col = i;
657
658                         if (tf->gdd_reads->total_ios)
659                                 svg_add_legend(plot, label, " Reads", tf->read_color);
660                         if (tf->gdd_writes->total_ios)
661                                 svg_add_legend(plot, label, " Writes", tf->write_color);
662
663                         batch_i = 0;
664                         while (i < cols && batch_i < batch_count) {
665                                 /* print just this column */
666                                 svg_io_graph_movie(tf->gdd_reads, read_history, i);
667
668                                 svg_io_graph_movie(tf->gdd_writes, write_history, i);
669                                 i++;
670                                 batch_i++;
671                         }
672
673                         add_history(read_history, &movie_history_reads);
674                         add_history(write_history, &movie_history_writes);
675
676                         plot_movie_history(plot, &movie_history_reads);
677                         plot_movie_history(plot, &movie_history_writes);
678
679                         svg_write_legend(plot);
680                         close_plot(plot);
681
682                         set_graph_size(cols, rows / 3);
683                         plot->add_xlabel = 1;
684                         __plot_tput(plot, tf->gdd_reads->seconds);
685                         svg_write_time_line(plot, i);
686                         close_plot(plot);
687                         set_graph_size(cols, rows);
688
689                         close_plot(plot);
690                 }
691                 free_all_plot_history(&movie_history_reads);
692                 free_all_plot_history(&movie_history_writes);
693         }
694         convert_movie_files(movie_dir);
695         mencode_movie(movie_dir);
696         cleanup_movie(movie_dir);
697         free(movie_dir);
698 }
699
700 static void plot_cpu(struct plot *plot, int seconds, char *label,
701                      int active_index, int gld_index)
702 {
703         struct trace_file *tf;
704         char line[128];
705         int max = 0;
706         int i;
707         int gld_i;
708         char *color;
709         double avg = 0;
710         int ymax;
711         int plotted = 0;
712
713         if (active_graphs[active_index] == 0)
714                 return;
715
716         list_for_each_entry(tf, &all_traces, list) {
717                 if (tf->trace->mpstat_num_cpus > max)
718                         max = tf->trace->mpstat_num_cpus;
719         }
720         if (max == 0)
721                 return;
722
723         tf = list_entry(all_traces.next, struct trace_file, list);
724
725         ymax = tf->mpstat_gld[gld_index]->max;
726         if (ymax == 0)
727                 return;
728
729         svg_alloc_legend(plot, num_traces * max);
730
731         plot->add_xlabel = last_active_graph == active_index;
732         setup_axis(plot);
733         set_plot_label(plot, label);
734
735         seconds = tf->mpstat_seconds;
736
737         set_yticks(plot, 4, 0, tf->mpstat_gld[gld_index]->max, "");
738         set_ylabel(plot, "Percent");
739         set_xticks(plot, 9, 0, seconds);
740
741         cpu_color_index = 0;
742         list_for_each_entry(tf, &all_traces, list) {
743                 for (i = 0; i < tf->mpstat_gld[0]->stop_seconds; i++) {
744                         avg += tf->mpstat_gld[gld_index]->data[i].sum;
745                 }
746                 avg /= tf->mpstat_gld[gld_index]->stop_seconds;
747                 color = pick_cpu_color();
748                 svg_line_graph(plot, tf->mpstat_gld[0], color);
749                 svg_add_legend(plot, tf->label, " avg", color);
750
751                 for (i = 1; i < tf->trace->mpstat_num_cpus + 1; i++) {
752                         struct graph_line_data *gld = tf->mpstat_gld[i * MPSTAT_GRAPHS + gld_index];
753                         double this_avg = 0;
754
755                         for (gld_i = 0; gld_i < gld->stop_seconds; gld_i++)
756                                 this_avg += gld->data[i].sum;
757
758                         this_avg /= gld->stop_seconds;
759
760                         for (gld_i = 0; gld_i < gld->stop_seconds; gld_i++) {
761                                 if (this_avg > avg + 30 ||
762                                     gld->data[gld_i].sum > 95) {
763                                         color = pick_cpu_color();
764                                         svg_line_graph(plot, gld, color);
765                                         snprintf(line, 128, " CPU %d\n", i - 1);
766                                         svg_add_legend(plot, tf->label, line, color);
767                                         plotted++;
768                                         break;
769                                 }
770
771                         }
772                 }
773         }
774
775         if (plot->add_xlabel)
776                 set_xlabel(plot, "Time (seconds)");
777
778         if (plot->legend_index <= 8)
779                 svg_write_legend(plot);
780         else
781                 svg_free_legend(plot);
782         close_plot(plot);
783 }
784
785 static void plot_latency(struct plot *plot, int seconds)
786 {
787         struct trace_file *tf;
788         char *units;
789         char line[128];
790         u64 max = 0;
791
792         if (active_graphs[LATENCY_GRAPH_INDEX] == 0)
793                 return;
794
795         if (num_traces > 1)
796                 svg_alloc_legend(plot, num_traces);
797         list_for_each_entry(tf, &all_traces, list) {
798                 if (tf->latency_gld->max > max)
799                         max = tf->latency_gld->max;
800         }
801         list_for_each_entry(tf, &all_traces, list)
802                 tf->latency_gld->max = max;
803
804         plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
805         setup_axis(plot);
806         set_plot_label(plot, "IO Latency");
807
808         tf = list_entry(all_traces.next, struct trace_file, list);
809
810         scale_line_graph_time(&max, &units);
811         sprintf(line, "latency (%ss)", units);
812         set_ylabel(plot, line);
813         set_yticks(plot, 4, 0, max, "");
814         set_xticks(plot, 9, 0, seconds);
815
816         list_for_each_entry(tf, &all_traces, list) {
817                 svg_line_graph(plot, tf->latency_gld, tf->read_color);
818                 if (num_traces > 1)
819                         svg_add_legend(plot, tf->label, "", tf->read_color);
820         }
821
822         if (plot->add_xlabel)
823                 set_xlabel(plot, "Time (seconds)");
824         if (num_traces > 1)
825                 svg_write_legend(plot);
826         close_plot(plot);
827 }
828
829 static void plot_queue_depth(struct plot *plot, int seconds)
830 {
831         struct trace_file *tf;
832
833         if (active_graphs[QUEUE_DEPTH_GRAPH_INDEX] == 0)
834                 return;
835
836         plot->add_xlabel = last_active_graph == QUEUE_DEPTH_GRAPH_INDEX;
837
838         setup_axis(plot);
839         set_plot_label(plot, "Queue Depth");
840         if (num_traces > 1)
841                 svg_alloc_legend(plot, num_traces);
842
843         tf = list_entry(all_traces.next, struct trace_file, list);
844         set_ylabel(plot, "Pending IO");
845         set_yticks(plot, 4, 0, tf->queue_depth_gld->max, "");
846         set_xticks(plot, 9, 0, seconds);
847
848         list_for_each_entry(tf, &all_traces, list) {
849                 svg_line_graph(plot, tf->queue_depth_gld, tf->read_color);
850                 if (num_traces > 1)
851                         svg_add_legend(plot, tf->label, "", tf->read_color);
852         }
853
854         if (plot->add_xlabel)
855                 set_xlabel(plot, "Time (seconds)");
856         if (num_traces > 1)
857                 svg_write_legend(plot);
858         close_plot(plot);
859 }
860
861 static void plot_iops(struct plot *plot, int seconds)
862 {
863         struct trace_file *tf;
864         char *units;
865         u64 max = 0;
866
867         if (active_graphs[IOPS_GRAPH_INDEX] == 0)
868                 return;
869
870         list_for_each_entry(tf, &all_traces, list) {
871                 if (tf->iop_gld->max > max)
872                         max = tf->iop_gld->max;
873         }
874
875         list_for_each_entry(tf, &all_traces, list)
876                 tf->iop_gld->max = max;
877
878
879         plot->add_xlabel = last_active_graph == IOPS_GRAPH_INDEX;
880         setup_axis(plot);
881         set_plot_label(plot, "IOPs");
882         if (num_traces > 1)
883                 svg_alloc_legend(plot, num_traces);
884
885         tf = list_entry(all_traces.next, struct trace_file, list);
886
887         scale_line_graph_bytes(&max, &units, 1000);
888         set_ylabel(plot, "IO/s");
889
890         set_yticks(plot, 4, 0, max, units);
891         set_xticks(plot, 9, 0, seconds);
892
893         list_for_each_entry(tf, &all_traces, list) {
894                 svg_line_graph(plot, tf->iop_gld, tf->read_color);
895                 if (num_traces > 1)
896                         svg_add_legend(plot, tf->label, "", tf->read_color);
897         }
898
899         if (plot->add_xlabel)
900                 set_xlabel(plot, "Time (seconds)");
901         if (num_traces > 1)
902                 svg_write_legend(plot);
903
904         close_plot(plot);
905 }
906
907 enum {
908         HELP_LONG_OPT = 1,
909 };
910
911 char *option_string = "T:t:o:l:r:O:N:d:p:mh:w:";
912 static struct option long_options[] = {
913         {"title", required_argument, 0, 'T'},
914         {"trace", required_argument, 0, 't'},
915         {"output", required_argument, 0, 'o'},
916         {"label", required_argument, 0, 'l'},
917         {"rolling", required_argument, 0, 'r'},
918         {"no-graph", required_argument, 0, 'N'},
919         {"only-graph", required_argument, 0, 'O'},
920         {"device", required_argument, 0, 'd'},
921         {"prog", required_argument, 0, 'p'},
922         {"movie", no_argument, 0, 'm'},
923         {"width", required_argument, 0, 'w'},
924         {"height", required_argument, 0, 'h'},
925         {"help", required_argument, 0, HELP_LONG_OPT},
926         {0, 0, 0, 0}
927 };
928
929 static void print_usage(void)
930 {
931         fprintf(stderr, "iowatcher usage:\n"
932                 "\t-d (--device): device for blktrace to trace\n"
933                 "\t-t (--trace): trace file name (more than one allowed)\n"
934                 "\t-l (--label): trace label in the graph\n"
935                 "\t-o (--output): output file name (SVG only)\n"
936                 "\t-p (--prog): program to run while blktrace is run\n"
937                 "\t-p (--movie): create IO animations\n"
938                 "\t-r (--rolling): number of seconds in the rolling averge\n"
939                 "\t-T (--title): graph title\n"
940                 "\t-N (--no-graph): skip a single graph (io, tput, latency, queue_depth, iops)\n"
941                 "\t-h (--height): set the height of each graph\n"
942                 "\t-w (--width): set the width of each graph\n"
943                );
944         exit(1);
945 }
946
947 static int parse_options(int ac, char **av)
948 {
949         int c;
950         int disabled = 0;
951
952         while (1) {
953                 // int this_option_optind = optind ? optind : 1;
954                 int option_index = 0;
955
956                 c = getopt_long(ac, av, option_string,
957                                 long_options, &option_index);
958
959                 if (c == -1)
960                         break;
961
962                 switch(c) {
963                 case 'T':
964                         graph_title = strdup(optarg);
965                         break;
966                 case 't':
967                         add_trace_file(optarg);
968                         set_blktrace_outfile(optarg);
969                         break;
970                 case 'o':
971                         output_filename = strdup(optarg);
972                         break;
973                 case 'l':
974                         set_trace_label(optarg);
975                         break;
976                 case 'r':
977                         set_rolling_avg(atoi(optarg));
978                         break;
979                 case 'O':
980                         if (!disabled) {
981                                 disable_all_graphs();
982                                 disabled = 1;
983                         }
984                         enable_one_graph(optarg);
985                         break;
986                 case 'N':
987                         disable_one_graph(optarg);
988                         break;
989                 case 'd':
990                         blktrace_device = strdup(optarg);
991                         break;
992                 case 'p':
993                         program_to_run = strdup(optarg);
994                         break;
995                 case 'm':
996                         make_movie = 1;
997                         break;
998                 case 'h':
999                         opt_graph_height = atoi(optarg);
1000                         break;
1001                 case 'w':
1002                         opt_graph_width = atoi(optarg);
1003                         break;
1004                 case '?':
1005                 case HELP_LONG_OPT:
1006                         print_usage();
1007                         break;
1008                 default:
1009                         break;
1010                 }
1011         }
1012         return 0;
1013 }
1014
1015
1016 int main(int ac, char **av)
1017 {
1018         struct plot *plot;
1019         int seconds = 0;
1020         u64 max_offset = 0;
1021         struct trace_file *tf;
1022         int ret;
1023
1024         init_io_hash_table();
1025
1026         enable_all_graphs();
1027
1028         parse_options(ac, av);
1029
1030         last_active_graph = last_graph();
1031         if (make_movie) {
1032                 set_io_graph_scale(256);
1033                 set_graph_size(700, 250);
1034         }
1035         if (opt_graph_height)
1036                 set_graph_height(opt_graph_height);
1037
1038         if (opt_graph_width)
1039                 set_graph_width(opt_graph_height);
1040
1041         if (list_empty(&all_traces)) {
1042                 fprintf(stderr, "No traces found, exiting\n");
1043                 exit(1);
1044         }
1045
1046         if (blktrace_device) {
1047                 ret = start_blktrace(blktrace_device, blktrace_outfile,
1048                                      blktrace_dest_dir);
1049                 if (ret) {
1050                         fprintf(stderr, "exiting due to blktrace failure\n");
1051                         exit(1);
1052                 }
1053                 start_mpstat(blktrace_outfile);
1054                 if (program_to_run) {
1055                         ret = run_program(program_to_run);
1056                         if (ret) {
1057                                 fprintf(stderr, "failed to run %s\n",
1058                                         program_to_run);
1059                                 exit(1);
1060                         }
1061                         wait_for_tracers();
1062                         blktrace_to_dump(blktrace_outfile);
1063                 } else {
1064                         /* no program specified, just wait for
1065                          * blktrace to exit
1066                          */
1067                         wait_for_tracers();
1068                 }
1069         }
1070
1071         /* step one, read all the traces */
1072         read_traces();
1073
1074         /* step two, find the maxes for time and offset */
1075         list_for_each_entry(tf, &all_traces, list)
1076                 compare_max_tf(tf, &seconds, &max_offset);
1077
1078         /* push the max we found into all the tfs */
1079         set_all_max_tf(seconds, max_offset);
1080
1081         /* alloc graphing structs for all the traces */
1082         setup_trace_file_graphs();
1083
1084         /* run through all the traces and read their events */
1085         read_trace_events();
1086
1087         plot = alloc_plot();
1088
1089         if (make_movie) {
1090                 plot_io_movie(plot);
1091                 exit(0);
1092         }
1093
1094         set_plot_output(plot, output_filename);
1095
1096         if (active_graphs[IO_GRAPH_INDEX] || found_mpstat)
1097                 set_legend_width(longest_label + strlen("writes"));
1098         else if (num_traces > 1)
1099                 set_legend_width(longest_label);
1100         else
1101                 set_legend_width(0);
1102
1103
1104         plot_io(plot, seconds, max_offset);
1105         plot_tput(plot, seconds);
1106         plot_cpu(plot, seconds, "CPU IO Wait Time",
1107                  CPU_IO_GRAPH_INDEX, MPSTAT_IO);
1108         plot_cpu(plot, seconds, "CPU System Time",
1109                  CPU_SYS_GRAPH_INDEX, MPSTAT_SYS);
1110         plot_cpu(plot, seconds, "CPU IRQ Time",
1111                  CPU_IRQ_GRAPH_INDEX, MPSTAT_IRQ);
1112         plot_cpu(plot, seconds, "CPU SoftIRQ Time",
1113                  CPU_SOFT_GRAPH_INDEX, MPSTAT_SOFT);
1114         plot_cpu(plot, seconds, "CPU User Time",
1115                  CPU_USER_GRAPH_INDEX, MPSTAT_USER);
1116
1117         plot_latency(plot, seconds);
1118         plot_queue_depth(plot, seconds);
1119         plot_iops(plot, seconds);
1120
1121         /* once for all */
1122         close_plot(plot);
1123         return 0;
1124 }