Commit | Line | Data |
---|---|---|
d0ca268b JA |
1 | #include <sys/types.h> |
2 | #include <sys/stat.h> | |
3 | #include <unistd.h> | |
4 | #include <stdio.h> | |
5 | #include <fcntl.h> | |
6 | #include <stdlib.h> | |
8fc0abbc | 7 | #include <string.h> |
d0ca268b | 8 | |
8fc0abbc JA |
9 | #include "blktrace.h" |
10 | #include "rbtree.h" | |
d0ca268b | 11 | |
fd92da24 | 12 | #define MAX_CPUS (512) |
d0ca268b JA |
13 | |
14 | struct per_file_info { | |
15 | int cpu; | |
16 | int nelems; | |
d0ca268b JA |
17 | |
18 | int fd; | |
87b72777 | 19 | char fname[128]; |
d0ca268b | 20 | |
87b72777 JA |
21 | FILE *ofp; |
22 | char ofname[128]; | |
8fc0abbc | 23 | |
d0ca268b JA |
24 | unsigned long long start_time; |
25 | }; | |
26 | ||
8fc0abbc JA |
27 | static struct rb_root rb_root; |
28 | ||
29 | struct trace { | |
30 | struct blk_io_trace *bit; | |
66fa7233 | 31 | unsigned int cpu; |
8fc0abbc JA |
32 | struct rb_node rb_node; |
33 | }; | |
34 | ||
87b72777 JA |
35 | static struct per_file_info per_file_info[MAX_CPUS]; |
36 | static struct per_file_info *cur_file; | |
d0ca268b JA |
37 | |
38 | static unsigned long qreads, qwrites, creads, cwrites, mreads, mwrites; | |
39 | static unsigned long long qread_kb, qwrite_kb, cread_kb, cwrite_kb; | |
87b72777 JA |
40 | static unsigned long long events; |
41 | ||
42 | static int max_cpus; | |
d0ca268b JA |
43 | |
44 | static inline void account_m(int rw, unsigned int bytes) | |
45 | { | |
46 | if (rw) { | |
47 | mwrites++; | |
48 | qwrite_kb += bytes >> 10; | |
49 | } else { | |
50 | mreads++; | |
51 | qread_kb += bytes >> 10; | |
52 | } | |
53 | } | |
54 | ||
55 | static inline void account_q(int rw, unsigned int bytes) | |
56 | { | |
57 | if (rw) { | |
58 | qwrites++; | |
59 | qwrite_kb += bytes >> 10; | |
60 | } else { | |
61 | qreads++; | |
62 | qread_kb += bytes >> 10; | |
63 | } | |
64 | } | |
65 | ||
66 | static inline void account_c(int rw, unsigned int bytes) | |
67 | { | |
68 | if (rw) { | |
69 | cwrites++; | |
70 | cwrite_kb += bytes >> 10; | |
71 | } else { | |
72 | creads++; | |
73 | cread_kb += bytes >> 10; | |
74 | } | |
75 | } | |
76 | ||
3aabcd89 | 77 | static void output(char *s) |
d0ca268b JA |
78 | { |
79 | printf("%s", s); | |
3aabcd89 | 80 | fprintf(cur_file->ofp,"%s",s); |
d0ca268b JA |
81 | } |
82 | ||
3aabcd89 JA |
83 | static char hstring[256]; |
84 | static char tstring[256]; | |
d0ca268b | 85 | |
3aabcd89 | 86 | static inline char *setup_header(struct blk_io_trace *t, char act) |
d0ca268b JA |
87 | { |
88 | int w = t->action & BLK_TC_ACT(BLK_TC_WRITE); | |
89 | int b = t->action & BLK_TC_ACT(BLK_TC_BARRIER); | |
90 | int s = t->action & BLK_TC_ACT(BLK_TC_SYNC); | |
91 | char rwbs[4]; | |
92 | int i = 0; | |
93 | ||
94 | if (w) | |
95 | rwbs[i++] = 'W'; | |
96 | else | |
97 | rwbs[i++] = 'R'; | |
98 | if (b) | |
99 | rwbs[i++] = 'B'; | |
100 | if (s) | |
101 | rwbs[i++] = 'S'; | |
102 | ||
103 | rwbs[i] = '\0'; | |
104 | ||
3aabcd89 | 105 | sprintf(hstring, "%3d %15ld %12Lu %5u %c %3s", cur_file->cpu, |
d0ca268b JA |
106 | (unsigned long)t->sequence, (unsigned long long)t->time, t->pid, |
107 | act, rwbs); | |
108 | ||
109 | return hstring; | |
110 | } | |
111 | ||
3aabcd89 | 112 | static void log_complete(struct blk_io_trace *t, char act) |
d0ca268b JA |
113 | { |
114 | sprintf(tstring,"%s %Lu + %u [%d]\n", setup_header(t, act), | |
115 | (unsigned long long)t->sector, t->bytes >> 9, t->error); | |
116 | output(tstring); | |
117 | } | |
118 | ||
3aabcd89 | 119 | static void log_queue(struct blk_io_trace *t, char act) |
d0ca268b JA |
120 | { |
121 | sprintf(tstring,"%s %Lu + %u\n", setup_header(t, act), | |
122 | (unsigned long long)t->sector, t->bytes >> 9); | |
123 | output(tstring); | |
124 | } | |
125 | ||
3aabcd89 | 126 | static void log_issue(struct blk_io_trace *t, char act) |
d0ca268b JA |
127 | { |
128 | sprintf(tstring,"%s %Lu + %u\n", setup_header(t, act), | |
129 | (unsigned long long)t->sector, t->bytes >> 9); | |
130 | output(tstring); | |
131 | } | |
132 | ||
3aabcd89 | 133 | static void log_merge(struct blk_io_trace *t, char act) |
d0ca268b JA |
134 | { |
135 | sprintf(tstring,"%s %Lu + %u\n", setup_header(t, act), | |
136 | (unsigned long long)t->sector, t->bytes >> 9); | |
137 | output(tstring); | |
138 | } | |
139 | ||
3aabcd89 | 140 | static void log_generic(struct blk_io_trace *t, char act) |
d0ca268b JA |
141 | { |
142 | sprintf(tstring,"%s %Lu + %u\n", setup_header(t, act), | |
143 | (unsigned long long)t->sector, t->bytes >> 9); | |
144 | output(tstring); | |
145 | } | |
146 | ||
87b72777 | 147 | static int log_pc(struct blk_io_trace *t, char act) |
d0ca268b | 148 | { |
87b72777 JA |
149 | unsigned char *buf; |
150 | int i; | |
d0ca268b | 151 | |
87b72777 | 152 | sprintf(tstring,"%s ", setup_header(t, act)); |
d0ca268b JA |
153 | output(tstring); |
154 | ||
87b72777 | 155 | buf = (unsigned char *) t + sizeof(*t); |
d0ca268b JA |
156 | for (i = 0; i < t->pdu_len; i++) { |
157 | sprintf(tstring,"%02x ", buf[i]); | |
158 | output(tstring); | |
159 | } | |
160 | ||
161 | if (act == 'C') { | |
162 | sprintf(tstring,"[%d]", t->error); | |
163 | output(tstring); | |
164 | } | |
165 | ||
166 | printf("\n"); | |
87b72777 | 167 | return 0; |
d0ca268b JA |
168 | } |
169 | ||
87b72777 | 170 | static int dump_trace_pc(struct blk_io_trace *t) |
d0ca268b | 171 | { |
87b72777 JA |
172 | int ret = 0; |
173 | ||
d0ca268b JA |
174 | switch (t->action & 0xffff) { |
175 | case __BLK_TA_QUEUE: | |
176 | log_generic(t, 'Q'); | |
177 | break; | |
178 | case __BLK_TA_GETRQ: | |
179 | log_generic(t, 'G'); | |
180 | break; | |
181 | case __BLK_TA_SLEEPRQ: | |
182 | log_generic(t, 'S'); | |
183 | break; | |
184 | case __BLK_TA_REQUEUE: | |
185 | log_generic(t, 'R'); | |
186 | break; | |
187 | case __BLK_TA_ISSUE: | |
87b72777 | 188 | ret = log_pc(t, 'D'); |
d0ca268b JA |
189 | break; |
190 | case __BLK_TA_COMPLETE: | |
191 | log_pc(t, 'C'); | |
192 | break; | |
193 | default: | |
194 | fprintf(stderr, "Bad pc action %x\n", t->action); | |
87b72777 JA |
195 | ret = 1; |
196 | break; | |
d0ca268b JA |
197 | } |
198 | ||
87b72777 | 199 | return ret; |
d0ca268b JA |
200 | } |
201 | ||
3aabcd89 | 202 | static void dump_trace_fs(struct blk_io_trace *t) |
d0ca268b JA |
203 | { |
204 | int w = t->action & BLK_TC_ACT(BLK_TC_WRITE); | |
205 | ||
206 | switch (t->action & 0xffff) { | |
207 | case __BLK_TA_QUEUE: | |
208 | account_q(w, t->bytes); | |
209 | log_queue(t, 'Q'); | |
210 | break; | |
211 | case __BLK_TA_BACKMERGE: | |
212 | account_m(w, t->bytes); | |
213 | log_merge(t, 'M'); | |
214 | break; | |
215 | case __BLK_TA_FRONTMERGE: | |
216 | account_m(w, t->bytes); | |
217 | log_merge(t, 'F'); | |
218 | break; | |
219 | case __BLK_TA_GETRQ: | |
220 | log_generic(t, 'G'); | |
221 | break; | |
222 | case __BLK_TA_SLEEPRQ: | |
223 | log_generic(t, 'S'); | |
224 | break; | |
225 | case __BLK_TA_REQUEUE: | |
226 | log_queue(t, 'R'); | |
227 | break; | |
228 | case __BLK_TA_ISSUE: | |
229 | log_issue(t, 'D'); | |
230 | break; | |
231 | case __BLK_TA_COMPLETE: | |
232 | account_c(w, t->bytes); | |
233 | log_complete(t, 'C'); | |
234 | break; | |
235 | default: | |
236 | fprintf(stderr, "Bad fs action %x\n", t->action); | |
237 | return; | |
238 | } | |
d0ca268b JA |
239 | } |
240 | ||
87b72777 | 241 | static int dump_trace(struct blk_io_trace *t) |
d0ca268b | 242 | { |
87b72777 JA |
243 | int ret = 0; |
244 | ||
d0ca268b | 245 | if (t->action & BLK_TC_ACT(BLK_TC_PC)) |
87b72777 | 246 | ret = dump_trace_pc(t); |
d0ca268b JA |
247 | else |
248 | dump_trace_fs(t); | |
87b72777 JA |
249 | |
250 | events++; | |
251 | return ret; | |
d0ca268b JA |
252 | } |
253 | ||
3aabcd89 | 254 | static void show_stats(void) |
d0ca268b JA |
255 | { |
256 | printf("\nReads:"); | |
257 | printf("\tQueued: %'8lu, %'8LuKiB\n", qreads, qread_kb); | |
258 | printf("\tCompleted: %'8lu, %'8LuKiB\n", creads, cread_kb); | |
259 | printf("\tMerges: %'8lu\n", mreads); | |
260 | ||
261 | printf("Writes:"); | |
262 | printf("\tQueued: %'8lu, %'8LuKiB\n", qwrites, qwrite_kb); | |
263 | printf("\tCompleted: %'8lu, %'8LuKiB\n", cwrites, cwrite_kb); | |
264 | printf("\tMerges: %'8lu\n", mwrites); | |
265 | ||
266 | printf("Events: %'Lu\n", events); | |
d0ca268b JA |
267 | } |
268 | ||
3aabcd89 | 269 | static inline int trace_rb_insert(struct trace *t) |
8fc0abbc JA |
270 | { |
271 | struct rb_node **p = &rb_root.rb_node; | |
272 | struct rb_node *parent = NULL; | |
273 | struct trace *__t; | |
274 | ||
275 | while (*p) { | |
276 | parent = *p; | |
277 | __t = rb_entry(parent, struct trace, rb_node); | |
278 | ||
279 | if (t->bit->sequence < __t->bit->sequence) | |
280 | p = &(*p)->rb_left; | |
281 | else if (t->bit->sequence > __t->bit->sequence) | |
282 | p = &(*p)->rb_right; | |
283 | else { | |
87b72777 | 284 | fprintf(stderr, "sequence alias!\n"); |
8fc0abbc JA |
285 | return 1; |
286 | } | |
287 | } | |
288 | ||
289 | rb_link_node(&t->rb_node, parent, p); | |
290 | rb_insert_color(&t->rb_node, &rb_root); | |
291 | return 0; | |
292 | } | |
293 | ||
66fa7233 JA |
294 | static inline int verify_trace(struct blk_io_trace *t) |
295 | { | |
296 | if (!CHECK_MAGIC(t)) { | |
297 | fprintf(stderr, "bad trace magic %x\n", t->magic); | |
298 | return 1; | |
299 | } | |
300 | if ((t->magic & 0xff) != SUPPORTED_VERSION) { | |
301 | fprintf(stderr, "unsupported trace version %x\n", | |
302 | t->magic & 0xff); | |
303 | return 1; | |
304 | } | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
309 | static int sort_entries(void *traces, unsigned long offset, int cpu) | |
8fc0abbc JA |
310 | { |
311 | struct blk_io_trace *bit; | |
312 | struct trace *t; | |
313 | void *start = traces; | |
314 | int nelems = 0; | |
315 | ||
316 | memset(&rb_root, 0, sizeof(rb_root)); | |
317 | ||
318 | do { | |
319 | bit = traces; | |
320 | t = malloc(sizeof(*t)); | |
321 | t->bit = bit; | |
66fa7233 | 322 | t->cpu = cpu; |
8fc0abbc JA |
323 | memset(&t->rb_node, 0, sizeof(t->rb_node)); |
324 | ||
66fa7233 JA |
325 | if (verify_trace(bit)) |
326 | break; | |
327 | ||
8fc0abbc JA |
328 | if (trace_rb_insert(t)) |
329 | return -1; | |
330 | ||
331 | traces += sizeof(*bit) + bit->pdu_len; | |
332 | nelems++; | |
87b72777 | 333 | } while (traces < start + offset + sizeof(*bit)); |
8fc0abbc JA |
334 | |
335 | return nelems; | |
336 | } | |
337 | ||
3aabcd89 | 338 | static void show_entries(void) |
8fc0abbc | 339 | { |
8fc0abbc | 340 | struct blk_io_trace *bit; |
3aabcd89 | 341 | struct rb_node *n; |
8fc0abbc JA |
342 | struct trace *t; |
343 | int cpu; | |
344 | ||
3aabcd89 JA |
345 | n = rb_first(&rb_root); |
346 | if (!n) | |
347 | return; | |
8fc0abbc | 348 | |
3aabcd89 | 349 | do { |
8fc0abbc JA |
350 | t = rb_entry(n, struct trace, rb_node); |
351 | bit = t->bit; | |
352 | ||
66fa7233 | 353 | cpu = t->cpu; |
87b72777 | 354 | if (cpu > max_cpus) { |
8fc0abbc | 355 | fprintf(stderr, "CPU number too large (%d)\n", cpu); |
87b72777 | 356 | break; |
8fc0abbc JA |
357 | } |
358 | ||
3aabcd89 | 359 | cur_file = &per_file_info[cpu]; |
8fc0abbc JA |
360 | |
361 | /* | |
362 | * offset time by first trace event. | |
363 | * | |
364 | * NOTE: This is *cpu* relative, thus you can not | |
365 | * compare times ACROSS cpus. | |
366 | */ | |
3aabcd89 JA |
367 | if (cur_file->start_time == 0) |
368 | cur_file->start_time = bit->time; | |
8fc0abbc | 369 | |
3aabcd89 | 370 | bit->time -= cur_file->start_time; |
8fc0abbc | 371 | |
87b72777 JA |
372 | if (dump_trace(bit)) |
373 | break; | |
374 | ||
8fc0abbc JA |
375 | } while ((n = rb_next(n)) != NULL); |
376 | } | |
377 | ||
d0ca268b JA |
378 | int main(int argc, char *argv[]) |
379 | { | |
3aabcd89 JA |
380 | int i, nfiles, ret; |
381 | char *dev; | |
d0ca268b JA |
382 | |
383 | if (argc != 2) { | |
87b72777 | 384 | fprintf(stderr, "Usage: %s <dev>\n", argv[0]); |
d0ca268b JA |
385 | return 1; |
386 | } | |
387 | ||
388 | dev = argv[1]; | |
389 | ||
87b72777 JA |
390 | for (max_cpus = 0, i = 0, nfiles = 0; i < MAX_CPUS; i++) { |
391 | struct per_file_info *pfi = &per_file_info[i]; | |
392 | struct stat st; | |
393 | void *tb; | |
394 | ||
d0ca268b JA |
395 | pfi->cpu = i; |
396 | pfi->start_time = 0; | |
397 | ||
87b72777 JA |
398 | snprintf(pfi->fname, sizeof(pfi->fname)-1,"%s_out.%d", dev, i); |
399 | if (stat(pfi->fname, &st) < 0) | |
d0ca268b | 400 | break; |
d0ca268b | 401 | |
87b72777 | 402 | snprintf(pfi->ofname, sizeof(pfi->ofname)-1, "%s_log.%d", dev, i); |
d0ca268b JA |
403 | pfi->ofp = fopen(pfi->ofname, "w"); |
404 | if (pfi->ofp == NULL) { | |
405 | perror(pfi->ofname); | |
3aabcd89 | 406 | break; |
d0ca268b JA |
407 | } |
408 | ||
8fc0abbc JA |
409 | printf("Processing %s\n", pfi->fname); |
410 | ||
87b72777 | 411 | tb = malloc(st.st_size); |
8fc0abbc JA |
412 | |
413 | pfi->fd = open(pfi->fname, O_RDONLY); | |
414 | if (pfi->fd < 0) { | |
d0ca268b | 415 | perror(pfi->fname); |
3aabcd89 | 416 | break; |
d0ca268b | 417 | } |
87b72777 | 418 | if (read(pfi->fd, tb, st.st_size) != st.st_size) { |
8fc0abbc | 419 | fprintf(stderr, "error reading\n"); |
3aabcd89 | 420 | break; |
fd92da24 | 421 | } |
d0ca268b | 422 | |
66fa7233 | 423 | ret = sort_entries(tb, st.st_size, i); |
8fc0abbc | 424 | if (ret == -1) |
3aabcd89 | 425 | break; |
d0ca268b | 426 | |
8fc0abbc JA |
427 | close(pfi->fd); |
428 | nfiles++; | |
87b72777 | 429 | max_cpus++; |
8fc0abbc | 430 | pfi->nelems = ret; |
8fc0abbc | 431 | printf("\t%2d %10s %15d\n", i, pfi->fname, pfi->nelems); |
d0ca268b | 432 | |
d0ca268b JA |
433 | } |
434 | ||
3aabcd89 JA |
435 | if (nfiles) { |
436 | show_entries(); | |
437 | show_stats(); | |
438 | return 0; | |
439 | } | |
8fc0abbc | 440 | |
3aabcd89 JA |
441 | fprintf(stderr, "No files found\n"); |
442 | return 1; | |
d0ca268b | 443 | } |