iowatcher: Add option to set action which should be displayed in the IO graph
[blktrace.git] / iowatcher / blkparse.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
34 #include "plot.h"
35 #include "blkparse.h"
36 #include "list.h"
37 #include "tracers.h"
38
39 #define IO_HASH_TABLE_BITS  11
40 #define IO_HASH_TABLE_SIZE (1 << IO_HASH_TABLE_BITS)
41 static struct list_head io_hash_table[IO_HASH_TABLE_SIZE];
42 static u64 ios_in_flight = 0;
43
44 extern int plot_io_action;
45
46 /*
47  * Trace categories
48  */
49 enum {
50         BLK_TC_READ     = 1 << 0,       /* reads */
51         BLK_TC_WRITE    = 1 << 1,       /* writes */
52         BLK_TC_FLUSH    = 1 << 2,       /* flush */
53         BLK_TC_SYNC     = 1 << 3,       /* sync */
54         BLK_TC_QUEUE    = 1 << 4,       /* queueing/merging */
55         BLK_TC_REQUEUE  = 1 << 5,       /* requeueing */
56         BLK_TC_ISSUE    = 1 << 6,       /* issue */
57         BLK_TC_COMPLETE = 1 << 7,       /* completions */
58         BLK_TC_FS       = 1 << 8,       /* fs requests */
59         BLK_TC_PC       = 1 << 9,       /* pc requests */
60         BLK_TC_NOTIFY   = 1 << 10,      /* special message */
61         BLK_TC_AHEAD    = 1 << 11,      /* readahead */
62         BLK_TC_META     = 1 << 12,      /* metadata */
63         BLK_TC_DISCARD  = 1 << 13,      /* discard requests */
64         BLK_TC_DRV_DATA = 1 << 14,      /* binary driver data */
65         BLK_TC_FUA      = 1 << 15,      /* fua requests */
66
67         BLK_TC_END      = 1 << 15,      /* we've run out of bits! */
68 };
69
70 #define BLK_TC_SHIFT            (16)
71 #define BLK_TC_ACT(act)         ((act) << BLK_TC_SHIFT)
72 #define BLK_DATADIR(a) (((a) >> BLK_TC_SHIFT) & (BLK_TC_READ | BLK_TC_WRITE))
73
74 /*
75  * Basic trace actions
76  */
77 enum {
78         __BLK_TA_QUEUE = 1,             /* queued */
79         __BLK_TA_BACKMERGE,             /* back merged to existing rq */
80         __BLK_TA_FRONTMERGE,            /* front merge to existing rq */
81         __BLK_TA_GETRQ,                 /* allocated new request */
82         __BLK_TA_SLEEPRQ,               /* sleeping on rq allocation */
83         __BLK_TA_REQUEUE,               /* request requeued */
84         __BLK_TA_ISSUE,                 /* sent to driver */
85         __BLK_TA_COMPLETE,              /* completed by driver */
86         __BLK_TA_PLUG,                  /* queue was plugged */
87         __BLK_TA_UNPLUG_IO,             /* queue was unplugged by io */
88         __BLK_TA_UNPLUG_TIMER,          /* queue was unplugged by timer */
89         __BLK_TA_INSERT,                /* insert request */
90         __BLK_TA_SPLIT,                 /* bio was split */
91         __BLK_TA_BOUNCE,                /* bio was bounced */
92         __BLK_TA_REMAP,                 /* bio was remapped */
93         __BLK_TA_ABORT,                 /* request aborted */
94         __BLK_TA_DRV_DATA,              /* binary driver data */
95 };
96
97 #define BLK_TA_MASK ((1 << BLK_TC_SHIFT) - 1)
98
99 /*
100  * Notify events.
101  */
102 enum blktrace_notify {
103         __BLK_TN_PROCESS = 0,           /* establish pid/name mapping */
104         __BLK_TN_TIMESTAMP,             /* include system clock */
105         __BLK_TN_MESSAGE,               /* Character string message */
106 };
107
108 /*
109  * Trace actions in full. Additionally, read or write is masked
110  */
111 #define BLK_TA_QUEUE            (__BLK_TA_QUEUE | BLK_TC_ACT(BLK_TC_QUEUE))
112 #define BLK_TA_BACKMERGE        (__BLK_TA_BACKMERGE | BLK_TC_ACT(BLK_TC_QUEUE))
113 #define BLK_TA_FRONTMERGE       (__BLK_TA_FRONTMERGE | BLK_TC_ACT(BLK_TC_QUEUE))
114 #define BLK_TA_GETRQ            (__BLK_TA_GETRQ | BLK_TC_ACT(BLK_TC_QUEUE))
115 #define BLK_TA_SLEEPRQ          (__BLK_TA_SLEEPRQ | BLK_TC_ACT(BLK_TC_QUEUE))
116 #define BLK_TA_REQUEUE          (__BLK_TA_REQUEUE | BLK_TC_ACT(BLK_TC_REQUEUE))
117 #define BLK_TA_ISSUE            (__BLK_TA_ISSUE | BLK_TC_ACT(BLK_TC_ISSUE))
118 #define BLK_TA_COMPLETE         (__BLK_TA_COMPLETE| BLK_TC_ACT(BLK_TC_COMPLETE))
119 #define BLK_TA_PLUG             (__BLK_TA_PLUG | BLK_TC_ACT(BLK_TC_QUEUE))
120 #define BLK_TA_UNPLUG_IO        (__BLK_TA_UNPLUG_IO | BLK_TC_ACT(BLK_TC_QUEUE))
121 #define BLK_TA_UNPLUG_TIMER     (__BLK_TA_UNPLUG_TIMER | BLK_TC_ACT(BLK_TC_QUEUE))
122 #define BLK_TA_INSERT           (__BLK_TA_INSERT | BLK_TC_ACT(BLK_TC_QUEUE))
123 #define BLK_TA_SPLIT            (__BLK_TA_SPLIT)
124 #define BLK_TA_BOUNCE           (__BLK_TA_BOUNCE)
125 #define BLK_TA_REMAP            (__BLK_TA_REMAP | BLK_TC_ACT(BLK_TC_QUEUE))
126 #define BLK_TA_ABORT            (__BLK_TA_ABORT | BLK_TC_ACT(BLK_TC_QUEUE))
127 #define BLK_TA_DRV_DATA         (__BLK_TA_DRV_DATA | BLK_TC_ACT(BLK_TC_DRV_DATA))
128
129 #define BLK_TN_PROCESS          (__BLK_TN_PROCESS | BLK_TC_ACT(BLK_TC_NOTIFY))
130 #define BLK_TN_TIMESTAMP        (__BLK_TN_TIMESTAMP | BLK_TC_ACT(BLK_TC_NOTIFY))
131 #define BLK_TN_MESSAGE          (__BLK_TN_MESSAGE | BLK_TC_ACT(BLK_TC_NOTIFY))
132
133 #define BLK_IO_TRACE_MAGIC      0x65617400
134 #define BLK_IO_TRACE_VERSION    0x07
135 /*
136  * The trace itself
137  */
138 struct blk_io_trace {
139         __u32 magic;            /* MAGIC << 8 | version */
140         __u32 sequence;         /* event number */
141         __u64 time;             /* in nanoseconds */
142         __u64 sector;           /* disk offset */
143         __u32 bytes;            /* transfer length */
144         __u32 action;           /* what happened */
145         __u32 pid;              /* who did it */
146         __u32 device;           /* device identifier (dev_t) */
147         __u32 cpu;              /* on what cpu did it happen */
148         __u16 error;            /* completion error */
149         __u16 pdu_len;          /* length of data after this trace */
150 };
151
152 struct pending_io {
153         /* sector offset of this IO */
154         u64 sector;
155
156         /* time this IO was dispatched */
157         u64 dispatch_time;
158         /* time this IO was finished */
159         u64 completion_time;
160         struct list_head hash_list;
161 };
162
163 #define MINORBITS 20
164 #define MINORMASK ((1 << MINORBITS) - 1)
165 #define SECONDS(x)              ((unsigned long long)(x) / 1000000000)
166 #define NANO_SECONDS(x)         ((unsigned long long)(x) % 1000000000)
167 #define DOUBLE_TO_NANO_ULL(d)   ((unsigned long long)((d) * 1000000000))
168 #define CHECK_MAGIC(t)          (((t)->magic & 0xffffff00) == BLK_IO_TRACE_MAGIC)
169
170 void init_io_hash_table(void)
171 {
172         int i;
173         struct list_head *head;
174
175         for (i = 0; i < IO_HASH_TABLE_SIZE; i++) {
176                 head = io_hash_table + i;
177                 INIT_LIST_HEAD(head);
178         }
179 }
180
181 /* taken from the kernel hash.h */
182 static inline u64 hash_sector(u64 val)
183 {
184         u64 hash = val;
185
186         /*  Sigh, gcc can't optimise this alone like it does for 32 bits. */
187         u64 n = hash;
188         n <<= 18;
189         hash -= n;
190         n <<= 33;
191         hash -= n;
192         n <<= 3;
193         hash += n;
194         n <<= 3;
195         hash -= n;
196         n <<= 4;
197         hash += n;
198         n <<= 2;
199         hash += n;
200
201         /* High bits are more random, so use them. */
202         return hash >> (64 - IO_HASH_TABLE_BITS);
203 }
204
205 static int hash_table_insert(struct pending_io *ins_pio)
206 {
207         u64 sector = ins_pio->sector;
208         int slot = hash_sector(sector);
209         struct list_head *head;
210         struct pending_io *pio;
211
212         head = io_hash_table + slot;
213         list_for_each_entry(pio, head, hash_list) {
214                 if (pio->sector == sector)
215                         return -EEXIST;
216         }
217         list_add_tail(&ins_pio->hash_list, head);
218         return 0;
219 }
220
221 static struct pending_io *hash_table_search(u64 sector)
222 {
223         int slot = hash_sector(sector);
224         struct list_head *head;
225         struct pending_io *pio;
226
227         head = io_hash_table + slot;
228         list_for_each_entry(pio, head, hash_list) {
229                 if (pio->sector == sector)
230                         return pio;
231         }
232         return NULL;
233 }
234
235 static int hash_dispatched_io(struct blk_io_trace *io)
236 {
237         struct pending_io *pio;
238         int ret;
239
240         pio = calloc(1, sizeof(*pio));
241         pio->sector = io->sector;
242         pio->dispatch_time = io->time;
243
244         ret = hash_table_insert(pio);
245         if (ret == -EEXIST) {
246                 /* crud, the IO isn't here */
247                 free(pio);
248         }
249         return ret;
250 }
251
252 static struct pending_io *hash_completed_io(struct blk_io_trace *io)
253 {
254         struct pending_io *pio;
255
256         pio = hash_table_search(io->sector);
257
258         if (!pio)
259                 return NULL;
260         return pio;
261 }
262
263 static void handle_notify(struct trace *trace)
264 {
265         struct blk_io_trace *io = trace->io;
266         void *payload = (char *)io + sizeof(*io);
267         u32 two32[2];
268
269
270         if (io->action != BLK_TN_TIMESTAMP)
271                 return;
272
273         if (io->pdu_len != sizeof(two32))
274                 return;
275
276         memcpy(two32, payload, sizeof(two32));
277         trace->start_timestamp = io->time;
278         trace->abs_start_time.tv_sec = two32[0];
279         trace->abs_start_time.tv_nsec = two32[1];
280         if (trace->abs_start_time.tv_nsec < 0) {
281                 trace->abs_start_time.tv_sec--;
282                 trace->abs_start_time.tv_nsec += 1000000000;
283         }
284 }
285
286 int next_record(struct trace *trace)
287 {
288         int skip = trace->io->pdu_len;
289         u64 offset;
290
291         trace->cur += sizeof(*trace->io) + skip;
292         offset = trace->cur - trace->start;
293         if (offset >= trace->len)
294                 return 1;
295
296         trace->io = (struct blk_io_trace *)trace->cur;
297         return 0;
298 }
299
300 void first_record(struct trace *trace)
301 {
302         trace->cur = trace->start;
303         trace->io = (struct blk_io_trace *)trace->cur;
304 }
305
306 int is_io_event(struct blk_io_trace *test)
307 {
308         char *message;
309         if (!(test->action & BLK_TC_ACT(BLK_TC_NOTIFY)))
310                 return 1;
311         if (test->action == BLK_TN_MESSAGE) {
312                 int len = test->pdu_len;
313                 if (len < 3)
314                         return 0;
315                 message = (char *)(test + 1);
316                 if (strncmp(message, "fio ", 4) == 0) {
317                         return 1;
318                 }
319         }
320         return 0;
321 }
322
323 u64 find_last_time(struct trace *trace)
324 {
325         char *p = trace->start + trace->len;
326         struct blk_io_trace *test;
327         int search_len = 0;
328         u64 found = 0;
329
330         if (trace->len < sizeof(*trace->io))
331                 return 0;
332         p -= sizeof(*trace->io);
333         while (p >= trace->start) {
334                 test = (struct blk_io_trace *)p;
335                 if (CHECK_MAGIC(test) && is_io_event(test)) {
336                         u64 offset = p - trace->start;
337                         if (offset + sizeof(*test) + test->pdu_len == trace->len) {
338                                 return test->time;
339                         }
340                 }
341                 p--;
342                 search_len++;
343                 if (search_len > 8192) {
344                         break;
345                 }
346         }
347
348         /* searching backwards didn't work out, we'll have to scan the file */
349         first_record(trace);
350         while (1) {
351                 if (is_io_event(trace->io))
352                         found = trace->io->time;
353                 if (next_record(trace))
354                         break;
355         }
356         first_record(trace);
357         return found;
358 }
359
360 int parse_fio_bank_message(struct trace *trace, u64 *bank_ret, u64 *offset_ret,
361                            u64 *num_banks_ret)
362 {
363         char *s;
364         char *next;
365         char *message;
366         struct blk_io_trace *test = trace->io;
367         int len = test->pdu_len;
368         u64 bank;
369         u64 offset;
370         u64 num_banks;
371
372         if (!(test->action & BLK_TC_ACT(BLK_TC_NOTIFY)))
373                 return -1;
374         if (test->action != BLK_TN_MESSAGE)
375                 return -1;
376
377         /* the message is fio rw bank offset num_banks */
378         if (len < 3)
379                 return -1;
380         message = (char *)(test + 1);
381         if (strncmp(message, "fio r ", 6) != 0)
382                 return -1;
383
384         message = strndup(message, len);
385         s = strchr(message, ' ');
386         if (!s)
387                 goto out;
388         s++;
389         s = strchr(s, ' ');
390         if (!s)
391                 goto out;
392
393         bank = strtoll(s, &next, 10);
394         if (s == next)
395                 goto out;
396         s = next;
397
398         offset = strtoll(s, &next, 10);
399         if (s == next)
400                 goto out;
401         s = next;
402
403         num_banks = strtoll(s, &next, 10);
404         if (s == next)
405                 goto out;
406
407         *bank_ret = bank;
408         *offset_ret = offset;
409         *num_banks_ret = num_banks;
410
411         return 0;
412 out:
413         free(message);
414         return -1;
415 }
416
417 void find_extreme_offsets(struct trace *trace, u64 *min_ret, u64 *max_ret, u64 *max_bank_ret,
418                           u64 *max_offset_ret)
419 {
420         u64 found = 0;
421         u64 max = 0, min = ~(u64)0;
422         u64 max_bank = 0;
423         u64 max_bank_offset = 0;
424         u64 num_banks = 0;
425         first_record(trace);
426         while (1) {
427                 if (!(trace->io->action & BLK_TC_ACT(BLK_TC_NOTIFY))) {
428                         found = trace->io->sector << 9;
429                         if (found < min)
430                                 min = found;
431
432                         found += trace->io->bytes;
433                         if (max < found)
434                                 max = found;
435                 } else {
436                         u64 bank;
437                         u64 offset;
438                         if (!parse_fio_bank_message(trace, &bank,
439                                                     &offset, &num_banks)) {
440                                 if (bank > max_bank)
441                                         max_bank = bank;
442                                 if (offset > max_bank_offset)
443                                         max_bank_offset = offset;
444                         }
445                 }
446                 if (next_record(trace))
447                         break;
448         }
449         first_record(trace);
450         *min_ret = min;
451         *max_ret = max;
452         *max_bank_ret = max_bank;
453         *max_offset_ret = max_bank_offset;
454 }
455
456 int filter_outliers(struct trace *trace, u64 min_offset, u64 max_offset,
457                     u64 *yzoom_min, u64 *yzoom_max)
458 {
459         int hits[11];
460         u64 max_per_bucket[11];
461         u64 min_per_bucket[11];
462         u64 bytes_per_bucket = (max_offset - min_offset + 1) / 10;
463         int slot;
464         int fat_count = 0;
465
466         memset(hits, 0, sizeof(int) * 11);
467         memset(max_per_bucket, 0, sizeof(u64) * 11);
468         memset(min_per_bucket, 0xff, sizeof(u64) * 11);
469         first_record(trace);
470         while (1) {
471                 if (!(trace->io->action & BLK_TC_ACT(BLK_TC_NOTIFY)) &&
472                     (trace->io->action & BLK_TA_MASK) == __BLK_TA_QUEUE) {
473                         u64 off = (trace->io->sector << 9) - min_offset;
474
475                         slot = (int)(off / bytes_per_bucket);
476                         hits[slot]++;
477                         if (off < min_per_bucket[slot])
478                                 min_per_bucket[slot] = off;
479
480                         off += trace->io->bytes;
481                         slot = (int)(off / bytes_per_bucket);
482                         hits[slot]++;
483                         if (off > max_per_bucket[slot])
484                                 max_per_bucket[slot] = off;
485                 }
486                 if (next_record(trace))
487                         break;
488         }
489         first_record(trace);
490         for (slot = 0; slot < 11; slot++) {
491                 if (hits[slot] > fat_count) {
492                         fat_count = hits[slot];
493                 }
494         }
495
496         *yzoom_max = max_offset;
497         for (slot = 10; slot >= 0; slot--) {
498                 double d = hits[slot];
499
500                 if (d >= (double)fat_count * .05) {
501                         *yzoom_max = max_per_bucket[slot] + min_offset;
502                         break;
503                 }
504         }
505
506         *yzoom_min = min_offset;
507         for (slot = 0; slot < 10; slot++) {
508                 double d = hits[slot];
509
510                 if (d >= (double)fat_count * .05) {
511                         *yzoom_min = min_per_bucket[slot] + min_offset;
512                         break;
513                 }
514         }
515         return 0;
516 }
517
518 static char *find_trace_file(char *filename)
519 {
520         int ret;
521         struct stat st;
522         char line[1024];
523         char *dot;
524         char *try;
525
526         ret = stat(filename, &st);
527         if (ret == 0)
528                 return strdup(filename);
529
530         snprintf(line, 1024, "%s.%s", filename, "dump");
531         ret = stat(line, &st);
532         if (ret == 0)
533                 return strdup(line);
534
535         try = strdup(filename);
536         dot = strrchr(try, '.');
537         if (!dot || strcmp(".dump", dot) != 0) {
538                 if (dot)
539                         *dot = '\0';
540                 snprintf(line, 1024, "%s%s", try, ".blktrace.0");
541                 ret = stat(line, &st);
542                 if (ret == 0) {
543                         blktrace_to_dump(try);
544                         snprintf(line, 1024, "%s.%s", try, "dump");
545                         ret = stat(line, &st);
546                         if (ret == 0) {
547                                 free(try);
548                                 return strdup(line);
549                         }
550                 }
551         }
552         free(try);
553         return NULL;
554 }
555 struct trace *open_trace(char *filename)
556 {
557         int fd;
558         char *p;
559         struct stat st;
560         int ret;
561         struct trace *trace;
562         char *found_filename;
563
564         trace = calloc(1, sizeof(*trace));
565         if (!trace) {
566                 fprintf(stderr, "unable to allocate memory for trace\n");
567                 return NULL;
568         }
569
570         found_filename = find_trace_file(filename);
571         if (!found_filename) {
572                 fprintf(stderr, "Unable to find trace file %s\n", filename);
573                 goto fail;
574         }
575         filename = found_filename;
576
577         fd = open(filename, O_RDONLY);
578         if (fd < 0) {
579                 fprintf(stderr, "Unable to open trace file %s err %s\n", filename, strerror(errno));
580                 goto fail;
581         }
582         ret = fstat(fd, &st);
583         if (ret < 0) {
584                 fprintf(stderr, "stat failed on %s err %s\n", filename, strerror(errno));
585                 goto fail_fd;
586         }
587         p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
588         if (p == MAP_FAILED) {
589                 fprintf(stderr, "Unable to mmap trace file %s, err %s\n", filename, strerror(errno));
590                 goto fail_fd;
591         }
592         trace->fd = fd;
593         trace->len = st.st_size;
594         trace->start = p;
595         trace->cur = p;
596         trace->io = (struct blk_io_trace *)p;
597         return trace;
598
599 fail_fd:
600         close(fd);
601 fail:
602         free(trace);
603         return NULL;
604 }
605 static inline int tput_event(struct trace *trace)
606 {
607         if (trace->found_completion)
608                 return __BLK_TA_COMPLETE;
609         if (trace->found_issue)
610                 return __BLK_TA_ISSUE;
611         if (trace->found_queue)
612                 return __BLK_TA_QUEUE;
613
614         return __BLK_TA_COMPLETE;
615 }
616
617 int action_char_to_num(char action)
618 {
619         switch (action) {
620         case 'Q':
621                 return __BLK_TA_QUEUE;
622         case 'D':
623                 return __BLK_TA_ISSUE;
624         case 'C':
625                 return __BLK_TA_COMPLETE;
626         }
627         return -1;
628 }
629
630 static inline int io_event(struct trace *trace)
631 {
632         if (plot_io_action)
633                 return plot_io_action;
634         if (trace->found_queue)
635                 return __BLK_TA_QUEUE;
636         if (trace->found_issue)
637                 return __BLK_TA_ISSUE;
638         if (trace->found_completion)
639                 return __BLK_TA_COMPLETE;
640
641         return __BLK_TA_COMPLETE;
642 }
643
644 void add_tput(struct trace *trace, struct graph_line_data *gld)
645 {
646         struct blk_io_trace *io = trace->io;
647         int action = io->action & BLK_TA_MASK;
648         int seconds;
649
650         if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY))
651                 return;
652
653         if (action != tput_event(trace))
654                 return;
655
656         seconds = SECONDS(io->time);
657         if (seconds > gld->max_seconds)
658                 return;
659
660         gld->data[seconds].sum += io->bytes;
661         gld->data[seconds].count = 1;
662         if (gld->data[seconds].sum > gld->max)
663                 gld->max = gld->data[seconds].sum;
664 }
665
666 void add_io(struct trace *trace, struct graph_dot_data *gdd_writes,
667             struct graph_dot_data *gdd_reads)
668 {
669         struct blk_io_trace *io = trace->io;
670         int action = io->action & BLK_TA_MASK;
671         u64 offset;
672
673         if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY))
674                 return;
675
676         if (action != io_event(trace))
677                 return;
678
679         offset = io->sector << 9;
680
681         if (BLK_DATADIR(io->action) & BLK_TC_READ)
682                 set_gdd_bit(gdd_reads, offset, io->bytes, io->time);
683         else if (BLK_DATADIR(io->action) & BLK_TC_WRITE)
684                 set_gdd_bit(gdd_writes, offset, io->bytes, io->time);
685 }
686
687 void add_pending_io(struct trace *trace, struct graph_line_data *gld)
688 {
689         int ret;
690         int seconds;
691         struct blk_io_trace *io = trace->io;
692         int action = io->action & BLK_TA_MASK;
693         double avg;
694
695         if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY))
696                 return;
697
698         if (action != __BLK_TA_ISSUE)
699                 return;
700
701         seconds = SECONDS(io->time);
702         if (seconds > gld->max_seconds)
703                 return;
704
705         ret = hash_dispatched_io(trace->io);
706         if (ret)
707                 return;
708
709         ios_in_flight++;
710
711         gld->data[seconds].sum += ios_in_flight;
712         gld->data[seconds].count++;
713
714         avg = (double)gld->data[seconds].sum / gld->data[seconds].count;
715         if (gld->max < (u64)avg) {
716                 gld->max = avg;
717         }
718 }
719
720 void add_completed_io(struct trace *trace,
721                       struct graph_line_data *latency_gld)
722 {
723         struct blk_io_trace *io = trace->io;
724         int seconds;
725         int action = io->action & BLK_TA_MASK;
726         struct pending_io *pio;
727         double avg;
728         u64 latency;
729
730         if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY))
731                 return;
732
733         if (action != __BLK_TA_COMPLETE)
734                 return;
735
736         seconds = SECONDS(io->time);
737
738         pio = hash_completed_io(trace->io);
739         if (!pio)
740                 return;
741
742         if (ios_in_flight > 0)
743                 ios_in_flight--;
744         if (io->time >= pio->dispatch_time) {
745                 latency = io->time - pio->dispatch_time;
746                 latency_gld->data[seconds].sum += latency;
747                 latency_gld->data[seconds].count++;
748         }
749
750         list_del(&pio->hash_list);
751         free(pio);
752
753         avg = (double)latency_gld->data[seconds].sum /
754                 latency_gld->data[seconds].count;
755         if (latency_gld->max < (u64)avg) {
756                 latency_gld->max = avg;
757         }
758 }
759
760 void add_iop(struct trace *trace, struct graph_line_data *gld)
761 {
762         struct blk_io_trace *io = trace->io;
763         int action = io->action & BLK_TA_MASK;
764         int seconds;
765
766         if (io->action & BLK_TC_ACT(BLK_TC_NOTIFY))
767                 return;
768
769         /* iops and tput use the same events */
770         if (action != tput_event(trace))
771                 return;
772
773         seconds = SECONDS(io->time);
774         if (seconds > gld->max_seconds)
775                 return;
776
777         gld->data[seconds].sum += 1;
778         gld->data[seconds].count = 1;
779         if (gld->data[seconds].sum > gld->max)
780                 gld->max = gld->data[seconds].sum;
781 }
782
783 void check_record(struct trace *trace)
784 {
785         struct blk_io_trace *io = trace->io;
786         int action = io->action & BLK_TA_MASK;
787
788         if (!(io->action & BLK_TC_ACT(BLK_TC_NOTIFY))) {
789                 switch (action) {
790                 case __BLK_TA_COMPLETE:
791                         trace->found_completion = 1;
792                         break;
793                 case __BLK_TA_ISSUE:
794                         trace->found_issue = 1;
795                         break;
796                 case __BLK_TA_QUEUE:
797                         trace->found_queue = 1;
798                         break;
799                 };
800         }
801         handle_notify(trace);
802 }