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