Commit | Line | Data |
---|---|---|
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 | ||
35 | struct mask_map { | |
36 | int mask; | |
37 | char *short_form; | |
38 | char *long_form; | |
39 | }; | |
40 | ||
41 | struct 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:" | |
55 | struct 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 |
82 | struct thread_information { |
83 | int cpu; | |
84 | pthread_t thread; | |
85 | unsigned long events_processed; | |
86 | }; | |
87 | ||
88 | static char relay_path[] = "/relay/"; | |
89 | ||
90 | #define is_done() (*(volatile int *)(&done)) | |
91 | static volatile int done; | |
92 | ||
93 | static int devfd, ncpus; | |
94 | static struct thread_information *thread_information; | |
95 | static char *buts_name_p; | |
d39c04ca AB |
96 | static char *dev; |
97 | static int act_mask = ~0; | |
98 | ||
99 | inline 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 | ||
111 | int 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 | 121 | static 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 | 146 | static void stop_trace(void) |
d0ca268b JA |
147 | { |
148 | if (ioctl(devfd, BLKSTOPTRACE) < 0) | |
149 | perror("BLKSTOPTRACE"); | |
150 | ||
151 | close(devfd); | |
152 | } | |
153 | ||
3aabcd89 JA |
154 | static 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 | 190 | static 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 | 265 | static 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 |
291 | static 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 |
304 | void 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 | ||
319 | void handle_sigint(int sig) | |
320 | { | |
321 | printf("exiting on signal %d\n", sig); | |
322 | done = 1; | |
323 | } | |
324 | ||
325 | int 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 |