You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

433 lines
8.8 KiB

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