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