[PATCH] blktrace: use the new setup/start/stop/teardown ioctls
[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                         munmap(tip->fs_buf, tip->fs_buf_len);
391                         tip->fs_buf = NULL;
392                 }
393
394                 tip->fs_off = tip->fs_size & (page_size - 1);
395                 tip->fs_buf_len = (16 * buf_size) - tip->fs_off;
396                 tip->fs_max_size += tip->fs_buf_len;
397
398                 if (ftruncate(ofd, tip->fs_max_size) < 0) {
399                         perror("ftruncate");
400                         return -1;
401                 }
402
403                 tip->fs_buf = mmap(NULL, tip->fs_buf_len, PROT_WRITE,
404                                    MAP_SHARED, ofd, tip->fs_size - tip->fs_off);
405                 if (tip->fs_buf == MAP_FAILED) {
406                         perror("mmap");
407                         return -1;
408                 }
409         }
410
411         ret = read_data(tip, tip->fs_buf + tip->fs_off, buf_size);
412         if (ret >= 0) {
413                 tip->data_read += ret;
414                 tip->fs_size += ret;
415                 tip->fs_off += ret;
416                 return 0;
417         }
418
419         return -1;
420 }
421
422 /*
423  * Use the copy approach for pipes
424  */
425 static int get_subbuf(struct thread_information *tip)
426 {
427         struct tip_subbuf *ts;
428         int ret;
429
430         ts = malloc(sizeof(*ts));
431         ts->buf = malloc(buf_size);
432         ts->max_len = buf_size;
433
434         ret = read_data(tip, ts->buf, ts->max_len);
435         if (ret > 0) {
436                 ts->len = ret;
437                 return subbuf_fifo_queue(tip, ts);
438         }
439
440         free(ts->buf);
441         free(ts);
442         return ret;
443 }
444
445 static void close_thread(struct thread_information *tip)
446 {
447         if (tip->fd != -1)
448                 close(tip->fd);
449         if (tip->ofile)
450                 fclose(tip->ofile);
451         if (tip->ofile_buffer)
452                 free(tip->ofile_buffer);
453         if (tip->fd_buf)
454                 free(tip->fd_buf);
455
456         tip->fd = -1;
457         tip->ofile = NULL;
458         tip->ofile_buffer = NULL;
459         tip->fd_buf = NULL;
460 }
461
462 static void *thread_main(void *arg)
463 {
464         struct thread_information *tip = arg;
465         pid_t pid = getpid();
466         cpu_set_t cpu_mask;
467
468         CPU_ZERO(&cpu_mask);
469         CPU_SET((tip->cpu), &cpu_mask);
470
471         if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
472                 perror("sched_setaffinity");
473                 exit_trace(1);
474         }
475
476         snprintf(tip->fn, sizeof(tip->fn), "%s/block/%s/trace%d",
477                         relay_path, tip->device->buts_name, tip->cpu);
478         tip->fd = open(tip->fn, O_RDONLY);
479         if (tip->fd < 0) {
480                 perror(tip->fn);
481                 fprintf(stderr,"Thread %d failed open of %s\n", tip->cpu,
482                         tip->fn);
483                 exit_trace(1);
484         }
485
486         while (!is_done()) {
487                 if (tip->ofile_stdout) {
488                         if (get_subbuf(tip))
489                                 break;
490                 } else {
491                         if (mmap_subbuf(tip))
492                                 break;
493                 }
494         }
495
496         /*
497          * truncate to right size and cleanup mmap
498          */
499         if (!tip->ofile_stdout) {
500                 int ofd = fileno(tip->ofile);
501
502                 if (tip->fs_buf)
503                         munmap(tip->fs_buf, tip->fs_buf_len);
504
505                 ftruncate(ofd, tip->fs_size);
506         }
507
508         tip->exited = 1;
509         return NULL;
510 }
511
512 static int write_data(struct thread_information *tip,
513                       void *buf, unsigned int buf_len)
514 {
515         int ret;
516
517         if (!buf_len)
518                 return 0;
519
520         while (1) {
521                 ret = fwrite(buf, buf_len, 1, tip->ofile);
522                 if (ret == 1)
523                         break;
524
525                 if (ret < 0) {
526                         perror("write");
527                         return 1;
528                 }
529         }
530
531         if (tip->ofile_stdout)
532                 fflush(tip->ofile);
533
534         return 0;
535 }
536
537 static int flush_subbuf(struct thread_information *tip, struct tip_subbuf *ts)
538 {
539         unsigned int offset = 0;
540         struct blk_io_trace *t;
541         int pdu_len, events = 0;
542
543         /*
544          * surplus from last run
545          */
546         if (tip->leftover_ts) {
547                 struct tip_subbuf *prev_ts = tip->leftover_ts;
548
549                 if (prev_ts->len + ts->len > prev_ts->max_len) {
550                         prev_ts->max_len += ts->len;
551                         prev_ts->buf = realloc(prev_ts->buf, prev_ts->max_len);
552                 }
553
554                 memcpy(prev_ts->buf + prev_ts->len, ts->buf, ts->len);
555                 prev_ts->len += ts->len;
556
557                 free(ts->buf);
558                 free(ts);
559
560                 ts = prev_ts;
561                 tip->leftover_ts = NULL;
562         }
563
564         while (offset + sizeof(*t) <= ts->len) {
565                 t = ts->buf + offset;
566
567                 if (verify_trace(t)) {
568                         write_data(tip, ts->buf, offset);
569                         return -1;
570                 }
571
572                 pdu_len = t->pdu_len;
573
574                 if (offset + sizeof(*t) + pdu_len > ts->len)
575                         break;
576
577                 offset += sizeof(*t) + pdu_len;
578                 tip->events_processed++;
579                 tip->data_read += sizeof(*t) + pdu_len;
580                 events++;
581         }
582
583         if (write_data(tip, ts->buf, offset))
584                 return -1;
585
586         /*
587          * leftover bytes, save them for next time
588          */
589         if (offset != ts->len) {
590                 tip->leftover_ts = ts;
591                 ts->len -= offset;
592                 memmove(ts->buf, ts->buf + offset, ts->len);
593         } else {
594                 free(ts->buf);
595                 free(ts);
596         }
597
598         return events;
599 }
600
601 static int write_tip_events(struct thread_information *tip)
602 {
603         struct tip_subbuf *ts = subbuf_fifo_dequeue(tip);
604
605         if (ts)
606                 return flush_subbuf(tip, ts);
607
608         return 0;
609 }
610
611 /*
612  * scans the tips we know and writes out the subbuffers we accumulate
613  */
614 static void get_and_write_events(void)
615 {
616         struct device_information *dip;
617         struct thread_information *tip;
618         int i, j, events, ret, tips_running;
619
620         while (!is_done()) {
621                 events = 0;
622
623                 for_each_dip(dip, i) {
624                         for_each_tip(dip, tip, j) {
625                                 ret = write_tip_events(tip);
626                                 if (ret > 0)
627                                         events += ret;
628                         }
629                 }
630
631                 if (!events)
632                         usleep(10);
633         }
634
635         /*
636          * reap stored events
637          */
638         do {
639                 events = 0;
640                 tips_running = 0;
641                 for_each_dip(dip, i) {
642                         for_each_tip(dip, tip, j) {
643                                 ret = write_tip_events(tip);
644                                 if (ret > 0)
645                                         events += ret;
646                                 tips_running += !tip->exited;
647                         }
648                 }
649                 usleep(10);
650         } while (events || tips_running);
651 }
652
653 static void wait_for_threads(void)
654 {
655         /*
656          * for piped output, poll and fetch data for writeout. for files,
657          * we just wait around for trace threads to exit
658          */
659         if (output_name && !strcmp(output_name, "-"))
660                 get_and_write_events();
661         else {
662                 struct device_information *dip;
663                 struct thread_information *tip;
664                 int i, j, tips_running;
665
666                 do {
667                         tips_running = 0;
668                         usleep(1000);
669
670                         for_each_dip(dip, i)
671                                 for_each_tip(dip, tip, j)
672                                         tips_running += !tip->exited;
673                 } while (tips_running);
674         }
675 }
676
677 static int start_threads(struct device_information *dip)
678 {
679         struct thread_information *tip;
680         char op[64];
681         int j, pipeline = output_name && !strcmp(output_name, "-");
682         int len, mode, vbuf_size;
683
684         for_each_tip(dip, tip, j) {
685                 tip->cpu = j;
686                 tip->device = dip;
687                 tip->events_processed = 0;
688                 memset(&tip->fifo, 0, sizeof(tip->fifo));
689                 tip->leftover_ts = NULL;
690
691                 if (pipeline) {
692                         tip->ofile = fdopen(STDOUT_FILENO, "w");
693                         tip->ofile_stdout = 1;
694                         mode = _IOLBF;
695                         vbuf_size = 512;
696                 } else {
697                         len = 0;
698
699                         if (output_dir)
700                                 len = sprintf(op, "%s/", output_dir);
701
702                         if (output_name) {
703                                 sprintf(op + len, "%s.blktrace.%d", output_name,
704                                         tip->cpu);
705                         } else {
706                                 sprintf(op + len, "%s.blktrace.%d",
707                                         dip->buts_name, tip->cpu);
708                         }
709                         tip->ofile = fopen(op, "w+");
710                         tip->ofile_stdout = 0;
711                         mode = _IOFBF;
712                         vbuf_size = OFILE_BUF;
713                 }
714
715                 if (tip->ofile == NULL) {
716                         perror(op);
717                         return 1;
718                 }
719
720                 tip->ofile_buffer = malloc(vbuf_size);
721                 if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) {
722                         perror("setvbuf");
723                         close_thread(tip);
724                         return 1;
725                 }
726
727                 if (pthread_create(&tip->thread, NULL, thread_main, tip)) {
728                         perror("pthread_create");
729                         close_thread(tip);
730                         return 1;
731                 }
732         }
733
734         return 0;
735 }
736
737 static void stop_threads(struct device_information *dip)
738 {
739         struct thread_information *tip;
740         unsigned long ret;
741         int i;
742
743         for_each_tip(dip, tip, i) {
744                 (void) pthread_join(tip->thread, (void *) &ret);
745                 close_thread(tip);
746         }
747 }
748
749 static void stop_all_threads(void)
750 {
751         struct device_information *dip;
752         int i;
753
754         for_each_dip(dip, i)
755                 stop_threads(dip);
756 }
757
758 static void stop_all_tracing(void)
759 {
760         struct device_information *dip;
761         int i;
762
763         for_each_dip(dip, i)
764                 stop_trace(dip);
765 }
766
767 static void exit_trace(int status)
768 {
769         if (!is_trace_stopped()) {
770                 trace_stopped = 1;
771                 stop_all_threads();
772                 stop_all_tracing();
773         }
774
775         exit(status);
776 }
777
778 static int resize_devices(char *path)
779 {
780         int size = (ndevs + 1) * sizeof(struct device_information);
781
782         device_information = realloc(device_information, size);
783         if (!device_information) {
784                 fprintf(stderr, "Out of memory, device %s (%d)\n", path, size);
785                 return 1;
786         }
787         device_information[ndevs].path = path;
788         ndevs++;
789         return 0;
790 }
791
792 static int open_devices(void)
793 {
794         struct device_information *dip;
795         int i;
796
797         for_each_dip(dip, i) {
798                 dip->fd = open(dip->path, O_RDONLY | O_NONBLOCK);
799                 if (dip->fd < 0) {
800                         perror(dip->path);
801                         return 1;
802                 }
803         }
804
805         return 0;
806 }
807
808 static int start_devices(void)
809 {
810         struct device_information *dip;
811         int i, j, size;
812
813         size = ncpus * sizeof(struct thread_information);
814         thread_information = malloc(size * ndevs);
815         if (!thread_information) {
816                 fprintf(stderr, "Out of memory, threads (%d)\n", size * ndevs);
817                 return 1;
818         }
819
820         for_each_dip(dip, i) {
821                 if (start_trace(dip)) {
822                         close(dip->fd);
823                         fprintf(stderr, "Failed to start trace on %s\n",
824                                 dip->path);
825                         break;
826                 }
827         }
828
829         if (i != ndevs) {
830                 __for_each_dip(dip, j, i)
831                         stop_trace(dip);
832
833                 return 1;
834         }
835
836         for_each_dip(dip, i) {
837                 dip->threads = thread_information + (i * ncpus);
838                 if (start_threads(dip)) {
839                         fprintf(stderr, "Failed to start worker threads\n");
840                         break;
841                 }
842         }
843
844         if (i != ndevs) {
845                 __for_each_dip(dip, j, i)
846                         stop_threads(dip);
847                 for_each_dip(dip, i)
848                         stop_trace(dip);
849
850                 return 1;
851         }
852
853         return 0;
854 }
855
856 static void show_stats(void)
857 {
858         struct device_information *dip;
859         struct thread_information *tip;
860         unsigned long long events_processed, data_read;
861         unsigned long total_drops;
862         int i, j, no_stdout = 0;
863
864         if (is_stat_shown())
865                 return;
866
867         if (output_name && !strcmp(output_name, "-"))
868                 no_stdout = 1;
869
870         stat_shown = 1;
871
872         total_drops = 0;
873         for_each_dip(dip, i) {
874                 if (!no_stdout)
875                         printf("Device: %s\n", dip->path);
876                 events_processed = 0;
877                 data_read = 0;
878                 for_each_tip(dip, tip, j) {
879                         if (!no_stdout)
880                                 printf("  CPU%3d: %20lu events, %8llu KiB data\n",
881                                         tip->cpu, tip->events_processed,
882                                         tip->data_read >> 10);
883                         events_processed += tip->events_processed;
884                         data_read += tip->data_read;
885                 }
886                 total_drops += dip->drop_count;
887                 if (!no_stdout)
888                         printf("  Total:  %20llu events (dropped %lu), %8llu KiB data\n",
889                                         events_processed, dip->drop_count,
890                                         data_read >> 10);
891         }
892
893         if (total_drops)
894                 fprintf(stderr, "You have dropped events, consider using a larger buffer size (-b)\n");
895 }
896
897 static char usage_str[] = \
898         "-d <dev> [ -r relay path ] [ -o <output> ] [-k ] [ -w time ]\n" \
899         "[ -a action ] [ -A action mask ] [ -v ]\n\n" \
900         "\t-d Use specified device. May also be given last after options\n" \
901         "\t-r Path to mounted relayfs, defaults to /relay\n" \
902         "\t-o File(s) to send output to\n" \
903         "\t-D Directory to prepend to output file names\n" \
904         "\t-k Kill a running trace\n" \
905         "\t-w Stop after defined time, in seconds\n" \
906         "\t-a Only trace specified actions. See documentation\n" \
907         "\t-A Give trace mask as a single value. See documentation\n" \
908         "\t-b Sub buffer size in KiB\n" \
909         "\t-n Number of sub buffers\n" \
910         "\t-v Print program version info\n\n";
911
912 static void show_usage(char *program)
913 {
914         fprintf(stderr, "Usage: %s %s %s",program, blktrace_version, usage_str);
915 }
916 static void handle_sigint(__attribute__((__unused__)) int sig)
917 {
918         done = 1;
919 }
920
921 int main(int argc, char *argv[])
922 {
923         static char default_relay_path[] = "/relay";
924         struct statfs st;
925         int i, c;
926         int stop_watch = 0;
927         int act_mask_tmp = 0;
928
929         while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
930                 switch (c) {
931                 case 'a':
932                         i = find_mask_map(optarg);
933                         if (i < 0) {
934                                 fprintf(stderr,"Invalid action mask %s\n",
935                                         optarg);
936                                 return 1;
937                         }
938                         act_mask_tmp |= i;
939                         break;
940
941                 case 'A':
942                         if ((sscanf(optarg, "%x", &i) != 1) || 
943                                                         !valid_act_opt(i)) {
944                                 fprintf(stderr,
945                                         "Invalid set action mask %s/0x%x\n",
946                                         optarg, i);
947                                 return 1;
948                         }
949                         act_mask_tmp = i;
950                         break;
951
952                 case 'd':
953                         if (resize_devices(optarg) != 0)
954                                 return 1;
955                         break;
956
957                 case 'r':
958                         relay_path = optarg;
959                         break;
960
961                 case 'o':
962                         output_name = optarg;
963                         break;
964                 case 'k':
965                         kill_running_trace = 1;
966                         break;
967                 case 'w':
968                         stop_watch = atoi(optarg);
969                         if (stop_watch <= 0) {
970                                 fprintf(stderr,
971                                         "Invalid stopwatch value (%d secs)\n",
972                                         stop_watch);
973                                 return 1;
974                         }
975                         break;
976                 case 'V':
977                         printf("%s version %s\n", argv[0], blktrace_version);
978                         return 0;
979                 case 'b':
980                         buf_size = strtoul(optarg, NULL, 10);
981                         if (buf_size <= 0 || buf_size > 16*1024) {
982                                 fprintf(stderr,
983                                         "Invalid buffer size (%lu)\n",buf_size);
984                                 return 1;
985                         }
986                         buf_size <<= 10;
987                         break;
988                 case 'n':
989                         buf_nr = strtoul(optarg, NULL, 10);
990                         if (buf_nr <= 0) {
991                                 fprintf(stderr,
992                                         "Invalid buffer nr (%lu)\n", buf_nr);
993                                 return 1;
994                         }
995                         break;
996                 case 'D':
997                         output_dir = optarg;
998                         break;
999                 default:
1000                         show_usage(argv[0]);
1001                         return 1;
1002                 }
1003         }
1004
1005         while (optind < argc) {
1006                 if (resize_devices(argv[optind++]) != 0)
1007                         return 1;
1008         }
1009
1010         if (ndevs == 0) {
1011                 show_usage(argv[0]);
1012                 return 1;
1013         }
1014
1015         if (!relay_path)
1016                 relay_path = default_relay_path;
1017
1018         if (act_mask_tmp != 0)
1019                 act_mask = act_mask_tmp;
1020
1021         if (statfs(relay_path, &st) < 0) {
1022                 perror("statfs");
1023                 fprintf(stderr,"%s does not appear to be a valid path\n",
1024                         relay_path);
1025                 return 1;
1026         } else if (st.f_type != (long) RELAYFS_TYPE) {
1027                 fprintf(stderr,"%s does not appear to be a relay filesystem\n",
1028                         relay_path);
1029                 return 1;
1030         }
1031
1032         if (open_devices() != 0)
1033                 return 1;
1034
1035         if (kill_running_trace) {
1036                 stop_all_traces();
1037                 return 0;
1038         }
1039
1040         setlocale(LC_NUMERIC, "en_US");
1041
1042         ncpus = sysconf(_SC_NPROCESSORS_ONLN);
1043         if (ncpus < 0) {
1044                 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
1045                 return 1;
1046         }
1047
1048         page_size = getpagesize();
1049
1050         if (start_devices() != 0)
1051                 return 1;
1052
1053         signal(SIGINT, handle_sigint);
1054         signal(SIGHUP, handle_sigint);
1055         signal(SIGTERM, handle_sigint);
1056         signal(SIGALRM, handle_sigint);
1057
1058         atexit(stop_all_tracing);
1059
1060         if (stop_watch)
1061                 alarm(stop_watch);
1062
1063         wait_for_threads();
1064
1065         if (!is_trace_stopped()) {
1066                 trace_stopped = 1;
1067                 stop_all_threads();
1068                 stop_all_traces();
1069         }
1070
1071         show_stats();
1072
1073         return 0;
1074 }
1075