blkparse: Initialize and test for undefined request tracking timestamps
[blktrace.git] / iowatcher / blkparse.c
index c9ae3e7409668d22a39e393d1786adecc5779592..41e20f0c01f9ed986a885916e0e85250a180c83a 100644 (file)
@@ -49,9 +49,6 @@ static struct list_head process_hash_table[PROCESS_HASH_TABLE_SIZE];
 extern int plot_io_action;
 extern int io_per_process;
 
-static const int line_len = 1024;
-static char line[1024];
-
 /*
  * Trace categories
  */
@@ -409,7 +406,7 @@ void first_record(struct trace *trace)
        trace->io = (struct blk_io_trace *)trace->cur;
 }
 
-int is_io_event(struct blk_io_trace *test)
+static int is_io_event(struct blk_io_trace *test)
 {
        char *message;
        if (!(test->action & BLK_TC_ACT(BLK_TC_NOTIFY)))
@@ -463,7 +460,7 @@ u64 find_last_time(struct trace *trace)
        return found;
 }
 
-int parse_fio_bank_message(struct trace *trace, u64 *bank_ret, u64 *offset_ret,
+static int parse_fio_bank_message(struct trace *trace, u64 *bank_ret, u64 *offset_ret,
                           u64 *num_banks_ret)
 {
        char *s;
@@ -573,7 +570,7 @@ static void map_devices(struct trace *trace)
        }
 }
 
