[PATCH] Stop threads before stopping trace
[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>
d0ca268b
JA
31#include <stdio.h>
32#include <stdlib.h>
33#include <sched.h>
d39c04ca
AB
34#include <ctype.h>
35#include <getopt.h>
d0ca268b
JA
36
37#include "blktrace.h"
38
39#define BUF_SIZE (128 *1024)
40#define BUF_NR (4)
41
d39c04ca
AB
42#define DECLARE_MASK_MAP(mask) { BLK_TC_##mask, #mask, "BLK_TC_"#mask }
43#define COMPARE_MASK_MAP(mmp, str) \
44 (!strcmp(mmp->short_form, toupper(str)) || \
45 !strcmp(mmp->long_form, toupper(str)))
46
47#define VALID_SET(x) ((1 <= (x)) && ((x) < (1 << BLK_TC_SHIFT)))
48
49struct mask_map {
50 int mask;
51 char *short_form;
52 char *long_form;
53};
54
55struct mask_map mask_maps[] = {
56 DECLARE_MASK_MAP( READ ),
57 DECLARE_MASK_MAP( WRITE ),
58 DECLARE_MASK_MAP( BARRIER ),
59 DECLARE_MASK_MAP( SYNC ),
60 DECLARE_MASK_MAP( QUEUE ),
61 DECLARE_MASK_MAP( REQUEUE ),
62 DECLARE_MASK_MAP( ISSUE ),
63 DECLARE_MASK_MAP( COMPLETE ),
64 DECLARE_MASK_MAP( FS ),
65 DECLARE_MASK_MAP( PC ),
66};
67
5270dddd 68#define S_OPTS "d:a:A:r:"
d39c04ca
AB
69struct option l_opts[] = {
70 {
71 .name = "dev",
72 .has_arg = 1,
73 .flag = NULL,
74 .val = 'd'
75 },
76 {
77 .name = "act-mask",
78 .has_arg = 1,
79 .flag = NULL,
80 .val = 'a'
81 },
82 {
83 .name = "set-mask",
84 .has_arg = 1,
85 .flag = NULL,
86 .val = 'A'
87 },
5270dddd
JA
88 {
89 .name = "relay",
90 .has_arg = 1,
91 .flag = NULL,
92 .val = 'r'
93 },
d39c04ca
AB
94 {
95 .name = NULL,
96 .has_arg = 0,
97 .flag = NULL,
98 .val = 0
99 }
100};
101
d0ca268b
JA
102struct thread_information {
103 int cpu;
104 pthread_t thread;
b9d4294e
JA
105
106 int fd;
107 char fn[MAXPATHLEN + 64];
108
d0ca268b
JA
109 unsigned long events_processed;
110};
111
5270dddd 112static char *relay_path;
d0ca268b
JA
113
114#define is_done() (*(volatile int *)(&done))
115static volatile int done;
116
117static int devfd, ncpus;
118static struct thread_information *thread_information;
119static char *buts_name_p;
d39c04ca
AB
120static char *dev;
121static int act_mask = ~0;
830fd65c 122static int trace_started;
d39c04ca
AB
123
124inline int compare_mask_map(struct mask_map *mmp, char *string)
125{
9cc38a45
NS
126 int i, result;
127 char *s, *ustring = strdup(string);
d39c04ca 128
9cc38a45
NS
129 for (i = 0, s = ustring; i < strlen(ustring); i++, s++)
130 *s = toupper(*s);
d39c04ca 131
9cc38a45
NS
132 result = !strcmp(mmp->short_form, ustring) ||
133 !strcmp(mmp->long_form, ustring);
134 free(ustring);
135 return result;
d39c04ca
AB
136}
137
138int find_mask_map(char *string)
139{
140 int i;
141
142 for (i = 0; i < sizeof(mask_maps)/sizeof(mask_maps[0]); i++)
143 if (compare_mask_map(&mask_maps[i], string))
144 return mask_maps[i].mask;
145 return -1;
146}
d0ca268b 147
3aabcd89 148static int start_trace(char *dev)
d0ca268b
JA
149{
150 struct blk_user_trace_setup buts;
151
152 devfd = open(dev, O_RDONLY);
153 if (devfd < 0) {
154 perror(dev);
155 return 1;
156 }
157
158 memset(&buts, sizeof(buts), 0);
159 buts.buf_size = BUF_SIZE;
160 buts.buf_nr = BUF_NR;
d39c04ca 161 buts.act_mask = act_mask;
d0ca268b
JA
162
163 printf("Starting trace on %s\n", dev);
164 if (ioctl(devfd, BLKSTARTTRACE, &buts) < 0) {
165 perror("BLKSTARTTRACE");
166 return 1;
167 }
168
830fd65c 169 trace_started = 1;
d0ca268b
JA
170 buts_name_p = strdup(buts.name);
171 return 0;
172}
173
3aabcd89 174static void stop_trace(void)
d0ca268b 175{
707b0914
JA
176 if (trace_started) {
177 if (ioctl(devfd, BLKSTOPTRACE) < 0)
178 perror("BLKSTOPTRACE");
d0ca268b 179
707b0914
JA
180 close(devfd);
181 trace_started = 0;
182 }
d0ca268b
JA
183}
184
b9d4294e
JA
185static void extract_data(struct thread_information *tip,
186 char *ofn, int ofd, int nb)
d0ca268b
JA
187{
188 int ret, bytes_left;
87b72777 189 unsigned char *buf, *p;
d0ca268b 190
87b72777 191 buf = malloc(nb);
d0ca268b
JA
192 p = buf;
193 bytes_left = nb;
194 while (bytes_left > 0) {
b9d4294e 195 ret = read(tip->fd, p, bytes_left);
3aabcd89
JA
196 if (!ret)
197 usleep(1000);
198 else if (ret < 0) {
b9d4294e 199 perror(tip->fn);
d0ca268b 200 fprintf(stderr, "Thread %d extract_data %s failed\n",
b9d4294e 201 tip->cpu, tip->fn);
87b72777 202 free(buf);
d0ca268b 203 exit(1);
3aabcd89 204 } else {
d0ca268b
JA
205 p += ret;
206 bytes_left -= ret;
207 }
208 }
209
210 ret = write(ofd, buf, nb);
211 if (ret != nb) {
212 perror(ofn);
b9d4294e 213 fprintf(stderr,"Thread %d extract_data %s failed\n", tip->cpu, ofn);
87b72777 214 free(buf);
d0ca268b
JA
215 exit(1);
216 }
87b72777
JA
217
218 free(buf);
d0ca268b
JA
219}
220
3aabcd89 221static void *extract(void *arg)
d0ca268b
JA
222{
223 struct thread_information *tip = arg;
b9d4294e
JA
224 int ret, ofd, pdu_len;
225 char op[64], dp[64];
d0ca268b
JA
226 struct blk_io_trace t;
227 pid_t pid = getpid();
228 cpu_set_t cpu_mask;
229
230 CPU_ZERO(&cpu_mask);
b9d4294e 231 CPU_SET((tip->cpu), &cpu_mask);
d0ca268b
JA
232
233 if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
234 perror("sched_setaffinity");
235 exit(1);
236 }
237
238 sprintf(op, "%s_out.%d", buts_name_p, tip->cpu);
239 ofd = open(op, O_CREAT|O_TRUNC|O_WRONLY, 0644);
240 if (ofd < 0) {
241 perror(op);
242 fprintf(stderr,"Thread %d failed creat of %s\n", tip->cpu, op);
243 exit(1);
244 }
245
b9d4294e
JA
246 snprintf(tip->fn, sizeof(tip->fn),
247 "%s/block/%s/trace%d", relay_path, buts_name_p, tip->cpu);
248 tip->fd = open(tip->fn, O_RDONLY);
249 if (tip->fd < 0) {
250 perror(tip->fn);
251 fprintf(stderr,"Thread %d failed open of %s\n", tip->cpu, tip->fn);
d0ca268b
JA
252 exit(1);
253 }
254
255 while (!is_done()) {
b9d4294e 256 ret = read(tip->fd, &t, sizeof(t));
d0ca268b
JA
257 if (ret != sizeof(t)) {
258 if (ret < 0) {
b9d4294e 259 perror(tip->fn);
d0ca268b 260 fprintf(stderr,"Thread %d failed read of %s\n",
b9d4294e 261 tip->cpu, tip->fn);
d0ca268b
JA
262 exit(1);
263 } else if (ret > 0) {
8fc0abbc 264 fprintf(stderr,"Thread %d misread %s %d,%d\n",
b9d4294e 265 tip->cpu, tip->fn, ret, (int)sizeof(t));
d0ca268b
JA
266 exit(1);
267 } else {
268 usleep(10000);
269 continue;
270 }
271 }
272
273 if (verify_trace(&t))
274 exit(1);
275
18ada3d4
JA
276 pdu_len = t.pdu_len;
277
6fe4709e
JA
278 trace_to_be(&t);
279
d0ca268b
JA
280 ret = write(ofd, &t, sizeof(t));
281 if (ret < 0) {
282 perror(op);
283 fprintf(stderr,"Thread %d failed write of %s\n",
284 tip->cpu, op);
285 exit(1);
286 }
287
18ada3d4 288 if (pdu_len)
b9d4294e 289 extract_data(tip, dp, ofd, pdu_len);
87b72777 290
d0ca268b
JA
291 tip->events_processed++;
292 }
293
294 return NULL;
295}
296
3aabcd89 297static int start_threads(void)
d0ca268b
JA
298{
299 struct thread_information *tip;
300 int i;
301
302 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
303 if (ncpus < 0) {
304 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
305 return 1;
306 }
307 printf("Processors online: %d\n", ncpus);
308
309 thread_information = malloc(ncpus * sizeof(struct thread_information));
310 for (i = 0, tip = thread_information; i < ncpus; i++, tip++) {
311 tip->cpu = i;
312 tip->events_processed = 0;
313
314 if (pthread_create(&tip->thread, NULL, extract, tip)) {
315 perror( "pthread_create");
316 return 0;
317 }
318 }
319
320 return ncpus;
321}
322
3aabcd89
JA
323static void stop_threads(void)
324{
325 struct thread_information *tip = thread_information;
326 int i;
327
328 for (i = 0; i < ncpus; i++, tip++) {
329 int ret;
330
331 if (pthread_join(tip->thread, (void *) &ret))
332 perror("thread_join");
b9d4294e 333 close(tip->fd);
3aabcd89
JA
334 }
335}
336
d0ca268b
JA
337void show_stats(void)
338{
339 int i;
340 struct thread_information *tip;
341 unsigned long events_processed = 0;
342
343 for (i = 0, tip = thread_information; i < ncpus; i++, tip++) {
344 printf("CPU%3d: %20ld events\n",
345 tip->cpu, tip->events_processed);
346 events_processed += tip->events_processed;
347 }
348
349 printf("Total: %20ld events\n", events_processed);
350}
351
352void handle_sigint(int sig)
353{
354 printf("exiting on signal %d\n", sig);
355 done = 1;
356}
357
358int main(int argc, char *argv[])
359{
5270dddd 360 static char default_relay_path[] = "/relay";
d0ca268b 361 struct stat st;
d39c04ca
AB
362 int i, c;
363 int act_mask_tmp = 0;
364
365 while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
366 switch (c) {
367 case 'a':
368 i = find_mask_map(optarg);
369 if (i < 0) {
370 fprintf(stderr,"Invalid action mask %s\n",
371 optarg);
372 return 4;
373 }
374 act_mask_tmp |= i;
375 break;
376
377 case 'A':
378 if ((sscanf(optarg, "%x", &i) != 1) || !VALID_SET(i)) {
379 fprintf(stderr,
380 "Invalid set action mask %s/0x%x\n",
381 optarg, i);
382 return 4;
383 }
384 act_mask_tmp = i;
385 break;
d0ca268b 386
d39c04ca
AB
387 case 'd':
388 dev = strdup(optarg);
389 break;
390
5270dddd
JA
391 case 'r':
392 relay_path = optarg;
393 break;
394
d39c04ca
AB
395 default:
396 fprintf(stderr,"Usage: %s -d <dev> "
397 "[-a <trace> [-a <trace>]]\n", argv[0]);
398 return 4;
399 }
400 }
401
402 if ((dev == NULL) || (optind < argc)) {
403 fprintf(stderr,"Usage: %s -d <dev> "
404 "[-a <trace> [-a <trace>]]\n", argv[0]);
405 return 4;
406 }
407
5270dddd
JA
408 if (!relay_path)
409 relay_path = default_relay_path;
410
d39c04ca
AB
411 if (act_mask_tmp != 0) {
412 act_mask = act_mask_tmp;
413 printf("Tracing 0x%04x: ", act_mask);
414 for (i = 0; i < BLK_TC_SHIFT; i++)
415 if (act_mask & (1 << i))
416 printf("%s ", mask_maps[i].short_form);
417 printf("\n");
d0ca268b
JA
418 }
419
420 if (stat(relay_path, &st) < 0) {
d39c04ca 421 fprintf(stderr,"%s does not appear to be mounted\n",
d0ca268b
JA
422 relay_path);
423 return 2;
424 }
425
d39c04ca 426 if (start_trace(dev)) {
5270dddd 427 close(devfd);
d39c04ca 428 fprintf(stderr, "Failed to start trace on %s\n", dev);
d0ca268b
JA
429 return 3;
430 }
431
432 setlocale(LC_NUMERIC, "en_US");
433
434 i = start_threads();
435 if (!i) {
436 fprintf(stderr, "Failed to start worker threads\n");
437 stop_trace();
438 return 4;
439 }
440
441 printf("Threads started : %d\n", i);
442
443 signal(SIGINT, handle_sigint);
444 signal(SIGHUP, handle_sigint);
445 signal(SIGTERM, handle_sigint);
446
707b0914 447 atexit(stop_trace);
830fd65c 448
d0ca268b
JA
449 while (!is_done())
450 sleep(1);
451
b9d4294e 452 stop_threads();
3a9995b9 453 stop_trace();
d0ca268b
JA
454 show_stats();
455
456 return 0;
457}
458