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