target: Do not use LOG_USER() for error messages
[openocd.git] / contrib / itmdump.c
blobe7523d9bc2b72275636258cbd5c8a109072fa522
1 // SPDX-License-Identifier: GPL-3.0-or-later
3 /* Copyright (C) 2010 by David Brownell */
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.
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.
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>
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)
40 * YY: task priority
41 * ZZ: task number
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;
49 char buf[16];
51 if (dump_swit)
52 return;
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;
72 printf("TASK %d, pri %d: %s",
73 (data >> 0) & 0xff,
74 (data >> 8) & 0xff,
75 buf);
78 static void show_reserved(FILE *f, char *label, int c)
80 unsigned i;
82 if (dump_swit)
83 return;
85 printf("%s - %#02x", label, c);
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;
93 printf(" %#02x", c);
96 printf("\n");
99 static bool read_varlen(FILE *f, int c, unsigned *value)
101 unsigned size;
102 unsigned char buf[4];
104 *value = 0;
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;
121 memset(buf, 0, sizeof buf);
122 if (fread(buf, 1, size, f) != size)
123 goto err;
125 *value = (buf[3] << 24)
126 + (buf[2] << 16)
127 + (buf[1] << 8)
128 + (buf[0] << 0);
129 return true;
131 err:
132 printf("(ERROR %d - %s)\n", errno, strerror(errno));
133 return false;
136 static void show_hard(FILE *f, int c)
138 unsigned type = c >> 3;
139 unsigned value;
140 char *label;
142 if (dump_swit)
143 return;
145 printf("DWT - ");
147 if (!read_varlen(f, c, &value))
148 return;
149 printf("%#x", value);
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;
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;
213 break;
214 default:
215 printf("UNDEFINED, rawtype: %x", type);
216 break;
219 printf("\n");
220 return;
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
231 struct {
232 int port;
233 void (*show)(int port, unsigned data);
234 } format[] = {
235 { .port = 31, .show = show_task, },
238 static void show_swit(FILE *f, int c)
240 unsigned port = c >> 3;
241 unsigned value = 0;
242 unsigned i;
244 if (port + 1 == dump_swit) {
245 if (!read_varlen(f, c, &value))
246 return;
247 printf("%c", value);
248 return;
251 if (!read_varlen(f, c, &value))
252 return;
254 if (dump_swit)
255 return;
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) {
263 printf(", ");
264 format[i].show(port, value);
265 break;
269 printf("\n");
270 return;
273 static void show_timestamp(FILE *f, int c)
275 unsigned counter = 0;
276 char *label = "";
277 bool delayed = false;
279 if (dump_swit)
280 return;
282 printf("TIMESTAMP - ");
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;
296 return;
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;
321 c = fgetc(f);
322 if (c == EOF)
323 goto err;
324 counter = c & 0x7f;
325 if (!(c & 0x80))
326 goto done;
328 c = fgetc(f);
329 if (c == EOF)
330 goto err;
331 counter |= (c & 0x7f) << 7;
332 if (!(c & 0x80))
333 goto done;
335 c = fgetc(f);
336 if (c == EOF)
337 goto err;
338 counter |= (c & 0x7f) << 14;
339 if (!(c & 0x80))
340 goto done;
342 c = fgetc(f);
343 if (c == EOF)
344 goto err;
345 counter |= (c & 0x7f) << 21;
347 done:
348 /* REVISIT should we try to convert from delta values? */
349 printf("+%u%s\n", counter, label);
350 return;
352 err:
353 printf("(ERROR %d - %s) ", errno, strerror(errno));
354 goto done;
357 int main(int argc, char **argv)
359 FILE *f = stdin;
360 int c;
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;
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;
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 */
391 if (c == 0) {
392 int i;
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;
401 c = fgetc(f);
402 if (c == 0x80) {
403 printf("SYNC\n");
404 continue;
406 bad_sync:
407 printf("BAD SYNC\n");
408 continue;
411 /* Overflow packet */
412 if (c == 0x70) {
413 /* REVISIT later, report just what overflowed!
414 * Timestamp and SWIT can happen. Non-ITM too?
416 overflow = true;
417 printf("OVERFLOW ...\n");
418 continue;
420 overflow = false;
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;
446 return 0;