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.

faultdump.c 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /*
  2. * Copyright (c) 2025, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2025-08-16 Rbb666 first version
  9. */
  10. #include <stdbool.h>
  11. #include <stdint.h>
  12. #include <rtconfig.h>
  13. #ifdef PKG_USING_MCOREDUMP_FILESYSTEM
  14. #include <dfs_file.h>
  15. #include <unistd.h>
  16. #include <sys/time.h>
  17. #endif /* PKG_USING_MCOREDUMP_FILESYSTEM */
  18. #include "coredump.h"
  19. #define COREDUMP_MEMORY_MAGIC (0x434D4450) /* "CMDP" */
  20. /* Static memory buffer for coredump storage */
  21. #ifndef PKG_MCOREDUMP_MEMORY_SIZE
  22. #define COREDUMP_MEMORY_SIZE 1//(8 * 1024) /* Default 8KB buffer size */
  23. #else
  24. #define COREDUMP_MEMORY_SIZE PKG_MCOREDUMP_MEMORY_SIZE
  25. #endif
  26. typedef struct
  27. {
  28. uint32_t magic; /* Magic number to identify valid coredump */
  29. uint32_t data_size; /* Actual coredump data size */
  30. uint32_t crc32; /* CRC32 checksum */
  31. uint8_t data[COREDUMP_MEMORY_SIZE]; /* Coredump data buffer */
  32. } coredump_memory_t;
  33. /* Static memory buffer for coredump storage - must persist across resets */
  34. #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
  35. /* ARM Compiler V6 (armclang) - use custom section with UNINIT attribute in scatter file */
  36. static coredump_memory_t coredump_memory_buffer __attribute__((section(".bss.NoInit")));
  37. #elif defined(__GNUC__)
  38. /* GCC - use .noinit section (not initialized by startup code) */
  39. static coredump_memory_t coredump_memory_buffer __attribute__((section(".noinit")));
  40. #elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 6010050)
  41. /* ARM Compiler 5 (armcc) - use custom section */
  42. static coredump_memory_t coredump_memory_buffer __attribute__((section(".bss.NoInit"), zero_init));
  43. #else
  44. /* Fallback for other compilers - normal static allocation */
  45. #warning "MCoreDump: Unknown compiler, coredump buffer may not persist across resets"
  46. #warning "MCoreDump: Data will be lost after system reset"
  47. static coredump_memory_t coredump_memory_buffer;
  48. #endif
  49. /* Memory write context */
  50. static struct
  51. {
  52. uint32_t offset;
  53. bool overflow;
  54. } memory_write_ctx;
  55. #ifdef PKG_USING_MCOREDUMP_FILESYSTEM
  56. /* Internal function declarations */
  57. static int create_coredump_filename(void);
  58. static int prepare_coredump_filesystem(void);
  59. #endif /* PKG_USING_MCOREDUMP_FILESYSTEM */
  60. /* Global variables for serial output */
  61. static uint32_t serial_crc32;
  62. static uint32_t serial_byte_count;
  63. #ifdef PKG_USING_MCOREDUMP_FILESYSTEM
  64. /* Global variables for filesystem output */
  65. static int coredump_fd = -1;
  66. static char coredump_filename[64];
  67. #ifndef PKG_MCOREDUMP_FILESYSTEM_DIR
  68. #define COREDUMP_DIR "/sdcard"
  69. #else
  70. #define COREDUMP_DIR PKG_MCOREDUMP_FILESYSTEM_DIR
  71. #endif
  72. #ifndef PKG_MCOREDUMP_FILESYSTEM_PREFIX
  73. #define COREDUMP_PREFIX "core_"
  74. #else
  75. #define COREDUMP_PREFIX PKG_MCOREDUMP_FILESYSTEM_PREFIX
  76. #endif
  77. #define COREDUMP_EXT ".elf"
  78. #endif /* PKG_USING_MCOREDUMP_FILESYSTEM */
  79. typedef struct
  80. {
  81. uint16_t magic;
  82. uint16_t page_size;
  83. uint32_t file_size;
  84. uint8_t corefile[1];
  85. } persistent_format_t;
  86. static uint32_t mcd_crc32b(const uint8_t *message, int32_t megLen, uint32_t initCrc)
  87. {
  88. int i, j;
  89. uint32_t byte, crc, mask;
  90. i = 0;
  91. crc = initCrc;
  92. for (i = 0; i < megLen; i++)
  93. {
  94. byte = message[i]; /* Get next byte. */
  95. crc = crc ^ byte;
  96. for (j = 7; j >= 0; j--) /* Do eight times. */
  97. {
  98. mask = -(crc & 1);
  99. crc = (crc >> 1) ^ (0xEDB88320 & mask);
  100. }
  101. }
  102. return ~crc;
  103. }
  104. static void corefile_serial_write(uint8_t *data, int len)
  105. {
  106. for (int i = 0; i < len; i++)
  107. {
  108. uint8_t b = data[i];
  109. serial_crc32 = mcd_crc32b(&b, 1, serial_crc32) ^ 0xFFFFFFFF;
  110. mcd_print("%02x", b);
  111. serial_byte_count++;
  112. }
  113. }
  114. static void corefile_memory_write(uint8_t *data, int len)
  115. {
  116. if (memory_write_ctx.overflow)
  117. return;
  118. /* Check if we have enough space */
  119. if (memory_write_ctx.offset + len > COREDUMP_MEMORY_SIZE)
  120. {
  121. memory_write_ctx.overflow = true;
  122. mcd_println("WARNING: Coredump memory buffer overflow! Data truncated.");
  123. len = COREDUMP_MEMORY_SIZE - memory_write_ctx.offset;
  124. if (len <= 0)
  125. return;
  126. }
  127. /* Copy data to memory buffer */
  128. mcd_memcpy(&coredump_memory_buffer.data[memory_write_ctx.offset], data, len);
  129. memory_write_ctx.offset += len;
  130. }
  131. /**
  132. * @brief Check if there's a valid coredump in memory buffer
  133. *
  134. * @return bool true if valid coredump exists, false otherwise
  135. */
  136. mcd_bool_t mcd_check_memory_coredump(void)
  137. {
  138. return (coredump_memory_buffer.magic == COREDUMP_MEMORY_MAGIC &&
  139. coredump_memory_buffer.data_size > 0 &&
  140. coredump_memory_buffer.data_size <= COREDUMP_MEMORY_SIZE);
  141. }
  142. /**
  143. * @brief Print coredump memory information at startup
  144. *
  145. * This function should be called during system initialization
  146. */
  147. void mcd_print_memoryinfo(void)
  148. {
  149. mcd_print("\n=== MCoreDump Memory Check ===\n");
  150. mcd_print("Memory buffer address: 0x%08X\n", (uint32_t)&coredump_memory_buffer);
  151. mcd_print("Buffer size: %d bytes\n", sizeof(coredump_memory_t));
  152. if (mcd_check_memory_coredump())
  153. {
  154. mcd_print("*** COREDUMP FOUND IN MEMORY ***\n");
  155. mcd_print("Data size: %d bytes\n", coredump_memory_buffer.data_size);
  156. #ifdef PKG_USING_MCOREDUMP_FILESYSTEM
  157. mcd_print("Use 'mcd_dump_filesystem' command to save to filesystem.\n");
  158. #endif /* PKG_USING_MCOREDUMP_FILESYSTEM */
  159. mcd_print("Use 'mcd_dump_memory' command to dump memory to terminal.\n");
  160. }
  161. else
  162. {
  163. mcd_print("No valid coredump found in memory.\n");
  164. }
  165. mcd_print("============================\n\n");
  166. }
  167. #ifdef PKG_USING_MCOREDUMP_FILESYSTEM
  168. /**
  169. * @brief Save coredump from memory buffer to filesystem
  170. *
  171. * @return int MCD_OK on success, error code on failure
  172. */
  173. int mcd_dump_filesystem(void)
  174. {
  175. if (!mcd_check_memory_coredump())
  176. {
  177. mcd_print("No valid coredump found in memory buffer.\n");
  178. return MCD_ERROR;
  179. }
  180. /* Verify CRC32 */
  181. uint32_t calculated_crc = mcd_crc32b(coredump_memory_buffer.data,
  182. coredump_memory_buffer.data_size,
  183. 0xFFFFFFFF) ^ 0xFFFFFFFF;
  184. if (calculated_crc != coredump_memory_buffer.crc32)
  185. {
  186. mcd_print("ERROR: Coredump data corruption detected (CRC mismatch)!\n");
  187. mcd_print("Expected: 0x%08X, Calculated: 0x%08X\n",
  188. coredump_memory_buffer.crc32, calculated_crc);
  189. return MCD_ERROR;
  190. }
  191. /* Prepare filesystem */
  192. if (prepare_coredump_filesystem() != 0)
  193. {
  194. mcd_print("ERROR: Failed to prepare filesystem for coredump\n");
  195. return MCD_ERROR;
  196. }
  197. mcd_print("Saving coredump from memory to file: %s\n", coredump_filename);
  198. /* Write coredump data directly (pure ELF format without custom header) */
  199. if (write(coredump_fd, coredump_memory_buffer.data, coredump_memory_buffer.data_size) < 0)
  200. {
  201. mcd_print("ERROR: Failed to write coredump data\n");
  202. close(coredump_fd);
  203. return MCD_ERROR;
  204. }
  205. close(coredump_fd);
  206. coredump_fd = -1;
  207. mcd_print("Coredump saved successfully to: %s\n", coredump_filename);
  208. /* Clear memory buffer after successful save */
  209. mcd_memset(&coredump_memory_buffer, 0, sizeof(coredump_memory_t));
  210. return MCD_OK;
  211. }
  212. MCD_CMD_EXPORT(mcd_dump_filesystem, Save coredump from memory to filesystem);
  213. static int create_coredump_filename(void)
  214. {
  215. struct timeval tv;
  216. struct tm *tm_info;
  217. /* Get current time */
  218. if (gettimeofday(&tv, NULL) != 0)
  219. {
  220. /* If time is not available, use a simple counter-based name */
  221. static int file_counter = 0;
  222. file_counter++;
  223. snprintf(coredump_filename, sizeof(coredump_filename),
  224. "%s/%s%04d%s",
  225. COREDUMP_DIR, COREDUMP_PREFIX, file_counter, COREDUMP_EXT);
  226. }
  227. else
  228. {
  229. tm_info = localtime(&tv.tv_sec);
  230. /* Create filename with timestamp */
  231. snprintf(coredump_filename, sizeof(coredump_filename),
  232. "%s/%s%04d%02d%02d_%02d%02d%02d%s",
  233. COREDUMP_DIR, COREDUMP_PREFIX,
  234. tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
  235. tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
  236. COREDUMP_EXT);
  237. }
  238. return 0;
  239. }
  240. static int prepare_coredump_filesystem(void)
  241. {
  242. struct stat st;
  243. /* Check if coredump directory exists first */
  244. if (stat(COREDUMP_DIR, &st) != 0)
  245. {
  246. mcd_print("ERROR: %s directory not found. Please mount filesystem first.\n", COREDUMP_DIR);
  247. return -1;
  248. }
  249. /* Create coredump directory if it doesn't exist */
  250. if (stat(COREDUMP_DIR, &st) != 0)
  251. {
  252. mcd_print("Creating directory: %s\n", COREDUMP_DIR);
  253. if (mkdir(COREDUMP_DIR, 0x777) != 0)
  254. {
  255. mcd_print("ERROR: Failed to create coredump directory: %s\n", COREDUMP_DIR);
  256. mcd_print("Please check if filesystem is mounted and writable.\n");
  257. return -1;
  258. }
  259. mcd_print("Directory created successfully: %s\n", COREDUMP_DIR);
  260. }
  261. /* Generate filename */
  262. create_coredump_filename();
  263. mcd_print("Creating coredump file: %s\n", coredump_filename);
  264. /* Open file for writing */
  265. coredump_fd = open(coredump_filename, O_WRONLY | O_CREAT);
  266. if (coredump_fd < 0)
  267. {
  268. mcd_print("ERROR: Failed to create coredump file: %s\n", coredump_filename);
  269. return -1;
  270. }
  271. mcd_print("Coredump file opened successfully (fd: %d)\n", coredump_fd);
  272. return 0;
  273. }
  274. #endif /* PKG_USING_MCOREDUMP_FILESYSTEM */
  275. /**
  276. * @brief Core dump with output mode selection
  277. *
  278. * @param output_mode MCD_OUTPUT_FLASH for flash storage, MCD_OUTPUT_SERIAL for serial output, MCD_OUTPUT_MEMORY for memory storage
  279. * @return int MCD_OK on success, error code on failure
  280. */
  281. int mcd_faultdump_ex(mcd_output_mode_t output_mode)
  282. {
  283. struct thread_info_ops ops;
  284. if (output_mode == MCD_OUTPUT_FAULT_SERIAL)
  285. {
  286. mcd_init(corefile_serial_write);
  287. mcd_irq_state_t irq_save = mcd_irq_disable();
  288. serial_crc32 = 0xFFFFFFFF;
  289. mcd_print("FAULT coredump start : {\n");
  290. mcd_hard_fault_exception_dump();
  291. mcd_print("\n} coredump end\n");
  292. serial_crc32 = ~serial_crc32;
  293. mcd_print("crc32 : %08x\n", serial_crc32);
  294. mcd_irq_enable(irq_save);
  295. }
  296. else if (output_mode == MCD_OUTPUT_SERIAL)
  297. {
  298. mcd_init(corefile_serial_write);
  299. mcd_irq_state_t irq_save = mcd_irq_disable();
  300. serial_crc32 = 0xFFFFFFFF;
  301. mcd_print("coredump start : {\n");
  302. mcd_multi_dump();
  303. mcd_print("\n} coredump end\n");
  304. serial_crc32 = ~serial_crc32;
  305. mcd_print("crc32 : %08x\n", serial_crc32);
  306. mcd_irq_enable(irq_save);
  307. }
  308. else if (output_mode == MCD_OUTPUT_MEMORY)
  309. {
  310. /* Memory buffer output mode - for exception handling */
  311. mcd_print("\n=== MCoreDump Memory Storage Started ===\n");
  312. /* Initialize memory write context */
  313. memory_write_ctx.offset = 0;
  314. memory_write_ctx.overflow = false;
  315. /* Clear memory buffer */
  316. mcd_memset(&coredump_memory_buffer, 0, sizeof(coredump_memory_t));
  317. /* Initialize mcd with memory write function */
  318. mcd_init(corefile_memory_write);
  319. /* Generate and write coredump to memory */
  320. mcd_rtos_thread_ops(&ops);
  321. mcd_gen_coredump(&ops);
  322. /* Finalize memory buffer */
  323. if (!memory_write_ctx.overflow && memory_write_ctx.offset > 0)
  324. {
  325. coredump_memory_buffer.magic = COREDUMP_MEMORY_MAGIC;
  326. coredump_memory_buffer.data_size = memory_write_ctx.offset;
  327. coredump_memory_buffer.crc32 = mcd_crc32b(coredump_memory_buffer.data,
  328. memory_write_ctx.offset,
  329. 0xFFFFFFFF) ^ 0xFFFFFFFF;
  330. mcd_println("Coredump saved to memory buffer:\n");
  331. mcd_println(" Address: 0x%08X", (uint32_t)&coredump_memory_buffer);
  332. mcd_println(" Size: %d bytes", memory_write_ctx.offset);
  333. mcd_println(" CRC32: 0x%08X", coredump_memory_buffer.crc32);
  334. }
  335. else
  336. {
  337. mcd_println("ERROR: Memory buffer overflow or no data written");
  338. mcd_println("=== MCoreDump Memory Storage Failed ===\n");
  339. return MCD_ERROR;
  340. }
  341. }
  342. mcd_println("=== MCoreDump Memory Storage Completed ===\n");
  343. return MCD_OK;
  344. }
  345. /**
  346. * @brief Generate coredump with specified output mode
  347. *
  348. * This is the main entry point for generating coredumps in MCoreDump system.
  349. * It supports multiple output modes to accommodate different debugging scenarios:
  350. *
  351. * - MCD_OUTPUT_MEMORY: Saves coredump to static memory buffer (persistent across resets)
  352. * - MCD_OUTPUT_SERIAL: Outputs coredump data via serial port in hex format
  353. * - MCD_OUTPUT_FILESYSTEM: Saves coredump to filesystem as ELF file (if filesystem support is enabled)
  354. *
  355. * The function is designed to be called from exception handlers, assert hooks,
  356. * or user applications when fault analysis is needed.
  357. *
  358. * @param output_mode Output destination for coredump data:
  359. * - MCD_OUTPUT_MEMORY: Store in memory buffer (recommended for fault handlers)
  360. * - MCD_OUTPUT_SERIAL: Output via serial port (for immediate analysis)
  361. * - MCD_OUTPUT_FILESYSTEM: Save to file (requires filesystem support)
  362. *
  363. * @return int Status code:
  364. * - MCD_OK: Coredump generated successfully
  365. * - MCD_ERROR: Failed to generate coredump (memory overflow, filesystem error, etc.)
  366. *
  367. * @note This function calls mcd_faultdump_ex() internally, which contains the actual implementation.
  368. * The memory buffer mode is particularly useful for hard fault handlers as the data
  369. * persists across system resets when power is maintained.
  370. *
  371. * @warning When called from interrupt context (e.g., hard fault handler), avoid using
  372. * filesystem mode as it may involve complex I/O operations.
  373. *
  374. * @see mcd_faultdump_ex() for detailed implementation
  375. * @see mcd_check_memory_coredump() to check for existing coredumps in memory
  376. * @see mcd_dump_filesystem() to save memory coredump to filesystem
  377. */
  378. int mcd_faultdump(mcd_output_mode_t output_mode)
  379. {
  380. /* Default to memory storage mode for exception handling */
  381. return mcd_faultdump_ex(output_mode);
  382. }
  383. /**
  384. * @brief Print coredump from memory buffer via serial output
  385. *
  386. * This function reads coredump data from memory buffer and prints it via serial port
  387. *
  388. * @return int MCD_OK on success, error code on failure
  389. */
  390. static int mcd_dump_memory(void)
  391. {
  392. if (!mcd_check_memory_coredump())
  393. {
  394. mcd_print("No valid coredump found in memory buffer.\n");
  395. return MCD_ERROR;
  396. }
  397. /* Verify CRC32 */
  398. uint32_t calculated_crc = mcd_crc32b(coredump_memory_buffer.data,
  399. coredump_memory_buffer.data_size,
  400. 0xFFFFFFFF) ^ 0xFFFFFFFF;
  401. if (calculated_crc != coredump_memory_buffer.crc32)
  402. {
  403. mcd_print("ERROR: Coredump data corruption detected (CRC mismatch)!\n");
  404. mcd_print("Expected: 0x%08X, Calculated: 0x%08X\n",
  405. coredump_memory_buffer.crc32, calculated_crc);
  406. return MCD_ERROR;
  407. }
  408. mcd_print("\n=== MCoreDump Memory Data Serial Output ===\n");
  409. mcd_print("Coredump found in memory buffer:\n");
  410. mcd_print(" Address: 0x%08X\n", (uint32_t)&coredump_memory_buffer);
  411. mcd_print(" Size: %d bytes\n", coredump_memory_buffer.data_size);
  412. mcd_print(" CRC32: 0x%08X\n", coredump_memory_buffer.crc32);
  413. mcd_print("\n");
  414. /* Calculate and display CRC32 for serial output */
  415. uint32_t serial_crc = 0xFFFFFFFF;
  416. uint32_t byte_count = 0;
  417. mcd_print("coredump start : {\n");
  418. /* Print coredump data in hex format */
  419. for (uint32_t i = 0; i < coredump_memory_buffer.data_size; i++)
  420. {
  421. uint8_t b = coredump_memory_buffer.data[i];
  422. serial_crc = mcd_crc32b(&b, 1, serial_crc) ^ 0xFFFFFFFF;
  423. mcd_print("%02x", b);
  424. byte_count++;
  425. }
  426. mcd_print("\n} coredump end\n");
  427. serial_crc = ~serial_crc;
  428. mcd_print("crc32 : %08x\n", serial_crc);
  429. mcd_print("bytes : %d\n", byte_count);
  430. mcd_print("=== MCoreDump Memory Data Serial Output Completed ===\n\n");
  431. return MCD_OK;
  432. }
  433. MCD_CMD_EXPORT(mcd_dump_memory, Print memory coredump via serial);
  434. // 自定义:和架构无关
  435. void print_mem(uint32_t addr, uint32_t len)
  436. {
  437. uint32_t *paddr;
  438. uint32_t i;
  439. if (len == 0)
  440. return;
  441. paddr = (uint32_t *)addr;
  442. mcd_print("mem@0x%08x,0x%08x\n", addr, len);
  443. for (i = 0; i < len;)
  444. {
  445. mcd_print("0x%08x", *paddr);
  446. paddr++;
  447. i += 4;
  448. if (i % 16 == 0)
  449. mcd_print("\n");
  450. else
  451. mcd_print(" ");
  452. }
  453. mcd_print("\n");
  454. if (len % 16 != 0)
  455. mcd_print("\n");
  456. }
  457. #pragma section = ".data"
  458. #pragma section = ".bss"
  459. // 打印变量用
  460. void *sg_cd_variable_addr[COREADUMP_PRINT_RANGE_DYNAMIC_MAX_NUM];
  461. int sg_cd_variable_sizeof[COREADUMP_PRINT_RANGE_DYNAMIC_MAX_NUM];
  462. static const int __coreadump_variable_start[COREADUMP_PRINT_ITEM_SIZE]MCD_SECTION(".coreadump_rang.0") = {0};
  463. static const int __coreadump_variable_stop[COREADUMP_PRINT_ITEM_SIZE]MCD_SECTION(".coreadump_rang.end") = {0};
  464. void print_variable(void *addr, int size)
  465. {
  466. mcd_print("Data segment:\n");
  467. print_mem((uint32_t)addr, size);
  468. }
  469. int mcd_variable_dump(void)
  470. {
  471. /* 打印变量 */
  472. // print_variable(&dev_property, sizeof(dev_property));
  473. for (int i = 0; i < COREADUMP_PRINT_RANGE_DYNAMIC_MAX_NUM; i++)
  474. {
  475. if (sg_cd_variable_addr[i] && sg_cd_variable_sizeof[i])
  476. print_variable(sg_cd_variable_addr[i], sg_cd_variable_sizeof[i]);
  477. }
  478. for (uint32_t *p = (uint32_t *)&__coreadump_variable_start + COREADUMP_PRINT_ITEM_SIZE; p < (uint32_t *)&__coreadump_variable_stop; p += COREADUMP_PRINT_ITEM_SIZE)
  479. print_variable((void *)(p[0]), p[1]);
  480. return 0;
  481. }