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