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