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