iowatcher: Switch to ffmpeg for movie encoding. Chrome and vlc like these better.
[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, "ffmpeg -r 25 -y -i %s/%%10d-%s.svg.png -b:v 250k "
604                  "-vcodec libx264 %s", movie_dir, output_filename, output_filename);
605         system(line);
606 }
607
608 static void cleanup_movie(char *movie_dir)
609 {
610         fprintf(stderr, "Removing movie dir %s\n", movie_dir);
611         snprintf(line, line_len, "rm %s/*", movie_dir);
612         system(line);
613
614         snprintf(line, line_len, "rmdir %s", movie_dir);
615         system(line);
616 }
617
618 static void plot_io_movie(struct plot *plot)
619 {
620         struct trace_file *tf;
621         char *movie_dir = create_movie_temp_dir();
622         int i;
623         struct plot_history *read_history;
624         struct plot_history *write_history;
625         int batch_i;
626         int movie_len = 30;
627         int movie_frames_per_sec = 16;
628         int total_frames = movie_len * movie_frames_per_sec;
629         int rows, cols;
630         int batch_count;
631
632         get_graph_size(&cols, &rows);
633         batch_count = cols / total_frames;
634
635         if (batch_count == 0)
636                 batch_count = 1;
637
638         list_for_each_entry(tf, &all_traces, list) {
639                 char *label = tf->label;
640                 if (!label)
641                         label = "";
642
643                 i = 0;
644                 while (i < cols) {
645                         snprintf(line, line_len, "%s/%010d-%s.svg", movie_dir, i, output_filename);
646                         set_plot_output(plot, line);
647
648                         set_plot_title(plot, graph_title);
649                         setup_axis(plot);
650                         svg_alloc_legend(plot, num_traces * 2);
651
652                         read_history = alloc_plot_history(tf->read_color);
653                         write_history = alloc_plot_history(tf->write_color);
654                         read_history->col = i;
655                         write_history->col = i;
656
657                         if (tf->gdd_reads->total_ios)
658                                 svg_add_legend(plot, label, " Reads", tf->read_color);
659                         if (tf->gdd_writes->total_ios)
660                                 svg_add_legend(plot, label, " Writes", tf->write_color);
661
662                         batch_i = 0;
663                         while (i < cols && batch_i < batch_count) {
664                                 /* print just this column */
665                                 svg_io_graph_movie(tf->gdd_reads, read_history, i);
666
667                                 svg_io_graph_movie(tf->gdd_writes, write_history, i);
668                                 i++;
669                                 batch_i++;
670                         }
671
672                         add_history(read_history, &movie_history_reads);
673                         add_history(write_history, &movie_history_writes);
674
675                         plot_movie_history(plot, &movie_history_reads);
676                         plot_movie_history(plot, &movie_history_writes);
677
678                         svg_write_legend(plot);
679                         close_plot(plot);
680
681                         set_graph_size(cols, rows / 3);
682                         plot->add_xlabel = 1;
683                         __plot_tput(plot, tf->gdd_reads->seconds);
684                         svg_write_time_line(plot, i);
685                         close_plot(plot);
686                         set_graph_size(cols, rows);
687
688                         close_plot(plot);
689                 }
690                 free_all_plot_history(&movie_history_reads);
691                 free_all_plot_history(&movie_history_writes);
692         }
693         convert_movie_files(movie_dir);
694         mencode_movie(movie_dir);
695         cleanup_movie(movie_dir);
696         free(movie_dir);
697 }
698
699 static void plot_cpu(struct plot *plot, int seconds, char *label,
700                      int active_index, int gld_index)
701 {
702         struct trace_file *tf;
703         char line[128];
704         int max = 0;
705         int i;
706         int gld_i;
707         char *color;
708         double avg = 0;
709         int ymax;
710         int plotted = 0;
711
712         if (active_graphs[active_index] == 0)
713                 return;
714
715         list_for_each_entry(tf, &all_traces, list) {
716                 if (tf->trace->mpstat_num_cpus > max)
717                         max = tf->trace->mpstat_num_cpus;
718         }
719         if (max == 0)
720                 return;
721
722         tf = list_entry(all_traces.next, struct trace_file, list);
723
724         ymax = tf->mpstat_gld[gld_index]->max;
725         if (ymax == 0)
726                 return;
727
728         svg_alloc_legend(plot, num_traces * max);
729
730         plot->add_xlabel = last_active_graph == active_index;
731         setup_axis(plot);
732         set_plot_label(plot, label);
733
734         seconds = tf->mpstat_seconds;
735
736         set_yticks(plot, 4, 0, tf->mpstat_gld[gld_index]->max, "");
737         set_ylabel(plot, "Percent");
738         set_xticks(plot, 9, 0, seconds);
739
740         cpu_color_index = 0;
741         list_for_each_entry(tf, &all_traces, list) {
742                 for (i = 0; i < tf->mpstat_gld[0]->stop_seconds; i++) {
743                         avg += tf->mpstat_gld[gld_index]->data[i].sum;
744                 }
745                 avg /= tf->mpstat_gld[gld_index]->stop_seconds;
746                 color = pick_cpu_color();
747                 svg_line_graph(plot, tf->mpstat_gld[0], color);
748                 svg_add_legend(plot, tf->label, " avg", color);
749
750                 for (i = 1; i < tf->trace->mpstat_num_cpus + 1; i++) {
751                         struct graph_line_data *gld = tf->mpstat_gld[i * MPSTAT_GRAPHS + gld_index];
752                         double this_avg = 0;
753
754                         for (gld_i = 0; gld_i < gld->stop_seconds; gld_i++)
755                                 this_avg += gld->data[i].sum;
756
757                         this_avg /= gld->stop_seconds;
758
759                         for (gld_i = 0; gld_i < gld->stop_seconds; gld_i++) {
760                                 if (this_avg > avg + 30 ||
761                                     gld->data[gld_i].sum > 95) {
762                                         color = pick_cpu_color();
763                                         svg_line_graph(plot, gld, color);
764                                         snprintf(line, 128, " CPU %d\n", i - 1);
765                                         svg_add_legend(plot, tf->label, line, color);
766                                         plotted++;
767                                         break;
768                                 }
769
770                         }
771                 }
772         }
773
774         if (plot->add_xlabel)
775                 set_xlabel(plot, "Time (seconds)");
776
777         if (plot->legend_index <= 8)
778                 svg_write_legend(plot);
779         else
780                 svg_free_legend(plot);
781         close_plot(plot);
782 }
783
784 static void plot_latency(struct plot *plot, int seconds)
785 {
786         struct trace_file *tf;
787         char *units;
788         char line[128];
789         u64 max = 0;
790
791         if (active_graphs[LATENCY_GRAPH_INDEX] == 0)
792                 return;
793
794         if (num_traces > 1)
795                 svg_alloc_legend(plot, num_traces);
796         list_for_each_entry(tf, &all_traces, list) {
797                 if (tf->latency_gld->max > max)
798                         max = tf->latency_gld->max;
799         }
800         list_for_each_entry(tf, &all_traces, list)
801                 tf->latency_gld->max = max;
802
803         plot->add_xlabel = last_active_graph == TPUT_GRAPH_INDEX;
804         setup_axis(plot);
805         set_plot_label(plot, "IO Latency");
806
807         tf = list_entry(all_traces.next, struct trace_file, list);
808
809         scale_line_graph_time(&max, &units);
810         sprintf(line, "latency (%ss)", units);
811         set_ylabel(plot, line);
812         set_yticks(plot, 4, 0, max, "");
813         set_xticks(plot, 9, 0, seconds);
814
815         list_for_each_entry(tf, &all_traces, list) {
816                 svg_line_graph(plot, tf->latency_gld, tf->read_color);
817                 if (num_traces > 1)
818                         svg_add_legend(plot, tf->label, "", tf->read_color);
819         }
820
821         if (plot->add_xlabel)
822                 set_xlabel(plot, "Time (seconds)");
823         if (num_traces > 1)
824                 svg_write_legend(plot);
825         close_plot(plot);
826 }
827
828 static void plot_queue_depth(struct plot *plot, int seconds)
829 {
830         struct trace_file *tf;
831
832         if (active_graphs[QUEUE_DEPTH_GRAPH_INDEX] == 0)
833                 return;
834
835         plot->add_xlabel = last_active_graph == QUEUE_DEPTH_GRAPH_INDEX;
836
837         setup_axis(plot);
838         set_plot_label(plot, "Queue Depth");
839         if (num_traces > 1)
840                 svg_alloc_legend(plot, num_traces);
841
842         tf = list_entry(all_traces.next, struct trace_file, list);
843         set_ylabel(plot, "Pending IO");
844         set_yticks(plot, 4, 0, tf->queue_depth_gld->max, "");
845         set_xticks(plot, 9, 0, seconds);
846
847         list_for_each_entry(tf, &all_traces, list) {
848                 svg_line_graph(plot, tf->queue_depth_gld, tf->read_color);
849                 if (num_traces > 1)
850                         svg_add_legend(plot, tf->label, "", tf->read_color);
851         }
852
853         if (plot->add_xlabel)
854                 set_xlabel(plot, "Time (seconds)");
855         if (num_traces > 1)
856                 svg_write_legend(plot);
857         close_plot(plot);
858 }
859
860 static void plot_iops(struct plot *plot, int seconds)
861 {
862         struct trace_file *tf;
863         char *units;
864         u64 max = 0;
865
866         if (active_graphs[IOPS_GRAPH_INDEX] == 0)
867                 return;
868
869         list_for_each_entry(tf, &all_traces, list) {
870                 if (tf->iop_gld->max > max)
871                         max = tf->iop_gld->max;
872         }
873
874         list_for_each_entry(tf, &all_traces, list)
875                 tf->iop_gld->max = max;
876
877
878         plot->add_xlabel = last_active_graph == IOPS_GRAPH_INDEX;
879         setup_axis(plot);
880         set_plot_label(plot, "IOPs");
881         if (num_traces > 1)
882                 svg_alloc_legend(plot, num_traces);
883
884         tf = list_entry(all_traces.next, struct trace_file, list);
885
886         scale_line_graph_bytes(&max, &units, 1000);
887         set_ylabel(plot, "IO/s");
888
889         set_yticks(plot, 4, 0, max, units);
890         set_xticks(plot, 9, 0, seconds);
891
892         list_for_each_entry(tf, &all_traces, list) {
893                 svg_line_graph(plot, tf->iop_gld, tf->read_color);
894                 if (num_traces > 1)
895                         svg_add_legend(plot, tf->label, "", tf->read_color);
896         }
897
898         if (plot->add_xlabel)
899                 set_xlabel(plot, "Time (seconds)");
900         if (num_traces > 1)
901                 svg_write_legend(plot);
902
903         close_plot(plot);
904 }
905
906 enum {
907         HELP_LONG_OPT = 1,
908 };
909
910 char *option_string = "T:t:o:l:r:O:N:d:p:mh:w:";
911 static struct option long_options[] = {
912         {"title", required_argument, 0, 'T'},
913         {"trace", required_argument, 0, 't'},
914         {"output", required_argument, 0, 'o'},
915         {"label", required_argument, 0, 'l'},
916         {"rolling", required_argument, 0, 'r'},
917         {"no-graph", required_argument, 0, 'N'},
918         {"only-graph", required_argument, 0, 'O'},
919         {"device", required_argument, 0, 'd'},
920         {"prog", required_argument, 0, 'p'},
921         {"movie", no_argument, 0, 'm'},
922         {"width", required_argument, 0, 'w'},
923         {"height", required_argument, 0, 'h'},
924         {"help", no_argument, 0, HELP_LONG_OPT},
925         {0, 0, 0, 0}
926 };
927
928 static void print_usage(void)
929 {
930         fprintf(stderr, "iowatcher usage:\n"
931                 "\t-d (--device): device for blktrace to trace\n"
932                 "\t-t (--trace): trace file name (more than one allowed)\n"
933                 "\t-l (--label): trace label in the graph\n"
934                 "\t-o (--output): output file name (SVG only)\n"
935                 "\t-p (--prog): program to run while blktrace is run\n"
936                 "\t-p (--movie): create IO animations\n"
937                 "\t-r (--rolling): number of seconds in the rolling averge\n"
938                 "\t-T (--title): graph title\n"
939                 "\t-N (--no-graph): skip a single graph (io, tput, latency, queue_depth, iops)\n"
940                 "\t-h (--height): set the height of each graph\n"
941                 "\t-w (--width): set the width of each graph\n"
942                );
943         exit(1);
944 }
945
946 static int parse_options(int ac, char **av)
947 {
948         int c;
949         int disabled = 0;
950
951         while (1) {
952                 // int this_option_optind = optind ? optind : 1;
953                 int option_index = 0;
954
955                 c = getopt_long(ac, av, option_string,
956                                 long_options, &option_index);
957
958                 if (c == -1)
959                         break;
960
961                 switch(c) {
962                 case 'T':
963                         graph_title = strdup(optarg);
964                         break;
965                 case 't':
966                         add_trace_file(optarg);
967                         set_blktrace_outfile(optarg);
968                         break;
969                 case 'o':
970                         output_filename = strdup(optarg);
971                         break;
972                 case 'l':
973                         set_trace_label(optarg);
974                         break;
975                 case 'r':
976                         set_rolling_avg(atoi(optarg));
977                         break;
978                 case 'O':
979                         if (!disabled) {
980                                 disable_all_graphs();
981                                 disabled = 1;
982                         }
983                         enable_one_graph(optarg);
984                         break;
985                 case 'N':
986                         disable_one_graph(optarg);
987                         break;
988                 case 'd':
989                         blktrace_device = strdup(optarg);
990                         break;
991                 case 'p':
992                         program_to_run = strdup(optarg);
993                         break;
994                 case 'm':
995                         make_movie = 1;
996                         break;
997                 case 'h':
998                         opt_graph_height = atoi(optarg);
999                         break;
1000                 case 'w':
1001                         opt_graph_width = atoi(optarg);
1002                         break;
1003                 case '?':
1004                 case HELP_LONG_OPT:
1005                         print_usage();
1006                         break;
1007                 default:
1008                         break;
1009                 }
1010         }
1011         return 0;
1012 }
1013
1014
1015 int main(int ac, char **av)
1016 {
1017         struct plot *plot;
1018         int seconds = 0;
1019         u64 max_offset = 0;
1020         struct trace_file *tf;
1021         int ret;
1022
1023         init_io_hash_table();
1024
1025         enable_all_graphs();
1026
1027         parse_options(ac, av);
1028
1029         last_active_graph = last_graph();
1030         if (make_movie) {
1031                 set_io_graph_scale(256);
1032                 set_graph_size(700, 250);
1033         }
1034         if (opt_graph_height)
1035                 set_graph_height(opt_graph_height);
1036
1037         if (opt_graph_width)
1038                 set_graph_width(opt_graph_height);
1039
1040         if (list_empty(&all_traces)) {
1041                 fprintf(stderr, "No traces found, exiting\n");
1042                 exit(1);
1043         }
1044
1045         if (blktrace_device) {
1046                 ret = start_blktrace(blktrace_device, blktrace_outfile,
1047                                      blktrace_dest_dir);
1048                 if (ret) {
1049                         fprintf(stderr, "exiting due to blktrace failure\n");
1050                         exit(1);
1051                 }
1052                 start_mpstat(blktrace_outfile);
1053                 if (program_to_run) {
1054                         ret = run_program(program_to_run);
1055                         if (ret) {
1056                                 fprintf(stderr, "failed to run %s\n",
1057                                         program_to_run);
1058                                 exit(1);
1059                         }
1060                         wait_for_tracers();
1061                         blktrace_to_dump(blktrace_outfile);
1062                 } else {
1063                         /* no program specified, just wait for
1064                          * blktrace to exit
1065                          */
1066                         wait_for_tracers();
1067                 }
1068         }
1069
1070         /* step one, read all the traces */
1071         read_traces();
1072
1073         /* step two, find the maxes for time and offset */
1074         list_for_each_entry(tf, &all_traces, list)
1075                 compare_max_tf(tf, &seconds, &max_offset);
1076
1077         /* push the max we found into all the tfs */
1078         set_all_max_tf(seconds, max_offset);
1079
1080         /* alloc graphing structs for all the traces */
1081         setup_trace_file_graphs();
1082
1083         /* run through all the traces and read their events */
1084         read_trace_events();
1085
1086         plot = alloc_plot();
1087
1088         if (make_movie) {
1089                 plot_io_movie(plot);
1090                 exit(0);
1091         }
1092
1093         set_plot_output(plot, output_filename);
1094
1095         if (active_graphs[IO_GRAPH_INDEX] || found_mpstat)
1096                 set_legend_width(longest_label + strlen("writes"));
1097         else if (num_traces > 1)
1098                 set_legend_width(longest_label);
1099         else
1100                 set_legend_width(0);
1101
1102         set_plot_title(plot, graph_title);
1103         plot_io(plot, seconds, max_offset);
1104         plot_tput(plot, seconds);
1105         plot_cpu(plot, seconds, "CPU IO Wait Time",
1106                  CPU_IO_GRAPH_INDEX, MPSTAT_IO);
1107         plot_cpu(plot, seconds, "CPU System Time",
1108                  CPU_SYS_GRAPH_INDEX, MPSTAT_SYS);
1109         plot_cpu(plot, seconds, "CPU IRQ Time",
1110                  CPU_IRQ_GRAPH_INDEX, MPSTAT_IRQ);
1111         plot_cpu(plot, seconds, "CPU SoftIRQ Time",
1112                  CPU_SOFT_GRAPH_INDEX, MPSTAT_SOFT);
1113         plot_cpu(plot, seconds, "CPU User Time",
1114                  CPU_USER_GRAPH_INDEX, MPSTAT_USER);
1115
1116         plot_latency(plot, seconds);
1117         plot_queue_depth(plot, seconds);
1118         plot_iops(plot, seconds);
1119
1120         /* once for all */
1121         close_plot(plot);
1122         return 0;
1123 }