[PATCH] Misc error handling and style fixes
[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         (!strcasecmp((mmp)->short_form, (str)) ||                      \
45          !strcasecmp((mmp)->long_form, (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:o:k"
69 static 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 = "output",
96                 .has_arg = 1,
97                 .flag = NULL,
98                 .val = 'o'
99         },
100         {
101                 .name = "kill",
102                 .has_arg = 0,
103                 .flag = NULL,
104                 .val = 'k'
105         },
106         {
107                 .name = NULL,
108                 .has_arg = 0,
109                 .flag = NULL,
110                 .val = 0
111         }
112 };
113
114 struct thread_information {
115         int cpu;
116         pthread_t thread;
117
118         int fd;
119         char fn[MAXPATHLEN + 64];
120
121         pthread_mutex_t *fd_lock;
122         int ofd;
123
124         unsigned long events_processed;
125 };
126
127 static char *relay_path;
128
129 #define is_done()       (*(volatile int *)(&done))
130 static volatile int done;
131
132 static int devfd, ncpus;
133 static struct thread_information *thread_information;
134 static char *buts_name_p;
135 static char *dev;
136 static char *output_name;
137 static int act_mask = ~0U;
138 static int trace_started;
139 static int kill_running_trace;
140
141 static pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
142
143 static int find_mask_map(char *string)
144 {
145         int i;
146
147         for (i = 0; i < sizeof(mask_maps)/sizeof(mask_maps[0]); i++)
148                 if (COMPARE_MASK_MAP(&mask_maps[i], string))
149                         return mask_maps[i].mask;
150
151         return -1;
152 }
153
154 static int start_trace(char *dev)
155 {
156         struct blk_user_trace_setup buts;
157
158         memset(&buts, 0, sizeof(buts));
159         buts.buf_size = BUF_SIZE;
160         buts.buf_nr = BUF_NR;
161         buts.act_mask = act_mask;
162
163         if (ioctl(devfd, BLKSTARTTRACE, &buts) < 0) {
164                 perror("BLKSTARTTRACE");
165                 return 1;
166         }
167
168         trace_started = 1;
169         buts_name_p = strdup(buts.name);
170         return 0;
171 }
172
173 static void stop_trace(void)
174 {
175         if (trace_started || kill_running_trace) {
176                 if (ioctl(devfd, BLKSTOPTRACE) < 0)
177                         perror("BLKSTOPTRACE");
178
179                 trace_started = 0;
180         }
181 }
182
183 static void extract_data(struct thread_information *tip, char *ofn, int nb)
184 {
185         int ret, bytes_left;
186         unsigned char *buf, *p;
187
188         buf = malloc(nb);
189         p = buf;
190         bytes_left = nb;
191         while (bytes_left > 0) {
192                 ret = read(tip->fd, p, bytes_left);
193                 if (!ret)
194                         usleep(1000);
195                 else if (ret < 0) {
196                         perror(tip->fn);
197                         fprintf(stderr, "Thread %d extract_data %s failed\n",
198                                 tip->cpu, tip->fn);
199                         free(buf);
200                         exit(1);
201                 } else {
202                         p += ret;
203                         bytes_left -= ret;
204                 }
205         }
206
207         ret = write(tip->ofd, buf, nb);
208         if (ret != nb) {
209                 perror(ofn);
210                 fprintf(stderr,"Thread %d extract_data %s failed\n", tip->cpu, ofn);
211                 free(buf);
212                 exit(1);
213         }
214
215         free(buf);
216 }
217
218 static inline void tip_fd_unlock(struct thread_information *tip)
219 {
220         if (tip->fd_lock)
221                 pthread_mutex_unlock(tip->fd_lock);
222 }
223
224 static inline void tip_fd_lock(struct thread_information *tip)
225 {
226         if (tip->fd_lock)
227                 pthread_mutex_lock(tip->fd_lock);
228 }
229
230 static void *extract(void *arg)
231 {
232         struct thread_information *tip = arg;
233         int ret, pdu_len;
234         char dp[64];
235         struct blk_io_trace t;
236         pid_t pid = getpid();
237         cpu_set_t cpu_mask;
238
239         CPU_ZERO(&cpu_mask);
240         CPU_SET((tip->cpu), &cpu_mask);
241
242         if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
243                 perror("sched_setaffinity");
244                 exit(1);
245         }
246
247         snprintf(tip->fn, sizeof(tip->fn),
248                  "%s/block/%s/trace%d", relay_path, buts_name_p, tip->cpu);
249         tip->fd = open(tip->fn, O_RDONLY);
250         if (tip->fd < 0) {
251                 perror(tip->fn);
252                 fprintf(stderr,"Thread %d failed open of %s\n", tip->cpu,
253                         tip->fn);
254                 exit(1);
255         }
256
257         while (!is_done()) {
258                 ret = read(tip->fd, &t, sizeof(t));
259                 if (ret != sizeof(t)) {
260                         if (ret < 0) {
261                                 perror(tip->fn);
262                                 fprintf(stderr,"Thread %d failed read of %s\n",
263                                         tip->cpu, tip->fn);
264                                 exit(1);
265                         } else if (ret > 0) {
266                                 fprintf(stderr,"Thread %d misread %s %d,%d\n",
267                                         tip->cpu, tip->fn, ret, (int)sizeof(t));
268                                 exit(1);
269                         } else {
270                                 usleep(10000);
271                                 continue;
272                         }
273                 }
274
275                 if (verify_trace(&t))
276                         exit(1);
277
278                 pdu_len = t.pdu_len;
279
280                 trace_to_be(&t);
281
282                 tip_fd_lock(tip);
283
284                 ret = write(tip->ofd, &t, sizeof(t));
285                 if (ret < 0) {
286                         fprintf(stderr,"Thread %d failed write\n", tip->cpu);
287                         tip_fd_unlock(tip);
288                         exit(1);
289                 }
290
291                 if (pdu_len)
292                         extract_data(tip, dp, pdu_len);
293
294                 tip_fd_unlock(tip);
295
296                 tip->events_processed++;
297         }
298
299         return NULL;
300 }
301
302 static int start_threads(void)
303 {
304         struct thread_information *tip;
305         char op[64];
306         int i;
307
308         ncpus = sysconf(_SC_NPROCESSORS_ONLN);
309         if (ncpus < 0) {
310                 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
311                 return 0;
312         }
313
314         thread_information = malloc(ncpus * sizeof(struct thread_information));
315         for (i = 0, tip = thread_information; i < ncpus; i++, tip++) {
316                 tip->fd_lock = NULL;
317                 tip->cpu = i;
318                 tip->events_processed = 0;
319
320                 if (!strcmp(output_name, "-")) {
321                         tip->ofd = dup(STDOUT_FILENO);
322                         tip->fd_lock = &stdout_mutex;
323                 } else {
324                         sprintf(op, "%s_out.%d", output_name, tip->cpu);
325                         tip->ofd = open(op, O_CREAT|O_TRUNC|O_WRONLY, 0644);
326                 }
327
328                 if (tip->ofd < 0) {
329                         perror(op);
330                         return 0;
331                 }
332
333                 if (pthread_create(&tip->thread, NULL, extract, tip)) {
334                         perror( "pthread_create");
335                         return 0;
336                 }
337         }
338
339         return ncpus;
340 }
341
342 static void stop_threads(void)
343 {
344         struct thread_information *tip = thread_information;
345         int i;
346
347         for (i = 0; i < ncpus; i++, tip++) {
348                 int ret;
349
350                 if (pthread_join(tip->thread, (void *) &ret))
351                         perror("thread_join");
352
353                 close(tip->fd);
354                 close(tip->ofd);
355         }
356 }
357
358 static void show_stats(void)
359 {
360         int i;
361         struct thread_information *tip;
362         unsigned long events_processed = 0;
363
364         if (!strcmp(output_name, "-"))
365                 return;
366
367         for (i = 0, tip = thread_information; i < ncpus; i++, tip++) {
368                 printf("CPU%3d: %20ld events\n",
369                        tip->cpu, tip->events_processed);
370                 events_processed += tip->events_processed;
371         }
372
373         printf("Total:  %20ld events\n", events_processed);
374 }
375
376 static void handle_sigint(int sig)
377 {
378         done = 1;
379 }
380
381 int main(int argc, char *argv[])
382 {
383         static char default_relay_path[] = "/relay";
384         struct stat st;
385         int i, c;
386         int act_mask_tmp = 0;
387
388         while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
389                 switch (c) {
390                 case 'a':
391                         i = find_mask_map(optarg);
392                         if (i < 0) {
393                                 fprintf(stderr,"Invalid action mask %s\n", 
394                                         optarg);
395                                 return 4;
396                         }
397                         act_mask_tmp |= i;
398                         break;
399
400                 case 'A':
401                         if ((sscanf(optarg, "%x", &i) != 1) || !VALID_SET(i)) {
402                                 fprintf(stderr,
403                                         "Invalid set action mask %s/0x%x\n", 
404                                         optarg, i);
405                                 return 4;
406                         }
407                         act_mask_tmp = i;
408                         break;
409
410                 case 'd':
411                         dev = strdup(optarg);
412                         break;
413
414                 case 'r':
415                         relay_path = optarg;
416                         break;
417
418                 case 'o':
419                         output_name = strdup(optarg);
420                         break;
421                 case 'k':
422                         kill_running_trace = 1;
423                         break;
424
425                 default:
426                         fprintf(stderr,"Usage: %s -d <dev> "
427                                        "[-a <trace> [-a <trace>]]\n", argv[0]);
428                         return 4;
429                 }
430         }
431
432         if ((dev == NULL) || (optind < argc)) {
433                 fprintf(stderr,"Usage: %s -d <dev> "
434                                "[-a <trace> [-a <trace>]]\n", argv[0]);
435                 return 4;
436         }
437
438         if (!relay_path)
439                 relay_path = default_relay_path;
440
441         if (act_mask_tmp != 0)
442                 act_mask = act_mask_tmp;
443
444         if (stat(relay_path, &st) < 0) {
445                 fprintf(stderr,"%s does not appear to be mounted\n",
446                         relay_path);
447                 return 2;
448         }
449
450         devfd = open(dev, O_RDONLY);
451         if (devfd < 0) {
452                 perror(dev);
453                 return 3;
454         }
455
456         if (kill_running_trace) {
457                 stop_trace();
458                 exit(0);
459         }
460
461         if (start_trace(dev)) {
462                 close(devfd);
463                 fprintf(stderr, "Failed to start trace on %s\n", dev);
464                 return 4;
465         }
466
467         setlocale(LC_NUMERIC, "en_US");
468
469         if (!output_name)
470                 output_name = strdup(buts_name_p);
471
472         i = start_threads();
473         if (!i) {
474                 fprintf(stderr, "Failed to start worker threads\n");
475                 stop_trace();
476                 return 5;
477         }
478
479         signal(SIGINT, handle_sigint);
480         signal(SIGHUP, handle_sigint);
481         signal(SIGTERM, handle_sigint);
482
483         atexit(stop_trace);
484
485         while (!is_done())
486                 sleep(1);
487
488         stop_threads();
489         stop_trace();
490         show_stats();
491         close(devfd);
492
493         return 0;
494 }
495