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.
 
 
 
 
 
 

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