-u64 map_io(struct trace *trace, struct blk_io_trace *io)
+static u64 map_io(struct trace *trace, struct blk_io_trace *io)
 {
        struct dev_info *di = lookup_dev(trace, io);
        u64 val = trace->io->sector << 9;
@@ -707,81 +704,110 @@ int filter_outliers(struct trace *trace, u64 min_offset, u64 max_offset,
 }
 
 static char footer[] = ".blktrace.0";
-static int footer_len = sizeof(footer);
+static int footer_len = sizeof(footer) - 1;
 
-static void match_trace(char *name, char **traces)
+static int match_trace(char *name, int *len)
 {
        int match_len;
-       char *match;
        int footer_start;
 
        match_len = strlen(name);
        if (match_len <= footer_len)
-               return;
+               return 0;
 
        footer_start = match_len - footer_len;
-       if (strcmp(name + footer_start + 1, footer) != 0)
-               return;
-
-       match = strdup(name);
-       if (!match)
-               goto enomem;
-
-       match[footer_start + 1] = '\0';
-       snprintf(line, line_len, "%s -i '%s'", *traces ? *traces : "", match);
-       free(match);
-
-       match = strdup(line);
-       if (!match)
-               goto enomem;
-
-       free(*traces);
-       *traces = match;
-       return;
+       if (strcmp(name + footer_start, footer) != 0)
+               return 0;
 
-enomem:
-       perror("memory allocation failed");
-       exit(1);
-       return;
+       if (len)
+               *len = match_len;
+       return 1;
 }
 
-static char *combine_blktrace_devs(char *dir_name)
-{
-       DIR *dir;
-       char *traces = NULL;
-       struct dirent *d;
-       int len;
-       int ret;
+struct tracelist {
+       struct tracelist *next;
+       char *name;
+};
 
-       dir = opendir(dir_name);
+static struct tracelist *traces_list(char *dir_name, int *len)
+{
+       int count = 0;
+       struct tracelist *traces = NULL;
+       int dlen = strlen(dir_name);
+       DIR *dir = opendir(dir_name);
        if (!dir)
                return NULL;
 
        while (1) {
-               d = readdir(dir);
+               int n = 0;
+               struct tracelist *tl;
+               struct dirent *d = readdir(dir);
                if (!d)
                        break;
 
-               len = strlen(d->d_name);
-               if (len > footer_len)
-                       match_trace(d->d_name, &traces);
+               if (!match_trace(d->d_name, &n))
+                       continue;
+
+               n += dlen + 1; /* dir + '/' + file */
+               /* Allocate space for tracelist + filename */
+               tl = calloc(1, sizeof(struct tracelist) + (sizeof(char) * (n + 1)));
+               if (!tl) {
+                       closedir(dir);
+                       return NULL;
+               }
+               tl->next = traces;
+               tl->name = (char *)(tl + 1);
+               snprintf(tl->name, n, "%s/%s", dir_name, d->d_name);
+               traces = tl;
+               count++;
        }
 
        closedir(dir);
 
-       if (!traces)
-               return NULL;
+       if (len)
+               *len = count;
 
-       snprintf(line, line_len, "blkparse -O %s -D %s -d '%s.%s'",
-                traces, dir_name, dir_name, "dump");
+       return traces;
+}
 
-       ret = system(line);
-       if (ret) {
-               fprintf(stderr, "blkparse failure %s\n", line);
-               exit(1);
+static void traces_free(struct tracelist *traces)
+{
+       while (traces) {
+               struct tracelist *tl = traces;
+               traces = traces->next;
+               free(tl);
        }
-       snprintf(line, line_len, "%s.%s", dir_name, "dump");
-       return strdup(line);
+}
+
+static int dump_traces(struct tracelist *traces, int count, char *dumpfile)
+{
+       struct tracelist *tl;
+       char **argv = NULL;
+       int argc = 0;
+       int i;
+       int err = 0;
+
+       argc = count * 2; /* {"-i", trace } */
+       argc += 4; /* See below */
+       argv = calloc(argc + 1, sizeof(char *));
+       if (!argv)
+               return -errno;
+
+       i = 0;
+       argv[i++] = "blkparse";
+       argv[i++] = "-O";
+       argv[i++] = "-d";
+       argv[i++] = dumpfile;
+       for (tl = traces; tl != NULL; tl = tl->next) {
+               argv[i++] = "-i";
+               argv[i++] = tl->name;
+       }
+
+       err = run_program(argc, argv, 1, NULL, NULL);
+       if (err)
+               fprintf(stderr, "%s exited with %d, expected 0\n", argv[0], err);
+       free(argv);
+       return err;
 }
 
 static char *find_trace_file(char *filename)
@@ -789,8 +815,9 @@ static char *find_trace_file(char *filename)
        int ret;
        struct stat st;
        char *dot;
-       char *try;
        int found_dir = 0;
+       char *dumpfile;
+       int len = strlen(filename);
 
        /* look for an exact match of whatever they pass in.
         * If it is a file, assume it is the dump file.
@@ -806,47 +833,69 @@ static char *find_trace_file(char *filename)
                        found_dir = 1;
        }
 
+       if (found_dir) {
+               int i;
+               /* Eat up trailing '/'s */
+               for (i = len - 1; filename[i] == '/'; i--)
+                       filename[i] = '\0';
+       }
+
        /*
         * try tacking .dump onto the end and see if that already
         * has been generated
         */
-       snprintf(line, line_len, "%s.%s", filename, "dump");
-       ret = stat(line, &st);
+       ret = asprintf(&dumpfile, "%s.dump", filename);
+       if (ret == -1) {
+               perror("Error building dump file name");
+               return NULL;
+       }
+       ret = stat(dumpfile, &st);
        if (ret == 0)
-               return strdup(line);
+               return dumpfile;
 
        /*
         * try to generate the .dump from all the traces in
         * a single dir.
         */
        if (found_dir) {
-               try = combine_blktrace_devs(filename);
-               if (try)
-                       return try;
+               int count;
+               struct tracelist *traces = traces_list(filename, &count);
+               if (traces) {
+                       ret = dump_traces(traces, count, dumpfile);
+                       traces_free(traces);
+                       if (ret == 0)
+                               return dumpfile;
+               }
        }
+       free(dumpfile);
 
        /*
         * try to generate the .dump from all the blktrace
         * files for a named trace
         */
-       try = strdup(filename);
-       dot = strrchr(try, '.');
+       dot = strrchr(filename, '.');
        if (!dot || strcmp(".dump", dot) != 0) {
-               if (dot && dot != try)
-                       *dot = '\0';
-               snprintf(line, line_len, "%s%s", try, ".blktrace.0");
-               ret = stat(line, &st);
+               struct tracelist trace = {0 ,NULL};
+               if (dot && dot != filename)
+                       len = dot - filename;
+
+               ret = asprintf(&trace.name, "%*s.blktrace.0", len, filename);
+               if (ret == -1)
+                       return NULL;
+               ret = asprintf(&dumpfile, "%*s.dump", len, filename);
+               if (ret == -1) {
+                       free(trace.name);
+                       return NULL;
+               }
+
+               ret = dump_traces(&trace, 1, dumpfile);
                if (ret == 0) {
-                       blktrace_to_dump(try);
-                       snprintf(line, line_len, "%s.%s", try, "dump");
-                       ret = stat(line, &st);
-                       if (ret == 0) {
-                               free(try);
-                               return strdup(line);
-                       }
+                       free(trace.name);
+                       return dumpfile;
                }
+               free(trace.name);
+               free(dumpfile);
        }
-       free(try);
        return NULL;
 }
 struct trace *open_trace(char *filename)
@@ -1048,8 +1097,36 @@ void add_pending_io(struct trace *trace, struct graph_line_data *gld)
                return;
 
        if (action == __BLK_TA_QUEUE) {
-               if (trace->found_issue || trace->found_completion)
-                       hash_queued_io(trace->io);
+               if (io->sector == 0)
+                       return;
+               /*
+                * If D (issue) events are available, use them for I/O
+                * accounting.  Nothing needs to be done for Q.
+                */
+               if (trace->found_issue)
+                       return;
+               /*
+                * If there are no D or C events, then all that can be
+                * done is to account the Q event (and make sure not to
+                * add the I/O to the hash, because it will never be
+                * removed).
+                */
+               if (!trace->found_completion)
+                       goto account_io;
+               /*
+                * When there are no ISSUE events, count depth and
+                * latency from queue events.
+                */
+               pio = hash_queued_io(trace->io);
+               if (pio) {
+                       pio->dispatch_time = io->time;
+                       goto account_io;
+               }
+               return;
+       }
+       if (action == __BLK_TA_REQUEUE) {
+               if (ios_in_flight > 0)
+                       ios_in_flight--;
                return;
        }
        if (action != __BLK_TA_ISSUE)
@@ -1064,6 +1141,7 @@ void add_pending_io(struct trace *trace, struct graph_line_data *gld)
                free(pio);
        }
 
+account_io:
        ios_in_flight++;
 
        seconds = SECONDS(io->time);