[PATCH] Stop threads before stopping trace
[blktrace.git] / blktrace.c
1 /*
2  * block queue tracing application
3  *
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  *
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>
30 #include <sys/param.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <sched.h>
34 #include <ctype.h>
35 #include <getopt.h>
36
37 #include "blktrace.h"
38
39 #define BUF_SIZE        (128 *1024)
40 #define BUF_NR          (4)
41
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
49 struct mask_map {
50         int mask;
51         char *short_form;
52         char *long_form;
53 };
54
55 struct 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
68 #define S_OPTS  "d:a:A:r:"
69 struct 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         },
88         { 
89                 .name = "relay",
90                 .has_arg = 1,
91                 .flag = NULL,
92                 .val = 'r'
93         },
94         {
95                 .name = NULL,
96                 .has_arg = 0,
97                 .flag = NULL,
98                 .val = 0
99         }
100 };
101
102 struct thread_information {
103         int cpu;
104         pthread_t thread;
105
106         int fd;
107         char fn[MAXPATHLEN + 64];
108
109         unsigned long events_processed;
110 };
111
112 static char *relay_path;
113
114 #define is_done()       (*(volatile int *)(&done))
115 static volatile int done;
116
117 static int devfd, ncpus;
118 static struct thread_information *thread_information;
119 static char *buts_name_p;
120 static char *dev;
121 static int act_mask = ~0;
122 static int trace_started;
123
124 inline int compare_mask_map(struct mask_map *mmp, char *string)
125 {
126         int i, result;
127         char *s, *ustring = strdup(string);
128
129         for (i = 0, s = ustring; i < strlen(ustring); i++, s++)
130                 *s = toupper(*s);
131
132         result = !strcmp(mmp->short_form, ustring) ||
133                  !strcmp(mmp->long_form, ustring);
134         free(ustring);
135         return result;
136 }
137
138 int 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 }
147
148 static int start_trace(char *dev)
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;
161         buts.act_mask = act_mask;
162
163         printf("Starting trace on %s\n", dev);
164         if (ioctl(devfd, BLKSTARTTRACE, &buts) < 0) {
165                 perror("BLKSTARTTRACE");
166                 return 1;
167         }
168
169         trace_started = 1;
170         buts_name_p = strdup(buts.name);
171         return 0;
172 }
173
174 static void stop_trace(void)
175 {
176         if (trace_started) {
177                 if (ioctl(devfd, BLKSTOPTRACE) < 0)
178                         perror("BLKSTOPTRACE");
179
180                 close(devfd);
181                 trace_started = 0;
182         }
183 }
184
185 static void extract_data(struct thread_information *tip,
186                          char *ofn, int ofd, int nb)
187 {
188         int ret, bytes_left;
189         unsigned char *buf, *p;
190
191         buf = malloc(nb);
192         p = buf;
193         bytes_left = nb;
194         while (bytes_left > 0) {
195                 ret = read(tip->fd, p, bytes_left);
196                 if (!ret)
197                         usleep(1000);
198                 else if (ret < 0) {
199                         perror(tip->fn);
200                         fprintf(stderr, "Thread %d extract_data %s failed\n",
201                                 tip->cpu, tip->fn);
202                         free(buf);
203                         exit(1);
204                 } else {
205                         p += ret;
206                         bytes_left -= ret;
207                 }
208         }
209
210         ret = write(ofd, buf, nb);
211         if (ret != nb) {
212                 perror(ofn);
213                 fprintf(stderr,"Thread %d extract_data %s failed\n", tip->cpu, ofn);
214                 free(buf);
215                 exit(1);
216         }
217
218         free(buf);
219 }
220
221 static void *extract(void *arg)
222 {
223         struct thread_information *tip = arg;
224         int ret, ofd, pdu_len;
225         char op[64], dp[64];
226         struct blk_io_trace t;
227         pid_t pid = getpid();
228         cpu_set_t cpu_mask;
229
230         CPU_ZERO(&cpu_mask);
231         CPU_SET((tip->cpu), &cpu_mask);
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
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);
252                 exit(1);
253         }
254
255         while (!is_done()) {
256                 ret = read(tip->fd, &t, sizeof(t));
257                 if (ret != sizeof(t)) {
258                         if (ret < 0) {
259                                 perror(tip->fn);
260                                 fprintf(stderr,"Thread %d failed read of %s\n",
261                                         tip->cpu, tip->fn);
262                                 exit(1);
263                         } else if (ret > 0) {
264                                 fprintf(stderr,"Thread %d misread %s %d,%d\n",
265                                         tip->cpu, tip->fn, ret, (int)sizeof(t));
266                                 exit(1);
267                         } else {
268                                 usleep(10000);
269                                 continue;
270                         }
271                 }
272
273                 if (verify_trace(&t))
274                         exit(1);
275
276                 pdu_len = t.pdu_len;
277
278                 trace_to_be(&t);
279
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
288                 if (pdu_len)
289                         extract_data(tip, dp, ofd, pdu_len);
290
291                 tip->events_processed++;
292         }
293
294         return NULL;
295 }
296
297 static int start_threads(void)
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
323 static 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");
333                 close(tip->fd);
334         }
335 }
336
337 void 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
352 void handle_sigint(int sig)
353 {
354         printf("exiting on signal %d\n", sig);
355         done = 1;
356 }
357
358 int main(int argc, char *argv[])
359 {
360         static char default_relay_path[] = "/relay";
361         struct stat st;
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;
386
387                 case 'd':
388                         dev = strdup(optarg);
389                         break;
390
391                 case 'r':
392                         relay_path = optarg;
393                         break;
394
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
408         if (!relay_path)
409                 relay_path = default_relay_path;
410
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");
418         }
419
420         if (stat(relay_path, &st) < 0) {
421                 fprintf(stderr,"%s does not appear to be mounted\n",
422                         relay_path);
423                 return 2;
424         }
425
426         if (start_trace(dev)) {
427                 close(devfd);
428                 fprintf(stderr, "Failed to start trace on %s\n", dev);
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
447         atexit(stop_trace);
448
449         while (!is_done())
450                 sleep(1);
451
452         stop_threads();
453         stop_trace();
454         show_stats();
455
456         return 0;
457 }
458