Commit | Line | Data |
---|---|---|
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 | ||
53 | struct mask_map { | |
54 | int mask; | |
55 | char *short_form; | |
56 | char *long_form; | |
57 | }; | |
58 | ||
59 | struct 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 | 73 | static 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 |
118 | struct 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 |
136 | struct 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 | 144 | static int ncpus; |
d0ca268b | 145 | static struct thread_information *thread_information; |
e7c9f3ff NS |
146 | static int ndevs; |
147 | static struct device_information *device_information; | |
148 | ||
149 | /* command line option globals */ | |
150 | static char *relay_path; | |
d5396421 | 151 | static char *output_name; |
5c86134e | 152 | static int act_mask = ~0U; |
bc39777c | 153 | static int kill_running_trace; |
8a43bac5 | 154 | static int use_mmap; |
d39c04ca | 155 | |
e7c9f3ff NS |
156 | #define is_done() (*(volatile int *)(&done)) |
157 | static volatile int done; | |
158 | ||
d5396421 JA |
159 | static pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER; |
160 | ||
72ca8801 NS |
161 | static void exit_trace(int status); |
162 | ||
1f79c4a0 | 163 | static 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 | 174 | static 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 | 193 | static 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 |
203 | static 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 | 212 | static 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 | ||
242 | static 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 | ||
276 | static 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 | ||
285 | static 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 |
298 | static inline void tip_fd_unlock(struct thread_information *tip) |
299 | { | |
300 | if (tip->fd_lock) | |
301 | pthread_mutex_unlock(tip->fd_lock); | |
302 | } | |
303 | ||
304 | static 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 | 310 | static 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 | 393 | static 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 |
434 | static 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 | 447 | static 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 | 460 | static 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 | ||
469 | static 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 | ||
482 | static void exit_trace(int status) | |
483 | { | |
e7c9f3ff | 484 | stop_all_tracing(); |
72ca8801 NS |
485 | exit(status); |
486 | } | |
487 | ||
e7c9f3ff NS |
488 | static 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 | ||
502 | static 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 | ||
517 | static 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 |
561 | static 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 |
583 | static 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 | 590 | static void handle_sigint(int sig) |
d0ca268b | 591 | { |
d0ca268b JA |
592 | done = 1; |
593 | } | |
594 | ||
595 | int 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 |