88099040a6dcb24bf0c67c38303ef7263d4e9927
[fw/openocd] / contrib / itmdump.c
1 /*
2  * Copyright (C) 2010 by David Brownell
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or (at
7  * your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * Simple utility to parse and dump ARM Cortex-M3 SWO trace output.  Once the
20  * mechanisms work right, this information can be used for various purposes
21  * including profiling (particularly easy for flat PC-sample profiles) and
22  * for debugging.
23  *
24  * SWO is the Single Wire Output found on some ARM cores, most notably on the
25  * Cortex-M3.  It combines data from several sources:
26  *
27  *  - Software trace (ITM):  so-called "printf-style" application messaging
28  *    using "ITM stimulus ports"; and differential timestamps.
29  *  - Hardware trace (DWT):  for profiling counters and comparator matches.
30  *  - TPIU may issue sync packets.
31  *
32  * The trace data format is defined in Appendix E, "Debug ITM and DWT packet
33  * protocol", of the ARMv7-M Architecture Reference Manual (DDI 0403C).  It
34  * is a superset of the ITM data format from the Coresight TRM.
35  *
36  * The trace data has two encodings.  The working assumption is that data
37  * gets into this program using the UART encoding.
38  */
39
40 #include <errno.h>
41 #include <libgen.h>
42 #include <stdbool.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 unsigned int dump_swit;
49
50 /* Example ITM trace word (0xWWXXYYZZ) parsing for task events, sent
51  * on port 31 (Reserved for "the" RTOS in CMSIS v1.30)
52  *   WWXX: event code (0..3 pre-assigned, 4..15 reserved)
53  *   YY:   task priority
54  *   ZZ:   task number
55  *
56  * NOTE that this specific encoding could be space-optimized; and that
57  * trace data streams could also be history-sensitive.
58  */
59 static void show_task(int port, unsigned data)
60 {
61         unsigned code = data >> 16;
62         char buf[16];
63
64         if (dump_swit)
65                 return;
66
67         switch (code) {
68         case 0:
69                 strcpy(buf, "run");
70                 break;
71         case 1:
72                 strcpy(buf, "block");
73                 break;
74         case 2:
75                 strcpy(buf, "create");
76                 break;
77         case 3:
78                 strcpy(buf, "destroy");
79                 break;
80         /* 4..15 reserved for other infrastructure ops */
81         default:
82                 sprintf(buf, "code %d", code);
83                 break;
84         }
85         printf("TASK %d, pri %d: %s",
86                 (data >> 0) & 0xff,
87                 (data >> 8) & 0xff,
88                 buf);
89 }
90
91 static void show_reserved(FILE *f, char *label, int c)
92 {
93         unsigned i;
94
95         if (dump_swit)
96                 return;
97
98         printf("%s - %#02x", label, c);
99
100         for (i = 0; (c & 0x80) && i < 4; i++) {
101                 c = fgetc(f);
102                 if (c == EOF) {
103                         printf("(ERROR %d - %s) ", errno, strerror(errno));
104                         break;
105                 }
106                 printf(" %#02x", c);
107         }
108
109         printf("\n");
110 }
111
112 static bool read_varlen(FILE *f, int c, unsigned *value)
113 {
114         unsigned size;
115         unsigned char buf[4];
116
117         *value = 0;
118
119         switch (c & 3) {
120         case 3:
121                 size = 4;
122                 break;
123         case 2:
124                 size = 2;
125                 break;
126         case 1:
127                 size = 1;
128                 break;
129         default:
130                 printf("INVALID SIZE\n");
131                 return false;
132         }
133
134         memset(buf, 0, sizeof buf);
135         if (fread(buf, 1, size, f) != size)
136                 goto err;
137
138         *value =  (buf[3] << 24)
139                 + (buf[2] << 16)
140                 + (buf[1] << 8)
141                 + (buf[0] << 0);
142         return true;
143
144 err:
145         printf("(ERROR %d - %s)\n", errno, strerror(errno));
146         return false;
147 }
148
149 static void show_hard(FILE *f, int c)
150 {
151         unsigned type = c >> 3;
152         unsigned value;
153         char *label;
154
155         if (dump_swit)
156                 return;
157
158         printf("DWT - ");
159
160         if (!read_varlen(f, c, &value))
161                 return;
162         printf("%#x", value);
163
164         switch (type) {
165         case 0:                         /* event counter wrapping */
166                 printf("overflow %s%s%s%s%s%s",
167                         (value & (1 << 5)) ? "cyc " : "",
168                         (value & (1 << 4)) ? "fold " : "",
169                         (value & (1 << 3)) ? "lsu " : "",
170                         (value & (1 << 2)) ? "slp " : "",
171                         (value & (1 << 1)) ? "exc " : "",
172                         (value & (1 << 0)) ? "cpi " : "");
173                 break;
174         case 1:                         /* exception tracing */
175                 switch (value >> 12) {
176                 case 1:
177                         label = "entry to";
178                         break;
179                 case 2:
180                         label = "exit from";
181                         break;
182                 case 3:
183                         label = "return to";
184                         break;
185                 default:
186                         label = "?";
187                         break;
188                 }
189                 printf("%s exception %d", label, value & 0x1ff);
190                 break;
191         case 2:                         /* PC sampling */
192                 if (c == 0x15)
193                         printf("PC - sleep");
194                 else
195                         printf("PC - %#08x", value);
196                 break;
197         case 8:                         /* data tracing, pc value */
198         case 10:
199         case 12:
200         case 14:
201                 printf("Data trace %d, PC %#08x", (c >> 4) & 3, value);
202                 /* optionally followed by data value */
203                 break;
204         case 9:                         /* data tracing, address offset */
205         case 11:
206         case 13:
207         case 15:
208                 printf("Data trace %d, address offset %#04x",
209                                 (c >> 4) & 3, value);
210                 /* always followed by data value */
211                 break;
212         case 16 ... 23:                 /* data tracing, data value */
213                 printf("Data trace %d, ", (c >> 4) & 3);
214                 label = (c & 0x8) ? "write" : "read";
215                 switch (c & 3) {
216                 case 3:
217                         printf("word %s, value %#08x", label, value);
218                         break;
219                 case 2:
220                         printf("halfword %s, value %#04x", label, value);
221                         break;
222                 case 1:
223                         printf("byte %s, value %#02x", label, value);
224                         break;
225                 }
226                 break;
227         default:
228                 printf("UNDEFINED, rawtype: %x", type);
229                 break;
230         }
231
232         printf("\n");
233         return;
234 }
235
236 /*
237  * Table of SWIT (SoftWare InstrumentTation) message dump formats, for
238  * ITM port 0..31 application data.
239  *
240  * Eventually this should be customizable; all usage is application defined.
241  *
242  * REVISIT there can be up to 256 trace ports, via "ITM Extension" packets
243  */
244 struct {
245         int port;
246         void (*show)(int port, unsigned data);
247 } format[] = {
248         { .port = 31,  .show = show_task, },
249 };
250
251 static void show_swit(FILE *f, int c)
252 {
253         unsigned port = c >> 3;
254         unsigned value = 0;
255         unsigned i;
256
257         if (port + 1 == dump_swit) {
258                 if (!read_varlen(f, c, &value))
259                         return;
260                 printf("%c", value);
261                 return;
262         }
263
264         if (!read_varlen(f, c, &value))
265                 return;
266
267         if (dump_swit)
268                 return;
269
270         printf("SWIT %u - ", port);
271
272         printf("%#08x", value);
273
274         for (i = 0; i < sizeof(format) / sizeof(format[0]); i++) {
275                 if (format[i].port == port) {
276                         printf(", ");
277                         format[i].show(port, value);
278                         break;
279                 }
280         }
281
282         printf("\n");
283         return;
284 }
285
286 static void show_timestamp(FILE *f, int c)
287 {
288         unsigned counter = 0;
289         char *label = "";
290         bool delayed = false;
291
292         if (dump_swit)
293                 return;
294
295         printf("TIMESTAMP - ");
296
297         /* Format 2: header only */
298         if (!(c & 0x80)) {
299                 switch (c) {
300                 case 0:         /* sync packet -- coding error! */
301                 case 0x70:      /* overflow -- ditto! */
302                         printf("ERROR - %#02x\n", c);
303                         break;
304                 default:
305                         /* synchronous to ITM */
306                         counter = c >> 4;
307                         goto done;
308                 }
309                 return;
310         }
311
312         /* Format 1:  one to four bytes of data too */
313         switch (c >> 4) {
314         default:
315                 label = ", reserved control\n";
316                 break;
317         case 0xc:
318                 /* synchronous to ITM */
319                 break;
320         case 0xd:
321                 label = ", timestamp delayed";
322                 delayed = true;
323                 break;
324         case 0xe:
325                 label = ", packet delayed";
326                 delayed = true;
327                 break;
328         case 0xf:
329                 label = ", packet and timestamp delayed";
330                 delayed = true;
331                 break;
332         }
333
334         c = fgetc(f);
335         if (c == EOF)
336                 goto err;
337         counter = c & 0x7f;
338         if (!(c & 0x80))
339                 goto done;
340
341         c = fgetc(f);
342         if (c == EOF)
343                 goto err;
344         counter |= (c & 0x7f) << 7;
345         if (!(c & 0x80))
346                 goto done;
347
348         c = fgetc(f);
349         if (c == EOF)
350                 goto err;
351         counter |= (c & 0x7f) << 14;
352         if (!(c & 0x80))
353                 goto done;
354
355         c = fgetc(f);
356         if (c == EOF)
357                 goto err;
358         counter |= (c & 0x7f) << 21;
359
360 done:
361         /* REVISIT should we try to convert from delta values?  */
362         printf("+%u%s\n", counter, label);
363         return;
364
365 err:
366         printf("(ERROR %d - %s) ", errno, strerror(errno));
367         goto done;
368 }
369
370 int main(int argc, char **argv)
371 {
372         FILE *f = stdin;
373         int c;
374
375         /* parse arguments */
376         while ((c = getopt(argc, argv, "f:d:")) != EOF) {
377                 switch (c) {
378                 case 'f':
379                         /* e.g. from UART connected to /dev/ttyUSB0 */
380                         f = fopen(optarg, "r");
381                         if (!f) {
382                                 perror(optarg);
383                                 return 1;
384                         }
385                         break;
386                 case 'd':
387                         dump_swit = atoi(optarg);
388                         break;
389                 default:
390                         fprintf(stderr, "usage: %s [-f input]",
391                                 basename(argv[0]));
392                         return 1;
393                 }
394         }
395
396         /* Parse data ... records have a header then data bytes.
397          * NOTE: we assume getc() deals in 8-bit bytes.
398          */
399         bool overflow = false;
400
401         while ((c = getc(f)) != EOF) {
402
403                 /* Sync packet ... 7 zeroes, 0x80 */
404                 if (c == 0) {
405                         int i;
406
407                         for (i = 0; i < 6; i++) {
408                                 c = fgetc(f);
409                                 if (c == EOF)
410                                         break;
411                                 if (c != 0)
412                                         goto bad_sync;
413                         }
414                         c = fgetc(f);
415                         if (c == 0x80) {
416                                 printf("SYNC\n");
417                                 continue;
418                         }
419 bad_sync:
420                         printf("BAD SYNC\n");
421                         continue;
422                 }
423
424                 /* Overflow packet */
425                 if (c == 0x70) {
426                         /* REVISIT later, report just what overflowed!
427                          * Timestamp and SWIT can happen.  Non-ITM too?
428                          */
429                         overflow = true;
430                         printf("OVERFLOW ...\n");
431                         continue;
432                 }
433                 overflow = false;
434
435                 switch (c & 0x0f) {
436                 case 0x00:              /* Timestamp */
437                         show_timestamp(f, c);
438                         break;
439                 case 0x04:              /* "Reserved" */
440                         show_reserved(f, "RESERVED", c);
441                         break;
442                 case 0x08:              /* ITM Extension */
443                         /* FIXME someday, handle these ...  */
444                         show_reserved(f, "ITM EXT", c);
445                         break;
446                 case 0x0c:              /* DWT Extension */
447                         show_reserved(f, "DWT EXT", c);
448                         break;
449                 default:
450                         if (c & 4)
451                                 show_hard(f, c);
452                         else
453                                 show_swit(f, c);
454                         break;
455                 }
456
457         }
458
459         return 0;
460 }