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