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