Commit | Line | Data |
---|---|---|
71d5d4c9 JA |
1 | /* |
2 | * This file contains format parsing code for blkparse, allowing you to | |
3 | * customize the individual action format and generel output format. | |
4 | */ | |
5 | #include <stdio.h> | |
6 | #include <string.h> | |
7 | #include <stdlib.h> | |
8 | #include <unistd.h> | |
9 | #include <ctype.h> | |
10 | ||
11 | #include "blktrace.h" | |
12 | ||
a8f30e64 | 13 | #define VALID_SPECS "ABCDFGMPQRSTUWX" |
71d5d4c9 JA |
14 | |
15 | #define HEADER "%D %2c %8s %5T.%9t %5p %2a %3d " | |
16 | ||
17 | static char *override_format[256]; | |
18 | ||
19 | static inline int valid_spec(int spec) | |
20 | { | |
21 | return strchr(VALID_SPECS, spec) != NULL; | |
22 | } | |
23 | ||
e820abd7 | 24 | void set_all_format_specs(char *option) |
71d5d4c9 JA |
25 | { |
26 | char *p; | |
27 | ||
28 | for (p = VALID_SPECS; *p; p++) | |
29 | if (override_format[(int)(*p)] == NULL) | |
e820abd7 | 30 | override_format[(int)(*p)] = strdup(option); |
71d5d4c9 JA |
31 | } |
32 | ||
e820abd7 | 33 | int add_format_spec(char *option) |
71d5d4c9 JA |
34 | { |
35 | int spec = optarg[0]; | |
36 | ||
37 | if (!valid_spec(spec)) { | |
38 | fprintf(stderr,"Bad format specifier %c\n", spec); | |
39 | return 1; | |
40 | } | |
41 | if (optarg[1] != ',') { | |
e820abd7 | 42 | fprintf(stderr,"Bad format specifier - need ',' %s\n", option); |
71d5d4c9 JA |
43 | return 1; |
44 | } | |
e820abd7 JA |
45 | option += 2; |
46 | if (*option == '\0') { | |
47 | fprintf(stderr,"Bad format specifier - need fmt %s\n", option); | |
71d5d4c9 JA |
48 | return 1; |
49 | } | |
50 | ||
51 | /* | |
52 | * Set both merges (front and back) | |
53 | */ | |
54 | if (spec == 'M') { | |
e820abd7 JA |
55 | override_format['B'] = strdup(option); |
56 | override_format['M'] = strdup(option); | |
71d5d4c9 | 57 | } else |
e820abd7 | 58 | override_format[spec] = strdup(option); |
71d5d4c9 JA |
59 | |
60 | return 0; | |
61 | } | |
62 | ||
768277cc JA |
63 | static inline void fill_rwbs(char *rwbs, struct blk_io_trace *t) |
64 | { | |
65 | int w = t->action & BLK_TC_ACT(BLK_TC_WRITE); | |
66 | int b = t->action & BLK_TC_ACT(BLK_TC_BARRIER); | |
67 | int s = t->action & BLK_TC_ACT(BLK_TC_SYNC); | |
68 | int i = 0; | |
69 | ||
70 | if (w) | |
71 | rwbs[i++] = 'W'; | |
72 | else | |
73 | rwbs[i++] = 'R'; | |
74 | if (b) | |
75 | rwbs[i++] = 'B'; | |
76 | if (s) | |
77 | rwbs[i++] = 'S'; | |
78 | ||
79 | rwbs[i] = '\0'; | |
80 | } | |
81 | ||
63756b50 | 82 | static inline int pdu_rest_is_zero(unsigned char *pdu, int len) |
1d6376a5 | 83 | { |
63756b50 | 84 | static char zero[4096]; |
1d6376a5 | 85 | |
63756b50 | 86 | return !memcmp(pdu, zero, len); |
1d6376a5 JA |
87 | } |
88 | ||
768277cc JA |
89 | static char *dump_pdu(unsigned char *pdu_buf, int pdu_len) |
90 | { | |
91 | static char p[4096]; | |
92 | int i, len; | |
93 | ||
94 | if (!pdu_buf || !pdu_len) | |
95 | return NULL; | |
96 | ||
97 | for (len = 0, i = 0; i < pdu_len; i++) { | |
98 | if (i) | |
99 | len += sprintf(p + len, " "); | |
100 | ||
101 | len += sprintf(p + len, "%02x", pdu_buf[i]); | |
1d6376a5 JA |
102 | |
103 | /* | |
104 | * usually dump for cdb dumps where we can see lots of | |
105 | * zeroes, stop when the rest is just zeroes and indicate | |
106 | * so with a .. appended | |
107 | */ | |
108 | if (!pdu_buf[i] && pdu_rest_is_zero(pdu_buf + i, pdu_len - i)) { | |
109 | sprintf(p + len, " .."); | |
110 | break; | |
111 | } | |
768277cc JA |
112 | } |
113 | ||
114 | return p; | |
115 | } | |
116 | ||
a8f30e64 JA |
117 | #define pdu_start(t) (((void *) (t) + sizeof(struct blk_io_trace))) |
118 | ||
768277cc JA |
119 | static unsigned int get_pdu_int(struct blk_io_trace *t) |
120 | { | |
a8f30e64 | 121 | __u64 *val = pdu_start(t); |
768277cc JA |
122 | |
123 | return be64_to_cpu(*val); | |
124 | } | |
125 | ||
a8f30e64 JA |
126 | static void get_pdu_remap(struct blk_io_trace *t, struct blk_io_trace_remap *r) |
127 | { | |
128 | struct blk_io_trace_remap *__r = pdu_start(t); | |
663af819 | 129 | __u64 sector = __r->sector; |
a8f30e64 JA |
130 | |
131 | r->device = be32_to_cpu(__r->device); | |
663af819 | 132 | r->sector = be64_to_cpu(sector); |
a8f30e64 JA |
133 | } |
134 | ||
71d5d4c9 JA |
135 | static void print_field(char *act, struct per_cpu_info *pci, |
136 | struct blk_io_trace *t, unsigned long long elapsed, | |
137 | int pdu_len, unsigned char *pdu_buf, char field, | |
138 | int minus, int has_w, int width) | |
139 | { | |
140 | char format[64]; | |
141 | ||
142 | if (has_w) { | |
143 | if (minus) | |
144 | sprintf(format, "%%-%d", width); | |
145 | else | |
146 | sprintf(format, "%%%d", width); | |
147 | } else | |
148 | sprintf(format, "%%"); | |
149 | ||
150 | switch (field) { | |
151 | case 'a': | |
152 | fprintf(ofp, strcat(format, "s"), act); | |
153 | break; | |
154 | case 'c': | |
155 | fprintf(ofp, strcat(format, "d"), pci->cpu); | |
156 | break; | |
157 | case 'C': | |
158 | fprintf(ofp, strcat(format, "s"), t->comm); | |
159 | break; | |
160 | case 'd': { | |
161 | char rwbs[4]; | |
768277cc JA |
162 | |
163 | fill_rwbs(rwbs, t); | |
71d5d4c9 JA |
164 | fprintf(ofp, strcat(format, "s"), rwbs); |
165 | break; | |
166 | } | |
167 | case 'D': /* format width ignored */ | |
168 | fprintf(ofp,"%3d,%-3d", MAJOR(t->device), MINOR(t->device)); | |
169 | break; | |
170 | case 'e': | |
171 | fprintf(ofp, strcat(format, "d"), t->error); | |
172 | break; | |
173 | case 'M': | |
174 | fprintf(ofp, strcat(format, "d"), MAJOR(t->device)); | |
175 | break; | |
176 | case 'm': | |
177 | fprintf(ofp, strcat(format, "d"), MINOR(t->device)); | |
178 | break; | |
179 | case 'n': | |
ae957cbc | 180 | fprintf(ofp, strcat(format, "u"), t_sec(t)); |
71d5d4c9 | 181 | break; |
1c8ca7b5 JA |
182 | case 'N': |
183 | fprintf(ofp, strcat(format, "u"), t->bytes); | |
184 | break; | |
71d5d4c9 JA |
185 | case 'p': |
186 | fprintf(ofp, strcat(format, "u"), t->pid); | |
187 | break; | |
768277cc JA |
188 | case 'P': { /* format width ignored */ |
189 | char *p = dump_pdu(pdu_buf, pdu_len); | |
190 | if (p) | |
191 | fprintf(ofp, "%s", p); | |
71d5d4c9 | 192 | break; |
768277cc | 193 | } |
71d5d4c9 JA |
194 | case 's': |
195 | fprintf(ofp, strcat(format, "ld"), t->sequence); | |
196 | break; | |
197 | case 'S': | |
198 | fprintf(ofp, strcat(format, "lu"), t->sector); | |
199 | break; | |
200 | case 't': | |
201 | sprintf(format, "%%0%dlu", has_w ? width : 9); | |
202 | fprintf(ofp, format, NANO_SECONDS(t->time)); | |
203 | break; | |
204 | case 'T': | |
205 | fprintf(ofp, strcat(format, "d"), SECONDS(t->time)); | |
206 | break; | |
207 | case 'u': | |
208 | if (elapsed == -1ULL) { | |
209 | fprintf(stderr, "Expecting elapsed value\n"); | |
210 | exit(1); | |
211 | } | |
212 | fprintf(ofp, strcat(format, "llu"), elapsed / 1000); | |
213 | break; | |
214 | case 'U': { | |
768277cc | 215 | fprintf(ofp, strcat(format, "u"), get_pdu_int(t)); |
71d5d4c9 JA |
216 | break; |
217 | } | |
218 | default: | |
219 | fprintf(ofp,strcat(format, "c"), field); | |
220 | break; | |
221 | } | |
222 | } | |
223 | ||
768277cc JA |
224 | static char *parse_field(char *act, struct per_cpu_info *pci, |
225 | struct blk_io_trace *t, unsigned long long elapsed, | |
226 | int pdu_len, unsigned char *pdu_buf, | |
71d5d4c9 JA |
227 | char *master_format) |
228 | { | |
229 | int minus = 0; | |
230 | int has_w = 0; | |
231 | int width = 0; | |
232 | char *p = master_format; | |
233 | ||
234 | if (*p == '-') { | |
235 | minus = 1; | |
236 | p++; | |
237 | } | |
238 | if (isdigit(*p)) { | |
239 | has_w = 1; | |
240 | do { | |
241 | width = (width * 10) + (*p++ - '0'); | |
242 | } while ((*p) && (isdigit(*p))); | |
243 | } | |
244 | if (*p) { | |
245 | print_field(act, pci, t, elapsed, pdu_len, pdu_buf, *p++, | |
246 | minus, has_w, width); | |
247 | } | |
248 | return p; | |
249 | } | |
250 | ||
768277cc JA |
251 | static void process_default(char *act, struct per_cpu_info *pci, |
252 | struct blk_io_trace *t, unsigned long long elapsed, | |
253 | int pdu_len, unsigned char *pdu_buf) | |
71d5d4c9 | 254 | { |
768277cc | 255 | char rwbs[4]; |
71d5d4c9 | 256 | |
768277cc JA |
257 | fill_rwbs(rwbs, t); |
258 | ||
259 | /* | |
260 | * The header is always the same | |
261 | */ | |
262 | fprintf(ofp, "%3d,%-3d %2d %8d %5d.%09lu %5u %2s %3s ", | |
263 | MAJOR(t->device), MINOR(t->device), pci->cpu, t->sequence, | |
264 | (int) SECONDS(t->time), (unsigned long) NANO_SECONDS(t->time), | |
265 | t->pid, act, rwbs); | |
71d5d4c9 | 266 | |
768277cc | 267 | switch (act[0]) { |
80c53608 | 268 | case 'R': /* Requeue */ |
71d5d4c9 | 269 | case 'C': /* Complete */ |
768277cc JA |
270 | if (t->action & BLK_TC_ACT(BLK_TC_PC)) { |
271 | char *p = dump_pdu(pdu_buf, pdu_len); | |
272 | if (p) | |
273 | fprintf(ofp, "(%s) ", p); | |
274 | fprintf(ofp, "[%d]\n", t->error); | |
275 | } else { | |
71d5d4c9 | 276 | if (elapsed != -1ULL) { |
768277cc JA |
277 | fprintf(ofp, "%llu + %u (%8llu) [%d]\n", |
278 | (unsigned long long) t->sector, | |
ae957cbc | 279 | t_sec(t), elapsed, t->error); |
768277cc JA |
280 | } else { |
281 | fprintf(ofp, "%llu + %u [%d]\n", | |
282 | (unsigned long long) t->sector, | |
ae957cbc | 283 | t_sec(t), t->error); |
768277cc | 284 | } |
71d5d4c9 | 285 | } |
71d5d4c9 JA |
286 | break; |
287 | ||
288 | case 'D': /* Issue */ | |
71d5d4c9 | 289 | case 'I': /* Insert */ |
71d5d4c9 JA |
290 | case 'Q': /* Queue */ |
291 | case 'W': /* Bounce */ | |
768277cc JA |
292 | if (t->action & BLK_TC_ACT(BLK_TC_PC)) { |
293 | char *p; | |
1c8ca7b5 | 294 | fprintf(ofp, "%u ", t->bytes); |
768277cc JA |
295 | p = dump_pdu(pdu_buf, pdu_len); |
296 | if (p) | |
297 | fprintf(ofp, "(%s) ", p); | |
298 | fprintf(ofp, "[%s]\n", t->comm); | |
299 | } else { | |
300 | if (elapsed != -1ULL) { | |
301 | fprintf(ofp, "%llu + %u (%8llu) [%s]\n", | |
302 | (unsigned long long) t->sector, | |
ae957cbc | 303 | t_sec(t), elapsed, t->comm); |
768277cc JA |
304 | } else { |
305 | fprintf(ofp, "%llu + %u [%s]\n", | |
306 | (unsigned long long) t->sector, | |
ae957cbc | 307 | t_sec(t), t->comm); |
768277cc JA |
308 | } |
309 | } | |
71d5d4c9 JA |
310 | break; |
311 | ||
312 | case 'B': /* Back merge */ | |
313 | case 'F': /* Front merge */ | |
314 | case 'M': /* Front or back merge */ | |
768277cc JA |
315 | case 'G': /* Get request */ |
316 | case 'S': /* Sleep request */ | |
317 | fprintf(ofp, "%llu + %u [%s]\n", (unsigned long long) t->sector, | |
ae957cbc | 318 | t_sec(t), t->comm); |
71d5d4c9 JA |
319 | break; |
320 | ||
321 | case 'P': /* Plug */ | |
768277cc | 322 | fprintf(ofp, "[%s]\n", t->comm); |
71d5d4c9 JA |
323 | break; |
324 | ||
325 | case 'U': /* Unplug IO */ | |
a8f30e64 | 326 | case 'T': /* Unplug timer */ |
768277cc | 327 | fprintf(ofp, "[%s] %u\n", t->comm, get_pdu_int(t)); |
71d5d4c9 JA |
328 | break; |
329 | ||
a8f30e64 JA |
330 | case 'A': { /* remap */ |
331 | struct blk_io_trace_remap r; | |
332 | ||
333 | get_pdu_remap(t, &r); | |
334 | fprintf(ofp, "%llu + %u <- (%d,%d) %llu\n", | |
ae957cbc | 335 | (unsigned long long) r.sector, t_sec(t), |
a8f30e64 JA |
336 | MAJOR(r.device), MINOR(r.device), |
337 | (unsigned long long) t->sector); | |
338 | break; | |
339 | } | |
340 | ||
71d5d4c9 | 341 | case 'X': /* Split */ |
768277cc JA |
342 | fprintf(ofp, "%llu / %u [%s]\n", (unsigned long long) t->sector, |
343 | get_pdu_int(t), t->comm); | |
71d5d4c9 JA |
344 | break; |
345 | ||
346 | default: | |
768277cc JA |
347 | fprintf(stderr, "Unknown action %c\n", act[0]); |
348 | break; | |
71d5d4c9 JA |
349 | } |
350 | ||
71d5d4c9 JA |
351 | } |
352 | ||
353 | void process_fmt(char *act, struct per_cpu_info *pci, struct blk_io_trace *t, | |
354 | unsigned long long elapsed, int pdu_len, | |
355 | unsigned char *pdu_buf) | |
356 | { | |
768277cc JA |
357 | char *p = override_format[(int) *act]; |
358 | ||
359 | if (!p) { | |
360 | process_default(act, pci, t, elapsed, pdu_len, pdu_buf); | |
361 | return; | |
362 | } | |
71d5d4c9 JA |
363 | |
364 | while (*p) { | |
365 | switch (*p) { | |
366 | case '%': /* Field specifier */ | |
367 | p++; | |
368 | if (*p == '%') | |
369 | fprintf(ofp, "%c", *p++); | |
370 | else if (!*p) | |
371 | fprintf(ofp, "%c", '%'); | |
372 | else | |
373 | p = parse_field(act, pci, t, elapsed, | |
374 | pdu_len, pdu_buf, p); | |
375 | break; | |
376 | case '\\': { /* escape */ | |
377 | switch (p[1]) { | |
378 | case 'b': fprintf(ofp, "\b"); break; | |
379 | case 'n': fprintf(ofp, "\n"); break; | |
380 | case 'r': fprintf(ofp, "\r"); break; | |
381 | case 't': fprintf(ofp, "\t"); break; | |
382 | default: | |
383 | fprintf(stderr, | |
384 | "Invalid escape char in format %c\n", | |
385 | p[1]); | |
386 | exit(1); | |
387 | /*NOTREACHED*/ | |
388 | } | |
389 | p += 2; | |
390 | break; | |
391 | } | |
392 | default: | |
393 | fprintf(ofp, "%c", *p++); | |
394 | break; | |
395 | } | |
396 | } | |
397 | } | |
398 | ||
399 |