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