[PATCH] blktrace: various net related fixes (accounting, stats, etc)
[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>
b7106311 33#include <sys/mman.h>
8e86c98a 34#include <sys/socket.h>
d0ca268b
JA
35#include <stdio.h>
36#include <stdlib.h>
37#include <sched.h>
d39c04ca
AB
38#include <ctype.h>
39#include <getopt.h>
da39451f 40#include <errno.h>
8e86c98a
JA
41#include <netinet/in.h>
42#include <arpa/inet.h>
43#include <netdb.h>
d0ca268b
JA
44
45#include "blktrace.h"
21f55651 46#include "barrier.h"
d0ca268b 47
13d928f0 48static char blktrace_version[] = "0.99";
52724a0e 49
8f551a39
JA
50/*
51 * You may want to increase this even more, if you are logging at a high
52 * rate and see skipped/missed events
53 */
007c233c 54#define BUF_SIZE (512 * 1024)
d0ca268b
JA
55#define BUF_NR (4)
56
007c233c
JA
57#define OFILE_BUF (128 * 1024)
58
e3e74029
NS
59#define RELAYFS_TYPE 0xF0B4A981
60
8e86c98a 61#define S_OPTS "d:a:A:r:o:kw:Vb:n:D:lh:p:"
d5396421 62static struct option l_opts[] = {
5c86134e 63 {
d39c04ca 64 .name = "dev",
428683db 65 .has_arg = required_argument,
d39c04ca
AB
66 .flag = NULL,
67 .val = 'd'
68 },
5c86134e 69 {
d39c04ca 70 .name = "act-mask",
428683db 71 .has_arg = required_argument,
d39c04ca
AB
72 .flag = NULL,
73 .val = 'a'
74 },
5c86134e 75 {
d39c04ca 76 .name = "set-mask",
428683db 77 .has_arg = required_argument,
d39c04ca
AB
78 .flag = NULL,
79 .val = 'A'
80 },
5c86134e 81 {
5270dddd 82 .name = "relay",
428683db 83 .has_arg = required_argument,
5270dddd
JA
84 .flag = NULL,
85 .val = 'r'
86 },
d5396421
JA
87 {
88 .name = "output",
428683db 89 .has_arg = required_argument,
d5396421
JA
90 .flag = NULL,
91 .val = 'o'
92 },
bc39777c
JA
93 {
94 .name = "kill",
428683db 95 .has_arg = no_argument,
bc39777c
JA
96 .flag = NULL,
97 .val = 'k'
98 },
ece238a6
NS
99 {
100 .name = "stopwatch",
428683db 101 .has_arg = required_argument,
ece238a6
NS
102 .flag = NULL,
103 .val = 'w'
104 },
52724a0e
JA
105 {
106 .name = "version",
107 .has_arg = no_argument,
108 .flag = NULL,
57ea8602 109 .val = 'V'
52724a0e 110 },
129aa440 111 {
3f65c585 112 .name = "buffer-size",
129aa440
JA
113 .has_arg = required_argument,
114 .flag = NULL,
115 .val = 'b'
116 },
117 {
3f65c585 118 .name = "num-sub-buffers",
129aa440
JA
119 .has_arg = required_argument,
120 .flag = NULL,
121 .val = 'n'
122 },
d1d7f15f 123 {
3f65c585 124 .name = "output-dir",
d1d7f15f
JA
125 .has_arg = required_argument,
126 .flag = NULL,
127 .val = 'D'
128 },
8e86c98a
JA
129 {
130 .name = "listen",
131 .has_arg = no_argument,
132 .flag = NULL,
133 .val = 'l'
134 },
135 {
136 .name = "host",
137 .has_arg = required_argument,
138 .flag = NULL,
139 .val = 'h'
140 },
141 {
142 .name = "port",
143 .has_arg = required_argument,
144 .flag = NULL,
145 .val = 'p'
146 },
71ef8b7c
JA
147 {
148 .name = NULL,
149 }
d39c04ca
AB
150};
151
9db17354 152struct tip_subbuf {
9db17354 153 void *buf;
5be4bdaf
JA
154 unsigned int len;
155 unsigned int max_len;
9db17354
JA
156};
157
21f55651
JA
158#define FIFO_SIZE (1024) /* should be plenty big! */
159#define CL_SIZE (128) /* cache line, any bigger? */
160
161struct tip_subbuf_fifo {
162 int tail __attribute__((aligned(CL_SIZE)));
163 int head __attribute__((aligned(CL_SIZE)));
164 struct tip_subbuf *q[FIFO_SIZE];
165};
166
d0ca268b
JA
167struct thread_information {
168 int cpu;
169 pthread_t thread;
b9d4294e
JA
170
171 int fd;
a3e4d330 172 void *fd_buf;
b9d4294e
JA
173 char fn[MAXPATHLEN + 64];
174
007c233c
JA
175 FILE *ofile;
176 char *ofile_buffer;
9db17354 177 int ofile_stdout;
8e86c98a 178 int ofile_mmap;
007c233c 179
d0ca268b 180 unsigned long events_processed;
b7106311 181 unsigned long long data_read;
e7c9f3ff 182 struct device_information *device;
9db17354
JA
183
184 int exited;
185
b7106311
JA
186 /*
187 * piped fifo buffers
188 */
21f55651 189 struct tip_subbuf_fifo fifo;
7de86b12 190 struct tip_subbuf *leftover_ts;
b7106311
JA
191
192 /*
193 * mmap controlled output files
194 */
195 unsigned long long fs_size;
196 unsigned long long fs_max_size;
197 unsigned long fs_off;
198 void *fs_buf;
199 unsigned long fs_buf_len;
d0ca268b
JA
200};
201
e7c9f3ff
NS
202struct device_information {
203 int fd;
204 char *path;
205 char buts_name[32];
99c1f5ab 206 volatile int trace_started;
eb3c8108 207 unsigned long drop_count;
e7c9f3ff
NS
208 struct thread_information *threads;
209};
d0ca268b 210
e7c9f3ff 211static int ncpus;
d0ca268b 212static struct thread_information *thread_information;
e7c9f3ff
NS
213static int ndevs;
214static struct device_information *device_information;
215
216/* command line option globals */
217static char *relay_path;
d5396421 218static char *output_name;
d1d7f15f 219static char *output_dir;
5c86134e 220static int act_mask = ~0U;
bc39777c 221static int kill_running_trace;
eb3c8108
JA
222static unsigned long buf_size = BUF_SIZE;
223static unsigned long buf_nr = BUF_NR;
b7106311 224static unsigned int page_size;
d39c04ca 225
e7c9f3ff
NS
226#define is_done() (*(volatile int *)(&done))
227static volatile int done;
228
eb3c8108
JA
229#define is_trace_stopped() (*(volatile int *)(&trace_stopped))
230static volatile int trace_stopped;
231
232#define is_stat_shown() (*(volatile int *)(&stat_shown))
233static volatile int stat_shown;
a3e4d330 234
8e86c98a
JA
235int data_is_native = -1;
236
72ca8801
NS
237static void exit_trace(int status);
238
99c1f5ab
JA
239#define dip_tracing(dip) (*(volatile int *)(&(dip)->trace_started))
240#define dip_set_tracing(dip, v) ((dip)->trace_started = (v))
241
242#define __for_each_dip(__d, __i, __e) \
243 for (__i = 0, __d = device_information; __i < __e; __i++, __d++)
244
245#define for_each_dip(__d, __i) __for_each_dip(__d, __i, ndevs)
9db17354
JA
246#define for_each_tip(__d, __t, __j) \
247 for (__j = 0, __t = (__d)->threads; __j < ncpus; __j++, __t++)
99c1f5ab 248
8e86c98a
JA
249/*
250 * networking stuff follows. we include a magic number so we know whether
251 * to endianness convert or not
252 */
253struct blktrace_net_hdr {
254 u32 magic; /* same as trace magic */
22cd0c02 255 char buts_name[32]; /* trace name */
8e86c98a 256 u32 cpu; /* for which cpu */
22cd0c02 257 u32 max_cpus;
8e86c98a
JA
258 u32 len; /* length of following trace data */
259};
260
261#define TRACE_NET_PORT (8462)
262
263enum {
264 Net_none = 0,
265 Net_server,
266 Net_client,
267};
268
269/*
270 * network cmd line params
271 */
272static char hostname[MAXHOSTNAMELEN];
273static int net_port = TRACE_NET_PORT;
274static int net_mode = 0;
275
276static int net_in_fd = -1;
277static int net_out_fd = -1;
278
279static void handle_sigint(__attribute__((__unused__)) int sig)
280{
281 done = 1;
282}
283
eb3c8108
JA
284static int get_dropped_count(const char *buts_name)
285{
286 int fd;
287 char tmp[MAXPATHLEN + 64];
288
289 snprintf(tmp, sizeof(tmp), "%s/block/%s/dropped",
290 relay_path, buts_name);
291
292 fd = open(tmp, O_RDONLY);
293 if (fd < 0) {
294 /*
295 * this may be ok, if the kernel doesn't support dropped counts
296 */
297 if (errno == ENOENT)
298 return 0;
299
300 fprintf(stderr, "Couldn't open dropped file %s\n", tmp);
301 return -1;
302 }
303
304 if (read(fd, tmp, sizeof(tmp)) < 0) {
305 perror(tmp);
306 close(fd);
307 return -1;
308 }
309
310 close(fd);
311
312 return atoi(tmp);
313}
314
e7c9f3ff 315static int start_trace(struct device_information *dip)
d0ca268b
JA
316{
317 struct blk_user_trace_setup buts;
318
1f79c4a0 319 memset(&buts, 0, sizeof(buts));
129aa440
JA
320 buts.buf_size = buf_size;
321 buts.buf_nr = buf_nr;
d39c04ca 322 buts.act_mask = act_mask;
d0ca268b 323
ed71a31e
JA
324 if (ioctl(dip->fd, BLKTRACESETUP, &buts) < 0) {
325 perror("BLKTRACESETUP");
326 return 1;
327 }
328
329 if (ioctl(dip->fd, BLKTRACESTART) < 0) {
330 perror("BLKTRACESTART");
d0ca268b
JA
331 return 1;
332 }
333
e7c9f3ff 334 memcpy(dip->buts_name, buts.name, sizeof(dip->buts_name));
99c1f5ab 335 dip_set_tracing(dip, 1);
d0ca268b
JA
336 return 0;
337}
338
e7c9f3ff 339static void stop_trace(struct device_information *dip)
d0ca268b 340{
99c1f5ab
JA
341 if (dip_tracing(dip) || kill_running_trace) {
342 dip_set_tracing(dip, 0);
cf9208ea 343
ed71a31e
JA
344 if (ioctl(dip->fd, BLKTRACESTOP) < 0)
345 perror("BLKTRACESTOP");
346 if (ioctl(dip->fd, BLKTRACETEARDOWN) < 0)
347 perror("BLKTRACETEARDOWN");
cf9208ea 348
e7c9f3ff 349 close(dip->fd);
cf9208ea 350 dip->fd = -1;
707b0914 351 }
d0ca268b
JA
352}
353
e7c9f3ff
NS
354static void stop_all_traces(void)
355{
356 struct device_information *dip;
357 int i;
358
eb3c8108
JA
359 for_each_dip(dip, i) {
360 dip->drop_count = get_dropped_count(dip->buts_name);
e7c9f3ff 361 stop_trace(dip);
eb3c8108 362 }
e7c9f3ff
NS
363}
364
eb3c8108
JA
365static void wait_for_data(struct thread_information *tip)
366{
367 struct pollfd pfd = { .fd = tip->fd, .events = POLLIN };
368
9db17354 369 do {
b4aabcb3 370 poll(&pfd, 1, 100);
9db17354
JA
371 if (pfd.revents & POLLIN)
372 break;
373 if (tip->ofile_stdout)
374 break;
375 } while (!is_done());
eb3c8108
JA
376}
377
8e86c98a 378static int read_data_file(struct thread_information *tip, void *buf, int len)
d0ca268b 379{
ae9f71b3 380 int ret = 0;
bbabf03a 381
9db17354
JA
382 do {
383 wait_for_data(tip);
ae9f71b3 384
9db17354
JA
385 ret = read(tip->fd, buf, len);
386 if (!ret)
387 continue;
388 else if (ret > 0)
389 return ret;
390 else {
bbabf03a 391 if (errno != EAGAIN) {
a3e4d330
JA
392 perror(tip->fn);
393 fprintf(stderr,"Thread %d failed read of %s\n",
394 tip->cpu, tip->fn);
395 break;
396 }
9db17354 397 continue;
bbabf03a 398 }
9db17354 399 } while (!is_done());
8a43bac5 400
bbabf03a 401 return ret;
8e86c98a 402
8a43bac5
JA
403}
404
8e86c98a
JA
405static int read_data_net(struct thread_information *tip, void *buf, int len)
406{
407 unsigned int bytes_left = len;
408 int ret = 0;
409
410 do {
411 ret = recv(net_in_fd, buf, bytes_left, MSG_WAITALL);
412
413 if (!ret)
414 continue;
415 else if (ret < 0) {
416 if (errno != EAGAIN) {
417 perror(tip->fn);
418 fprintf(stderr, "server: failed read\n");
419 return 0;
420 }
421 continue;
422 } else {
423 buf += ret;
424 bytes_left -= ret;
425 }
426 } while (!is_done() && bytes_left);
427
410d7c62 428 return len - bytes_left;
8e86c98a
JA
429}
430
431static int read_data(struct thread_information *tip, void *buf, int len)
432{
410d7c62
JA
433 int ret;
434
8e86c98a 435 if (net_mode == Net_server)
410d7c62
JA
436 ret = read_data_net(tip, buf, len);
437 else
438 ret = read_data_file(tip, buf, len);
8e86c98a 439
410d7c62
JA
440 if (ret > 0)
441 tip->data_read += ret;
442
443 return ret;
8e86c98a
JA
444}
445
446static inline struct tip_subbuf *
447subbuf_fifo_dequeue(struct thread_information *tip)
a3e4d330 448{
21f55651
JA
449 const int head = tip->fifo.head;
450 const int next = (head + 1) & (FIFO_SIZE - 1);
451
452 if (head != tip->fifo.tail) {
453 struct tip_subbuf *ts = tip->fifo.q[head];
454
455 store_barrier();
456 tip->fifo.head = next;
457 return ts;
458 }
459
460 return NULL;
9db17354 461}
eb3c8108 462
21f55651
JA
463static inline int subbuf_fifo_queue(struct thread_information *tip,
464 struct tip_subbuf *ts)
9db17354 465{
21f55651
JA
466 const int tail = tip->fifo.tail;
467 const int next = (tail + 1) & (FIFO_SIZE - 1);
468
469 if (next != tip->fifo.head) {
470 tip->fifo.q[tail] = ts;
471 store_barrier();
472 tip->fifo.tail = next;
473 return 0;
474 }
475
476 fprintf(stderr, "fifo too small!\n");
477 return 1;
a3e4d330
JA
478}
479
b7106311
JA
480/*
481 * For file output, truncate and mmap the file appropriately
482 */
8e86c98a 483static int mmap_subbuf(struct thread_information *tip, unsigned int maxlen)
b7106311
JA
484{
485 int ofd = fileno(tip->ofile);
486 int ret;
487
488 /*
489 * extend file, if we have to. use chunks of 16 subbuffers.
490 */
491 if (tip->fs_off + buf_size > tip->fs_buf_len) {
492 if (tip->fs_buf) {
5975d309 493 munlock(tip->fs_buf, tip->fs_buf_len);
b7106311
JA
494 munmap(tip->fs_buf, tip->fs_buf_len);
495 tip->fs_buf = NULL;
496 }
497
498 tip->fs_off = tip->fs_size & (page_size - 1);
499 tip->fs_buf_len = (16 * buf_size) - tip->fs_off;
500 tip->fs_max_size += tip->fs_buf_len;
501
502 if (ftruncate(ofd, tip->fs_max_size) < 0) {
503 perror("ftruncate");
504 return -1;
505 }
506
507 tip->fs_buf = mmap(NULL, tip->fs_buf_len, PROT_WRITE,
508 MAP_SHARED, ofd, tip->fs_size - tip->fs_off);
509 if (tip->fs_buf == MAP_FAILED) {
510 perror("mmap");
511 return -1;
512 }
5975d309 513 mlock(tip->fs_buf, tip->fs_buf_len);
b7106311
JA
514 }
515
8e86c98a 516 ret = read_data(tip, tip->fs_buf + tip->fs_off, maxlen);
b7106311 517 if (ret >= 0) {
b7106311
JA
518 tip->fs_size += ret;
519 tip->fs_off += ret;
520 return 0;
521 }
522
523 return -1;
524}
525
526/*
22cd0c02 527 * Use the copy approach for pipes and network
b7106311 528 */
9db17354 529static int get_subbuf(struct thread_information *tip)
a3e4d330 530{
8e86c98a 531 struct tip_subbuf *ts = malloc(sizeof(*ts));
22cd0c02 532 int ret;
8e86c98a 533
22cd0c02
JA
534 ts->buf = malloc(buf_size);
535 ts->max_len = buf_size;
8e86c98a 536
22cd0c02
JA
537 ret = read_data(tip, ts->buf, ts->max_len);
538 if (ret > 0) {
539 ts->len = ret;
540 return subbuf_fifo_queue(tip, ts);
9db17354 541 }
a3e4d330 542
b7106311 543 return ret;
a3e4d330
JA
544}
545
9db17354 546static void close_thread(struct thread_information *tip)
a3e4d330 547{
9db17354
JA
548 if (tip->fd != -1)
549 close(tip->fd);
550 if (tip->ofile)
551 fclose(tip->ofile);
552 if (tip->ofile_buffer)
553 free(tip->ofile_buffer);
554 if (tip->fd_buf)
555 free(tip->fd_buf);
1c99bc21 556
9db17354
JA
557 tip->fd = -1;
558 tip->ofile = NULL;
559 tip->ofile_buffer = NULL;
560 tip->fd_buf = NULL;
a3e4d330
JA
561}
562
8e86c98a
JA
563static void tip_ftrunc_final(struct thread_information *tip)
564{
565 /*
566 * truncate to right size and cleanup mmap
567 */
568 if (tip->ofile_mmap) {
569 int ofd = fileno(tip->ofile);
570
571 if (tip->fs_buf)
572 munmap(tip->fs_buf, tip->fs_buf_len);
573
574 ftruncate(ofd, tip->fs_size);
575 }
576}
577
9db17354 578static void *thread_main(void *arg)
a3e4d330 579{
9db17354
JA
580 struct thread_information *tip = arg;
581 pid_t pid = getpid();
582 cpu_set_t cpu_mask;
a3e4d330 583
9db17354
JA
584 CPU_ZERO(&cpu_mask);
585 CPU_SET((tip->cpu), &cpu_mask);
a3e4d330 586
9db17354
JA
587 if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
588 perror("sched_setaffinity");
589 exit_trace(1);
590 }
a3e4d330 591
9db17354
JA
592 snprintf(tip->fn, sizeof(tip->fn), "%s/block/%s/trace%d",
593 relay_path, tip->device->buts_name, tip->cpu);
594 tip->fd = open(tip->fn, O_RDONLY);
595 if (tip->fd < 0) {
596 perror(tip->fn);
597 fprintf(stderr,"Thread %d failed open of %s\n", tip->cpu,
598 tip->fn);
599 exit_trace(1);
a3e4d330
JA
600 }
601
b7106311 602 while (!is_done()) {
8e86c98a
JA
603 if (tip->ofile_mmap && net_mode != Net_client) {
604 if (mmap_subbuf(tip, buf_size))
b7106311
JA
605 break;
606 } else {
8e86c98a 607 if (get_subbuf(tip))
b7106311
JA
608 break;
609 }
610 }
611
8e86c98a
JA
612 tip_ftrunc_final(tip);
613 tip->exited = 1;
614 return NULL;
615}
b7106311 616
8e86c98a
JA
617static int write_data_net(int fd, void *buf, unsigned int buf_len)
618{
619 unsigned int bytes_left = buf_len;
620 int ret;
b7106311 621
8e86c98a
JA
622 while (bytes_left) {
623 ret = send(fd, buf, bytes_left, 0);
624 if (ret < 0) {
625 perror("send");
626 return 1;
627 }
628
629 buf += ret;
630 bytes_left -= ret;
9db17354 631 }
a3e4d330 632
8e86c98a 633 return 0;
a3e4d330
JA
634}
635
8e86c98a
JA
636static int flush_subbuf_net(struct thread_information *tip,
637 struct tip_subbuf *ts)
638{
639 struct blktrace_net_hdr hdr;
8e86c98a
JA
640
641 hdr.magic = BLK_IO_TRACE_MAGIC;
22cd0c02 642 strcpy(hdr.buts_name, tip->device->buts_name);
8e86c98a 643 hdr.cpu = tip->cpu;
22cd0c02 644 hdr.max_cpus = ncpus;
8e86c98a
JA
645 hdr.len = ts->len;
646
647 if (write_data_net(net_out_fd, &hdr, sizeof(hdr)))
648 return 1;
649
22cd0c02
JA
650 if (write_data_net(net_out_fd, ts->buf, ts->len))
651 return 1;
8e86c98a 652
f0597a7e 653 free(ts->buf);
8e86c98a
JA
654 free(ts);
655 return 0;
656}
657
658static int write_data(struct thread_information *tip, void *buf,
659 unsigned int buf_len)
8a43bac5 660{
7126171a 661 int ret;
8a43bac5 662
6480258a
JA
663 if (!buf_len)
664 return 0;
665
7126171a
JA
666 while (1) {
667 ret = fwrite(buf, buf_len, 1, tip->ofile);
007c233c 668 if (ret == 1)
8a43bac5
JA
669 break;
670
db6fe5bc
JA
671 if (ret < 0) {
672 perror("write");
673 return 1;
8a43bac5 674 }
d0ca268b
JA
675 }
676
9db17354 677 if (tip->ofile_stdout)
7126171a
JA
678 fflush(tip->ofile);
679
8a43bac5
JA
680 return 0;
681}
682
8e86c98a
JA
683static int flush_subbuf_file(struct thread_information *tip,
684 struct tip_subbuf *ts)
8a43bac5 685{
9db17354
JA
686 unsigned int offset = 0;
687 struct blk_io_trace *t;
688 int pdu_len, events = 0;
8a43bac5 689
9db17354 690 /*
7de86b12 691 * surplus from last run
9db17354 692 */
7de86b12
AB
693 if (tip->leftover_ts) {
694 struct tip_subbuf *prev_ts = tip->leftover_ts;
695
9e8b753c 696 if (prev_ts->len + ts->len > prev_ts->max_len) {
7de86b12
AB
697 prev_ts->max_len += ts->len;
698 prev_ts->buf = realloc(prev_ts->buf, prev_ts->max_len);
699 }
700
9e8b753c 701 memcpy(prev_ts->buf + prev_ts->len, ts->buf, ts->len);
7de86b12
AB
702 prev_ts->len += ts->len;
703
704 free(ts->buf);
705 free(ts);
706
707 ts = prev_ts;
708 tip->leftover_ts = NULL;
9db17354 709 }
d0ca268b 710
9db17354
JA
711 while (offset + sizeof(*t) <= ts->len) {
712 t = ts->buf + offset;
3a9d6c13 713
9cfa6c2b
AB
714 if (verify_trace(t)) {
715 write_data(tip, ts->buf, offset);
9db17354 716 return -1;
9cfa6c2b 717 }
3a9d6c13 718
9db17354 719 pdu_len = t->pdu_len;
3a9d6c13 720
9db17354 721 if (offset + sizeof(*t) + pdu_len > ts->len)
3a9d6c13 722 break;
4b5db44a 723
9db17354
JA
724 offset += sizeof(*t) + pdu_len;
725 tip->events_processed++;
b7106311 726 tip->data_read += sizeof(*t) + pdu_len;
9db17354 727 events++;
3a9d6c13
JA
728 }
729
9cfa6c2b
AB
730 if (write_data(tip, ts->buf, offset))
731 return -1;
732
3a9d6c13 733 /*
9db17354 734 * leftover bytes, save them for next time
3a9d6c13 735 */
9db17354 736 if (offset != ts->len) {
7de86b12 737 tip->leftover_ts = ts;
9e8b753c
JA
738 ts->len -= offset;
739 memmove(ts->buf, ts->buf + offset, ts->len);
7de86b12
AB
740 } else {
741 free(ts->buf);
742 free(ts);
9db17354 743 }
4b5db44a 744
9db17354 745 return events;
4b5db44a
JA
746}
747
9db17354 748static int write_tip_events(struct thread_information *tip)
d5396421 749{
21f55651 750 struct tip_subbuf *ts = subbuf_fifo_dequeue(tip);
d5396421 751
8e86c98a
JA
752 if (ts) {
753 if (net_mode == Net_client)
754 return flush_subbuf_net(tip, ts);
755
756 return flush_subbuf_file(tip, ts);
757 }
91816d54 758
9db17354 759 return 0;
91816d54
JA
760}
761
9db17354
JA
762/*
763 * scans the tips we know and writes out the subbuffers we accumulate
764 */
765static void get_and_write_events(void)
d0ca268b 766{
9db17354
JA
767 struct device_information *dip;
768 struct thread_information *tip;
27223f19 769 int i, j, events, ret, tips_running;
d0ca268b 770
9db17354
JA
771 while (!is_done()) {
772 events = 0;
d0ca268b 773
9db17354
JA
774 for_each_dip(dip, i) {
775 for_each_tip(dip, tip, j) {
776 ret = write_tip_events(tip);
777 if (ret > 0)
778 events += ret;
779 }
780 }
d0ca268b 781
9db17354
JA
782 if (!events)
783 usleep(10);
d0ca268b
JA
784 }
785
a3e4d330 786 /*
9db17354 787 * reap stored events
a3e4d330 788 */
9db17354
JA
789 do {
790 events = 0;
27223f19 791 tips_running = 0;
9db17354
JA
792 for_each_dip(dip, i) {
793 for_each_tip(dip, tip, j) {
794 ret = write_tip_events(tip);
795 if (ret > 0)
796 events += ret;
27223f19 797 tips_running += !tip->exited;
9db17354 798 }
69e65a9e 799 }
9db17354 800 usleep(10);
27223f19 801 } while (events || tips_running);
d0ca268b
JA
802}
803
b7106311
JA
804static void wait_for_threads(void)
805{
806 /*
8e86c98a
JA
807 * for piped or network output, poll and fetch data for writeout.
808 * for files, we just wait around for trace threads to exit
b7106311 809 */
8e86c98a
JA
810 if ((output_name && !strcmp(output_name, "-")) ||
811 net_mode == Net_client)
b7106311
JA
812 get_and_write_events();
813 else {
814 struct device_information *dip;
815 struct thread_information *tip;
816 int i, j, tips_running;
817
818 do {
819 tips_running = 0;
820 usleep(1000);
821
822 for_each_dip(dip, i)
823 for_each_tip(dip, tip, j)
824 tips_running += !tip->exited;
825 } while (tips_running);
826 }
827}
828
8e86c98a
JA
829static void fill_ofname(char *dst, char *buts_name, int cpu)
830{
831 int len = 0;
832
833 if (output_dir)
834 len = sprintf(dst, "%s/", output_dir);
835
836 if (output_name)
837 sprintf(dst + len, "%s.blktrace.%d", output_name, cpu);
838 else
839 sprintf(dst + len, "%s.blktrace.%d", buts_name, cpu);
840}
841
e7c9f3ff 842static int start_threads(struct device_information *dip)
d0ca268b
JA
843{
844 struct thread_information *tip;
e7c9f3ff 845 int j, pipeline = output_name && !strcmp(output_name, "-");
8e86c98a 846 int mode, vbuf_size;
22cd0c02 847 char op[64];
d0ca268b 848
99c1f5ab 849 for_each_tip(dip, tip, j) {
e7c9f3ff
NS
850 tip->cpu = j;
851 tip->device = dip;
d0ca268b 852 tip->events_processed = 0;
21f55651 853 memset(&tip->fifo, 0, sizeof(tip->fifo));
9db17354 854 tip->leftover_ts = NULL;
d0ca268b 855
e7c9f3ff 856 if (pipeline) {
007c233c 857 tip->ofile = fdopen(STDOUT_FILENO, "w");
9db17354 858 tip->ofile_stdout = 1;
8e86c98a 859 tip->ofile_mmap = 0;
007c233c 860 mode = _IOLBF;
57e8a2ad 861 vbuf_size = 512;
d5396421 862 } else {
22cd0c02
JA
863 fill_ofname(op, dip->buts_name, tip->cpu);
864 tip->ofile = fopen(op, "w+");
9db17354 865 tip->ofile_stdout = 0;
8e86c98a 866 tip->ofile_mmap = 1;
007c233c 867 mode = _IOFBF;
57e8a2ad 868 vbuf_size = OFILE_BUF;
d5396421
JA
869 }
870
007c233c 871 if (tip->ofile == NULL) {
22cd0c02 872 perror(op);
e7c9f3ff 873 return 1;
d5396421
JA
874 }
875
57e8a2ad
TZ
876 tip->ofile_buffer = malloc(vbuf_size);
877 if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) {
007c233c
JA
878 perror("setvbuf");
879 close_thread(tip);
880 return 1;
881 }
882
9db17354 883 if (pthread_create(&tip->thread, NULL, thread_main, tip)) {
e7c9f3ff 884 perror("pthread_create");
007c233c 885 close_thread(tip);
e7c9f3ff 886 return 1;
d0ca268b
JA
887 }
888 }
889
e7c9f3ff 890 return 0;
d0ca268b
JA
891}
892
e7c9f3ff 893static void stop_threads(struct device_information *dip)
3aabcd89 894{
e7c9f3ff 895 struct thread_information *tip;
91816d54 896 unsigned long ret;
007c233c
JA
897 int i;
898
9db17354 899 for_each_tip(dip, tip, i) {
91816d54 900 (void) pthread_join(tip->thread, (void *) &ret);
9db17354
JA
901 close_thread(tip);
902 }
3aabcd89
JA
903}
904
e7c9f3ff 905static void stop_all_threads(void)
72ca8801 906{
e7c9f3ff 907 struct device_information *dip;
72ca8801
NS
908 int i;
909
99c1f5ab 910 for_each_dip(dip, i)
e7c9f3ff
NS
911 stop_threads(dip);
912}
913
914static void stop_all_tracing(void)
915{
916 struct device_information *dip;
91816d54 917 int i;
007c233c 918
91816d54 919 for_each_dip(dip, i)
e7c9f3ff 920 stop_trace(dip);
72ca8801
NS
921}
922
923static void exit_trace(int status)
924{
eb3c8108
JA
925 if (!is_trace_stopped()) {
926 trace_stopped = 1;
927 stop_all_threads();
928 stop_all_tracing();
929 }
930
72ca8801
NS
931 exit(status);
932}
933
e7c9f3ff
NS
934static int resize_devices(char *path)
935{
936 int size = (ndevs + 1) * sizeof(struct device_information);
937
938 device_information = realloc(device_information, size);
939 if (!device_information) {
940 fprintf(stderr, "Out of memory, device %s (%d)\n", path, size);
941 return 1;
942 }
943 device_information[ndevs].path = path;
944 ndevs++;
945 return 0;
946}
947
948static int open_devices(void)
d0ca268b 949{
e7c9f3ff 950 struct device_information *dip;
d0ca268b 951 int i;
d0ca268b 952
99c1f5ab 953 for_each_dip(dip, i) {
cf9208ea 954 dip->fd = open(dip->path, O_RDONLY | O_NONBLOCK);
e7c9f3ff
NS
955 if (dip->fd < 0) {
956 perror(dip->path);
957 return 1;
958 }
959 }
99c1f5ab 960
e7c9f3ff
NS
961 return 0;
962}
963
964static int start_devices(void)
965{
966 struct device_information *dip;
967 int i, j, size;
968
969 size = ncpus * sizeof(struct thread_information);
970 thread_information = malloc(size * ndevs);
971 if (!thread_information) {
972 fprintf(stderr, "Out of memory, threads (%d)\n", size * ndevs);
973 return 1;
974 }
d5396421 975
99c1f5ab 976 for_each_dip(dip, i) {
e7c9f3ff
NS
977 if (start_trace(dip)) {
978 close(dip->fd);
979 fprintf(stderr, "Failed to start trace on %s\n",
980 dip->path);
981 break;
982 }
983 }
99c1f5ab 984
e7c9f3ff 985 if (i != ndevs) {
99c1f5ab 986 __for_each_dip(dip, j, i)
e7c9f3ff 987 stop_trace(dip);
99c1f5ab 988
e7c9f3ff
NS
989 return 1;
990 }
991
99c1f5ab 992 for_each_dip(dip, i) {
e7c9f3ff
NS
993 dip->threads = thread_information + (i * ncpus);
994 if (start_threads(dip)) {
995 fprintf(stderr, "Failed to start worker threads\n");
996 break;
997 }
998 }
99c1f5ab 999
e7c9f3ff 1000 if (i != ndevs) {
99c1f5ab 1001 __for_each_dip(dip, j, i)
e7c9f3ff 1002 stop_threads(dip);
99c1f5ab 1003 for_each_dip(dip, i)
e7c9f3ff 1004 stop_trace(dip);
99c1f5ab 1005
e7c9f3ff 1006 return 1;
d0ca268b
JA
1007 }
1008
e7c9f3ff 1009 return 0;
d0ca268b
JA
1010}
1011
e7c9f3ff
NS
1012static void show_stats(void)
1013{
e7c9f3ff
NS
1014 struct device_information *dip;
1015 struct thread_information *tip;
b7106311 1016 unsigned long long events_processed, data_read;
eb3c8108 1017 unsigned long total_drops;
2f903295 1018 int i, j, no_stdout = 0;
eb3c8108
JA
1019
1020 if (is_stat_shown())
1021 return;
1022
2f903295
JA
1023 if (output_name && !strcmp(output_name, "-"))
1024 no_stdout = 1;
1025
eb3c8108 1026 stat_shown = 1;
428683db 1027
56070ea4 1028 total_drops = 0;
99c1f5ab 1029 for_each_dip(dip, i) {
2f903295 1030 if (!no_stdout)
56070ea4 1031 printf("Device: %s\n", dip->path);
e7c9f3ff 1032 events_processed = 0;
b7106311 1033 data_read = 0;
99c1f5ab 1034 for_each_tip(dip, tip, j) {
2f903295 1035 if (!no_stdout)
b7106311
JA
1036 printf(" CPU%3d: %20lu events, %8llu KiB data\n",
1037 tip->cpu, tip->events_processed,
1038 tip->data_read >> 10);
e7c9f3ff 1039 events_processed += tip->events_processed;
b7106311 1040 data_read += tip->data_read;
e7c9f3ff 1041 }
eb3c8108 1042 total_drops += dip->drop_count;
2f903295 1043 if (!no_stdout)
b7106311
JA
1044 printf(" Total: %20llu events (dropped %lu), %8llu KiB data\n",
1045 events_processed, dip->drop_count,
1046 data_read >> 10);
e7c9f3ff 1047 }
56070ea4
JA
1048
1049 if (total_drops)
1050 fprintf(stderr, "You have dropped events, consider using a larger buffer size (-b)\n");
e7c9f3ff 1051}
52724a0e 1052
22cd0c02 1053static struct device_information *net_get_dip(char *buts_name)
8e86c98a 1054{
22cd0c02 1055 struct device_information *dip;
8e86c98a
JA
1056 int i;
1057
22cd0c02
JA
1058 for (i = 0; i < ndevs; i++) {
1059 dip = &device_information[i];
8e86c98a 1060
22cd0c02
JA
1061 if (!strcmp(dip->buts_name, buts_name))
1062 return dip;
8e86c98a
JA
1063 }
1064
22cd0c02
JA
1065 device_information = realloc(device_information, (ndevs + 1) * sizeof(*dip));
1066 dip = &device_information[ndevs];
1067 strcpy(dip->buts_name, buts_name);
410d7c62 1068 strcpy(dip->path, buts_name);
22cd0c02
JA
1069 ndevs++;
1070 dip->threads = malloc(ncpus * sizeof(struct thread_information));
1071 memset(dip->threads, 0, ncpus * sizeof(struct thread_information));
1072
1073 /*
1074 * open all files
1075 */
1076 for (i = 0; i < ncpus; i++) {
1077 struct thread_information *tip = &dip->threads[i];
1078 char op[64];
8e86c98a 1079
22cd0c02
JA
1080 tip->cpu = i;
1081 tip->ofile_stdout = 0;
1082 tip->ofile_mmap = 1;
1083 tip->device = dip;
8e86c98a 1084
22cd0c02 1085 fill_ofname(op, dip->buts_name, tip->cpu);
8e86c98a 1086
22cd0c02
JA
1087 tip->ofile = fopen(op, "w+");
1088 if (!tip->ofile) {
1089 perror("fopen");
1090 return NULL;
1091 }
8e86c98a
JA
1092 }
1093
22cd0c02
JA
1094 return dip;
1095}
1096
1097static struct thread_information *net_get_tip(struct blktrace_net_hdr *bnh)
1098{
1099 struct device_information *dip;
1100
1101 ncpus = bnh->max_cpus;
1102 dip = net_get_dip(bnh->buts_name);
1103 return &dip->threads[bnh->cpu];
8e86c98a
JA
1104}
1105
1106static int net_get_header(struct blktrace_net_hdr *bnh)
1107{
1108 int fl = fcntl(net_in_fd, F_GETFL);
1109 int bytes_left, ret;
1110 void *p = bnh;
1111
1112 fcntl(net_in_fd, F_SETFL, fl | O_NONBLOCK);
1113 bytes_left = sizeof(*bnh);
1114 while (bytes_left && !is_done()) {
1115 ret = recv(net_in_fd, p, bytes_left, MSG_WAITALL);
1116 if (ret < 0) {
1117 if (errno != EAGAIN) {
1118 perror("recv header");
1119 return 1;
1120 }
1121 usleep(100);
1122 continue;
1123 } else if (!ret) {
1124 usleep(100);
1125 continue;
1126 } else {
1127 p += ret;
1128 bytes_left -= ret;
1129 }
1130 }
1131 fcntl(net_in_fd, F_SETFL, fl & ~O_NONBLOCK);
1132 return 0;
1133}
1134
1135static int net_server_loop(void)
1136{
1137 struct thread_information *tip;
1138 struct blktrace_net_hdr bnh;
1139
1140 if (net_get_header(&bnh))
1141 return 1;
1142
1143 if (data_is_native == -1 && check_data_endianness(bnh.magic)) {
1144 fprintf(stderr, "server: received data is bad\n");
1145 return 1;
1146 }
1147
1148 if (!data_is_native) {
1149 bnh.cpu = be32_to_cpu(bnh.cpu);
1150 bnh.len = be32_to_cpu(bnh.len);
1151 }
1152
1153 tip = net_get_tip(&bnh);
1154 if (!tip)
1155 return 1;
1156
1157 if (mmap_subbuf(tip, bnh.len))
1158 return 1;
1159
1160 return 0;
1161}
1162
1163/*
1164 * Start here when we are in server mode - just fetch data from the network
1165 * and dump to files
1166 */
1167static int net_server(void)
1168{
1169 struct sockaddr_in addr;
1170 socklen_t socklen;
22cd0c02 1171 int fd, opt, i, j;
8e86c98a
JA
1172
1173 fd = socket(AF_INET, SOCK_STREAM, 0);
1174 if (fd < 0) {
1175 perror("server: socket");
1176 return 1;
1177 }
1178
1179 opt = 1;
1180 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
1181 perror("setsockopt");
1182 return 1;
1183 }
1184
1185 memset(&addr, 0, sizeof(addr));
1186 addr.sin_family = AF_INET;
1187 addr.sin_addr.s_addr = htonl(INADDR_ANY);
1188 addr.sin_port = htons(net_port);
1189
1190 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
1191 perror("bind");
1192 return 1;
1193 }
1194
1195 if (listen(fd, 1) < 0) {
1196 perror("listen");
1197 return 1;
1198 }
1199
1200 printf("blktrace: waiting for incoming connection...\n");
1201
1202 socklen = sizeof(addr);
1203 net_in_fd = accept(fd, (struct sockaddr *) &addr, &socklen);
1204 if (net_in_fd < 0) {
1205 perror("accept");
1206 return 1;
1207 }
1208
1209 signal(SIGINT, handle_sigint);
1210 signal(SIGHUP, handle_sigint);
1211 signal(SIGTERM, handle_sigint);
1212 signal(SIGALRM, handle_sigint);
1213
1214 printf("blktrace: connected!\n");
1215
1216 while (!is_done()) {
1217 if (net_server_loop())
1218 break;
1219 }
1220
22cd0c02
JA
1221 for (i = 0; i < ndevs; i++) {
1222 struct device_information *dip = &device_information[i];
1223
1224 for (j = 0; j < ncpus; j++)
1225 tip_ftrunc_final(&dip->threads[j]);
1226 }
8e86c98a 1227
410d7c62 1228 show_stats();
8e86c98a
JA
1229 return 0;
1230}
1231
1232/*
1233 * Setup outgoing network connection where we will transmit data
1234 */
1235static int net_setup_client(void)
1236{
1237 struct sockaddr_in addr;
1238 int fd;
1239
1240 fd = socket(AF_INET, SOCK_STREAM, 0);
1241 if (fd < 0) {
1242 perror("client: socket");
1243 return 1;
1244 }
1245
1246 memset(&addr, 0, sizeof(addr));
1247 addr.sin_family = AF_INET;
1248 addr.sin_port = htons(net_port);
1249
1250 if (inet_aton(hostname, &addr.sin_addr) != 1) {
1251 struct hostent *hent = gethostbyname(hostname);
1252 if (!hent) {
1253 perror("gethostbyname");
1254 return 1;
1255 }
1256
1257 memcpy(&addr.sin_addr, hent->h_addr, 4);
1258 strcpy(hostname, hent->h_name);
1259 }
1260
1261 printf("blktrace: connecting to %s\n", hostname);
1262
1263 if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
1264 perror("client: connect");
1265 return 1;
1266 }
1267
1268 printf("blktrace: connected!\n");
1269 net_out_fd = fd;
1270 return 0;
1271}
1272
52724a0e
JA
1273static char usage_str[] = \
1274 "-d <dev> [ -r relay path ] [ -o <output> ] [-k ] [ -w time ]\n" \
1275 "[ -a action ] [ -A action mask ] [ -v ]\n\n" \
1276 "\t-d Use specified device. May also be given last after options\n" \
1277 "\t-r Path to mounted relayfs, defaults to /relay\n" \
1278 "\t-o File(s) to send output to\n" \
d1d7f15f 1279 "\t-D Directory to prepend to output file names\n" \
52724a0e
JA
1280 "\t-k Kill a running trace\n" \
1281 "\t-w Stop after defined time, in seconds\n" \
1282 "\t-a Only trace specified actions. See documentation\n" \
1283 "\t-A Give trace mask as a single value. See documentation\n" \
129aa440
JA
1284 "\t-b Sub buffer size in KiB\n" \
1285 "\t-n Number of sub buffers\n" \
52724a0e
JA
1286 "\t-v Print program version info\n\n";
1287
ee1f4158
NS
1288static void show_usage(char *program)
1289{
52724a0e 1290 fprintf(stderr, "Usage: %s %s %s",program, blktrace_version, usage_str);
ee1f4158 1291}
d0ca268b
JA
1292
1293int main(int argc, char *argv[])
1294{
5270dddd 1295 static char default_relay_path[] = "/relay";
e3e74029 1296 struct statfs st;
d39c04ca 1297 int i, c;
ece238a6 1298 int stop_watch = 0;
d39c04ca
AB
1299 int act_mask_tmp = 0;
1300
1301 while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
1302 switch (c) {
1303 case 'a':
1304 i = find_mask_map(optarg);
1305 if (i < 0) {
ab197ca7 1306 fprintf(stderr,"Invalid action mask %s\n",
d39c04ca 1307 optarg);
7425d456 1308 return 1;
d39c04ca
AB
1309 }
1310 act_mask_tmp |= i;
1311 break;
1312
1313 case 'A':
98f8386b
AB
1314 if ((sscanf(optarg, "%x", &i) != 1) ||
1315 !valid_act_opt(i)) {
d39c04ca 1316 fprintf(stderr,
ab197ca7 1317 "Invalid set action mask %s/0x%x\n",
d39c04ca 1318 optarg, i);
7425d456 1319 return 1;
d39c04ca
AB
1320 }
1321 act_mask_tmp = i;
1322 break;
d0ca268b 1323
d39c04ca 1324 case 'd':
e7c9f3ff
NS
1325 if (resize_devices(optarg) != 0)
1326 return 1;
d39c04ca
AB
1327 break;
1328
5270dddd
JA
1329 case 'r':
1330 relay_path = optarg;
1331 break;
1332
d5396421 1333 case 'o':
66efebf8 1334 output_name = optarg;
d5396421 1335 break;
bc39777c
JA
1336 case 'k':
1337 kill_running_trace = 1;
1338 break;
ece238a6
NS
1339 case 'w':
1340 stop_watch = atoi(optarg);
1341 if (stop_watch <= 0) {
1342 fprintf(stderr,
1343 "Invalid stopwatch value (%d secs)\n",
1344 stop_watch);
1345 return 1;
1346 }
1347 break;
57ea8602 1348 case 'V':
52724a0e
JA
1349 printf("%s version %s\n", argv[0], blktrace_version);
1350 return 0;
129aa440 1351 case 'b':
eb3c8108 1352 buf_size = strtoul(optarg, NULL, 10);
183a0855 1353 if (buf_size <= 0 || buf_size > 16*1024) {
129aa440 1354 fprintf(stderr,
eb3c8108 1355 "Invalid buffer size (%lu)\n",buf_size);
129aa440
JA
1356 return 1;
1357 }
1358 buf_size <<= 10;
1359 break;
1360 case 'n':
eb3c8108 1361 buf_nr = strtoul(optarg, NULL, 10);
129aa440
JA
1362 if (buf_nr <= 0) {
1363 fprintf(stderr,
eb3c8108 1364 "Invalid buffer nr (%lu)\n", buf_nr);
129aa440
JA
1365 return 1;
1366 }
1367 break;
d1d7f15f
JA
1368 case 'D':
1369 output_dir = optarg;
1370 break;
8e86c98a
JA
1371 case 'h':
1372 net_mode = Net_client;
1373 strcpy(hostname, optarg);
1374 break;
1375 case 'l':
1376 net_mode = Net_server;
1377 break;
1378 case 'p':
1379 net_port = atoi(optarg);
1380 break;
d39c04ca 1381 default:
ee1f4158 1382 show_usage(argv[0]);
7425d456 1383 return 1;
d39c04ca
AB
1384 }
1385 }
1386
8e86c98a
JA
1387 setlocale(LC_NUMERIC, "en_US");
1388
1389 page_size = getpagesize();
1390
1391 if (net_mode == Net_server)
1392 return net_server();
1393
22cd0c02
JA
1394 while (optind < argc) {
1395 if (resize_devices(argv[optind++]) != 0)
1396 return 1;
1397 }
1398
e7c9f3ff 1399 if (ndevs == 0) {
ee1f4158 1400 show_usage(argv[0]);
7425d456 1401 return 1;
d39c04ca
AB
1402 }
1403
5270dddd
JA
1404 if (!relay_path)
1405 relay_path = default_relay_path;
1406
d5396421 1407 if (act_mask_tmp != 0)
d39c04ca 1408 act_mask = act_mask_tmp;
d0ca268b 1409
e3e74029
NS
1410 if (statfs(relay_path, &st) < 0) {
1411 perror("statfs");
1412 fprintf(stderr,"%s does not appear to be a valid path\n",
1413 relay_path);
1414 return 1;
64acacae 1415 } else if (st.f_type != (long) RELAYFS_TYPE) {
e3e74029 1416 fprintf(stderr,"%s does not appear to be a relay filesystem\n",
d0ca268b 1417 relay_path);
7425d456 1418 return 1;
d0ca268b
JA
1419 }
1420
e7c9f3ff 1421 if (open_devices() != 0)
7425d456 1422 return 1;
bc39777c
JA
1423
1424 if (kill_running_trace) {
e7c9f3ff 1425 stop_all_traces();
7425d456 1426 return 0;
bc39777c
JA
1427 }
1428
e7c9f3ff
NS
1429 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
1430 if (ncpus < 0) {
1431 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
7425d456 1432 return 1;
d0ca268b
JA
1433 }
1434
d0ca268b
JA
1435 signal(SIGINT, handle_sigint);
1436 signal(SIGHUP, handle_sigint);
1437 signal(SIGTERM, handle_sigint);
ece238a6 1438 signal(SIGALRM, handle_sigint);
d0ca268b 1439
8e86c98a
JA
1440 if (net_mode == Net_client && net_setup_client())
1441 return 1;
1442
1443 if (start_devices() != 0)
1444 return 1;
1445
e7c9f3ff 1446 atexit(stop_all_tracing);
830fd65c 1447
ece238a6
NS
1448 if (stop_watch)
1449 alarm(stop_watch);
1450
b7106311 1451 wait_for_threads();
d0ca268b 1452
eb3c8108
JA
1453 if (!is_trace_stopped()) {
1454 trace_stopped = 1;
91816d54
JA
1455 stop_all_threads();
1456 stop_all_traces();
91816d54 1457 }
d0ca268b 1458
eb3c8108
JA
1459 show_stats();
1460
d0ca268b
JA
1461 return 0;
1462}
1463