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