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