[PATCH] blktrace: fix leftover_ts offsetting
[blktrace.git] / blktrace.c
CommitLineData
d0ca268b
JA
1/*
2 * block queue tracing application
3 *
d956a2cd
JA
4 * Copyright (C) 2005 Jens Axboe <axboe@suse.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
d0ca268b
JA
20 */
21#include <pthread.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <locale.h>
26#include <signal.h>
27#include <fcntl.h>
28#include <string.h>
29#include <sys/ioctl.h>
b9d4294e 30#include <sys/param.h>
e3e74029 31#include <sys/statfs.h>
eb3c8108 32#include <sys/poll.h>
d0ca268b
JA
33#include <stdio.h>
34#include <stdlib.h>
35#include <sched.h>
d39c04ca
AB
36#include <ctype.h>
37#include <getopt.h>
da39451f 38#include <errno.h>
a3e4d330 39#include <assert.h>
d0ca268b
JA
40
41#include "blktrace.h"
9db17354 42#include "list.h"
d0ca268b 43
13d928f0 44static char blktrace_version[] = "0.99";
52724a0e 45
8f551a39
JA
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 */
007c233c 50#define BUF_SIZE (512 * 1024)
d0ca268b
JA
51#define BUF_NR (4)
52
007c233c
JA
53#define OFILE_BUF (128 * 1024)
54
e3e74029
NS
55#define RELAYFS_TYPE 0xF0B4A981
56
a3e4d330 57#define RING_INIT_NR (2)
eb3c8108 58#define RING_MAX_NR (16UL)
a3e4d330 59
57ea8602 60#define S_OPTS "d:a:A:r:o:kw:Vb:n:D:"
d5396421 61static struct option l_opts[] = {
5c86134e 62 {
d39c04ca 63 .name = "dev",
428683db 64 .has_arg = required_argument,
d39c04ca
AB
65 .flag = NULL,
66 .val = 'd'
67 },
5c86134e 68 {
d39c04ca 69 .name = "act-mask",
428683db 70 .has_arg = required_argument,
d39c04ca
AB
71 .flag = NULL,
72 .val = 'a'
73 },
5c86134e 74 {
d39c04ca 75 .name = "set-mask",
428683db 76 .has_arg = required_argument,
d39c04ca
AB
77 .flag = NULL,
78 .val = 'A'
79 },
5c86134e 80 {
5270dddd 81 .name = "relay",
428683db 82 .has_arg = required_argument,
5270dddd
JA
83 .flag = NULL,
84 .val = 'r'
85 },
d5396421
JA
86 {
87 .name = "output",
428683db 88 .has_arg = required_argument,
d5396421
JA
89 .flag = NULL,
90 .val = 'o'
91 },
bc39777c
JA
92 {
93 .name = "kill",
428683db 94 .has_arg = no_argument,
bc39777c
JA
95 .flag = NULL,
96 .val = 'k'
97 },
ece238a6
NS
98 {
99 .name = "stopwatch",
428683db 100 .has_arg = required_argument,
ece238a6
NS
101 .flag = NULL,
102 .val = 'w'
103 },
52724a0e
JA
104 {
105 .name = "version",
106 .has_arg = no_argument,
107 .flag = NULL,
57ea8602 108 .val = 'V'
52724a0e 109 },
129aa440 110 {
3f65c585 111 .name = "buffer-size",
129aa440
JA
112 .has_arg = required_argument,
113 .flag = NULL,
114 .val = 'b'
115 },
116 {
3f65c585 117 .name = "num-sub-buffers",
129aa440
JA
118 .has_arg = required_argument,
119 .flag = NULL,
120 .val = 'n'
121 },
d1d7f15f 122 {
3f65c585 123 .name = "output-dir",
d1d7f15f
JA
124 .has_arg = required_argument,
125 .flag = NULL,
126 .val = 'D'
127 },
71ef8b7c
JA
128 {
129 .name = NULL,
130 }
d39c04ca
AB
131};
132
9db17354
JA
133struct tip_subbuf {
134 struct list_head list;
135 void *buf;
5be4bdaf
JA
136 unsigned int len;
137 unsigned int max_len;
9db17354
JA
138};
139
d0ca268b
JA
140struct thread_information {
141 int cpu;
142 pthread_t thread;
b9d4294e
JA
143
144 int fd;
a3e4d330
JA
145 void *fd_buf;
146 unsigned long fd_off;
147 unsigned long fd_size;
148 unsigned long fd_max_size;
b9d4294e
JA
149 char fn[MAXPATHLEN + 64];
150
007c233c
JA
151 FILE *ofile;
152 char *ofile_buffer;
9db17354 153 int ofile_stdout;
007c233c 154
d0ca268b 155 unsigned long events_processed;
e7c9f3ff 156 struct device_information *device;
9db17354
JA
157
158 int exited;
159
160 pthread_mutex_t lock;
161 struct list_head subbuf_list;
7de86b12 162 struct tip_subbuf *leftover_ts;
d0ca268b
JA
163};
164
e7c9f3ff
NS
165struct device_information {
166 int fd;
167 char *path;
168 char buts_name[32];
99c1f5ab 169 volatile int trace_started;
eb3c8108 170 unsigned long drop_count;
e7c9f3ff
NS
171 struct thread_information *threads;
172};
d0ca268b 173
e7c9f3ff 174static int ncpus;
d0ca268b 175static struct thread_information *thread_information;
e7c9f3ff
NS
176static int ndevs;
177static struct device_information *device_information;
178
179/* command line option globals */
180static char *relay_path;
d5396421 181static char *output_name;
d1d7f15f 182static char *output_dir;
5c86134e 183static int act_mask = ~0U;
bc39777c 184static int kill_running_trace;
eb3c8108
JA
185static unsigned long buf_size = BUF_SIZE;
186static unsigned long buf_nr = BUF_NR;
d39c04ca 187
e7c9f3ff
NS
188#define is_done() (*(volatile int *)(&done))
189static volatile int done;
190
eb3c8108
JA
191#define is_trace_stopped() (*(volatile int *)(&trace_stopped))
192static volatile int trace_stopped;
193
194#define is_stat_shown() (*(volatile int *)(&stat_shown))
195static volatile int stat_shown;
a3e4d330 196
72ca8801
NS
197static void exit_trace(int status);
198
99c1f5ab
JA
199#define dip_tracing(dip) (*(volatile int *)(&(dip)->trace_started))
200#define dip_set_tracing(dip, v) ((dip)->trace_started = (v))
201
202#define __for_each_dip(__d, __i, __e) \
203 for (__i = 0, __d = device_information; __i < __e; __i++, __d++)
204
205#define for_each_dip(__d, __i) __for_each_dip(__d, __i, ndevs)
9db17354
JA
206#define for_each_tip(__d, __t, __j) \
207 for (__j = 0, __t = (__d)->threads; __j < ncpus; __j++, __t++)
99c1f5ab 208
eb3c8108
JA
209static int get_dropped_count(const char *buts_name)
210{
211 int fd;
212 char tmp[MAXPATHLEN + 64];
213
214 snprintf(tmp, sizeof(tmp), "%s/block/%s/dropped",
215 relay_path, buts_name);
216
217 fd = open(tmp, O_RDONLY);
218 if (fd < 0) {
219 /*
220 * this may be ok, if the kernel doesn't support dropped counts
221 */
222 if (errno == ENOENT)
223 return 0;
224
225 fprintf(stderr, "Couldn't open dropped file %s\n", tmp);
226 return -1;
227 }
228
229 if (read(fd, tmp, sizeof(tmp)) < 0) {
230 perror(tmp);
231 close(fd);
232 return -1;
233 }
234
235 close(fd);
236
237 return atoi(tmp);
238}
239
e7c9f3ff 240static int start_trace(struct device_information *dip)
d0ca268b
JA
241{
242 struct blk_user_trace_setup buts;
243
1f79c4a0 244 memset(&buts, 0, sizeof(buts));
129aa440
JA
245 buts.buf_size = buf_size;
246 buts.buf_nr = buf_nr;
d39c04ca 247 buts.act_mask = act_mask;
d0ca268b 248
e7c9f3ff 249 if (ioctl(dip->fd, BLKSTARTTRACE, &buts) < 0) {
d0ca268b
JA
250 perror("BLKSTARTTRACE");
251 return 1;
252 }
253
e7c9f3ff 254 memcpy(dip->buts_name, buts.name, sizeof(dip->buts_name));
99c1f5ab 255 dip_set_tracing(dip, 1);
d0ca268b
JA
256 return 0;
257}
258
e7c9f3ff 259static void stop_trace(struct device_information *dip)
d0ca268b 260{
99c1f5ab
JA
261 if (dip_tracing(dip) || kill_running_trace) {
262 dip_set_tracing(dip, 0);
cf9208ea 263
e7c9f3ff 264 if (ioctl(dip->fd, BLKSTOPTRACE) < 0)
707b0914 265 perror("BLKSTOPTRACE");
cf9208ea 266
e7c9f3ff 267 close(dip->fd);
cf9208ea 268 dip->fd = -1;
707b0914 269 }
d0ca268b
JA
270}
271
e7c9f3ff
NS
272static void stop_all_traces(void)
273{
274 struct device_information *dip;
275 int i;
276
eb3c8108
JA
277 for_each_dip(dip, i) {
278 dip->drop_count = get_dropped_count(dip->buts_name);
e7c9f3ff 279 stop_trace(dip);
eb3c8108 280 }
e7c9f3ff
NS
281}
282
eb3c8108
JA
283static void wait_for_data(struct thread_information *tip)
284{
285 struct pollfd pfd = { .fd = tip->fd, .events = POLLIN };
286
9db17354
JA
287 do {
288 poll(&pfd, 1, 10);
289 if (pfd.revents & POLLIN)
290 break;
291 if (tip->ofile_stdout)
292 break;
293 } while (!is_done());
eb3c8108
JA
294}
295
9db17354 296static int read_data(struct thread_information *tip, void *buf, int len)
d0ca268b 297{
ae9f71b3 298 int ret = 0;
bbabf03a 299
9db17354
JA
300 do {
301 wait_for_data(tip);
ae9f71b3 302
9db17354
JA
303 ret = read(tip->fd, buf, len);
304 if (!ret)
305 continue;
306 else if (ret > 0)
307 return ret;
308 else {
bbabf03a 309 if (errno != EAGAIN) {
a3e4d330
JA
310 perror(tip->fn);
311 fprintf(stderr,"Thread %d failed read of %s\n",
312 tip->cpu, tip->fn);
313 break;
314 }
9db17354 315 continue;
bbabf03a 316 }
9db17354 317 } while (!is_done());
8a43bac5 318
bbabf03a 319 return ret;
8a43bac5
JA
320}
321
9db17354 322static inline void tip_fd_unlock(struct thread_information *tip)
a3e4d330 323{
9db17354
JA
324 pthread_mutex_unlock(&tip->lock);
325}
eb3c8108 326
9db17354
JA
327static inline void tip_fd_lock(struct thread_information *tip)
328{
329 pthread_mutex_lock(&tip->lock);
a3e4d330
JA
330}
331
9db17354 332static int get_subbuf(struct thread_information *tip)
a3e4d330 333{
9db17354 334 struct tip_subbuf *ts;
5be4bdaf 335 int ts_size, ret;
a3e4d330 336
9db17354
JA
337 /*
338 * live tracing should get a lower count to make it more "realtime"
339 */
340 if (tip->ofile_stdout)
341 ts_size = 1024;
342 else
343 ts_size = buf_size;
a3e4d330 344
9db17354
JA
345 ts = malloc(sizeof(*ts));
346 ts->buf = malloc(ts_size);
347 ts->max_len = ts_size;
a3e4d330 348
5be4bdaf
JA
349 ret = read_data(tip, ts->buf, ts_size);
350 if (ret > 0) {
351 ts->len = ret;
9db17354
JA
352 tip_fd_lock(tip);
353 list_add_tail(&ts->list, &tip->subbuf_list);
354 tip_fd_unlock(tip);
355 return 0;
356 }
a3e4d330 357
9db17354
JA
358 free(ts->buf);
359 free(ts);
360 return -1;
a3e4d330
JA
361}
362
9db17354 363static void close_thread(struct thread_information *tip)
a3e4d330 364{
9db17354
JA
365 if (tip->fd != -1)
366 close(tip->fd);
367 if (tip->ofile)
368 fclose(tip->ofile);
369 if (tip->ofile_buffer)
370 free(tip->ofile_buffer);
371 if (tip->fd_buf)
372 free(tip->fd_buf);
1c99bc21 373
9db17354
JA
374 tip->fd = -1;
375 tip->ofile = NULL;
376 tip->ofile_buffer = NULL;
377 tip->fd_buf = NULL;
a3e4d330
JA
378}
379
9db17354 380static void *thread_main(void *arg)
a3e4d330 381{
9db17354
JA
382 struct thread_information *tip = arg;
383 pid_t pid = getpid();
384 cpu_set_t cpu_mask;
a3e4d330 385
9db17354
JA
386 CPU_ZERO(&cpu_mask);
387 CPU_SET((tip->cpu), &cpu_mask);
a3e4d330 388
9db17354
JA
389 if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
390 perror("sched_setaffinity");
391 exit_trace(1);
392 }
a3e4d330 393
9db17354
JA
394 snprintf(tip->fn, sizeof(tip->fn), "%s/block/%s/trace%d",
395 relay_path, tip->device->buts_name, tip->cpu);
396 tip->fd = open(tip->fn, O_RDONLY);
397 if (tip->fd < 0) {
398 perror(tip->fn);
399 fprintf(stderr,"Thread %d failed open of %s\n", tip->cpu,
400 tip->fn);
401 exit_trace(1);
a3e4d330
JA
402 }
403
9db17354
JA
404 for (;;) {
405 if (get_subbuf(tip))
406 break;
407 }
a3e4d330 408
9db17354
JA
409 tip->exited = 1;
410 return NULL;
a3e4d330
JA
411}
412
7126171a
JA
413static int write_data(struct thread_information *tip,
414 void *buf, unsigned int buf_len)
8a43bac5 415{
7126171a 416 int ret;
8a43bac5 417
7126171a
JA
418 while (1) {
419 ret = fwrite(buf, buf_len, 1, tip->ofile);
007c233c 420 if (ret == 1)
8a43bac5
JA
421 break;
422
db6fe5bc
JA
423 if (ret < 0) {
424 perror("write");
425 return 1;
8a43bac5 426 }
d0ca268b
JA
427 }
428
9db17354 429 if (tip->ofile_stdout)
7126171a
JA
430 fflush(tip->ofile);
431
8a43bac5
JA
432 return 0;
433}
434
9db17354 435static int flush_subbuf(struct thread_information *tip, struct tip_subbuf *ts)
8a43bac5 436{
9db17354
JA
437 unsigned int offset = 0;
438 struct blk_io_trace *t;
439 int pdu_len, events = 0;
8a43bac5 440
9db17354 441 /*
7de86b12 442 * surplus from last run
9db17354 443 */
7de86b12
AB
444 if (tip->leftover_ts) {
445 struct tip_subbuf *prev_ts = tip->leftover_ts;
446
9e8b753c 447 if (prev_ts->len + ts->len > prev_ts->max_len) {
7de86b12
AB
448 prev_ts->max_len += ts->len;
449 prev_ts->buf = realloc(prev_ts->buf, prev_ts->max_len);
450 }
451
9e8b753c 452 memcpy(prev_ts->buf + prev_ts->len, ts->buf, ts->len);
7de86b12
AB
453 prev_ts->len += ts->len;
454
455 free(ts->buf);
456 free(ts);
457
458 ts = prev_ts;
459 tip->leftover_ts = NULL;
9db17354 460 }
d0ca268b 461
9db17354
JA
462 while (offset + sizeof(*t) <= ts->len) {
463 t = ts->buf + offset;
3a9d6c13 464
9db17354
JA
465 if (verify_trace(t))
466 return -1;
3a9d6c13 467
9db17354 468 pdu_len = t->pdu_len;
3a9d6c13 469
9db17354 470 if (offset + sizeof(*t) + pdu_len > ts->len)
3a9d6c13 471 break;
4b5db44a 472
9db17354 473 trace_to_be(t);
3a9d6c13 474
9db17354
JA
475 if (write_data(tip, t, sizeof(*t) + pdu_len))
476 return -1;
3a9d6c13 477
9db17354
JA
478 offset += sizeof(*t) + pdu_len;
479 tip->events_processed++;
480 events++;
3a9d6c13
JA
481 }
482
483 /*
9db17354 484 * leftover bytes, save them for next time
3a9d6c13 485 */
9db17354 486 if (offset != ts->len) {
7de86b12 487 tip->leftover_ts = ts;
9e8b753c
JA
488 ts->len -= offset;
489 memmove(ts->buf, ts->buf + offset, ts->len);
7de86b12
AB
490 } else {
491 free(ts->buf);
492 free(ts);
9db17354 493 }
4b5db44a 494
9db17354 495 return events;
4b5db44a
JA
496}
497
9db17354 498static int write_tip_events(struct thread_information *tip)
d5396421 499{
9db17354 500 struct tip_subbuf *ts = NULL;
d5396421 501
9db17354
JA
502 tip_fd_lock(tip);
503 if (!list_empty(&tip->subbuf_list)) {
504 ts = list_entry(tip->subbuf_list.next, struct tip_subbuf, list);
505 list_del(&ts->list);
506 }
507 tip_fd_unlock(tip);
d5396421 508
9db17354
JA
509 if (ts)
510 return flush_subbuf(tip, ts);
91816d54 511
9db17354 512 return 0;
91816d54
JA
513}
514
9db17354
JA
515/*
516 * scans the tips we know and writes out the subbuffers we accumulate
517 */
518static void get_and_write_events(void)
d0ca268b 519{
9db17354
JA
520 struct device_information *dip;
521 struct thread_information *tip;
27223f19 522 int i, j, events, ret, tips_running;
d0ca268b 523
9db17354
JA
524 while (!is_done()) {
525 events = 0;
d0ca268b 526
9db17354
JA
527 for_each_dip(dip, i) {
528 for_each_tip(dip, tip, j) {
529 ret = write_tip_events(tip);
530 if (ret > 0)
531 events += ret;
532 }
533 }
d0ca268b 534
9db17354
JA
535 if (!events)
536 usleep(10);
d0ca268b
JA
537 }
538
a3e4d330 539 /*
9db17354 540 * reap stored events
a3e4d330 541 */
9db17354
JA
542 do {
543 events = 0;
27223f19 544 tips_running = 0;
9db17354
JA
545 for_each_dip(dip, i) {
546 for_each_tip(dip, tip, j) {
547 ret = write_tip_events(tip);
548 if (ret > 0)
549 events += ret;
27223f19 550 tips_running += !tip->exited;
9db17354 551 }
69e65a9e 552 }
9db17354 553 usleep(10);
27223f19 554 } while (events || tips_running);
d0ca268b
JA
555}
556
e7c9f3ff 557static int start_threads(struct device_information *dip)
d0ca268b
JA
558{
559 struct thread_information *tip;
d5396421 560 char op[64];
e7c9f3ff 561 int j, pipeline = output_name && !strcmp(output_name, "-");
57e8a2ad 562 int len, mode, vbuf_size;
d0ca268b 563
99c1f5ab 564 for_each_tip(dip, tip, j) {
e7c9f3ff
NS
565 tip->cpu = j;
566 tip->device = dip;
d0ca268b 567 tip->events_processed = 0;
9db17354 568 INIT_LIST_HEAD(&tip->subbuf_list);
9db17354 569 tip->leftover_ts = NULL;
d0ca268b 570
e7c9f3ff 571 if (pipeline) {
007c233c 572 tip->ofile = fdopen(STDOUT_FILENO, "w");
9db17354 573 tip->ofile_stdout = 1;
007c233c 574 mode = _IOLBF;
57e8a2ad 575 vbuf_size = 512;
d5396421 576 } else {
d1d7f15f
JA
577 len = 0;
578
579 if (output_dir)
580 len = sprintf(op, "%s/", output_dir);
581
9f6486bd 582 if (output_name) {
d1d7f15f 583 sprintf(op + len, "%s.blktrace.%d", output_name,
9f6486bd
JA
584 tip->cpu);
585 } else {
d1d7f15f 586 sprintf(op + len, "%s.blktrace.%d",
e7c9f3ff 587 dip->buts_name, tip->cpu);
9f6486bd 588 }
007c233c 589 tip->ofile = fopen(op, "w");
9db17354 590 tip->ofile_stdout = 0;
007c233c 591 mode = _IOFBF;
57e8a2ad 592 vbuf_size = OFILE_BUF;
d5396421
JA
593 }
594
007c233c 595 if (tip->ofile == NULL) {
d5396421 596 perror(op);
e7c9f3ff 597 return 1;
d5396421
JA
598 }
599
57e8a2ad
TZ
600 tip->ofile_buffer = malloc(vbuf_size);
601 if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) {
007c233c
JA
602 perror("setvbuf");
603 close_thread(tip);
604 return 1;
605 }
606
9db17354 607 if (pthread_create(&tip->thread, NULL, thread_main, tip)) {
e7c9f3ff 608 perror("pthread_create");
007c233c 609 close_thread(tip);
e7c9f3ff 610 return 1;
d0ca268b
JA
611 }
612 }
613
e7c9f3ff 614 return 0;
d0ca268b
JA
615}
616
e7c9f3ff 617static void stop_threads(struct device_information *dip)
3aabcd89 618{
e7c9f3ff 619 struct thread_information *tip;
91816d54 620 unsigned long ret;
007c233c
JA
621 int i;
622
9db17354 623 for_each_tip(dip, tip, i) {
91816d54 624 (void) pthread_join(tip->thread, (void *) &ret);
9db17354
JA
625 close_thread(tip);
626 }
3aabcd89
JA
627}
628
e7c9f3ff 629static void stop_all_threads(void)
72ca8801 630{
e7c9f3ff 631 struct device_information *dip;
72ca8801
NS
632 int i;
633
99c1f5ab 634 for_each_dip(dip, i)
e7c9f3ff
NS
635 stop_threads(dip);
636}
637
638static void stop_all_tracing(void)
639{
640 struct device_information *dip;
91816d54 641 int i;
007c233c 642
91816d54 643 for_each_dip(dip, i)
e7c9f3ff 644 stop_trace(dip);
72ca8801
NS
645}
646
647static void exit_trace(int status)
648{
eb3c8108
JA
649 if (!is_trace_stopped()) {
650 trace_stopped = 1;
651 stop_all_threads();
652 stop_all_tracing();
653 }
654
72ca8801
NS
655 exit(status);
656}
657
e7c9f3ff
NS
658static int resize_devices(char *path)
659{
660 int size = (ndevs + 1) * sizeof(struct device_information);
661
662 device_information = realloc(device_information, size);
663 if (!device_information) {
664 fprintf(stderr, "Out of memory, device %s (%d)\n", path, size);
665 return 1;
666 }
667 device_information[ndevs].path = path;
668 ndevs++;
669 return 0;
670}
671
672static int open_devices(void)
d0ca268b 673{
e7c9f3ff 674 struct device_information *dip;
d0ca268b 675 int i;
d0ca268b 676
99c1f5ab 677 for_each_dip(dip, i) {
cf9208ea 678 dip->fd = open(dip->path, O_RDONLY | O_NONBLOCK);
e7c9f3ff
NS
679 if (dip->fd < 0) {
680 perror(dip->path);
681 return 1;
682 }
683 }
99c1f5ab 684
e7c9f3ff
NS
685 return 0;
686}
687
688static int start_devices(void)
689{
690 struct device_information *dip;
691 int i, j, size;
692
693 size = ncpus * sizeof(struct thread_information);
694 thread_information = malloc(size * ndevs);
695 if (!thread_information) {
696 fprintf(stderr, "Out of memory, threads (%d)\n", size * ndevs);
697 return 1;
698 }
d5396421 699
99c1f5ab 700 for_each_dip(dip, i) {
e7c9f3ff
NS
701 if (start_trace(dip)) {
702 close(dip->fd);
703 fprintf(stderr, "Failed to start trace on %s\n",
704 dip->path);
705 break;
706 }
707 }
99c1f5ab 708
e7c9f3ff 709 if (i != ndevs) {
99c1f5ab 710 __for_each_dip(dip, j, i)
e7c9f3ff 711 stop_trace(dip);
99c1f5ab 712
e7c9f3ff
NS
713 return 1;
714 }
715
99c1f5ab 716 for_each_dip(dip, i) {
e7c9f3ff
NS
717 dip->threads = thread_information + (i * ncpus);
718 if (start_threads(dip)) {
719 fprintf(stderr, "Failed to start worker threads\n");
720 break;
721 }
722 }
99c1f5ab 723
e7c9f3ff 724 if (i != ndevs) {
99c1f5ab 725 __for_each_dip(dip, j, i)
e7c9f3ff 726 stop_threads(dip);
99c1f5ab 727 for_each_dip(dip, i)
e7c9f3ff 728 stop_trace(dip);
99c1f5ab 729
e7c9f3ff 730 return 1;
d0ca268b
JA
731 }
732
e7c9f3ff 733 return 0;
d0ca268b
JA
734}
735
e7c9f3ff
NS
736static void show_stats(void)
737{
e7c9f3ff
NS
738 struct device_information *dip;
739 struct thread_information *tip;
740 unsigned long long events_processed;
eb3c8108 741 unsigned long total_drops;
2f903295 742 int i, j, no_stdout = 0;
eb3c8108
JA
743
744 if (is_stat_shown())
745 return;
746
2f903295
JA
747 if (output_name && !strcmp(output_name, "-"))
748 no_stdout = 1;
749
eb3c8108 750 stat_shown = 1;
428683db 751
56070ea4 752 total_drops = 0;
99c1f5ab 753 for_each_dip(dip, i) {
2f903295 754 if (!no_stdout)
56070ea4 755 printf("Device: %s\n", dip->path);
e7c9f3ff 756 events_processed = 0;
99c1f5ab 757 for_each_tip(dip, tip, j) {
2f903295 758 if (!no_stdout)
56070ea4
JA
759 printf(" CPU%3d: %20ld events\n",
760 tip->cpu, tip->events_processed);
e7c9f3ff
NS
761 events_processed += tip->events_processed;
762 }
eb3c8108 763 total_drops += dip->drop_count;
2f903295 764 if (!no_stdout)
eb3c8108
JA
765 printf(" Total: %20lld events (dropped %lu)\n",
766 events_processed, dip->drop_count);
e7c9f3ff 767 }
56070ea4
JA
768
769 if (total_drops)
770 fprintf(stderr, "You have dropped events, consider using a larger buffer size (-b)\n");
e7c9f3ff 771}
52724a0e
JA
772
773static char usage_str[] = \
774 "-d <dev> [ -r relay path ] [ -o <output> ] [-k ] [ -w time ]\n" \
775 "[ -a action ] [ -A action mask ] [ -v ]\n\n" \
776 "\t-d Use specified device. May also be given last after options\n" \
777 "\t-r Path to mounted relayfs, defaults to /relay\n" \
778 "\t-o File(s) to send output to\n" \
d1d7f15f 779 "\t-D Directory to prepend to output file names\n" \
52724a0e
JA
780 "\t-k Kill a running trace\n" \
781 "\t-w Stop after defined time, in seconds\n" \
782 "\t-a Only trace specified actions. See documentation\n" \
783 "\t-A Give trace mask as a single value. See documentation\n" \
129aa440
JA
784 "\t-b Sub buffer size in KiB\n" \
785 "\t-n Number of sub buffers\n" \
52724a0e
JA
786 "\t-v Print program version info\n\n";
787
ee1f4158
NS
788static void show_usage(char *program)
789{
52724a0e 790 fprintf(stderr, "Usage: %s %s %s",program, blktrace_version, usage_str);
ee1f4158 791}
e820abd7 792static void handle_sigint(__attribute__((__unused__)) int sig)
d0ca268b 793{
d0ca268b
JA
794 done = 1;
795}
796
797int main(int argc, char *argv[])
798{
5270dddd 799 static char default_relay_path[] = "/relay";
e3e74029 800 struct statfs st;
d39c04ca 801 int i, c;
ece238a6 802 int stop_watch = 0;
d39c04ca
AB
803 int act_mask_tmp = 0;
804
805 while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
806 switch (c) {
807 case 'a':
808 i = find_mask_map(optarg);
809 if (i < 0) {
ab197ca7 810 fprintf(stderr,"Invalid action mask %s\n",
d39c04ca 811 optarg);
7425d456 812 return 1;
d39c04ca
AB
813 }
814 act_mask_tmp |= i;
815 break;
816
817 case 'A':
98f8386b
AB
818 if ((sscanf(optarg, "%x", &i) != 1) ||
819 !valid_act_opt(i)) {
d39c04ca 820 fprintf(stderr,
ab197ca7 821 "Invalid set action mask %s/0x%x\n",
d39c04ca 822 optarg, i);
7425d456 823 return 1;
d39c04ca
AB
824 }
825 act_mask_tmp = i;
826 break;
d0ca268b 827
d39c04ca 828 case 'd':
e7c9f3ff
NS
829 if (resize_devices(optarg) != 0)
830 return 1;
d39c04ca
AB
831 break;
832
5270dddd
JA
833 case 'r':
834 relay_path = optarg;
835 break;
836
d5396421 837 case 'o':
66efebf8 838 output_name = optarg;
d5396421 839 break;
bc39777c
JA
840 case 'k':
841 kill_running_trace = 1;
842 break;
ece238a6
NS
843 case 'w':
844 stop_watch = atoi(optarg);
845 if (stop_watch <= 0) {
846 fprintf(stderr,
847 "Invalid stopwatch value (%d secs)\n",
848 stop_watch);
849 return 1;
850 }
851 break;
57ea8602 852 case 'V':
52724a0e
JA
853 printf("%s version %s\n", argv[0], blktrace_version);
854 return 0;
129aa440 855 case 'b':
eb3c8108 856 buf_size = strtoul(optarg, NULL, 10);
183a0855 857 if (buf_size <= 0 || buf_size > 16*1024) {
129aa440 858 fprintf(stderr,
eb3c8108 859 "Invalid buffer size (%lu)\n",buf_size);
129aa440
JA
860 return 1;
861 }
862 buf_size <<= 10;
863 break;
864 case 'n':
eb3c8108 865 buf_nr = strtoul(optarg, NULL, 10);
129aa440
JA
866 if (buf_nr <= 0) {
867 fprintf(stderr,
eb3c8108 868 "Invalid buffer nr (%lu)\n", buf_nr);
129aa440
JA
869 return 1;
870 }
871 break;
d1d7f15f
JA
872 case 'D':
873 output_dir = optarg;
874 break;
d39c04ca 875 default:
ee1f4158 876 show_usage(argv[0]);
7425d456 877 return 1;
d39c04ca
AB
878 }
879 }
880
e7c9f3ff
NS
881 while (optind < argc) {
882 if (resize_devices(argv[optind++]) != 0)
883 return 1;
884 }
ee1f4158 885
e7c9f3ff 886 if (ndevs == 0) {
ee1f4158 887 show_usage(argv[0]);
7425d456 888 return 1;
d39c04ca
AB
889 }
890
5270dddd
JA
891 if (!relay_path)
892 relay_path = default_relay_path;
893
d5396421 894 if (act_mask_tmp != 0)
d39c04ca 895 act_mask = act_mask_tmp;
d0ca268b 896
e3e74029
NS
897 if (statfs(relay_path, &st) < 0) {
898 perror("statfs");
899 fprintf(stderr,"%s does not appear to be a valid path\n",
900 relay_path);
901 return 1;
64acacae 902 } else if (st.f_type != (long) RELAYFS_TYPE) {
e3e74029 903 fprintf(stderr,"%s does not appear to be a relay filesystem\n",
d0ca268b 904 relay_path);
7425d456 905 return 1;
d0ca268b
JA
906 }
907
e7c9f3ff 908 if (open_devices() != 0)
7425d456 909 return 1;
bc39777c
JA
910
911 if (kill_running_trace) {
e7c9f3ff 912 stop_all_traces();
7425d456 913 return 0;
bc39777c
JA
914 }
915
d0ca268b
JA
916 setlocale(LC_NUMERIC, "en_US");
917
e7c9f3ff
NS
918 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
919 if (ncpus < 0) {
920 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
7425d456 921 return 1;
d0ca268b
JA
922 }
923
e7c9f3ff
NS
924 if (start_devices() != 0)
925 return 1;
926
d0ca268b
JA
927 signal(SIGINT, handle_sigint);
928 signal(SIGHUP, handle_sigint);
929 signal(SIGTERM, handle_sigint);
ece238a6 930 signal(SIGALRM, handle_sigint);
d0ca268b 931
e7c9f3ff 932 atexit(stop_all_tracing);
830fd65c 933
ece238a6
NS
934 if (stop_watch)
935 alarm(stop_watch);
936
9db17354 937 get_and_write_events();
d0ca268b 938
eb3c8108
JA
939 if (!is_trace_stopped()) {
940 trace_stopped = 1;
91816d54
JA
941 stop_all_threads();
942 stop_all_traces();
91816d54 943 }
d0ca268b 944
eb3c8108
JA
945 show_stats();
946
d0ca268b
JA
947 return 0;
948}
949