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.
 
 
 
 
 
 

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