[PATCH] blktrace: add support for checking events dropped by the kernel
[blktrace.git] / blktrace.c
CommitLineData
d0ca268b
JA
1/*
2 * block queue tracing application
3 *
d956a2cd
JA
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 *
d0ca268b
JA
20 */
21#include <pthread.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <locale.h>
26#include <signal.h>
27#include <fcntl.h>
28#include <string.h>
29#include <sys/ioctl.h>
b9d4294e 30#include <sys/param.h>
e3e74029 31#include <sys/statfs.h>
d0ca268b
JA
32#include <stdio.h>
33#include <stdlib.h>
34#include <sched.h>
d39c04ca
AB
35#include <ctype.h>
36#include <getopt.h>
da39451f 37#include <errno.h>
d0ca268b
JA
38
39#include "blktrace.h"
40
13d928f0 41static char blktrace_version[] = "0.99";
52724a0e 42
8f551a39
JA
43/*
44 * You may want to increase this even more, if you are logging at a high
45 * rate and see skipped/missed events
46 */
007c233c 47#define BUF_SIZE (512 * 1024)
d0ca268b
JA
48#define BUF_NR (4)
49
007c233c
JA
50#define OFILE_BUF (128 * 1024)
51
e3e74029
NS
52#define RELAYFS_TYPE 0xF0B4A981
53
57ea8602 54#define S_OPTS "d:a:A:r:o:kw:Vb:n:D:"
d5396421 55static struct option l_opts[] = {
5c86134e 56 {
d39c04ca 57 .name = "dev",
428683db 58 .has_arg = required_argument,
d39c04ca
AB
59 .flag = NULL,
60 .val = 'd'
61 },
5c86134e 62 {
d39c04ca 63 .name = "act-mask",
428683db 64 .has_arg = required_argument,
d39c04ca
AB
65 .flag = NULL,
66 .val = 'a'
67 },
5c86134e 68 {
d39c04ca 69 .name = "set-mask",
428683db 70 .has_arg = required_argument,
d39c04ca
AB
71 .flag = NULL,
72 .val = 'A'
73 },
5c86134e 74 {
5270dddd 75 .name = "relay",
428683db 76 .has_arg = required_argument,
5270dddd
JA
77 .flag = NULL,
78 .val = 'r'
79 },
d5396421
JA
80 {
81 .name = "output",
428683db 82 .has_arg = required_argument,
d5396421
JA
83 .flag = NULL,
84 .val = 'o'
85 },
bc39777c
JA
86 {
87 .name = "kill",
428683db 88 .has_arg = no_argument,
bc39777c
JA
89 .flag = NULL,
90 .val = 'k'
91 },
ece238a6
NS
92 {
93 .name = "stopwatch",
428683db 94 .has_arg = required_argument,
ece238a6
NS
95 .flag = NULL,
96 .val = 'w'
97 },
52724a0e
JA
98 {
99 .name = "version",
100 .has_arg = no_argument,
101 .flag = NULL,
57ea8602 102 .val = 'V'
52724a0e 103 },
129aa440 104 {
3f65c585 105 .name = "buffer-size",
129aa440
JA
106 .has_arg = required_argument,
107 .flag = NULL,
108 .val = 'b'
109 },
110 {
3f65c585 111 .name = "num-sub-buffers",
129aa440
JA
112 .has_arg = required_argument,
113 .flag = NULL,
114 .val = 'n'
115 },
d1d7f15f 116 {
3f65c585 117 .name = "output-dir",
d1d7f15f
JA
118 .has_arg = required_argument,
119 .flag = NULL,
120 .val = 'D'
121 },
71ef8b7c
JA
122 {
123 .name = NULL,
124 }
d39c04ca
AB
125};
126
d0ca268b
JA
127struct thread_information {
128 int cpu;
129 pthread_t thread;
b9d4294e
JA
130
131 int fd;
132 char fn[MAXPATHLEN + 64];
8a43bac5
JA
133 void *buf;
134 unsigned long buf_offset;
135 unsigned int buf_subbuf;
136 unsigned int sequence;
b9d4294e 137
d5396421 138 pthread_mutex_t *fd_lock;
007c233c
JA
139 FILE *ofile;
140 char *ofile_buffer;
141
99c1f5ab 142 volatile int closed;
d5396421 143
d0ca268b 144 unsigned long events_processed;
e7c9f3ff 145 struct device_information *device;
d0ca268b
JA
146};
147
e7c9f3ff
NS
148struct device_information {
149 int fd;
150 char *path;
151 char buts_name[32];
99c1f5ab 152 volatile int trace_started;
e7c9f3ff
NS
153 struct thread_information *threads;
154};
d0ca268b 155
e7c9f3ff 156static int ncpus;
d0ca268b 157static struct thread_information *thread_information;
e7c9f3ff
NS
158static int ndevs;
159static struct device_information *device_information;
160
161/* command line option globals */
162static char *relay_path;
d5396421 163static char *output_name;
d1d7f15f 164static char *output_dir;
5c86134e 165static int act_mask = ~0U;
bc39777c 166static int kill_running_trace;
e820abd7
JA
167static unsigned int buf_size = BUF_SIZE;
168static unsigned int buf_nr = BUF_NR;
d39c04ca 169
e7c9f3ff
NS
170#define is_done() (*(volatile int *)(&done))
171static volatile int done;
172
d5396421
JA
173static pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
174
72ca8801
NS
175static void exit_trace(int status);
176
007c233c
JA
177#define tip_closed(tip) (*(volatile int *)(&(tip)->closed))
178#define set_tip_closed(tip) ((tip)->closed = 1)
179
99c1f5ab
JA
180#define dip_tracing(dip) (*(volatile int *)(&(dip)->trace_started))
181#define dip_set_tracing(dip, v) ((dip)->trace_started = (v))
182
183#define __for_each_dip(__d, __i, __e) \
184 for (__i = 0, __d = device_information; __i < __e; __i++, __d++)
185
186#define for_each_dip(__d, __i) __for_each_dip(__d, __i, ndevs)
187#define for_each_tip(__d, __t, __i) \
188 for (__i = 0, __t = (__d)->threads; __i < ncpus; __i++, __t++)
189
e7c9f3ff 190static int start_trace(struct device_information *dip)
d0ca268b
JA
191{
192 struct blk_user_trace_setup buts;
193
1f79c4a0 194 memset(&buts, 0, sizeof(buts));
129aa440
JA
195 buts.buf_size = buf_size;
196 buts.buf_nr = buf_nr;
d39c04ca 197 buts.act_mask = act_mask;
d0ca268b 198
e7c9f3ff 199 if (ioctl(dip->fd, BLKSTARTTRACE, &buts) < 0) {
d0ca268b
JA
200 perror("BLKSTARTTRACE");
201 return 1;
202 }
203
e7c9f3ff 204 memcpy(dip->buts_name, buts.name, sizeof(dip->buts_name));
99c1f5ab 205 dip_set_tracing(dip, 1);
d0ca268b
JA
206 return 0;
207}
208
e7c9f3ff 209static void stop_trace(struct device_information *dip)
d0ca268b 210{
99c1f5ab
JA
211 if (dip_tracing(dip) || kill_running_trace) {
212 dip_set_tracing(dip, 0);
cf9208ea 213
e7c9f3ff 214 if (ioctl(dip->fd, BLKSTOPTRACE) < 0)
707b0914 215 perror("BLKSTOPTRACE");
cf9208ea 216
e7c9f3ff 217 close(dip->fd);
cf9208ea 218 dip->fd = -1;
707b0914 219 }
d0ca268b
JA
220}
221
e7c9f3ff
NS
222static void stop_all_traces(void)
223{
224 struct device_information *dip;
225 int i;
226
99c1f5ab 227 for_each_dip(dip, i)
e7c9f3ff
NS
228 stop_trace(dip);
229}
230
db6fe5bc 231static int read_data(struct thread_information *tip, void *buf, int len)
d0ca268b 232{
8a43bac5
JA
233 char *p = buf;
234 int ret, bytes_left = len;
d0ca268b 235
8a43bac5 236 while (!is_done() && bytes_left > 0) {
b9d4294e 237 ret = read(tip->fd, p, bytes_left);
db6fe5bc 238 if (ret == bytes_left)
3752a433 239 return 0;
8a43bac5
JA
240
241 if (ret < 0) {
b9d4294e 242 perror(tip->fn);
8a43bac5 243 fprintf(stderr,"Thread %d failed read of %s\n",
b9d4294e 244 tip->cpu, tip->fn);
db6fe5bc 245 break;
8a43bac5 246 } else if (ret > 0) {
d0ca268b
JA
247 p += ret;
248 bytes_left -= ret;
db6fe5bc
JA
249 } else
250 usleep(1000);
8a43bac5
JA
251 }
252
3752a433 253 return -1;
8a43bac5
JA
254}
255
007c233c 256static int write_data(FILE *file, void *buf, unsigned int buf_len)
8a43bac5 257{
db6fe5bc
JA
258 int ret, bytes_left;
259 char *p = buf;
8a43bac5 260
db6fe5bc
JA
261 bytes_left = buf_len;
262 while (bytes_left > 0) {
007c233c
JA
263 ret = fwrite(p, bytes_left, 1, file);
264 if (ret == 1)
8a43bac5
JA
265 break;
266
db6fe5bc
JA
267 if (ret < 0) {
268 perror("write");
269 return 1;
8a43bac5 270 }
d0ca268b
JA
271 }
272
8a43bac5
JA
273 return 0;
274}
275
e820abd7 276static void *extract_data(struct thread_information *tip, int nb)
8a43bac5
JA
277{
278 unsigned char *buf;
279
280 buf = malloc(nb);
db6fe5bc 281 if (!read_data(tip, buf, nb))
8a43bac5
JA
282 return buf;
283
284 free(buf);
8a43bac5 285 return NULL;
d0ca268b
JA
286}
287
3a9d6c13
JA
288/*
289 * trace may start inside 'bit' or may need to be gotten further on
290 */
291static int get_event_slow(struct thread_information *tip,
292 struct blk_io_trace *bit)
4b5db44a 293{
3a9d6c13
JA
294 const int inc = sizeof(__u32);
295 struct blk_io_trace foo;
fb39f32f 296 unsigned int offset;
3a9d6c13
JA
297 void *p;
298
299 /*
300 * check is trace is inside
301 */
302 offset = 0;
303 p = bit;
304 while (offset < sizeof(*bit)) {
305 p += inc;
306 offset += inc;
307
308 memcpy(&foo, p, inc);
309
310 if (CHECK_MAGIC(&foo))
311 break;
312 }
4b5db44a 313
3a9d6c13
JA
314 /*
315 * part trace found inside, read the rest
316 */
317 if (offset < sizeof(*bit)) {
318 int good_bytes = sizeof(*bit) - offset;
319
320 memmove(bit, p, good_bytes);
321 p = (void *) bit + good_bytes;
322
323 return read_data(tip, p, offset);
324 }
325
326 /*
327 * nothing found, keep looking for start of trace
328 */
4b5db44a
JA
329 do {
330 if (read_data(tip, bit, sizeof(bit->magic)))
331 return -1;
4b5db44a
JA
332 } while (!CHECK_MAGIC(bit));
333
3a9d6c13
JA
334 /*
335 * now get the rest of it
336 */
337 p = &bit->sequence;
338 if (!read_data(tip, p, sizeof(*bit) - inc))
339 return -1;
340
341 return 0;
342}
343
344/*
345 * Sometimes relayfs screws us a little, if an event crosses a sub buffer
346 * boundary. So keep looking forward in the trace data until an event
347 * is found
348 */
349static int get_event(struct thread_information *tip, struct blk_io_trace *bit)
350{
351 /*
352 * optimize for the common fast case, a full trace read that
353 * succeeds
354 */
355 if (read_data(tip, bit, sizeof(*bit)))
356 return -1;
357
358 if (CHECK_MAGIC(bit))
4b5db44a
JA
359 return 0;
360
3a9d6c13
JA
361 /*
362 * ok that didn't work, the event may start somewhere inside the
363 * trace itself
364 */
365 return get_event_slow(tip, bit);
4b5db44a
JA
366}
367
d5396421
JA
368static inline void tip_fd_unlock(struct thread_information *tip)
369{
370 if (tip->fd_lock)
371 pthread_mutex_unlock(tip->fd_lock);
372}
373
374static inline void tip_fd_lock(struct thread_information *tip)
375{
376 if (tip->fd_lock)
377 pthread_mutex_lock(tip->fd_lock);
378}
379
3aabcd89 380static void *extract(void *arg)
d0ca268b
JA
381{
382 struct thread_information *tip = arg;
db6fe5bc 383 int pdu_len;
e820abd7 384 char *pdu_data;
d0ca268b
JA
385 struct blk_io_trace t;
386 pid_t pid = getpid();
387 cpu_set_t cpu_mask;
388
389 CPU_ZERO(&cpu_mask);
b9d4294e 390 CPU_SET((tip->cpu), &cpu_mask);
d0ca268b
JA
391
392 if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
393 perror("sched_setaffinity");
76718bcd 394 exit_trace(1);
d0ca268b
JA
395 }
396
e7c9f3ff
NS
397 snprintf(tip->fn, sizeof(tip->fn), "%s/block/%s/trace%d",
398 relay_path, tip->device->buts_name, tip->cpu);
b9d4294e
JA
399 tip->fd = open(tip->fn, O_RDONLY);
400 if (tip->fd < 0) {
401 perror(tip->fn);
5c86134e
JA
402 fprintf(stderr,"Thread %d failed open of %s\n", tip->cpu,
403 tip->fn);
76718bcd 404 exit_trace(1);
d0ca268b
JA
405 }
406
69e65a9e 407 pdu_data = NULL;
d0ca268b 408 while (!is_done()) {
4b5db44a 409 if (get_event(tip, &t))
8a43bac5 410 break;
d0ca268b
JA
411
412 if (verify_trace(&t))
db6fe5bc 413 break;
d0ca268b 414
18ada3d4
JA
415 pdu_len = t.pdu_len;
416
6fe4709e
JA
417 trace_to_be(&t);
418
db6fe5bc 419 if (pdu_len) {
e820abd7 420 pdu_data = extract_data(tip, pdu_len);
db6fe5bc
JA
421 if (!pdu_data)
422 break;
423 }
69e65a9e
JA
424
425 /*
426 * now we have both trace and payload, get a lock on the
427 * output descriptor and send it off
428 */
d5396421
JA
429 tip_fd_lock(tip);
430
007c233c 431 if (write_data(tip->ofile, &t, sizeof(t))) {
d5396421 432 tip_fd_unlock(tip);
db6fe5bc 433 break;
d0ca268b
JA
434 }
435
007c233c 436 if (pdu_data && write_data(tip->ofile, pdu_data, pdu_len)) {
db6fe5bc
JA
437 tip_fd_unlock(tip);
438 break;
439 }
440
441 tip_fd_unlock(tip);
d5396421 442
db6fe5bc 443 if (pdu_data) {
69e65a9e
JA
444 free(pdu_data);
445 pdu_data = NULL;
446 }
87b72777 447
d0ca268b
JA
448 tip->events_processed++;
449 }
450
db6fe5bc 451 exit_trace(1);
d0ca268b
JA
452 return NULL;
453}
454
007c233c
JA
455static void close_thread(struct thread_information *tip)
456{
457 if (tip_closed(tip))
458 return;
459
460 set_tip_closed(tip);
461
462 if (tip->fd != -1)
463 close(tip->fd);
464 if (tip->ofile)
465 fclose(tip->ofile);
466 if (tip->ofile_buffer)
467 free(tip->ofile_buffer);
468
469 tip->fd = -1;
470 tip->ofile = NULL;
471 tip->ofile_buffer = NULL;
472}
473
e7c9f3ff 474static int start_threads(struct device_information *dip)
d0ca268b
JA
475{
476 struct thread_information *tip;
d5396421 477 char op[64];
e7c9f3ff 478 int j, pipeline = output_name && !strcmp(output_name, "-");
007c233c 479 int len, mode;
d0ca268b 480
99c1f5ab 481 for_each_tip(dip, tip, j) {
e7c9f3ff
NS
482 tip->cpu = j;
483 tip->device = dip;
d5396421 484 tip->fd_lock = NULL;
d0ca268b
JA
485 tip->events_processed = 0;
486
e7c9f3ff 487 if (pipeline) {
007c233c 488 tip->ofile = fdopen(STDOUT_FILENO, "w");
d5396421 489 tip->fd_lock = &stdout_mutex;
007c233c
JA
490 mode = _IOLBF;
491 buf_size = 512;
d5396421 492 } else {
d1d7f15f
JA
493 len = 0;
494
495 if (output_dir)
496 len = sprintf(op, "%s/", output_dir);
497
9f6486bd 498 if (output_name) {
d1d7f15f 499 sprintf(op + len, "%s.blktrace.%d", output_name,
9f6486bd
JA
500 tip->cpu);
501 } else {
d1d7f15f 502 sprintf(op + len, "%s.blktrace.%d",
e7c9f3ff 503 dip->buts_name, tip->cpu);
9f6486bd 504 }
007c233c
JA
505 tip->ofile = fopen(op, "w");
506 mode = _IOFBF;
507 buf_size = OFILE_BUF;
d5396421
JA
508 }
509
007c233c 510 if (tip->ofile == NULL) {
d5396421 511 perror(op);
e7c9f3ff 512 return 1;
d5396421
JA
513 }
514
007c233c
JA
515 tip->ofile_buffer = malloc(buf_size);
516 if (setvbuf(tip->ofile, tip->ofile_buffer, mode, buf_size)) {
517 perror("setvbuf");
518 close_thread(tip);
519 return 1;
520 }
521
d0ca268b 522 if (pthread_create(&tip->thread, NULL, extract, tip)) {
e7c9f3ff 523 perror("pthread_create");
007c233c 524 close_thread(tip);
e7c9f3ff 525 return 1;
d0ca268b
JA
526 }
527 }
528
e7c9f3ff 529 return 0;
d0ca268b
JA
530}
531
e7c9f3ff 532static void stop_threads(struct device_information *dip)
3aabcd89 533{
e7c9f3ff
NS
534 struct thread_information *tip;
535 long ret;
007c233c
JA
536 int i;
537
99c1f5ab 538 for_each_tip(dip, tip, i) {
3aabcd89
JA
539 if (pthread_join(tip->thread, (void *) &ret))
540 perror("thread_join");
007c233c 541
72ca8801 542 close_thread(tip);
3aabcd89
JA
543 }
544}
545
e7c9f3ff 546static void stop_all_threads(void)
72ca8801 547{
e7c9f3ff 548 struct device_information *dip;
72ca8801
NS
549 int i;
550
99c1f5ab 551 for_each_dip(dip, i)
e7c9f3ff
NS
552 stop_threads(dip);
553}
554
555static void stop_all_tracing(void)
556{
557 struct device_information *dip;
558 struct thread_information *tip;
559 int i, j;
560
99c1f5ab
JA
561 for_each_dip(dip, i) {
562 for_each_tip(dip, tip, j)
e7c9f3ff 563 close_thread(tip);
007c233c 564
e7c9f3ff
NS
565 stop_trace(dip);
566 }
72ca8801
NS
567}
568
569static void exit_trace(int status)
570{
e7c9f3ff 571 stop_all_tracing();
72ca8801
NS
572 exit(status);
573}
574
e7c9f3ff
NS
575static int resize_devices(char *path)
576{
577 int size = (ndevs + 1) * sizeof(struct device_information);
578
579 device_information = realloc(device_information, size);
580 if (!device_information) {
581 fprintf(stderr, "Out of memory, device %s (%d)\n", path, size);
582 return 1;
583 }
584 device_information[ndevs].path = path;
585 ndevs++;
586 return 0;
587}
588
589static int open_devices(void)
d0ca268b 590{
e7c9f3ff 591 struct device_information *dip;
d0ca268b 592 int i;
d0ca268b 593
99c1f5ab 594 for_each_dip(dip, i) {
cf9208ea 595 dip->fd = open(dip->path, O_RDONLY | O_NONBLOCK);
e7c9f3ff
NS
596 if (dip->fd < 0) {
597 perror(dip->path);
598 return 1;
599 }
600 }
99c1f5ab 601
e7c9f3ff
NS
602 return 0;
603}
604
605static int start_devices(void)
606{
607 struct device_information *dip;
608 int i, j, size;
609
610 size = ncpus * sizeof(struct thread_information);
611 thread_information = malloc(size * ndevs);
612 if (!thread_information) {
613 fprintf(stderr, "Out of memory, threads (%d)\n", size * ndevs);
614 return 1;
615 }
d5396421 616
99c1f5ab 617 for_each_dip(dip, i) {
e7c9f3ff
NS
618 if (start_trace(dip)) {
619 close(dip->fd);
620 fprintf(stderr, "Failed to start trace on %s\n",
621 dip->path);
622 break;
623 }
624 }
99c1f5ab 625
e7c9f3ff 626 if (i != ndevs) {
99c1f5ab 627 __for_each_dip(dip, j, i)
e7c9f3ff 628 stop_trace(dip);
99c1f5ab 629
e7c9f3ff
NS
630 return 1;
631 }
632
99c1f5ab 633 for_each_dip(dip, i) {
e7c9f3ff
NS
634 dip->threads = thread_information + (i * ncpus);
635 if (start_threads(dip)) {
636 fprintf(stderr, "Failed to start worker threads\n");
637 break;
638 }
639 }
99c1f5ab 640
e7c9f3ff 641 if (i != ndevs) {
99c1f5ab 642 __for_each_dip(dip, j, i)
e7c9f3ff 643 stop_threads(dip);
99c1f5ab 644 for_each_dip(dip, i)
e7c9f3ff 645 stop_trace(dip);
99c1f5ab 646
e7c9f3ff 647 return 1;
d0ca268b
JA
648 }
649
e7c9f3ff 650 return 0;
d0ca268b
JA
651}
652
da39451f
TZ
653static int get_dropped_count(const char *buts_name)
654{
655 int fd;
656 char tmp[MAXPATHLEN + 64];
657
658 snprintf(tmp, sizeof(tmp), "%s/block/%s/dropped",
659 relay_path, buts_name);
660
661 fd = open(tmp, O_RDONLY);
662 if (fd < 0) {
663 /*
664 * this may be ok, if the kernel doesn't support dropped counts
665 */
666 if (errno == ENOENT)
667 return 0;
668
669 fprintf(stderr, "Couldn't open dropped file %s\n", tmp);
670 return -1;
671 }
672
673 if (read(fd, tmp, sizeof(tmp)) < 0) {
674 perror(tmp);
675 close(fd);
676 return -1;
677 }
678
679 close(fd);
680
681 return atoi(tmp);
682}
683
e7c9f3ff
NS
684static void show_stats(void)
685{
da39451f 686 int i, j, dropped;
e7c9f3ff
NS
687 struct device_information *dip;
688 struct thread_information *tip;
689 unsigned long long events_processed;
428683db 690
e7c9f3ff
NS
691 if (output_name && !strcmp(output_name, "-"))
692 return;
693
99c1f5ab 694 for_each_dip(dip, i) {
e7c9f3ff
NS
695 printf("Device: %s\n", dip->path);
696 events_processed = 0;
99c1f5ab 697 for_each_tip(dip, tip, j) {
e7c9f3ff
NS
698 printf(" CPU%3d: %20ld events\n",
699 tip->cpu, tip->events_processed);
700 events_processed += tip->events_processed;
701 }
da39451f
TZ
702 dropped = get_dropped_count(dip->buts_name);
703 printf(" Total: %20lld events (dropped %d)\n",
704 events_processed, dropped);
e7c9f3ff
NS
705 }
706}
52724a0e
JA
707
708static char usage_str[] = \
709 "-d <dev> [ -r relay path ] [ -o <output> ] [-k ] [ -w time ]\n" \
710 "[ -a action ] [ -A action mask ] [ -v ]\n\n" \
711 "\t-d Use specified device. May also be given last after options\n" \
712 "\t-r Path to mounted relayfs, defaults to /relay\n" \
713 "\t-o File(s) to send output to\n" \
d1d7f15f 714 "\t-D Directory to prepend to output file names\n" \
52724a0e
JA
715 "\t-k Kill a running trace\n" \
716 "\t-w Stop after defined time, in seconds\n" \
717 "\t-a Only trace specified actions. See documentation\n" \
718 "\t-A Give trace mask as a single value. See documentation\n" \
129aa440
JA
719 "\t-b Sub buffer size in KiB\n" \
720 "\t-n Number of sub buffers\n" \
52724a0e
JA
721 "\t-v Print program version info\n\n";
722
ee1f4158
NS
723static void show_usage(char *program)
724{
52724a0e 725 fprintf(stderr, "Usage: %s %s %s",program, blktrace_version, usage_str);
ee1f4158
NS
726}
727
e820abd7 728static void handle_sigint(__attribute__((__unused__)) int sig)
d0ca268b 729{
d0ca268b
JA
730 done = 1;
731}
732
733int main(int argc, char *argv[])
734{
5270dddd 735 static char default_relay_path[] = "/relay";
e3e74029 736 struct statfs st;
d39c04ca 737 int i, c;
ece238a6 738 int stop_watch = 0;
d39c04ca
AB
739 int act_mask_tmp = 0;
740
741 while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
742 switch (c) {
743 case 'a':
744 i = find_mask_map(optarg);
745 if (i < 0) {
ab197ca7 746 fprintf(stderr,"Invalid action mask %s\n",
d39c04ca 747 optarg);
7425d456 748 return 1;
d39c04ca
AB
749 }
750 act_mask_tmp |= i;
751 break;
752
753 case 'A':
98f8386b
AB
754 if ((sscanf(optarg, "%x", &i) != 1) ||
755 !valid_act_opt(i)) {
d39c04ca 756 fprintf(stderr,
ab197ca7 757 "Invalid set action mask %s/0x%x\n",
d39c04ca 758 optarg, i);
7425d456 759 return 1;
d39c04ca
AB
760 }
761 act_mask_tmp = i;
762 break;
d0ca268b 763
d39c04ca 764 case 'd':
e7c9f3ff
NS
765 if (resize_devices(optarg) != 0)
766 return 1;
d39c04ca
AB
767 break;
768
5270dddd
JA
769 case 'r':
770 relay_path = optarg;
771 break;
772
d5396421 773 case 'o':
66efebf8 774 output_name = optarg;
d5396421 775 break;
bc39777c
JA
776 case 'k':
777 kill_running_trace = 1;
778 break;
ece238a6
NS
779 case 'w':
780 stop_watch = atoi(optarg);
781 if (stop_watch <= 0) {
782 fprintf(stderr,
783 "Invalid stopwatch value (%d secs)\n",
784 stop_watch);
785 return 1;
786 }
787 break;
57ea8602 788 case 'V':
52724a0e
JA
789 printf("%s version %s\n", argv[0], blktrace_version);
790 return 0;
129aa440
JA
791 case 'b':
792 buf_size = atoi(optarg);
183a0855 793 if (buf_size <= 0 || buf_size > 16*1024) {
129aa440
JA
794 fprintf(stderr,
795 "Invalid buffer size (%d)\n", buf_size);
796 return 1;
797 }
798 buf_size <<= 10;
799 break;
800 case 'n':
801 buf_nr = atoi(optarg);
802 if (buf_nr <= 0) {
803 fprintf(stderr,
804 "Invalid buffer nr (%d)\n", buf_nr);
805 return 1;
806 }
807 break;
d1d7f15f
JA
808 case 'D':
809 output_dir = optarg;
810 break;
d39c04ca 811 default:
ee1f4158 812 show_usage(argv[0]);
7425d456 813 return 1;
d39c04ca
AB
814 }
815 }
816
e7c9f3ff
NS
817 while (optind < argc) {
818 if (resize_devices(argv[optind++]) != 0)
819 return 1;
820 }
ee1f4158 821
e7c9f3ff 822 if (ndevs == 0) {
ee1f4158 823 show_usage(argv[0]);
7425d456 824 return 1;
d39c04ca
AB
825 }
826
5270dddd
JA
827 if (!relay_path)
828 relay_path = default_relay_path;
829
d5396421 830 if (act_mask_tmp != 0)
d39c04ca 831 act_mask = act_mask_tmp;
d0ca268b 832
e3e74029
NS
833 if (statfs(relay_path, &st) < 0) {
834 perror("statfs");
835 fprintf(stderr,"%s does not appear to be a valid path\n",
836 relay_path);
837 return 1;
64acacae 838 } else if (st.f_type != (long) RELAYFS_TYPE) {
e3e74029 839 fprintf(stderr,"%s does not appear to be a relay filesystem\n",
d0ca268b 840 relay_path);
7425d456 841 return 1;
d0ca268b
JA
842 }
843
e7c9f3ff 844 if (open_devices() != 0)
7425d456 845 return 1;
bc39777c
JA
846
847 if (kill_running_trace) {
e7c9f3ff 848 stop_all_traces();
7425d456 849 return 0;
bc39777c
JA
850 }
851
d0ca268b
JA
852 setlocale(LC_NUMERIC, "en_US");
853
e7c9f3ff
NS
854 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
855 if (ncpus < 0) {
856 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
7425d456 857 return 1;
d0ca268b
JA
858 }
859
e7c9f3ff
NS
860 if (start_devices() != 0)
861 return 1;
862
d0ca268b
JA
863 signal(SIGINT, handle_sigint);
864 signal(SIGHUP, handle_sigint);
865 signal(SIGTERM, handle_sigint);
ece238a6 866 signal(SIGALRM, handle_sigint);
d0ca268b 867
e7c9f3ff 868 atexit(stop_all_tracing);
830fd65c 869
ece238a6
NS
870 if (stop_watch)
871 alarm(stop_watch);
872
d0ca268b
JA
873 while (!is_done())
874 sleep(1);
875
e7c9f3ff
NS
876 stop_all_threads();
877 stop_all_traces();
d0ca268b
JA
878 show_stats();
879
880 return 0;
881}
882