[PATCH] README: typo
[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>
d0ca268b
JA
32#include <stdio.h>
33#include <stdlib.h>
34#include <sched.h>
d39c04ca
AB
35#include <ctype.h>
36#include <getopt.h>
8a43bac5 37#include <sys/mman.h>
d0ca268b
JA
38
39#include "blktrace.h"
40
41#define BUF_SIZE (128 *1024)
42#define BUF_NR (4)
43
e3e74029
NS
44#define RELAYFS_TYPE 0xF0B4A981
45
d39c04ca
AB
46#define DECLARE_MASK_MAP(mask) { BLK_TC_##mask, #mask, "BLK_TC_"#mask }
47#define COMPARE_MASK_MAP(mmp, str) \
75da3c6a
NS
48 (!strcasecmp((mmp)->short_form, (str)) || \
49 !strcasecmp((mmp)->long_form, (str)))
d39c04ca
AB
50
51#define VALID_SET(x) ((1 <= (x)) && ((x) < (1 << BLK_TC_SHIFT)))
52
53struct mask_map {
54 int mask;
55 char *short_form;
56 char *long_form;
57};
58
59struct mask_map mask_maps[] = {
5c86134e
JA
60 DECLARE_MASK_MAP(READ),
61 DECLARE_MASK_MAP(WRITE),
62 DECLARE_MASK_MAP(BARRIER),
63 DECLARE_MASK_MAP(SYNC),
64 DECLARE_MASK_MAP(QUEUE),
65 DECLARE_MASK_MAP(REQUEUE),
66 DECLARE_MASK_MAP(ISSUE),
67 DECLARE_MASK_MAP(COMPLETE),
68 DECLARE_MASK_MAP(FS),
69 DECLARE_MASK_MAP(PC),
d39c04ca
AB
70};
71
ece238a6 72#define S_OPTS "d:a:A:r:o:kw:"
d5396421 73static struct option l_opts[] = {
5c86134e 74 {
d39c04ca 75 .name = "dev",
428683db 76 .has_arg = required_argument,
d39c04ca
AB
77 .flag = NULL,
78 .val = 'd'
79 },
5c86134e 80 {
d39c04ca 81 .name = "act-mask",
428683db 82 .has_arg = required_argument,
d39c04ca
AB
83 .flag = NULL,
84 .val = 'a'
85 },
5c86134e 86 {
d39c04ca 87 .name = "set-mask",
428683db 88 .has_arg = required_argument,
d39c04ca
AB
89 .flag = NULL,
90 .val = 'A'
91 },
5c86134e 92 {
5270dddd 93 .name = "relay",
428683db 94 .has_arg = required_argument,
5270dddd
JA
95 .flag = NULL,
96 .val = 'r'
97 },
d5396421
JA
98 {
99 .name = "output",
428683db 100 .has_arg = required_argument,
d5396421
JA
101 .flag = NULL,
102 .val = 'o'
103 },
bc39777c
JA
104 {
105 .name = "kill",
428683db 106 .has_arg = no_argument,
bc39777c
JA
107 .flag = NULL,
108 .val = 'k'
109 },
ece238a6
NS
110 {
111 .name = "stopwatch",
428683db 112 .has_arg = required_argument,
ece238a6
NS
113 .flag = NULL,
114 .val = 'w'
115 },
d39c04ca
AB
116};
117
d0ca268b
JA
118struct thread_information {
119 int cpu;
120 pthread_t thread;
b9d4294e
JA
121
122 int fd;
123 char fn[MAXPATHLEN + 64];
8a43bac5
JA
124 void *buf;
125 unsigned long buf_offset;
126 unsigned int buf_subbuf;
127 unsigned int sequence;
b9d4294e 128
d5396421
JA
129 pthread_mutex_t *fd_lock;
130 int ofd;
131
d0ca268b 132 unsigned long events_processed;
e7c9f3ff 133 struct device_information *device;
d0ca268b
JA
134};
135
e7c9f3ff
NS
136struct device_information {
137 int fd;
138 char *path;
139 char buts_name[32];
140 int trace_started;
141 struct thread_information *threads;
142};
d0ca268b 143
e7c9f3ff 144static int ncpus;
d0ca268b 145static struct thread_information *thread_information;
e7c9f3ff
NS
146static int ndevs;
147static struct device_information *device_information;
148
149/* command line option globals */
150static char *relay_path;
d5396421 151static char *output_name;
5c86134e 152static int act_mask = ~0U;
bc39777c 153static int kill_running_trace;
8a43bac5 154static int use_mmap;
d39c04ca 155
e7c9f3ff
NS
156#define is_done() (*(volatile int *)(&done))
157static volatile int done;
158
d5396421
JA
159static pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
160
72ca8801
NS
161static void exit_trace(int status);
162
1f79c4a0 163static int find_mask_map(char *string)
d39c04ca 164{
5c86134e
JA
165 int i;
166
167 for (i = 0; i < sizeof(mask_maps)/sizeof(mask_maps[0]); i++)
75da3c6a 168 if (COMPARE_MASK_MAP(&mask_maps[i], string))
5c86134e 169 return mask_maps[i].mask;
d39c04ca 170
d39c04ca
AB
171 return -1;
172}
d0ca268b 173
e7c9f3ff 174static int start_trace(struct device_information *dip)
d0ca268b
JA
175{
176 struct blk_user_trace_setup buts;
177
1f79c4a0 178 memset(&buts, 0, sizeof(buts));
d0ca268b
JA
179 buts.buf_size = BUF_SIZE;
180 buts.buf_nr = BUF_NR;
d39c04ca 181 buts.act_mask = act_mask;
d0ca268b 182
e7c9f3ff 183 if (ioctl(dip->fd, BLKSTARTTRACE, &buts) < 0) {
d0ca268b
JA
184 perror("BLKSTARTTRACE");
185 return 1;
186 }
187
e7c9f3ff
NS
188 memcpy(dip->buts_name, buts.name, sizeof(dip->buts_name));
189 dip->trace_started = 1;
d0ca268b
JA
190 return 0;
191}
192
e7c9f3ff 193static void stop_trace(struct device_information *dip)
d0ca268b 194{
e7c9f3ff
NS
195 if (dip->trace_started || kill_running_trace) {
196 if (ioctl(dip->fd, BLKSTOPTRACE) < 0)
707b0914 197 perror("BLKSTOPTRACE");
e7c9f3ff
NS
198 close(dip->fd);
199 dip->trace_started = 0;
707b0914 200 }
d0ca268b
JA
201}
202
e7c9f3ff
NS
203static void stop_all_traces(void)
204{
205 struct device_information *dip;
206 int i;
207
208 for (dip = device_information, i = 0; i < ndevs; i++, dip++)
209 stop_trace(dip);
210}
211
8a43bac5 212static int get_data_read(struct thread_information *tip, void *buf, int len)
d0ca268b 213{
8a43bac5
JA
214 char *p = buf;
215 int ret, bytes_left = len;
d0ca268b 216
8a43bac5 217 while (!is_done() && bytes_left > 0) {
b9d4294e 218 ret = read(tip->fd, p, bytes_left);
8a43bac5 219 if (ret == len)
3752a433 220 return 0;
8a43bac5
JA
221
222 if (ret < 0) {
b9d4294e 223 perror(tip->fn);
8a43bac5 224 fprintf(stderr,"Thread %d failed read of %s\n",
b9d4294e 225 tip->cpu, tip->fn);
76718bcd 226 exit_trace(1);
8a43bac5
JA
227 } else if (ret > 0) {
228 fprintf(stderr,"Thread %d misread %s %d,%d\n",
229 tip->cpu, tip->fn, ret, len);
230 exit_trace(1);
3aabcd89 231 } else {
d0ca268b
JA
232 p += ret;
233 bytes_left -= ret;
234 }
8a43bac5
JA
235
236 usleep(10000);
237 }
238
3752a433 239 return -1;
8a43bac5
JA
240}
241
242static int get_data_mmap(struct thread_information *tip, void *buf, int len,
243 int check_magic)
244{
245 if (len > (BUF_SIZE * (tip->buf_subbuf + 1)) - tip->buf_offset) {
246 tip->buf_subbuf++;
247 if (tip->buf_subbuf == BUF_NR)
248 tip->buf_subbuf = 0;
249
250 tip->buf_offset = tip->buf_subbuf * BUF_SIZE;
251 }
252
3752a433 253 while (1) {
8a43bac5
JA
254 struct blk_io_trace *t = buf;
255
256 memcpy(buf, tip->buf + tip->buf_offset, len);
257
258 if (!check_magic)
259 break;
260
261 if (CHECK_MAGIC(t) && t->sequence >= tip->sequence) {
262 tip->sequence = t->sequence;
263 break;
264 }
3752a433
JA
265
266 if (is_done())
267 return -1;
8a43bac5
JA
268
269 usleep(10000);
d0ca268b
JA
270 }
271
8a43bac5
JA
272 tip->buf_offset += len;
273 return 0;
274}
275
276static int get_data(struct thread_information *tip, void *buf, int len,
277 int check_magic)
278{
279 if (tip->buf)
280 return get_data_mmap(tip, buf, len, check_magic);
281 else
282 return get_data_read(tip, buf, len);
283}
284
285static void *extract_data(struct thread_information *tip, char *ofn, int nb)
286{
287 unsigned char *buf;
288
289 buf = malloc(nb);
290 if (!get_data(tip, buf, nb, 0))
291 return buf;
292
293 free(buf);
294 exit_trace(1);
295 return NULL;
d0ca268b
JA
296}
297
d5396421
JA
298static inline void tip_fd_unlock(struct thread_information *tip)
299{
300 if (tip->fd_lock)
301 pthread_mutex_unlock(tip->fd_lock);
302}
303
304static inline void tip_fd_lock(struct thread_information *tip)
305{
306 if (tip->fd_lock)
307 pthread_mutex_lock(tip->fd_lock);
308}
309
3aabcd89 310static void *extract(void *arg)
d0ca268b
JA
311{
312 struct thread_information *tip = arg;
d5396421 313 int ret, pdu_len;
69e65a9e 314 char dp[64], *pdu_data;
d0ca268b
JA
315 struct blk_io_trace t;
316 pid_t pid = getpid();
317 cpu_set_t cpu_mask;
318
319 CPU_ZERO(&cpu_mask);
b9d4294e 320 CPU_SET((tip->cpu), &cpu_mask);
d0ca268b
JA
321
322 if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
323 perror("sched_setaffinity");
76718bcd 324 exit_trace(1);
d0ca268b
JA
325 }
326
e7c9f3ff
NS
327 snprintf(tip->fn, sizeof(tip->fn), "%s/block/%s/trace%d",
328 relay_path, tip->device->buts_name, tip->cpu);
b9d4294e
JA
329 tip->fd = open(tip->fn, O_RDONLY);
330 if (tip->fd < 0) {
331 perror(tip->fn);
5c86134e
JA
332 fprintf(stderr,"Thread %d failed open of %s\n", tip->cpu,
333 tip->fn);
76718bcd 334 exit_trace(1);
d0ca268b
JA
335 }
336
8a43bac5
JA
337 if (use_mmap) {
338 tip->buf = mmap(NULL, BUF_SIZE * BUF_NR, PROT_READ,
339 MAP_PRIVATE | MAP_POPULATE, tip->fd, 0);
340 if (tip->buf == MAP_FAILED) {
341 perror("mmap");
342 exit_trace(1);
343 }
344 }
345
69e65a9e 346 pdu_data = NULL;
d0ca268b 347 while (!is_done()) {
8a43bac5
JA
348 if (get_data(tip, &t, sizeof(t), 1))
349 break;
d0ca268b
JA
350
351 if (verify_trace(&t))
76718bcd 352 exit_trace(1);
d0ca268b 353
18ada3d4
JA
354 pdu_len = t.pdu_len;
355
6fe4709e
JA
356 trace_to_be(&t);
357
69e65a9e
JA
358 if (pdu_len)
359 pdu_data = extract_data(tip, dp, pdu_len);
360
361 /*
362 * now we have both trace and payload, get a lock on the
363 * output descriptor and send it off
364 */
d5396421
JA
365 tip_fd_lock(tip);
366
367 ret = write(tip->ofd, &t, sizeof(t));
d0ca268b 368 if (ret < 0) {
d5396421
JA
369 fprintf(stderr,"Thread %d failed write\n", tip->cpu);
370 tip_fd_unlock(tip);
76718bcd 371 exit_trace(1);
d0ca268b
JA
372 }
373
69e65a9e
JA
374 if (pdu_data) {
375 ret = write(tip->ofd, pdu_data, pdu_len);
376 if (ret != pdu_len) {
377 perror("write pdu data");
8a43bac5 378 tip_fd_unlock(tip);
69e65a9e
JA
379 exit_trace(1);
380 }
d5396421 381
69e65a9e
JA
382 free(pdu_data);
383 pdu_data = NULL;
384 }
87b72777 385
69e65a9e 386 tip_fd_unlock(tip);
d0ca268b
JA
387 tip->events_processed++;
388 }
389
390 return NULL;
391}
392
e7c9f3ff 393static int start_threads(struct device_information *dip)
d0ca268b
JA
394{
395 struct thread_information *tip;
d5396421 396 char op[64];
e7c9f3ff 397 int j, pipeline = output_name && !strcmp(output_name, "-");
d0ca268b 398
e7c9f3ff
NS
399 for (tip = dip->threads, j = 0; j < ncpus; j++, tip++) {
400 tip->cpu = j;
401 tip->device = dip;
d5396421 402 tip->fd_lock = NULL;
d0ca268b
JA
403 tip->events_processed = 0;
404
e7c9f3ff 405 if (pipeline) {
1f79c4a0 406 tip->ofd = dup(STDOUT_FILENO);
d5396421
JA
407 tip->fd_lock = &stdout_mutex;
408 } else {
9f6486bd
JA
409 if (output_name) {
410 sprintf(op, "%s.blktrace.%d", output_name,
411 tip->cpu);
412 } else {
413 sprintf(op, "%s.blktrace.%d",
e7c9f3ff 414 dip->buts_name, tip->cpu);
9f6486bd 415 }
d5396421
JA
416 tip->ofd = open(op, O_CREAT|O_TRUNC|O_WRONLY, 0644);
417 }
418
419 if (tip->ofd < 0) {
420 perror(op);
e7c9f3ff 421 return 1;
d5396421
JA
422 }
423
d0ca268b 424 if (pthread_create(&tip->thread, NULL, extract, tip)) {
e7c9f3ff
NS
425 perror("pthread_create");
426 close(tip->ofd);
427 return 1;
d0ca268b
JA
428 }
429 }
430
e7c9f3ff 431 return 0;
d0ca268b
JA
432}
433
72ca8801
NS
434static void close_thread(struct thread_information *tip)
435{
8a43bac5
JA
436 if (tip->buf)
437 munmap(tip->buf, BUF_SIZE * BUF_NR);
438
72ca8801
NS
439 if (tip->fd != -1)
440 close(tip->fd);
441 if (tip->ofd != -1)
442 close(tip->ofd);
8a43bac5 443
72ca8801
NS
444 tip->fd = tip->ofd = -1;
445}
446
e7c9f3ff 447static void stop_threads(struct device_information *dip)
3aabcd89 448{
e7c9f3ff
NS
449 struct thread_information *tip;
450 long ret;
451 int j;
3aabcd89 452
e7c9f3ff 453 for (tip = dip->threads, j = 0; j < ncpus; j++, tip++) {
3aabcd89
JA
454 if (pthread_join(tip->thread, (void *) &ret))
455 perror("thread_join");
72ca8801 456 close_thread(tip);
3aabcd89
JA
457 }
458}
459
e7c9f3ff 460static void stop_all_threads(void)
72ca8801 461{
e7c9f3ff 462 struct device_information *dip;
72ca8801
NS
463 int i;
464
e7c9f3ff
NS
465 for (dip = device_information, i = 0; i < ndevs; i++, dip++)
466 stop_threads(dip);
467}
468
469static void stop_all_tracing(void)
470{
471 struct device_information *dip;
472 struct thread_information *tip;
473 int i, j;
474
475 for (dip = device_information, i = 0; i < ndevs; i++, dip++) {
476 for (tip = dip->threads, j = 0; j < ncpus; j++, tip++)
477 close_thread(tip);
478 stop_trace(dip);
479 }
72ca8801
NS
480}
481
482static void exit_trace(int status)
483{
e7c9f3ff 484 stop_all_tracing();
72ca8801
NS
485 exit(status);
486}
487
e7c9f3ff
NS
488static int resize_devices(char *path)
489{
490 int size = (ndevs + 1) * sizeof(struct device_information);
491
492 device_information = realloc(device_information, size);
493 if (!device_information) {
494 fprintf(stderr, "Out of memory, device %s (%d)\n", path, size);
495 return 1;
496 }
497 device_information[ndevs].path = path;
498 ndevs++;
499 return 0;
500}
501
502static int open_devices(void)
d0ca268b 503{
e7c9f3ff 504 struct device_information *dip;
d0ca268b 505 int i;
d0ca268b 506
e7c9f3ff
NS
507 for (dip = device_information, i = 0; i < ndevs; i++, dip++) {
508 dip->fd = open(dip->path, O_RDONLY);
509 if (dip->fd < 0) {
510 perror(dip->path);
511 return 1;
512 }
513 }
514 return 0;
515}
516
517static int start_devices(void)
518{
519 struct device_information *dip;
520 int i, j, size;
521
522 size = ncpus * sizeof(struct thread_information);
523 thread_information = malloc(size * ndevs);
524 if (!thread_information) {
525 fprintf(stderr, "Out of memory, threads (%d)\n", size * ndevs);
526 return 1;
527 }
d5396421 528
e7c9f3ff
NS
529 for (dip = device_information, i = 0; i < ndevs; i++, dip++) {
530 if (start_trace(dip)) {
531 close(dip->fd);
532 fprintf(stderr, "Failed to start trace on %s\n",
533 dip->path);
534 break;
535 }
536 }
537 if (i != ndevs) {
538 for (dip = device_information, j = 0; j < i; j++, dip++)
539 stop_trace(dip);
540 return 1;
541 }
542
543 for (dip = device_information, i = 0; i < ndevs; i++, dip++) {
544 dip->threads = thread_information + (i * ncpus);
545 if (start_threads(dip)) {
546 fprintf(stderr, "Failed to start worker threads\n");
547 break;
548 }
549 }
550 if (i != ndevs) {
551 for (dip = device_information, j = 0; j < i; j++, dip++)
552 stop_threads(dip);
553 for (dip = device_information, i = 0; i < ndevs; i++, dip++)
554 stop_trace(dip);
555 return 1;
d0ca268b
JA
556 }
557
e7c9f3ff 558 return 0;
d0ca268b
JA
559}
560
e7c9f3ff
NS
561static void show_stats(void)
562{
563 int i, j;
564 struct device_information *dip;
565 struct thread_information *tip;
566 unsigned long long events_processed;
428683db 567
e7c9f3ff
NS
568 if (output_name && !strcmp(output_name, "-"))
569 return;
570
571 for (dip = device_information, i = 0; i < ndevs; i++, dip++) {
572 printf("Device: %s\n", dip->path);
573 events_processed = 0;
574 for (tip = dip->threads, j = 0; j < ncpus; j++, tip++) {
575 printf(" CPU%3d: %20ld events\n",
576 tip->cpu, tip->events_processed);
577 events_processed += tip->events_processed;
578 }
579 printf(" Total: %20lld events\n", events_processed);
580 }
581}
582
ee1f4158
NS
583static void show_usage(char *program)
584{
585 fprintf(stderr,"Usage: %s [-d <dev>] "
586 "[-a <trace> [-a <trace>]] <dev>\n",
587 program);
588}
589
1f79c4a0 590static void handle_sigint(int sig)
d0ca268b 591{
d0ca268b
JA
592 done = 1;
593}
594
595int main(int argc, char *argv[])
596{
5270dddd 597 static char default_relay_path[] = "/relay";
e3e74029 598 struct statfs st;
d39c04ca 599 int i, c;
ece238a6 600 int stop_watch = 0;
d39c04ca
AB
601 int act_mask_tmp = 0;
602
603 while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
604 switch (c) {
605 case 'a':
606 i = find_mask_map(optarg);
607 if (i < 0) {
ab197ca7 608 fprintf(stderr,"Invalid action mask %s\n",
d39c04ca 609 optarg);
7425d456 610 return 1;
d39c04ca
AB
611 }
612 act_mask_tmp |= i;
613 break;
614
615 case 'A':
616 if ((sscanf(optarg, "%x", &i) != 1) || !VALID_SET(i)) {
617 fprintf(stderr,
ab197ca7 618 "Invalid set action mask %s/0x%x\n",
d39c04ca 619 optarg, i);
7425d456 620 return 1;
d39c04ca
AB
621 }
622 act_mask_tmp = i;
623 break;
d0ca268b 624
d39c04ca 625 case 'd':
e7c9f3ff
NS
626 if (resize_devices(optarg) != 0)
627 return 1;
d39c04ca
AB
628 break;
629
5270dddd
JA
630 case 'r':
631 relay_path = optarg;
632 break;
633
d5396421 634 case 'o':
66efebf8 635 output_name = optarg;
d5396421 636 break;
bc39777c
JA
637 case 'k':
638 kill_running_trace = 1;
639 break;
ece238a6
NS
640 case 'w':
641 stop_watch = atoi(optarg);
642 if (stop_watch <= 0) {
643 fprintf(stderr,
644 "Invalid stopwatch value (%d secs)\n",
645 stop_watch);
646 return 1;
647 }
648 break;
d5396421 649
d39c04ca 650 default:
ee1f4158 651 show_usage(argv[0]);
7425d456 652 return 1;
d39c04ca
AB
653 }
654 }
655
e7c9f3ff
NS
656 while (optind < argc) {
657 if (resize_devices(argv[optind++]) != 0)
658 return 1;
659 }
ee1f4158 660
e7c9f3ff 661 if (ndevs == 0) {
ee1f4158 662 show_usage(argv[0]);
7425d456 663 return 1;
d39c04ca
AB
664 }
665
5270dddd
JA
666 if (!relay_path)
667 relay_path = default_relay_path;
668
d5396421 669 if (act_mask_tmp != 0)
d39c04ca 670 act_mask = act_mask_tmp;
d0ca268b 671
e3e74029
NS
672 if (statfs(relay_path, &st) < 0) {
673 perror("statfs");
674 fprintf(stderr,"%s does not appear to be a valid path\n",
675 relay_path);
676 return 1;
677 } else if (st.f_type != RELAYFS_TYPE) {
678 fprintf(stderr,"%s does not appear to be a relay filesystem\n",
d0ca268b 679 relay_path);
7425d456 680 return 1;
d0ca268b
JA
681 }
682
e7c9f3ff 683 if (open_devices() != 0)
7425d456 684 return 1;
bc39777c
JA
685
686 if (kill_running_trace) {
e7c9f3ff 687 stop_all_traces();
7425d456 688 return 0;
bc39777c
JA
689 }
690
d0ca268b
JA
691 setlocale(LC_NUMERIC, "en_US");
692
e7c9f3ff
NS
693 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
694 if (ncpus < 0) {
695 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
7425d456 696 return 1;
d0ca268b
JA
697 }
698
e7c9f3ff
NS
699 if (start_devices() != 0)
700 return 1;
701
d0ca268b
JA
702 signal(SIGINT, handle_sigint);
703 signal(SIGHUP, handle_sigint);
704 signal(SIGTERM, handle_sigint);
ece238a6 705 signal(SIGALRM, handle_sigint);
d0ca268b 706
e7c9f3ff 707 atexit(stop_all_tracing);
830fd65c 708
ece238a6
NS
709 if (stop_watch)
710 alarm(stop_watch);
711
d0ca268b
JA
712 while (!is_done())
713 sleep(1);
714
e7c9f3ff
NS
715 stop_all_threads();
716 stop_all_traces();
d0ca268b
JA
717 show_stats();
718
719 return 0;
720}
721