doc: add a note to use 'stm32l4x option_load' after changing option bytes
[openocd.git] / contrib / itmdump.c
blob88099040a6dcb24bf0c67c38303ef7263d4e9927
1 /*
2 * Copyright (C) 2010 by David Brownell
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.
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.
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/>.
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.
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:
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.
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.
36 * The trace data has two encodings. The working assumption is that data
37 * gets into this program using the UART encoding.
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>
48 unsigned int dump_swit;
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
56 * NOTE that this specific encoding could be space-optimized; and that
57 * trace data streams could also be history-sensitive.
59 static void show_task(int port, unsigned data)
61 unsigned code = data >> 16;
62 char buf[16];
64 if (dump_swit)
65 return;
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;
85 printf("TASK %d, pri %d: %s",
86 (data >> 0) & 0xff,
87 (data >> 8) & 0xff,
88 buf);
91 static void show_reserved(FILE *f, char *label, int c)
93 unsigned i;
95 if (dump_swit)
96 return;
98 printf("%s - %#02x", label, c);
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;
106 printf(" %#02x", c);
109 printf("\n");
112 static bool read_varlen(FILE *f, int c, unsigned *value)
114 unsigned size;
115 unsigned char buf[4];
117 *value = 0;
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;
134 memset(buf, 0, sizeof buf);
135 if (fread(buf, 1, size, f) != size)
136 goto err;
138 *value = (buf[3] << 24)
139 + (buf[2] << 16)
140 + (buf[1] << 8)
141 + (buf[0] << 0);
142 return true;
144 err:
145 printf("(ERROR %d - %s)\n", errno, strerror(errno));
146 return false;
149 static void show_hard(FILE *f, int c)
151 unsigned type = c >> 3;
152 unsigned value;
153 char *label;
155 if (dump_swit)
156 return;
158 printf("DWT - ");
160 if (!read_varlen(f, c, &value))
161 return;
162 printf("%#x", value);
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;
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;
226 break;
227 default:
228 printf("UNDEFINED, rawtype: %x", type);
229 break;
232 printf("\n");
233 return;
237 * Table of SWIT (SoftWare InstrumentTation) message dump formats, for
238 * ITM port 0..31 application data.
240 * Eventually this should be customizable; all usage is application defined.
242 * REVISIT there can be up to 256 trace ports, via "ITM Extension" packets
244 struct {
245 int port;
246 void (*show)(int port, unsigned data);
247 } format[] = {
248 { .port = 31, .show = show_task, },
251 static void show_swit(FILE *f, int c)
253 unsigned port = c >> 3;
254 unsigned value = 0;
255 unsigned i;
257 if (port + 1 == dump_swit) {
258 if (!read_varlen(f, c, &value))
259 return;
260 printf("%c", value);
261 return;
264 if (!read_varlen(f, c, &value))
265 return;
267 if (dump_swit)
268 return;
270 printf("SWIT %u - ", port);
272 printf("%#08x", value);
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;
282 printf("\n");
283 return;
286 static void show_timestamp(FILE *f, int c)
288 unsigned counter = 0;
289 char *label = "";
290 bool delayed = false;
292 if (dump_swit)
293 return;
295 printf("TIMESTAMP - ");
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;
309 return;
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;
334 c = fgetc(f);
335 if (c == EOF)
336 goto err;
337 counter = c & 0x7f;
338 if (!(c & 0x80))
339 goto done;
341 c = fgetc(f);
342 if (c == EOF)
343 goto err;
344 counter |= (c & 0x7f) << 7;
345 if (!(c & 0x80))
346 goto done;
348 c = fgetc(f);
349 if (c == EOF)
350 goto err;
351 counter |= (c & 0x7f) << 14;
352 if (!(c & 0x80))
353 goto done;
355 c = fgetc(f);
356 if (c == EOF)
357 goto err;
358 counter |= (c & 0x7f) << 21;
360 done:
361 /* REVISIT should we try to convert from delta values? */
362 printf("+%u%s\n", counter, label);
363 return;
365 err:
366 printf("(ERROR %d - %s) ", errno, strerror(errno));
367 goto done;
370 int main(int argc, char **argv)
372 FILE *f = stdin;
373 int c;
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;
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;
396 /* Parse data ... records have a header then data bytes.
397 * NOTE: we assume getc() deals in 8-bit bytes.
399 bool overflow = false;
401 while ((c = getc(f)) != EOF) {
403 /* Sync packet ... 7 zeroes, 0x80 */
404 if (c == 0) {
405 int i;
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;
414 c = fgetc(f);
415 if (c == 0x80) {
416 printf("SYNC\n");
417 continue;
419 bad_sync:
420 printf("BAD SYNC\n");
421 continue;
424 /* Overflow packet */
425 if (c == 0x70) {
426 /* REVISIT later, report just what overflowed!
427 * Timestamp and SWIT can happen. Non-ITM too?
429 overflow = true;
430 printf("OVERFLOW ...\n");
431 continue;
433 overflow = false;
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;
459 return 0;