Skip to content

Instantly share code, notes, and snippets.

@bdpdx
Created July 22, 2023 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bdpdx/376d0dab638227556bf7631d9089e005 to your computer and use it in GitHub Desktop.
Save bdpdx/376d0dab638227556bf7631d9089e005 to your computer and use it in GitHub Desktop.
ESP-IDF All tasks backtrace to JSON
/*
Based on this fantastic work by Chip Weinberger:
https://github.com/espressif/esp-idf/pull/11575
This function can be added to:
esp-idf/components/esp_system/port/arch/xtensa/debug_helpers.c
with declaration to:
esp-idf/components/esp_system/include/esp_debug_helpers.h
It will return a string containing json with backtraces for all tasks.
Write a helper function to push each backtrace through:
xtensa-esp32-elf-addr2line -pfiaC -e build/<MYAPP>.elf <backtrace>
e.g.
xtensa-esp32-elf-addr2line -pfiaC -e build/<MYAPP>.elf 0x12345678:0x12345678 0xDEADBEEF:0xFEEDFACE ...
Note that if you log this to the console while running the IDF monitor the monitor can do some magic if it sees
an address associated with a function and print backtrace symbols, but this is an IDE feature not something that
can be done by the ESP32 app at runtime as symbols aren't stored in the binary.
(see post by markwj here: https://esp32.com/viewtopic.php?t=5579)
*** NOTE: ***
If esp_backtrace_create_json_for_all_tasks() returns a non-NULL value it is the responsibility of the caller
to free() it.
Unformatted JSON will be returned which looks like (formatted here for readability):
{
"tasks":[
{
"name":"main",
"backtrace":"0x40082A01:0x3FFBBA80 0x40082AD0:0x3FFBBAA0"
},
{
"name":"websocket_task",
"backtrace":"0x4000BFED:0x3FFD35A0 0x40092621:0x3FFD35B0 0x4009020E:0x3FFD35D0 0x4011FAAA:0x3FFD3610 0x40122473:0x3FFD3630 0x400D7595:0x3FFD36C0 0x4012F5AB:0x3FFD3730 0x401A2F0D:0x3FFD3790 0x4012FF5B:0x3FFD37B0 0x401A2F0D:0x3FFD37D0 0x400F7D3D:0x3FFD37F0 0x4009233D:0x3FFD3830"
},
{
"name":"mdns",
"backtrace":"0x4000BFED:0x3FFD2350 0x40092621:0x3FFD2360 0x400900FD:0x3FFD2380 0x400FED62:0x3FFD23C0 0x4009233D:0x3FFD23F0"
},
{
"name":"wifi",
"backtrace":"0x4000BFED:0x3FFC37E0 0x40092621:0x3FFC37F0 0x400900FD:0x3FFC3810 0x401292D5:0x3FFC3850 0x40098369:0x3FFC3870 0x4009233D:0x3FFC38A0"
}
]
}
*/
#if CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT
#include <stdio.h>
char * IRAM_ATTR esp_backtrace_create_json_for_all_tasks(int depth) {
esp_err_t err = ESP_OK;
char *json = NULL;
UBaseType_t i, snapshots_count = 0, tasks_count = 0, tcb_size = 0;
int j;
TaskSnapshot_t *snapshots = NULL;
FILE *stream = NULL;
size_t stream_length = 0;
if (depth <= 0) depth = INT32_MAX;
tasks_count = uxTaskGetNumberOfTasks();
if ((snapshots = (TaskSnapshot_t *) calloc(tasks_count * sizeof(TaskSnapshot_t), 1)) == NULL) err = ESP_ERR_NO_MEM;
if (!err && (stream = open_memstream(&json, &stream_length)) == NULL) err = ESP_FAIL;
if (!err) {
fprintf(stream, "{\"tasks\":[");
snapshots_count = uxTaskGetSnapshotAll(snapshots, tasks_count, &tcb_size);
for (i = 0; !err && i < snapshots_count; ++i) {
bool corrupt;
esp_backtrace_frame_t frame = esp_task_snapshot_to_backtrace_frame(snapshots[i]);
esp_backtrace_frame_t frame_copy = {0};
TaskHandle_t handle = (TaskHandle_t) snapshots[i].pxTCB;
char *name = pcTaskGetName(handle);
memcpy(&frame_copy, &frame, sizeof(esp_backtrace_frame_t));
corrupt = !(
esp_stack_ptr_is_sane(frame_copy.sp) &&
(
esp_ptr_executable((void *) esp_cpu_process_stack_pc(frame_copy.pc)) ||
(
frame_copy.exc_frame &&
((XtExcFrame *) frame_copy.exc_frame)->exccause == EXCCAUSE_INSTR_PROHIBITED
)
)
);
fprintf(stream,
"%s"
"{"
"\"name\":\"%s\","
"\"backtrace\":\"0x%08X:0x%08X",
i > 0 ? "," : "",
name,
(unsigned int) esp_cpu_process_stack_pc(frame_copy.pc),
(unsigned int) frame_copy.sp
);
for (j = (int) depth; j > 0 && frame_copy.next_pc && !corrupt; --j) {
if (!esp_backtrace_get_next_frame(&frame_copy)) {
corrupt = true;
}
fprintf(stream,
" 0x%08X:0x%08X",
(unsigned int) esp_cpu_process_stack_pc(frame_copy.pc),
(unsigned int) frame_copy.sp
);
}
fprintf(stream, "\"");
if (corrupt) {
fprintf(stream, ",\"corrupt\":true");
} else if (frame_copy.next_pc) {
fprintf(stream, ",\"continues\":true");
}
fprintf(stream, "}");
}
}
if (!err) {
fprintf(stream, "]}");
}
if (stream) fclose(stream);
if (snapshots) free(snapshots);
if (err) {
if (json) {
free(json);
json = NULL;
}
}
return json;
}
#endif // CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment