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