[PATCH] Add support for remap actions
[blktrace.git] / blkparse_fmt.c
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
13 #define VALID_SPECS     "ABCDFGMPQRSTUWX"
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
24 void set_all_format_specs(char *option)
25 {
26         char *p;
27
28         for (p = VALID_SPECS; *p; p++)
29                 if (override_format[(int)(*p)] == NULL)
30                         override_format[(int)(*p)] = strdup(option);
31 }
32
33 int add_format_spec(char *option)
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] != ',') {
42                 fprintf(stderr,"Bad format specifier - need ',' %s\n", option);
43                 return 1;
44         }
45         option += 2;
46         if (*option == '\0') {
47                 fprintf(stderr,"Bad format specifier - need fmt %s\n", option);
48                 return 1;
49         }
50
51         /*
52          * Set both merges (front and back)
53          */
54         if (spec == 'M') {
55                 override_format['B'] = strdup(option);
56                 override_format['M'] = strdup(option);
57         } else
58                 override_format[spec] = strdup(option);
59
60         return 0;
61 }
62
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
82 static char *dump_pdu(unsigned char *pdu_buf, int pdu_len)
83 {
84         static char p[4096];
85         int i, len;
86
87         if (!pdu_buf || !pdu_len)
88                 return NULL;
89
90         for (len = 0, i = 0; i < pdu_len; i++) {
91                 if (i)
92                         len += sprintf(p + len, " ");
93
94                 len += sprintf(p + len, "%02x", pdu_buf[i]);
95         }
96
97         return p;
98 }
99
100 #define pdu_start(t)    (((void *) (t) + sizeof(struct blk_io_trace)))
101
102 static unsigned int get_pdu_int(struct blk_io_trace *t)
103 {
104         __u64 *val = pdu_start(t);
105
106         return be64_to_cpu(*val);
107 }
108
109 static void get_pdu_remap(struct blk_io_trace *t, struct blk_io_trace_remap *r)
110 {
111         struct blk_io_trace_remap *__r = pdu_start(t);
112
113         r->device = be32_to_cpu(__r->device);
114         r->sector = be64_to_cpu(__r->sector);
115 }
116
117 static void print_field(char *act, struct per_cpu_info *pci,
118                         struct blk_io_trace *t, unsigned long long elapsed,
119                         int pdu_len, unsigned char *pdu_buf, char field,
120                         int minus, int has_w, int width)
121 {
122         char format[64];
123
124         if (has_w) {
125                 if (minus)
126                         sprintf(format, "%%-%d", width);
127                 else
128                         sprintf(format, "%%%d", width);
129         } else
130                 sprintf(format, "%%");
131
132         switch (field) {
133         case 'a':
134                 fprintf(ofp, strcat(format, "s"), act);
135                 break;
136         case 'c':
137                 fprintf(ofp, strcat(format, "d"), pci->cpu);
138                 break;
139         case 'C':
140                 fprintf(ofp, strcat(format, "s"), t->comm);
141                 break;
142         case 'd': {
143                 char rwbs[4];
144
145                 fill_rwbs(rwbs, t);
146                 fprintf(ofp, strcat(format, "s"), rwbs);
147                 break;
148         }
149         case 'D':       /* format width ignored */
150                 fprintf(ofp,"%3d,%-3d", MAJOR(t->device), MINOR(t->device));
151                 break;
152         case 'e':
153                 fprintf(ofp, strcat(format, "d"), t->error);
154                 break;
155         case 'M':
156                 fprintf(ofp, strcat(format, "d"), MAJOR(t->device));
157                 break;
158         case 'm':
159                 fprintf(ofp, strcat(format, "d"), MINOR(t->device));
160                 break;
161         case 'n':
162                 fprintf(ofp, strcat(format, "u"), t->bytes >> 9);
163                 break;
164         case 'N':
165                 fprintf(ofp, strcat(format, "u"), t->bytes);
166                 break;
167         case 'p':
168                 fprintf(ofp, strcat(format, "u"), t->pid);
169                 break;
170         case 'P': { /* format width ignored */
171                 char *p = dump_pdu(pdu_buf, pdu_len);
172                 if (p)
173                         fprintf(ofp, "%s", p);
174                 break;
175         }
176         case 's':
177                 fprintf(ofp, strcat(format, "ld"), t->sequence);
178                 break;
179         case 'S':
180                 fprintf(ofp, strcat(format, "lu"), t->sector);
181                 break;
182         case 't':
183                 sprintf(format, "%%0%dlu", has_w ? width : 9);
184                 fprintf(ofp, format, NANO_SECONDS(t->time));
185                 break;
186         case 'T':
187                 fprintf(ofp, strcat(format, "d"), SECONDS(t->time));
188                 break;
189         case 'u':
190                 if (elapsed == -1ULL) {
191                         fprintf(stderr, "Expecting elapsed value\n");
192                         exit(1);
193                 }
194                 fprintf(ofp, strcat(format, "llu"), elapsed / 1000);
195                 break;
196         case 'U': {
197                 fprintf(ofp, strcat(format, "u"), get_pdu_int(t));
198                 break;
199         }
200         default:
201                 fprintf(ofp,strcat(format, "c"), field);
202                 break;
203         }
204 }
205
206 static char *parse_field(char *act, struct per_cpu_info *pci,
207                          struct blk_io_trace *t, unsigned long long elapsed,
208                          int pdu_len, unsigned char *pdu_buf,
209                          char *master_format)
210 {
211         int minus = 0;
212         int has_w = 0;
213         int width = 0;
214         char *p = master_format;
215
216         if (*p == '-') {
217                 minus = 1;
218                 p++;
219         }
220         if (isdigit(*p)) {
221                 has_w = 1;
222                 do {
223                         width = (width * 10) + (*p++ - '0');
224                 } while ((*p) && (isdigit(*p)));
225         }
226         if (*p) {
227                 print_field(act, pci, t, elapsed, pdu_len, pdu_buf, *p++,
228                             minus, has_w, width);
229         }
230         return p;
231 }
232
233 static void process_default(char *act, struct per_cpu_info *pci,
234                             struct blk_io_trace *t, unsigned long long elapsed,
235                             int pdu_len, unsigned char *pdu_buf)
236 {
237         char rwbs[4];
238
239         fill_rwbs(rwbs, t);
240
241         /*
242          * The header is always the same
243          */
244         fprintf(ofp, "%3d,%-3d %2d %8d %5d.%09lu %5u %2s %3s ",
245                 MAJOR(t->device), MINOR(t->device), pci->cpu, t->sequence,
246                 (int) SECONDS(t->time), (unsigned long) NANO_SECONDS(t->time),
247                 t->pid, act, rwbs);
248
249         switch (act[0]) {
250         case 'C':       /* Complete */
251                 if (t->action & BLK_TC_ACT(BLK_TC_PC)) {
252                         char *p = dump_pdu(pdu_buf, pdu_len);
253                         if (p)
254                                 fprintf(ofp, "(%s) ", p);
255                         fprintf(ofp, "[%d]\n", t->error);
256                 } else {
257                         if (elapsed != -1ULL) {
258                                 fprintf(ofp, "%llu + %u (%8llu) [%d]\n",
259                                         (unsigned long long) t->sector,
260                                         t->bytes >> 9, elapsed, t->error);
261                         } else {
262                                 fprintf(ofp, "%llu + %u [%d]\n",
263                                         (unsigned long long) t->sector,
264                                         t->bytes >> 9, t->error);
265                         }
266                 }
267                 break;
268
269         case 'D':       /* Issue */
270         case 'I':       /* Insert */
271         case 'Q':       /* Queue */
272         case 'W':       /* Bounce */
273                 if (t->action & BLK_TC_ACT(BLK_TC_PC)) {
274                         char *p;
275                         fprintf(ofp, "%u ", t->bytes);
276                         p = dump_pdu(pdu_buf, pdu_len);
277                         if (p)
278                                 fprintf(ofp, "(%s) ", p);
279                         fprintf(ofp, "[%s]\n", t->comm);
280                 } else {
281                         if (elapsed != -1ULL) {
282                                 fprintf(ofp, "%llu + %u (%8llu) [%s]\n",
283                                         (unsigned long long) t->sector,
284                                         t->bytes >> 9, elapsed, t->comm);
285                         } else {
286                                 fprintf(ofp, "%llu + %u [%s]\n",
287                                         (unsigned long long) t->sector,
288                                         t->bytes >> 9, t->comm);
289                         }
290                 }
291                 break;
292
293         case 'B':       /* Back merge */
294         case 'F':       /* Front merge */
295         case 'M':       /* Front or back merge */
296         case 'G':       /* Get request */
297         case 'S':       /* Sleep request */
298                 fprintf(ofp, "%llu + %u [%s]\n", (unsigned long long) t->sector,
299                         t->bytes >> 9, t->comm);
300                 break;
301
302         case 'P':       /* Plug */
303                 fprintf(ofp, "[%s]\n", t->comm);
304                 break;
305
306         case 'U':       /* Unplug IO */
307         case 'T':       /* Unplug timer */
308                 fprintf(ofp, "[%s] %u\n", t->comm, get_pdu_int(t));
309                 break;
310
311         case 'A': {     /* remap */
312                 struct blk_io_trace_remap r;
313
314                 get_pdu_remap(t, &r);
315                 fprintf(ofp, "%llu + %u <- (%d,%d) %llu\n",
316                         (unsigned long long) r.sector, t->bytes >> 10,
317                         MAJOR(r.device), MINOR(r.device),
318                         (unsigned long long) t->sector);
319                 break;
320         }
321                 
322         case 'X':       /* Split */
323                 fprintf(ofp, "%llu / %u [%s]\n", (unsigned long long) t->sector,
324                         get_pdu_int(t), t->comm);
325                 break;
326
327         default:
328                 fprintf(stderr, "Unknown action %c\n", act[0]);
329                 break;
330         }
331
332 }
333
334 void process_fmt(char *act, struct per_cpu_info *pci, struct blk_io_trace *t,
335                  unsigned long long elapsed, int pdu_len,
336                  unsigned char *pdu_buf)
337 {
338         char *p = override_format[(int) *act];
339
340         if (!p) {
341                 process_default(act, pci, t, elapsed, pdu_len, pdu_buf);
342                 return;
343         }
344
345         while (*p) {
346                 switch (*p) {
347                 case '%':       /* Field specifier */
348                         p++;
349                         if (*p == '%')
350                                 fprintf(ofp, "%c", *p++);
351                         else if (!*p)
352                                 fprintf(ofp, "%c", '%');
353                         else
354                                 p = parse_field(act, pci, t, elapsed,
355                                                 pdu_len, pdu_buf, p);
356                         break;
357                 case '\\': {    /* escape */
358                         switch (p[1]) {
359                         case 'b': fprintf(ofp, "\b"); break;
360                         case 'n': fprintf(ofp, "\n"); break;
361                         case 'r': fprintf(ofp, "\r"); break;
362                         case 't': fprintf(ofp, "\t"); break;
363                         default:
364                                 fprintf(stderr, 
365                                         "Invalid escape char in format %c\n",
366                                         p[1]);
367                                 exit(1);
368                                 /*NOTREACHED*/
369                         }
370                         p += 2;
371                         break;
372                 }
373                 default:
374                         fprintf(ofp, "%c", *p++);
375                         break;
376                 }
377         }
378 }
379
380