1 // SPDX-License-Identifier: GPL-3.0-or-later
3 /* Copyright (C) 2010 by David Brownell */
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
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:
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.
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.
23 * The trace data has two encodings. The working assumption is that data
24 * gets into this program using the UART encoding.
35 unsigned int dump_swit
;
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)
43 * NOTE that this specific encoding could be space-optimized; and that
44 * trace data streams could also be history-sensitive.
46 static void show_task(int port
, unsigned data
)
48 unsigned code
= data
>> 16;
62 strcpy(buf
, "create");
65 strcpy(buf
, "destroy");
67 /* 4..15 reserved for other infrastructure ops */
69 sprintf(buf
, "code %d", code
);
72 printf("TASK %d, pri %d: %s",
78 static void show_reserved(FILE *f
, char *label
, int c
)
85 printf("%s - %#02x", label
, c
);
87 for (i
= 0; (c
& 0x80) && i
< 4; i
++) {
90 printf("(ERROR %d - %s) ", errno
, strerror(errno
));
99 static bool read_varlen(FILE *f
, int c
, unsigned *value
)
102 unsigned char buf
[4];
117 printf("INVALID SIZE\n");
121 memset(buf
, 0, sizeof buf
);
122 if (fread(buf
, 1, size
, f
) != size
)
125 *value
= (buf
[3] << 24)
132 printf("(ERROR %d - %s)\n", errno
, strerror(errno
));
136 static void show_hard(FILE *f
, int c
)
138 unsigned type
= c
>> 3;
147 if (!read_varlen(f
, c
, &value
))
149 printf("%#x", value
);
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 " : "");
161 case 1: /* exception tracing */
162 switch (value
>> 12) {
176 printf("%s exception %d", label
, value
& 0x1ff);
178 case 2: /* PC sampling */
180 printf("PC - sleep");
182 printf("PC - %#08x", value
);
184 case 8: /* data tracing, pc value */
188 printf("Data trace %d, PC %#08x", (c
>> 4) & 3, value
);
189 /* optionally followed by data value */
191 case 9: /* data tracing, address offset */
195 printf("Data trace %d, address offset %#04x",
196 (c
>> 4) & 3, value
);
197 /* always followed by data value */
199 case 16 ... 23: /* data tracing, data value */
200 printf("Data trace %d, ", (c
>> 4) & 3);
201 label
= (c
& 0x8) ? "write" : "read";
204 printf("word %s, value %#08x", label
, value
);
207 printf("halfword %s, value %#04x", label
, value
);
210 printf("byte %s, value %#02x", label
, value
);
215 printf("UNDEFINED, rawtype: %x", type
);
224 * Table of SWIT (SoftWare InstrumentTation) message dump formats, for
225 * ITM port 0..31 application data.
227 * Eventually this should be customizable; all usage is application defined.
229 * REVISIT there can be up to 256 trace ports, via "ITM Extension" packets
233 void (*show
)(int port
, unsigned data
);
235 { .port
= 31, .show
= show_task
, },
238 static void show_swit(FILE *f
, int c
)
240 unsigned port
= c
>> 3;
244 if (port
+ 1 == dump_swit
) {
245 if (!read_varlen(f
, c
, &value
))
251 if (!read_varlen(f
, c
, &value
))
257 printf("SWIT %u - ", port
);
259 printf("%#08x", value
);
261 for (i
= 0; i
< sizeof(format
) / sizeof(format
[0]); i
++) {
262 if (format
[i
].port
== port
) {
264 format
[i
].show(port
, value
);
273 static void show_timestamp(FILE *f
, int c
)
275 unsigned counter
= 0;
277 bool delayed
= false;
282 printf("TIMESTAMP - ");
284 /* Format 2: header only */
287 case 0: /* sync packet -- coding error! */
288 case 0x70: /* overflow -- ditto! */
289 printf("ERROR - %#02x\n", c
);
292 /* synchronous to ITM */
299 /* Format 1: one to four bytes of data too */
302 label
= ", reserved control\n";
305 /* synchronous to ITM */
308 label
= ", timestamp delayed";
312 label
= ", packet delayed";
316 label
= ", packet and timestamp delayed";
331 counter
|= (c
& 0x7f) << 7;
338 counter
|= (c
& 0x7f) << 14;
345 counter
|= (c
& 0x7f) << 21;
348 /* REVISIT should we try to convert from delta values? */
349 printf("+%u%s\n", counter
, label
);
353 printf("(ERROR %d - %s) ", errno
, strerror(errno
));
357 int main(int argc
, char **argv
)
362 /* parse arguments */
363 while ((c
= getopt(argc
, argv
, "f:d:")) != EOF
) {
366 /* e.g. from UART connected to /dev/ttyUSB0 */
367 f
= fopen(optarg
, "r");
374 dump_swit
= atoi(optarg
);
377 fprintf(stderr
, "usage: %s [-f input]",
383 /* Parse data ... records have a header then data bytes.
384 * NOTE: we assume getc() deals in 8-bit bytes.
386 bool overflow
= false;
388 while ((c
= getc(f
)) != EOF
) {
390 /* Sync packet ... 7 zeroes, 0x80 */
394 for (i
= 0; i
< 6; i
++) {
407 printf("BAD SYNC\n");
411 /* Overflow packet */
413 /* REVISIT later, report just what overflowed!
414 * Timestamp and SWIT can happen. Non-ITM too?
417 printf("OVERFLOW ...\n");
423 case 0x00: /* Timestamp */
424 show_timestamp(f
, c
);
426 case 0x04: /* "Reserved" */
427 show_reserved(f
, "RESERVED", c
);
429 case 0x08: /* ITM Extension */
430 /* FIXME someday, handle these ... */
431 show_reserved(f
, "ITM EXT", c
);
433 case 0x0c: /* DWT Extension */
434 show_reserved(f
, "DWT EXT", c
);