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