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