[PATCH] blktrace: remember to initialize mutex
[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 287 do {
b4aabcb3 288 poll(&pfd, 1, 100);
9db17354
JA
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;
b4aabcb3 335 int ret;
a3e4d330 336
9db17354 337 ts = malloc(sizeof(*ts));
b4aabcb3
JA
338 ts->buf = malloc(buf_size);
339 ts->max_len = buf_size;
a3e4d330 340
b4aabcb3 341 ret = read_data(tip, ts->buf, ts->max_len);
5be4bdaf
JA
342 if (ret > 0) {
343 ts->len = ret;
9db17354
JA
344 tip_fd_lock(tip);
345 list_add_tail(&ts->list, &tip->subbuf_list);
346 tip_fd_unlock(tip);
347 return 0;
348 }
a3e4d330 349
9db17354
JA
350 free(ts->buf);
351 free(ts);
352 return -1;
a3e4d330
JA
353}
354
9db17354 355static void close_thread(struct thread_information *tip)
a3e4d330 356{
9db17354
JA
357 if (tip->fd != -1)
358 close(tip->fd);
359 if (tip->ofile)
360 fclose(tip->ofile);
361 if (tip->ofile_buffer)
362 free(tip->ofile_buffer);
363 if (tip->fd_buf)
364 free(tip->fd_buf);
1c99bc21 365
9db17354
JA
366 tip->fd = -1;
367 tip->ofile = NULL;
368 tip->ofile_buffer = NULL;
369 tip->fd_buf = NULL;
a3e4d330
JA
370}
371
9db17354 372static void *thread_main(void *arg)
a3e4d330 373{
9db17354
JA
374 struct thread_information *tip = arg;
375 pid_t pid = getpid();
376 cpu_set_t cpu_mask;
a3e4d330 377
9db17354
JA
378 CPU_ZERO(&cpu_mask);
379 CPU_SET((tip->cpu), &cpu_mask);
a3e4d330 380
9db17354
JA
381 if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
382 perror("sched_setaffinity");
383 exit_trace(1);
384 }
a3e4d330 385
9db17354
JA
386 snprintf(tip->fn, sizeof(tip->fn), "%s/block/%s/trace%d",
387 relay_path, tip->device->buts_name, tip->cpu);
388 tip->fd = open(tip->fn, O_RDONLY);
389 if (tip->fd < 0) {
390 perror(tip->fn);
391 fprintf(stderr,"Thread %d failed open of %s\n", tip->cpu,
392 tip->fn);
393 exit_trace(1);
a3e4d330
JA
394 }
395
9db17354
JA
396 for (;;) {
397 if (get_subbuf(tip))
398 break;
399 }
a3e4d330 400
9db17354
JA
401 tip->exited = 1;
402 return NULL;
a3e4d330
JA
403}
404
7126171a
JA
405static int write_data(struct thread_information *tip,
406 void *buf, unsigned int buf_len)
8a43bac5 407{
7126171a 408 int ret;
8a43bac5 409
7126171a
JA
410 while (1) {
411 ret = fwrite(buf, buf_len, 1, tip->ofile);
007c233c 412 if (ret == 1)
8a43bac5
JA
413 break;
414
db6fe5bc
JA
415 if (ret < 0) {
416 perror("write");
417 return 1;
8a43bac5 418 }
d0ca268b
JA
419 }
420
9db17354 421 if (tip->ofile_stdout)
7126171a
JA
422 fflush(tip->ofile);
423
8a43bac5
JA
424 return 0;
425}
426
9db17354 427static int flush_subbuf(struct thread_information *tip, struct tip_subbuf *ts)
8a43bac5 428{
9db17354
JA
429 unsigned int offset = 0;
430 struct blk_io_trace *t;
431 int pdu_len, events = 0;
8a43bac5 432
9db17354 433 /*
7de86b12 434 * surplus from last run
9db17354 435 */
7de86b12
AB
436 if (tip->leftover_ts) {
437 struct tip_subbuf *prev_ts = tip->leftover_ts;
438
9e8b753c 439 if (prev_ts->len + ts->len > prev_ts->max_len) {
7de86b12
AB
440 prev_ts->max_len += ts->len;
441 prev_ts->buf = realloc(prev_ts->buf, prev_ts->max_len);
442 }
443
9e8b753c 444 memcpy(prev_ts->buf + prev_ts->len, ts->buf, ts->len);
7de86b12
AB
445 prev_ts->len += ts->len;
446
447 free(ts->buf);
448 free(ts);
449
450 ts = prev_ts;
451 tip->leftover_ts = NULL;
9db17354 452 }
d0ca268b 453
9db17354
JA
454 while (offset + sizeof(*t) <= ts->len) {
455 t = ts->buf + offset;
3a9d6c13 456
9db17354
JA
457 if (verify_trace(t))
458 return -1;
3a9d6c13 459
9db17354 460 pdu_len = t->pdu_len;
3a9d6c13 461
9db17354 462 if (offset + sizeof(*t) + pdu_len > ts->len)
3a9d6c13 463 break;
4b5db44a 464
9db17354 465 trace_to_be(t);
3a9d6c13 466
9db17354
JA
467 if (write_data(tip, t, sizeof(*t) + pdu_len))
468 return -1;
3a9d6c13 469
9db17354
JA
470 offset += sizeof(*t) + pdu_len;
471 tip->events_processed++;
472 events++;
3a9d6c13
JA
473 }
474
475 /*
9db17354 476 * leftover bytes, save them for next time
3a9d6c13 477 */
9db17354 478 if (offset != ts->len) {
7de86b12 479 tip->leftover_ts = ts;
9e8b753c
JA
480 ts->len -= offset;
481 memmove(ts->buf, ts->buf + offset, ts->len);
7de86b12
AB
482 } else {
483 free(ts->buf);
484 free(ts);
9db17354 485 }
4b5db44a 486
9db17354 487 return events;
4b5db44a
JA
488}
489
9db17354 490static int write_tip_events(struct thread_information *tip)
d5396421 491{
9db17354 492 struct tip_subbuf *ts = NULL;
d5396421 493
9db17354
JA
494 tip_fd_lock(tip);
495 if (!list_empty(&tip->subbuf_list)) {
496 ts = list_entry(tip->subbuf_list.next, struct tip_subbuf, list);
497 list_del(&ts->list);
498 }
499 tip_fd_unlock(tip);
d5396421 500
9db17354
JA
501 if (ts)
502 return flush_subbuf(tip, ts);
91816d54 503
9db17354 504 return 0;
91816d54
JA
505}
506
9db17354
JA
507/*
508 * scans the tips we know and writes out the subbuffers we accumulate
509 */
510static void get_and_write_events(void)
d0ca268b 511{
9db17354
JA
512 struct device_information *dip;
513 struct thread_information *tip;
27223f19 514 int i, j, events, ret, tips_running;
d0ca268b 515
9db17354
JA
516 while (!is_done()) {
517 events = 0;
d0ca268b 518
9db17354
JA
519 for_each_dip(dip, i) {
520 for_each_tip(dip, tip, j) {
521 ret = write_tip_events(tip);
522 if (ret > 0)
523 events += ret;
524 }
525 }
d0ca268b 526
9db17354
JA
527 if (!events)
528 usleep(10);
d0ca268b
JA
529 }
530
a3e4d330 531 /*
9db17354 532 * reap stored events
a3e4d330 533 */
9db17354
JA
534 do {
535 events = 0;
27223f19 536 tips_running = 0;
9db17354
JA
537 for_each_dip(dip, i) {
538 for_each_tip(dip, tip, j) {
539 ret = write_tip_events(tip);
540 if (ret > 0)
541 events += ret;
27223f19 542 tips_running += !tip->exited;
9db17354 543 }
69e65a9e 544 }
9db17354 545 usleep(10);
27223f19 546 } while (events || tips_running);
d0ca268b
JA
547}
548
e7c9f3ff 549static int start_threads(struct device_information *dip)
d0ca268b
JA
550{
551 struct thread_information *tip;
d5396421 552 char op[64];
e7c9f3ff 553 int j, pipeline = output_name && !strcmp(output_name, "-");
57e8a2ad 554 int len, mode, vbuf_size;
d0ca268b 555
99c1f5ab 556 for_each_tip(dip, tip, j) {
e7c9f3ff
NS
557 tip->cpu = j;
558 tip->device = dip;
d0ca268b 559 tip->events_processed = 0;
74b92379 560 pthread_mutex_init(&tip->lock, NULL);
9db17354 561 INIT_LIST_HEAD(&tip->subbuf_list);
9db17354 562 tip->leftover_ts = NULL;
d0ca268b 563
e7c9f3ff 564 if (pipeline) {
007c233c 565 tip->ofile = fdopen(STDOUT_FILENO, "w");
9db17354 566 tip->ofile_stdout = 1;
007c233c 567 mode = _IOLBF;
57e8a2ad 568 vbuf_size = 512;
d5396421 569 } else {
d1d7f15f
JA
570 len = 0;
571
572 if (output_dir)
573 len = sprintf(op, "%s/", output_dir);
574
9f6486bd 575 if (output_name) {
d1d7f15f 576 sprintf(op + len, "%s.blktrace.%d", output_name,
9f6486bd
JA
577 tip->cpu);
578 } else {
d1d7f15f 579 sprintf(op + len, "%s.blktrace.%d",
e7c9f3ff 580 dip->buts_name, tip->cpu);
9f6486bd 581 }
007c233c 582 tip->ofile = fopen(op, "w");
9db17354 583 tip->ofile_stdout = 0;
007c233c 584 mode = _IOFBF;
57e8a2ad 585 vbuf_size = OFILE_BUF;
d5396421
JA
586 }
587
007c233c 588 if (tip->ofile == NULL) {
d5396421 589 perror(op);
e7c9f3ff 590 return 1;
d5396421
JA
591 }
592
57e8a2ad
TZ
593 tip->ofile_buffer = malloc(vbuf_size);
594 if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) {
007c233c
JA
595 perror("setvbuf");
596 close_thread(tip);
597 return 1;
598 }
599
9db17354 600 if (pthread_create(&tip->thread, NULL, thread_main, tip)) {
e7c9f3ff 601 perror("pthread_create");
007c233c 602 close_thread(tip);
e7c9f3ff 603 return 1;
d0ca268b
JA
604 }
605 }
606
e7c9f3ff 607 return 0;
d0ca268b
JA
608}
609
e7c9f3ff 610static void stop_threads(struct device_information *dip)
3aabcd89 611{
e7c9f3ff 612 struct thread_information *tip;
91816d54 613 unsigned long ret;
007c233c
JA
614 int i;
615
9db17354 616 for_each_tip(dip, tip, i) {
91816d54 617 (void) pthread_join(tip->thread, (void *) &ret);
9db17354
JA
618 close_thread(tip);
619 }
3aabcd89
JA
620}
621
e7c9f3ff 622static void stop_all_threads(void)
72ca8801 623{
e7c9f3ff 624 struct device_information *dip;
72ca8801
NS
625 int i;
626
99c1f5ab 627 for_each_dip(dip, i)
e7c9f3ff
NS
628 stop_threads(dip);
629}
630
631static void stop_all_tracing(void)
632{
633 struct device_information *dip;
91816d54 634 int i;
007c233c 635
91816d54 636 for_each_dip(dip, i)
e7c9f3ff 637 stop_trace(dip);
72ca8801
NS
638}
639
640static void exit_trace(int status)
641{
eb3c8108
JA
642 if (!is_trace_stopped()) {
643 trace_stopped = 1;
644 stop_all_threads();
645 stop_all_tracing();
646 }
647
72ca8801
NS
648 exit(status);
649}
650
e7c9f3ff
NS
651static int resize_devices(char *path)
652{
653 int size = (ndevs + 1) * sizeof(struct device_information);
654
655 device_information = realloc(device_information, size);
656 if (!device_information) {
657 fprintf(stderr, "Out of memory, device %s (%d)\n", path, size);
658 return 1;
659 }
660 device_information[ndevs].path = path;
661 ndevs++;
662 return 0;
663}
664
665static int open_devices(void)
d0ca268b 666{
e7c9f3ff 667 struct device_information *dip;
d0ca268b 668 int i;
d0ca268b 669
99c1f5ab 670 for_each_dip(dip, i) {
cf9208ea 671 dip->fd = open(dip->path, O_RDONLY | O_NONBLOCK);
e7c9f3ff
NS
672 if (dip->fd < 0) {
673 perror(dip->path);
674 return 1;
675 }
676 }
99c1f5ab 677
e7c9f3ff
NS
678 return 0;
679}
680
681static int start_devices(void)
682{
683 struct device_information *dip;
684 int i, j, size;
685
686 size = ncpus * sizeof(struct thread_information);
687 thread_information = malloc(size * ndevs);
688 if (!thread_information) {
689 fprintf(stderr, "Out of memory, threads (%d)\n", size * ndevs);
690 return 1;
691 }
d5396421 692
99c1f5ab 693 for_each_dip(dip, i) {
e7c9f3ff
NS
694 if (start_trace(dip)) {
695 close(dip->fd);
696 fprintf(stderr, "Failed to start trace on %s\n",
697 dip->path);
698 break;
699 }
700 }
99c1f5ab 701
e7c9f3ff 702 if (i != ndevs) {
99c1f5ab 703 __for_each_dip(dip, j, i)
e7c9f3ff 704 stop_trace(dip);
99c1f5ab 705
e7c9f3ff
NS
706 return 1;
707 }
708
99c1f5ab 709 for_each_dip(dip, i) {
e7c9f3ff
NS
710 dip->threads = thread_information + (i * ncpus);
711 if (start_threads(dip)) {
712 fprintf(stderr, "Failed to start worker threads\n");
713 break;
714 }
715 }
99c1f5ab 716
e7c9f3ff 717 if (i != ndevs) {
99c1f5ab 718 __for_each_dip(dip, j, i)
e7c9f3ff 719 stop_threads(dip);
99c1f5ab 720 for_each_dip(dip, i)
e7c9f3ff 721 stop_trace(dip);
99c1f5ab 722
e7c9f3ff 723 return 1;
d0ca268b
JA
724 }
725
e7c9f3ff 726 return 0;
d0ca268b
JA
727}
728
e7c9f3ff
NS
729static void show_stats(void)
730{
e7c9f3ff
NS
731 struct device_information *dip;
732 struct thread_information *tip;
733 unsigned long long events_processed;
eb3c8108 734 unsigned long total_drops;
2f903295 735 int i, j, no_stdout = 0;
eb3c8108
JA
736
737 if (is_stat_shown())
738 return;
739
2f903295
JA
740 if (output_name && !strcmp(output_name, "-"))
741 no_stdout = 1;
742
eb3c8108 743 stat_shown = 1;
428683db 744
56070ea4 745 total_drops = 0;
99c1f5ab 746 for_each_dip(dip, i) {
2f903295 747 if (!no_stdout)
56070ea4 748 printf("Device: %s\n", dip->path);
e7c9f3ff 749 events_processed = 0;
99c1f5ab 750 for_each_tip(dip, tip, j) {
2f903295 751 if (!no_stdout)
56070ea4
JA
752 printf(" CPU%3d: %20ld events\n",
753 tip->cpu, tip->events_processed);
e7c9f3ff
NS
754 events_processed += tip->events_processed;
755 }
eb3c8108 756 total_drops += dip->drop_count;
2f903295 757 if (!no_stdout)
eb3c8108
JA
758 printf(" Total: %20lld events (dropped %lu)\n",
759 events_processed, dip->drop_count);
e7c9f3ff 760 }
56070ea4
JA
761
762 if (total_drops)
763 fprintf(stderr, "You have dropped events, consider using a larger buffer size (-b)\n");
e7c9f3ff 764}
52724a0e
JA
765
766static char usage_str[] = \
767 "-d <dev> [ -r relay path ] [ -o <output> ] [-k ] [ -w time ]\n" \
768 "[ -a action ] [ -A action mask ] [ -v ]\n\n" \
769 "\t-d Use specified device. May also be given last after options\n" \
770 "\t-r Path to mounted relayfs, defaults to /relay\n" \
771 "\t-o File(s) to send output to\n" \
d1d7f15f 772 "\t-D Directory to prepend to output file names\n" \
52724a0e
JA
773 "\t-k Kill a running trace\n" \
774 "\t-w Stop after defined time, in seconds\n" \
775 "\t-a Only trace specified actions. See documentation\n" \
776 "\t-A Give trace mask as a single value. See documentation\n" \
129aa440
JA
777 "\t-b Sub buffer size in KiB\n" \
778 "\t-n Number of sub buffers\n" \
52724a0e
JA
779 "\t-v Print program version info\n\n";
780
ee1f4158
NS
781static void show_usage(char *program)
782{
52724a0e 783 fprintf(stderr, "Usage: %s %s %s",program, blktrace_version, usage_str);
ee1f4158 784}
e820abd7 785static void handle_sigint(__attribute__((__unused__)) int sig)
d0ca268b 786{
d0ca268b
JA
787 done = 1;
788}
789
790int main(int argc, char *argv[])
791{
5270dddd 792 static char default_relay_path[] = "/relay";
e3e74029 793 struct statfs st;
d39c04ca 794 int i, c;
ece238a6 795 int stop_watch = 0;
d39c04ca
AB
796 int act_mask_tmp = 0;
797
798 while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
799 switch (c) {
800 case 'a':
801 i = find_mask_map(optarg);
802 if (i < 0) {
ab197ca7 803 fprintf(stderr,"Invalid action mask %s\n",
d39c04ca 804 optarg);
7425d456 805 return 1;
d39c04ca
AB
806 }
807 act_mask_tmp |= i;
808 break;
809
810 case 'A':
98f8386b
AB
811 if ((sscanf(optarg, "%x", &i) != 1) ||
812 !valid_act_opt(i)) {
d39c04ca 813 fprintf(stderr,
ab197ca7 814 "Invalid set action mask %s/0x%x\n",
d39c04ca 815 optarg, i);
7425d456 816 return 1;
d39c04ca
AB
817 }
818 act_mask_tmp = i;
819 break;
d0ca268b 820
d39c04ca 821 case 'd':
e7c9f3ff
NS
822 if (resize_devices(optarg) != 0)
823 return 1;
d39c04ca
AB
824 break;
825
5270dddd
JA
826 case 'r':
827 relay_path = optarg;
828 break;
829
d5396421 830 case 'o':
66efebf8 831 output_name = optarg;
d5396421 832 break;
bc39777c
JA
833 case 'k':
834 kill_running_trace = 1;
835 break;
ece238a6
NS
836 case 'w':
837 stop_watch = atoi(optarg);
838 if (stop_watch <= 0) {
839 fprintf(stderr,
840 "Invalid stopwatch value (%d secs)\n",
841 stop_watch);
842 return 1;
843 }
844 break;
57ea8602 845 case 'V':
52724a0e
JA
846 printf("%s version %s\n", argv[0], blktrace_version);
847 return 0;
129aa440 848 case 'b':
eb3c8108 849 buf_size = strtoul(optarg, NULL, 10);
183a0855 850 if (buf_size <= 0 || buf_size > 16*1024) {
129aa440 851 fprintf(stderr,
eb3c8108 852 "Invalid buffer size (%lu)\n",buf_size);
129aa440
JA
853 return 1;
854 }
855 buf_size <<= 10;
856 break;
857 case 'n':
eb3c8108 858 buf_nr = strtoul(optarg, NULL, 10);
129aa440
JA
859 if (buf_nr <= 0) {
860 fprintf(stderr,
eb3c8108 861 "Invalid buffer nr (%lu)\n", buf_nr);
129aa440
JA
862 return 1;
863 }
864 break;
d1d7f15f
JA
865 case 'D':
866 output_dir = optarg;
867 break;
d39c04ca 868 default:
ee1f4158 869 show_usage(argv[0]);
7425d456 870 return 1;
d39c04ca
AB
871 }
872 }
873
e7c9f3ff
NS
874 while (optind < argc) {
875 if (resize_devices(argv[optind++]) != 0)
876 return 1;
877 }
ee1f4158 878
e7c9f3ff 879 if (ndevs == 0) {
ee1f4158 880 show_usage(argv[0]);
7425d456 881 return 1;
d39c04ca
AB
882 }
883
5270dddd
JA
884 if (!relay_path)
885 relay_path = default_relay_path;
886
d5396421 887 if (act_mask_tmp != 0)
d39c04ca 888 act_mask = act_mask_tmp;
d0ca268b 889
e3e74029
NS
890 if (statfs(relay_path, &st) < 0) {
891 perror("statfs");
892 fprintf(stderr,"%s does not appear to be a valid path\n",
893 relay_path);
894 return 1;
64acacae 895 } else if (st.f_type != (long) RELAYFS_TYPE) {
e3e74029 896 fprintf(stderr,"%s does not appear to be a relay filesystem\n",
d0ca268b 897 relay_path);
7425d456 898 return 1;
d0ca268b
JA
899 }
900
e7c9f3ff 901 if (open_devices() != 0)
7425d456 902 return 1;
bc39777c
JA
903
904 if (kill_running_trace) {
e7c9f3ff 905 stop_all_traces();
7425d456 906 return 0;
bc39777c
JA
907 }
908
d0ca268b
JA
909 setlocale(LC_NUMERIC, "en_US");
910
e7c9f3ff
NS
911 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
912 if (ncpus < 0) {
913 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
7425d456 914 return 1;
d0ca268b
JA
915 }
916
e7c9f3ff
NS
917 if (start_devices() != 0)
918 return 1;
919
d0ca268b
JA
920 signal(SIGINT, handle_sigint);
921 signal(SIGHUP, handle_sigint);
922 signal(SIGTERM, handle_sigint);
ece238a6 923 signal(SIGALRM, handle_sigint);
d0ca268b 924
e7c9f3ff 925 atexit(stop_all_tracing);
830fd65c 926
ece238a6
NS
927 if (stop_watch)
928 alarm(stop_watch);
929
9db17354 930 get_and_write_events();
d0ca268b 931
eb3c8108
JA
932 if (!is_trace_stopped()) {
933 trace_stopped = 1;
91816d54
JA
934 stop_all_threads();
935 stop_all_traces();
91816d54 936 }
d0ca268b 937
eb3c8108
JA
938 show_stats();
939
d0ca268b
JA
940 return 0;
941}
942