[PATCH] blkparse: Add per-process stats and fix output
[blktrace.git] / blkparse.c
CommitLineData
d956a2cd
JA
1/*
2 * block queue tracing parse application
3 *
4 * Copyright (C) 2005 Jens Axboe <axboe@suse.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
d0ca268b
JA
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <unistd.h>
24#include <stdio.h>
25#include <fcntl.h>
26#include <stdlib.h>
8fc0abbc 27#include <string.h>
d5396421 28#include <getopt.h>
412819ce
JA
29#include <errno.h>
30#include <signal.h>
d69db225 31#include <locale.h>
d0ca268b 32
8fc0abbc
JA
33#include "blktrace.h"
34#include "rbtree.h"
d0ca268b 35
cfab07eb
AB
36#define SECONDS(x) ((unsigned long long)(x) / 1000000000)
37#define NANO_SECONDS(x) ((unsigned long long)(x) % 1000000000)
38
d5396421
JA
39static int backwards;
40static unsigned long long genesis_time, last_reported_time;
41
152f6476
JA
42struct io_stats {
43 unsigned long qreads, qwrites, creads, cwrites, mreads, mwrites;
44 unsigned long ireads, iwrites;
45 unsigned long long qread_kb, qwrite_kb, cread_kb, cwrite_kb;
46 unsigned long long iread_kb, iwrite_kb;
47};
48
d5396421 49struct per_cpu_info {
d0ca268b
JA
50 int cpu;
51 int nelems;
d0ca268b
JA
52
53 int fd;
87b72777 54 char fname[128];
d0ca268b 55
152f6476
JA
56 struct io_stats io_stats;
57};
8fc0abbc 58
152f6476
JA
59struct per_process_info {
60 char name[16];
61 __u32 pid;
62 struct io_stats io_stats;
63 struct per_process_info *hash_next, *list_next;
d0ca268b
JA
64};
65
152f6476
JA
66#define PPI_HASH_SHIFT (8)
67static struct per_process_info *ppi_hash[1 << PPI_HASH_SHIFT];
68static struct per_process_info *ppi_list;
69
70#define S_OPTS "i:o:b:s"
d5396421
JA
71static struct option l_opts[] = {
72 {
73 .name = "input",
74 .has_arg = 1,
75 .flag = NULL,
76 .val = 'i'
77 },
78 {
79 .name = "output",
80 .has_arg = 1,
81 .flag = NULL,
82 .val = 'o'
83 },
79f19470
JA
84 {
85 .name = "batch",
86 .has_arg = 1,
87 .flag = NULL,
88 .val = 'b'
89 },
152f6476
JA
90 {
91 .name = "per program stats",
92 .has_arg = 0,
93 .flag = NULL,
94 .val = 's'
95 },
d5396421
JA
96 {
97 .name = NULL,
98 .has_arg = 0,
99 .flag = NULL,
100 .val = 0
101 }
102};
103
8fc0abbc
JA
104static struct rb_root rb_root;
105
106struct trace {
107 struct blk_io_trace *bit;
108 struct rb_node rb_node;
109};
110
a718bd37
NS
111static int max_cpus;
112static struct per_cpu_info *per_cpu_info;
d0ca268b 113
87b72777
JA
114static unsigned long long events;
115
d5396421 116static char *dev, *output_name;
152f6476
JA
117static FILE *ofp;
118
119static int per_process_stats;
d0ca268b 120
79f19470
JA
121#define RB_BATCH_DEFAULT (1024)
122static int rb_batch = RB_BATCH_DEFAULT;
123
412819ce
JA
124#define is_done() (*(volatile int *)(&done))
125static volatile int done;
126
152f6476
JA
127static inline unsigned long hash_long(unsigned long val)
128{
129 const unsigned int bpl = 8 * sizeof(long);
130
131 /*
132 * assuming 32 or 64-bit
133 */
134 if (bpl == 32)
135 val *= 0x9e370001UL;
136 else
137 val *= 0x9e37fffffffc0001UL;
138
139 return val >> (bpl - PPI_HASH_SHIFT);
140}
141
142static inline void add_process_to_hash(struct per_process_info *ppi)
143{
144 const int hash_idx = hash_long(ppi->pid);
145
146 ppi->hash_next = ppi_hash[hash_idx];
147 ppi_hash[hash_idx] = ppi;
148}
149
150static inline void add_process_to_list(struct per_process_info *ppi)
151{
152 ppi->list_next = ppi_list;
153 ppi_list = ppi;
154}
155
156static struct per_process_info *find_process_by_pid(__u32 pid)
157{
158 const int hash_idx = hash_long(pid);
159 struct per_process_info *ppi;
160
161 ppi = ppi_hash[hash_idx];
162 while (ppi) {
163 if (ppi->pid == pid)
164 return ppi;
165
166 ppi = ppi->hash_next;
167 }
168
169 return NULL;
170}
171
172static struct io_stats *find_process_io_stats(__u32 pid, char *name)
173{
174 struct per_process_info *ppi = find_process_by_pid(pid);
175
176 if (!ppi) {
177 ppi = malloc(sizeof(*ppi));
178 memset(ppi, 0, sizeof(*ppi));
179 strncpy(ppi->name, name, sizeof(ppi->name));
180 ppi->pid = pid;
181 add_process_to_hash(ppi);
182 add_process_to_list(ppi);
183 }
184
185 return &ppi->io_stats;
186}
187
a718bd37
NS
188static void resize_cpu_info(int cpuid)
189{
190 int new_space, new_max = cpuid + 1;
191 char *new_start;
192
193 per_cpu_info = realloc(per_cpu_info, new_max * sizeof(*per_cpu_info));
194 if (!per_cpu_info) {
195 fprintf(stderr, "Cannot allocate CPU info -- %d x %d bytes\n",
196 new_max, (int) sizeof(*per_cpu_info));
197 exit(1);
198 }
199
200 new_start = (char *)per_cpu_info + (max_cpus * sizeof(*per_cpu_info));
201 new_space = (new_max - max_cpus) * sizeof(*per_cpu_info);
202 memset(new_start, 0, new_space);
203 max_cpus = new_max;
204}
205
206static struct per_cpu_info *get_cpu_info(int cpu)
207{
c499bf38
JA
208 struct per_cpu_info *pci;
209
a718bd37
NS
210 if (cpu >= max_cpus)
211 resize_cpu_info(cpu);
212
c499bf38
JA
213 /*
214 * ->cpu might already be set, but just set it unconditionally
215 */
216 pci = &per_cpu_info[cpu];
217 pci->cpu = cpu;
218
219 return pci;
a718bd37
NS
220}
221
cfab07eb
AB
222static inline void check_time(struct blk_io_trace *bit)
223{
224 unsigned long long this = bit->time;
225 unsigned long long last = last_reported_time;
226
227 backwards = (this < last) ? 'B' : ' ';
228 last_reported_time = this;
229}
230
152f6476
JA
231static inline void __account_m(struct io_stats *ios, struct blk_io_trace *t,
232 int rw)
d0ca268b
JA
233{
234 if (rw) {
152f6476
JA
235 ios->mwrites++;
236 ios->qwrite_kb += t->bytes >> 10;
d0ca268b 237 } else {
152f6476
JA
238 ios->mreads++;
239 ios->qread_kb += t->bytes >> 10;
240 }
241}
242
243static inline void account_m(struct blk_io_trace *t, struct per_cpu_info *pci,
244 int rw)
245{
246 __account_m(&pci->io_stats, t, rw);
247
248 if (per_process_stats) {
249 struct io_stats *ios = find_process_io_stats(t->pid, t->comm);
250
251 __account_m(ios, t, rw);
d0ca268b
JA
252 }
253}
254
152f6476
JA
255static inline void __account_q(struct io_stats *ios, struct blk_io_trace *t,
256 int rw)
d0ca268b
JA
257{
258 if (rw) {
152f6476
JA
259 ios->qwrites++;
260 ios->qwrite_kb += t->bytes >> 10;
d0ca268b 261 } else {
152f6476
JA
262 ios->qreads++;
263 ios->qread_kb += t->bytes >> 10;
264 }
265}
266
267static inline void account_q(struct blk_io_trace *t, struct per_cpu_info *pci,
268 int rw)
269{
270 __account_q(&pci->io_stats, t, rw);
271
272 if (per_process_stats) {
273 struct io_stats *ios = find_process_io_stats(t->pid, t->comm);
274
275 __account_q(ios, t, rw);
d0ca268b
JA
276 }
277}
278
152f6476 279static inline void __account_c(struct io_stats *ios, int rw, unsigned int bytes)
d0ca268b
JA
280{
281 if (rw) {
152f6476
JA
282 ios->cwrites++;
283 ios->cwrite_kb += bytes >> 10;
d0ca268b 284 } else {
152f6476
JA
285 ios->creads++;
286 ios->cread_kb += bytes >> 10;
287 }
288}
289
290static inline void account_c(struct blk_io_trace *t, struct per_cpu_info *pci,
291 int rw, int bytes)
292{
293 __account_c(&pci->io_stats, rw, bytes);
294
295 if (per_process_stats) {
296 struct io_stats *ios = find_process_io_stats(t->pid, t->comm);
297
298 __account_c(ios, rw, bytes);
d0ca268b
JA
299 }
300}
301
152f6476 302static inline void __account_i(struct io_stats *ios, int rw, unsigned int bytes)
afd2d7ad 303{
304 if (rw) {
152f6476
JA
305 ios->iwrites++;
306 ios->iwrite_kb += bytes >> 10;
afd2d7ad 307 } else {
152f6476
JA
308 ios->ireads++;
309 ios->iread_kb += bytes >> 10;
afd2d7ad 310 }
311}
312
152f6476
JA
313static inline void account_i(struct blk_io_trace *t, struct per_cpu_info *pci,
314 int rw)
d0ca268b 315{
152f6476
JA
316 __account_i(&pci->io_stats, rw, t->bytes);
317
318 if (per_process_stats) {
319 struct io_stats *ios = find_process_io_stats(t->pid, t->comm);
d5396421 320
152f6476
JA
321 __account_i(ios, rw, t->bytes);
322 }
323}
324
325static void output(struct per_cpu_info *pci, char *s)
326{
327 fprintf(ofp, "%s", s);
d0ca268b
JA
328}
329
3aabcd89
JA
330static char hstring[256];
331static char tstring[256];
d0ca268b 332
d5396421
JA
333static inline char *setup_header(struct per_cpu_info *pci,
334 struct blk_io_trace *t, char act)
d0ca268b
JA
335{
336 int w = t->action & BLK_TC_ACT(BLK_TC_WRITE);
337 int b = t->action & BLK_TC_ACT(BLK_TC_BARRIER);
338 int s = t->action & BLK_TC_ACT(BLK_TC_SYNC);
339 char rwbs[4];
340 int i = 0;
341
342 if (w)
343 rwbs[i++] = 'W';
344 else
345 rwbs[i++] = 'R';
346 if (b)
347 rwbs[i++] = 'B';
348 if (s)
349 rwbs[i++] = 'S';
350
351 rwbs[i] = '\0';
352
cfab07eb 353 sprintf(hstring, "%c %3d %15ld %5Lu.%09Lu %5u %c %3s", backwards,
d5396421 354 pci->cpu,
cfab07eb
AB
355 (unsigned long)t->sequence, SECONDS(t->time),
356 NANO_SECONDS(t->time), t->pid, act, rwbs);
d0ca268b
JA
357
358 return hstring;
359}
360
d5396421
JA
361static void log_complete(struct per_cpu_info *pci, struct blk_io_trace *t,
362 char act)
d0ca268b 363{
d5396421 364 sprintf(tstring,"%s %Lu + %u [%d]\n", setup_header(pci, t, act),
d0ca268b 365 (unsigned long long)t->sector, t->bytes >> 9, t->error);
d5396421 366 output(pci, tstring);
d0ca268b
JA
367}
368
d5396421
JA
369static void log_queue(struct per_cpu_info *pci, struct blk_io_trace *t,
370 char act)
d0ca268b 371{
2955af9d
NS
372 sprintf(tstring,"%s %Lu + %u [%s]\n", setup_header(pci, t, act),
373 (unsigned long long)t->sector, t->bytes >> 9, t->comm);
d5396421 374 output(pci, tstring);
d0ca268b
JA
375}
376
d5396421
JA
377static void log_issue(struct per_cpu_info *pci, struct blk_io_trace *t,
378 char act)
d0ca268b 379{
2955af9d
NS
380 sprintf(tstring,"%s %Lu + %u [%s]\n", setup_header(pci, t, act),
381 (unsigned long long)t->sector, t->bytes >> 9, t->comm);
d5396421 382 output(pci, tstring);
d0ca268b
JA
383}
384
d5396421
JA
385static void log_merge(struct per_cpu_info *pci, struct blk_io_trace *t,
386 char act)
d0ca268b 387{
2955af9d
NS
388 sprintf(tstring,"%s %Lu + %u [%s]\n", setup_header(pci, t, act),
389 (unsigned long long)t->sector, t->bytes >> 9, t->comm);
d5396421 390 output(pci, tstring);
d0ca268b
JA
391}
392
d5396421
JA
393static void log_generic(struct per_cpu_info *pci, struct blk_io_trace *t,
394 char act)
d0ca268b 395{
2955af9d
NS
396 sprintf(tstring,"%s %Lu + %u [%s]\n", setup_header(pci, t, act),
397 (unsigned long long)t->sector, t->bytes >> 9, t->comm);
d5396421 398 output(pci, tstring);
d0ca268b
JA
399}
400
d5396421 401static int log_pc(struct per_cpu_info *pci, struct blk_io_trace *t, char act)
d0ca268b 402{
87b72777
JA
403 unsigned char *buf;
404 int i;
d0ca268b 405
d5396421
JA
406 sprintf(tstring,"%s ", setup_header(pci, t, act));
407 output(pci, tstring);
d0ca268b 408
87b72777 409 buf = (unsigned char *) t + sizeof(*t);
d0ca268b
JA
410 for (i = 0; i < t->pdu_len; i++) {
411 sprintf(tstring,"%02x ", buf[i]);
d5396421 412 output(pci, tstring);
d0ca268b
JA
413 }
414
415 if (act == 'C') {
2955af9d
NS
416 sprintf(tstring,"[%d]\n", t->error);
417 output(pci, tstring);
418 } else {
419 sprintf(tstring,"[%s]\n", t->comm);
d5396421 420 output(pci, tstring);
d0ca268b 421 }
87b72777 422 return 0;
d0ca268b
JA
423}
424
d5396421 425static int dump_trace_pc(struct blk_io_trace *t, struct per_cpu_info *pci)
d0ca268b 426{
87b72777
JA
427 int ret = 0;
428
d0ca268b
JA
429 switch (t->action & 0xffff) {
430 case __BLK_TA_QUEUE:
d5396421 431 log_generic(pci, t, 'Q');
d0ca268b
JA
432 break;
433 case __BLK_TA_GETRQ:
d5396421 434 log_generic(pci, t, 'G');
d0ca268b
JA
435 break;
436 case __BLK_TA_SLEEPRQ:
d5396421 437 log_generic(pci, t, 'S');
d0ca268b
JA
438 break;
439 case __BLK_TA_REQUEUE:
d5396421 440 log_generic(pci, t, 'R');
d0ca268b
JA
441 break;
442 case __BLK_TA_ISSUE:
d5396421 443 ret = log_pc(pci, t, 'D');
d0ca268b
JA
444 break;
445 case __BLK_TA_COMPLETE:
d5396421 446 log_pc(pci, t, 'C');
d0ca268b
JA
447 break;
448 default:
449 fprintf(stderr, "Bad pc action %x\n", t->action);
87b72777
JA
450 ret = 1;
451 break;
d0ca268b
JA
452 }
453
87b72777 454 return ret;
d0ca268b
JA
455}
456
d5396421 457static void dump_trace_fs(struct blk_io_trace *t, struct per_cpu_info *pci)
d0ca268b
JA
458{
459 int w = t->action & BLK_TC_ACT(BLK_TC_WRITE);
460
461 switch (t->action & 0xffff) {
462 case __BLK_TA_QUEUE:
152f6476 463 account_q(t, pci, w);
d5396421 464 log_queue(pci, t, 'Q');
d0ca268b
JA
465 break;
466 case __BLK_TA_BACKMERGE:
152f6476 467 account_m(t, pci, w);
d5396421 468 log_merge(pci, t, 'M');
d0ca268b
JA
469 break;
470 case __BLK_TA_FRONTMERGE:
152f6476 471 account_m(t, pci, w);
d5396421 472 log_merge(pci, t, 'F');
d0ca268b
JA
473 break;
474 case __BLK_TA_GETRQ:
d5396421 475 log_generic(pci, t, 'G');
d0ca268b
JA
476 break;
477 case __BLK_TA_SLEEPRQ:
d5396421 478 log_generic(pci, t, 'S');
d0ca268b
JA
479 break;
480 case __BLK_TA_REQUEUE:
152f6476 481 account_c(t, pci, w, -t->bytes);
d5396421 482 log_queue(pci, t, 'R');
d0ca268b
JA
483 break;
484 case __BLK_TA_ISSUE:
152f6476 485 account_i(t, pci, w);
d5396421 486 log_issue(pci, t, 'D');
d0ca268b
JA
487 break;
488 case __BLK_TA_COMPLETE:
152f6476 489 account_c(t, pci, w, t->bytes);
d5396421 490 log_complete(pci, t, 'C');
d0ca268b
JA
491 break;
492 default:
493 fprintf(stderr, "Bad fs action %x\n", t->action);
1f79c4a0 494 break;
d0ca268b 495 }
d0ca268b
JA
496}
497
d5396421 498static int dump_trace(struct blk_io_trace *t, struct per_cpu_info *pci)
d0ca268b 499{
87b72777
JA
500 int ret = 0;
501
d0ca268b 502 if (t->action & BLK_TC_ACT(BLK_TC_PC))
d5396421 503 ret = dump_trace_pc(t, pci);
d0ca268b 504 else
d5396421 505 dump_trace_fs(t, pci);
87b72777
JA
506
507 events++;
508 return ret;
d0ca268b
JA
509}
510
152f6476 511static void dump_io_stats(struct io_stats *ios, char *msg)
5c017e4b 512{
152f6476
JA
513 fprintf(ofp, "%s\n", msg);
514
515 fprintf(ofp, " Reads Queued: %'8lu, %'8LuKiB\t", ios->qreads, ios->qread_kb);
516 fprintf(ofp, " Writes Queued: %'8lu, %'8LuKiB\n", ios->qwrites,ios->qwrite_kb);
0a6b8fc4 517
152f6476
JA
518 fprintf(ofp, " Read Dispatches: %'8lu, %'8LuKiB\t", ios->ireads, ios->iread_kb);
519 fprintf(ofp, " Write Dispatches: %'8lu, %'8LuKiB\n", ios->iwrites,ios->iwrite_kb);
520 fprintf(ofp, " Reads Completed: %'8lu, %'8LuKiB\t", ios->creads, ios->cread_kb);
521 fprintf(ofp, " Writes Completed: %'8lu, %'8LuKiB\n", ios->cwrites,ios->cwrite_kb);
522 fprintf(ofp, " Read Merges: %'8lu%8c\t", ios->mreads, ' ');
0a6b8fc4 523
152f6476 524 fprintf(ofp, " Write Merges: %'8lu\n", ios->mwrites);
5c017e4b
JA
525}
526
152f6476
JA
527static void show_process_stats(void)
528{
529 struct per_process_info *ppi;
530
531 ppi = ppi_list;
532 while (ppi) {
533 dump_io_stats(&ppi->io_stats, ppi->name);
534 ppi = ppi->list_next;
535 }
536
537 fprintf(ofp, "\n");
538}
539
540static void show_cpu_stats(void)
d0ca268b 541{
d5396421 542 struct per_cpu_info foo, *pci;
152f6476 543 struct io_stats *ios;
412819ce 544 int i, pci_events = 0;
5c017e4b
JA
545
546 memset(&foo, 0, sizeof(foo));
d0ca268b 547
a718bd37 548 for (i = 0; i < max_cpus; i++) {
152f6476
JA
549 char cpu[8];
550
d5396421 551 pci = &per_cpu_info[i];
152f6476 552 ios = &pci->io_stats;
5c017e4b 553
d5396421 554 if (!pci->nelems)
afd2d7ad 555 continue;
556
152f6476
JA
557 foo.io_stats.qreads += ios->qreads;
558 foo.io_stats.qwrites += ios->qwrites;
559 foo.io_stats.creads += ios->creads;
560 foo.io_stats.cwrites += ios->cwrites;
561 foo.io_stats.mreads += ios->mreads;
562 foo.io_stats.mwrites += ios->mwrites;
563 foo.io_stats.qread_kb += ios->qread_kb;
564 foo.io_stats.qwrite_kb += ios->qwrite_kb;
565 foo.io_stats.cread_kb += ios->cread_kb;
566 foo.io_stats.cwrite_kb += ios->cwrite_kb;
567
568 snprintf(cpu, sizeof(cpu) - 1, "CPU%d:", i);
569 dump_io_stats(ios, cpu);
412819ce 570 pci_events++;
5c017e4b
JA
571 }
572
412819ce 573 if (pci_events > 1) {
152f6476
JA
574 fprintf(ofp, "\n");
575 dump_io_stats(&foo.io_stats, "Total:");
5c017e4b 576 }
d0ca268b 577
152f6476 578 fprintf(ofp, "\nEvents: %'Lu\n", events);
d0ca268b
JA
579}
580
3aabcd89 581static inline int trace_rb_insert(struct trace *t)
8fc0abbc
JA
582{
583 struct rb_node **p = &rb_root.rb_node;
584 struct rb_node *parent = NULL;
585 struct trace *__t;
586
587 while (*p) {
588 parent = *p;
589 __t = rb_entry(parent, struct trace, rb_node);
590
591 if (t->bit->sequence < __t->bit->sequence)
592 p = &(*p)->rb_left;
593 else if (t->bit->sequence > __t->bit->sequence)
594 p = &(*p)->rb_right;
595 else {
87b72777 596 fprintf(stderr, "sequence alias!\n");
8fc0abbc
JA
597 return 1;
598 }
599 }
600
601 rb_link_node(&t->rb_node, parent, p);
602 rb_insert_color(&t->rb_node, &rb_root);
603 return 0;
604}
605
2ff323b0
JA
606#define min(a, b) ((a) < (b) ? (a) : (b))
607
608static struct blk_io_trace *find_trace(void *p, unsigned long offset, int nr)
609{
610 unsigned long max_offset = min(offset,nr * sizeof(struct blk_io_trace));
611 unsigned long off;
612 struct blk_io_trace *bit;
613 __u32 magic;
614
615 for (off = 0; off < max_offset; off++) {
616 bit = p + off;
617
618 magic = be32_to_cpu(bit->magic);
619 if ((magic & 0xffffff00) == BLK_IO_TRACE_MAGIC)
620 return bit;
621 }
622
623 return NULL;
624}
625
412819ce 626static int sort_entries(void *traces, unsigned long offset, int nr)
8fc0abbc 627{
412819ce 628 struct per_cpu_info *pci;
8fc0abbc
JA
629 struct blk_io_trace *bit;
630 struct trace *t;
631 void *start = traces;
632 int nelems = 0;
633
6fe4709e 634 while (traces - start <= offset - sizeof(*bit)) {
412819ce
JA
635 if (!nr)
636 break;
637
2ff323b0
JA
638 bit = find_trace(traces, offset - (traces - start), nr);
639 if (!bit)
640 break;
6fe4709e 641
8fc0abbc
JA
642 t = malloc(sizeof(*t));
643 t->bit = bit;
644 memset(&t->rb_node, 0, sizeof(t->rb_node));
645
6fe4709e
JA
646 trace_to_cpu(bit);
647
66fa7233
JA
648 if (verify_trace(bit))
649 break;
650
a718bd37 651 pci = get_cpu_info(bit->cpu);
412819ce
JA
652 pci->nelems++;
653
8fc0abbc
JA
654 if (trace_rb_insert(t))
655 return -1;
656
657 traces += sizeof(*bit) + bit->pdu_len;
658 nelems++;
412819ce 659 nr--;
6fe4709e 660 }
8fc0abbc
JA
661
662 return nelems;
663}
664
412819ce
JA
665static void free_entries_rb(void)
666{
667 struct rb_node *n;
668
669 while ((n = rb_first(&rb_root)) != NULL) {
670 struct trace *t = rb_entry(n, struct trace, rb_node);
671
672 rb_erase(&t->rb_node, &rb_root);
673 free(t);
674 }
675}
676
d5396421 677static void show_entries_rb(void)
8fc0abbc 678{
8fc0abbc 679 struct blk_io_trace *bit;
3aabcd89 680 struct rb_node *n;
8fc0abbc
JA
681 struct trace *t;
682 int cpu;
683
3aabcd89
JA
684 n = rb_first(&rb_root);
685 if (!n)
686 return;
8fc0abbc 687
3aabcd89 688 do {
8fc0abbc
JA
689 t = rb_entry(n, struct trace, rb_node);
690 bit = t->bit;
691
d5396421 692 cpu = bit->cpu;
87b72777 693 if (cpu > max_cpus) {
8fc0abbc 694 fprintf(stderr, "CPU number too large (%d)\n", cpu);
87b72777 695 break;
8fc0abbc
JA
696 }
697
cfab07eb
AB
698 if (genesis_time == 0)
699 genesis_time = bit->time;
700 bit->time -= genesis_time;
8fc0abbc 701
cfab07eb 702 check_time(bit);
8fc0abbc 703
d5396421 704 if (dump_trace(bit, &per_cpu_info[cpu]))
87b72777
JA
705 break;
706
8fc0abbc
JA
707 } while ((n = rb_next(n)) != NULL);
708}
709
1f79c4a0
JA
710static int read_data(int fd, void *buffer, int bytes, int block)
711{
712 int ret, bytes_left, fl;
713 void *p;
714
715 fl = fcntl(fd, F_GETFL);
716
717 if (!block)
718 fcntl(fd, F_SETFL, fl | O_NONBLOCK);
719 else
720 fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
721
722 bytes_left = bytes;
723 p = buffer;
724 while (bytes_left > 0) {
725 ret = read(fd, p, bytes_left);
726 if (!ret)
727 return 1;
728 else if (ret < 0) {
729 if (errno != EAGAIN)
730 perror("read");
731 return -1;
732 } else {
733 p += ret;
734 bytes_left -= ret;
735 }
736 }
737
738 return 0;
739}
740
d5396421 741static int do_file(void)
d0ca268b 742{
a718bd37 743 int i, nfiles;
d0ca268b 744
a718bd37
NS
745 for (i = 0, nfiles = 0;; i++, nfiles++) {
746 struct per_cpu_info *pci;
87b72777
JA
747 struct stat st;
748 void *tb;
749
a718bd37 750 pci = get_cpu_info(i);
d0ca268b 751
d5396421
JA
752 snprintf(pci->fname, sizeof(pci->fname)-1,"%s_out.%d", dev, i);
753 if (stat(pci->fname, &st) < 0)
d0ca268b 754 break;
afd2d7ad 755 if (!st.st_size)
756 continue;
d0ca268b 757
d5396421 758 printf("Processing %s\n", pci->fname);
8fc0abbc 759
87b72777 760 tb = malloc(st.st_size);
8fc0abbc 761
d5396421
JA
762 pci->fd = open(pci->fname, O_RDONLY);
763 if (pci->fd < 0) {
764 perror(pci->fname);
3aabcd89 765 break;
d0ca268b 766 }
afd2d7ad 767
1f79c4a0 768 if (read_data(pci->fd, tb, st.st_size, 1))
3aabcd89 769 break;
d0ca268b 770
a718bd37 771 if (sort_entries(tb, st.st_size, ~0U) == -1)
3aabcd89 772 break;
d0ca268b 773
d5396421 774 close(pci->fd);
d5396421
JA
775 printf("\t%2d %10s %15d\n", i, pci->fname, pci->nelems);
776
777 }
778
779 if (!nfiles) {
780 fprintf(stderr, "No files found\n");
781 return 1;
782 }
783
784 show_entries_rb();
d5396421
JA
785 return 0;
786}
787
2ff323b0 788static void resize_buffer(void **buffer, long *size, long offset)
d5396421 789{
2ff323b0 790 long old_size = *size;
d5396421 791
2ff323b0
JA
792 *size *= 2;
793 *buffer = realloc(*buffer, *size);
794 memset(*buffer + offset, 0, *size - old_size);
412819ce 795}
d5396421 796
412819ce
JA
797static int read_sort_events(int fd, void **buffer)
798{
799 long offset, max_offset;
800 int events;
d5396421 801
412819ce
JA
802 max_offset = 128 * sizeof(struct blk_io_trace);
803 *buffer = malloc(max_offset);
804 events = 0;
805 offset = 0;
d5396421 806
412819ce
JA
807 do {
808 struct blk_io_trace *t;
809 int pdu_len;
d5396421 810
412819ce 811 if (max_offset - offset < sizeof(*t))
2ff323b0 812 resize_buffer(buffer, &max_offset, offset);
d5396421 813
412819ce
JA
814 if (read_data(fd, *buffer + offset, sizeof(*t), !events)) {
815 if (events)
d5396421 816 break;
412819ce
JA
817
818 usleep(1000);
819 continue;
d5396421
JA
820 }
821
412819ce
JA
822 t = *buffer + offset;
823 offset += sizeof(*t);
d5396421 824
412819ce 825 pdu_len = be16_to_cpu(t->pdu_len);
2ff323b0
JA
826 if (pdu_len) {
827 if (max_offset - offset < pdu_len)
828 resize_buffer(buffer, &max_offset, offset);
d5396421 829
2ff323b0
JA
830 if (read_data(fd, *buffer + offset, pdu_len, 1))
831 break;
d5396421 832
2ff323b0
JA
833 offset += pdu_len;
834 }
d5396421 835
412819ce 836 events++;
79f19470 837 } while (!is_done() && events < rb_batch);
d5396421 838
412819ce
JA
839 return events;
840}
d5396421 841
412819ce
JA
842static int do_stdin(void)
843{
844 int fd;
845 void *ptr;
d5396421 846
1f79c4a0 847 fd = dup(STDIN_FILENO);
412819ce
JA
848 do {
849 int events;
d5396421 850
412819ce
JA
851 events = read_sort_events(fd, &ptr);
852 if (!events)
853 break;
854
2ff323b0
JA
855 if (sort_entries(ptr, ~0UL, events) == -1)
856 break;
857
412819ce
JA
858 show_entries_rb();
859 free_entries_rb();
d5396421
JA
860 } while (1);
861
862 close(fd);
863 free(ptr);
d5396421
JA
864 return 0;
865}
d0ca268b 866
1f79c4a0 867static void flush_output(void)
412819ce 868{
152f6476 869 fflush(ofp);
412819ce
JA
870}
871
1f79c4a0 872static void handle_sigint(int sig)
412819ce
JA
873{
874 done = 1;
875 flush_output();
876}
877
1f79c4a0
JA
878static void usage(char *prog)
879{
152f6476 880 fprintf(stderr, "Usage: %s -i <name> [-o <output>][-s]\n", prog);
1f79c4a0
JA
881}
882
d5396421
JA
883int main(int argc, char *argv[])
884{
152f6476 885 char *ofp_buffer;
412819ce 886 int c, ret;
d5396421
JA
887
888 while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) != -1) {
889 switch (c) {
890 case 'i':
891 dev = strdup(optarg);
892 break;
893 case 'o':
894 output_name = strdup(optarg);
895 break;
79f19470
JA
896 case 'b':
897 rb_batch = atoi(optarg);
898 if (rb_batch <= 0)
899 rb_batch = RB_BATCH_DEFAULT;
900 break;
152f6476
JA
901 case 's':
902 per_process_stats = 1;
903 break;
d5396421 904 default:
1f79c4a0 905 usage(argv[0]);
d5396421
JA
906 return 1;
907 }
d0ca268b
JA
908 }
909
d5396421 910 if (!dev) {
1f79c4a0 911 usage(argv[0]);
d5396421
JA
912 return 1;
913 }
914
412819ce
JA
915 memset(&rb_root, 0, sizeof(rb_root));
916
917 signal(SIGINT, handle_sigint);
918 signal(SIGHUP, handle_sigint);
919 signal(SIGTERM, handle_sigint);
d5396421 920
d69db225
JA
921 setlocale(LC_NUMERIC, "en_US");
922
152f6476
JA
923 if (!output_name)
924 ofp = fdopen(STDOUT_FILENO, "w");
925 else {
926 char ofname[128];
927
928 snprintf(ofname, sizeof(ofname) - 1, "%s.log", output_name);
929 ofp = fopen(ofname, "w");
930 }
931
932 if (!ofp) {
933 perror("fopen");
934 return 1;
935 }
936
937 ofp_buffer = malloc(4096);
938 if (setvbuf(ofp, ofp_buffer, _IOFBF, 4096)) {
939 perror("setvbuf");
940 return 1;
941 }
942
d5396421
JA
943 if (!strcmp(dev, "-"))
944 ret = do_stdin();
945 else
946 ret = do_file();
947
152f6476
JA
948 if (per_process_stats)
949 show_process_stats();
950
951 show_cpu_stats();
952
412819ce 953 flush_output();
d5396421 954 return ret;
d0ca268b 955}