-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Heap: Improve debug caller address reporting #8720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 29 commits
4ec3c81
6088147
a716193
1102256
6dfd46a
8b3bb1f
ea9befa
3dd08be
45f1c3b
57eb2b3
c2590a7
0619034
19da675
af7b962
9423c8e
471f838
d829540
4c96365
840e55a
95c7e2c
0ce295e
4717dcf
b083043
ecc1b14
a0044d6
01b238a
b5aa1de
1c99daf
2bbadca
49b48fb
3786ce6
b9a0e55
a5f6d7d
54b034b
e948329
5653249
d303d01
01a89fd
0574c1a
31217f6
ecc7eed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,45 +33,55 @@ | |
#include "gdb_hooks.h" | ||
#include "StackThunk.h" | ||
#include "coredecls.h" | ||
#include "umm_malloc/umm_malloc.h" | ||
|
||
extern "C" { | ||
|
||
// These will be pointers to PROGMEM const strings | ||
static const char* s_panic_file = 0; | ||
static int s_panic_line = 0; | ||
static const char* s_panic_func = 0; | ||
static const char* s_panic_what = 0; | ||
|
||
// Our wiring for abort() and C++ exceptions | ||
static bool s_abort_called = false; | ||
static const char* s_unhandled_exception = NULL; | ||
|
||
// Common way to notify about where the stack smashing happened | ||
// (but, **only** if caller uses our handler function) | ||
static uint32_t s_stack_chk_addr = 0; | ||
|
||
void abort() __attribute__((noreturn)); | ||
static void uart_write_char_d(char c); | ||
static void uart0_write_char_d(char c); | ||
static void uart1_write_char_d(char c); | ||
static void print_stack(uint32_t start, uint32_t end); | ||
|
||
// using numbers different from "REASON_" in user_interface.h (=0..6) | ||
enum rst_reason_sw | ||
{ | ||
REASON_USER_STACK_OVERFLOW = 252, | ||
REASON_USER_STACK_SMASH = 253, | ||
REASON_USER_SWEXCEPTION_RST = 254 | ||
}; | ||
static int s_user_reset_reason = REASON_DEFAULT_RST; | ||
|
||
// Confirmed on 12/17/22: s_pm is in the .bss section and is in the | ||
// _bss_start/end range to be zeroed by the SDK this happens after the SDK first | ||
// calls to Cache_Read_Enable_New. | ||
static struct PostmortemInfo { | ||
int user_reset_reason = REASON_DEFAULT_RST; | ||
|
||
// These will be pointers to PROGMEM const strings | ||
const char* panic_file = 0; | ||
int panic_line = 0; | ||
const char* panic_func = 0; | ||
const char* panic_what = 0; | ||
|
||
// Our wiring for abort() and C++ exceptions | ||
bool abort_called = false; | ||
const char* unhandled_exception = NULL; | ||
|
||
// Common way to notify about where the stack smashing happened | ||
// (but, **only** if caller uses our handler function) | ||
uint32_t stack_chk_addr = 0; | ||
} s_pm; | ||
|
||
// From UMM, the last caller of a malloc/realloc/calloc which failed: | ||
extern void *umm_last_fail_alloc_addr; | ||
extern int umm_last_fail_alloc_size; | ||
extern struct umm_last_fail_alloc { | ||
const void *addr; | ||
size_t size; | ||
#if defined(DEBUG_ESP_OOM) | ||
extern const char *umm_last_fail_alloc_file; | ||
extern int umm_last_fail_alloc_line; | ||
const char *file; | ||
int line; | ||
#endif | ||
} _umm_last_fail_alloc; | ||
|
||
|
||
void abort() __attribute__((noreturn)); | ||
static void uart_write_char_d(char c); | ||
static void uart0_write_char_d(char c); | ||
static void uart1_write_char_d(char c); | ||
static void print_stack(uint32_t start, uint32_t end); | ||
|
||
static void raise_exception() __attribute__((noreturn)); | ||
|
||
|
@@ -139,7 +149,7 @@ asm( | |
static void postmortem_report(uint32_t sp_dump) { | ||
struct rst_info rst_info; | ||
memset(&rst_info, 0, sizeof(rst_info)); | ||
if (s_user_reset_reason == REASON_DEFAULT_RST) | ||
if (s_pm.user_reset_reason == REASON_DEFAULT_RST) | ||
{ | ||
system_rtc_mem_read(0, &rst_info, sizeof(rst_info)); | ||
if (rst_info.reason != REASON_SOFT_WDT_RST && | ||
|
@@ -150,26 +160,26 @@ static void postmortem_report(uint32_t sp_dump) { | |
} | ||
} | ||
else | ||
rst_info.reason = s_user_reset_reason; | ||
rst_info.reason = s_pm.user_reset_reason; | ||
|
||
ets_install_putc1(&uart_write_char_d); | ||
|
||
cut_here(); | ||
|
||
if (s_panic_line) { | ||
ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func); | ||
if (s_panic_what) { | ||
ets_printf_P(PSTR(": Assertion '%S' failed."), s_panic_what); | ||
if (s_pm.panic_line) { | ||
ets_printf_P(PSTR("\nPanic %S:%d %S"), s_pm.panic_file, s_pm.panic_line, s_pm.panic_func); | ||
if (s_pm.panic_what) { | ||
ets_printf_P(PSTR(": Assertion '%S' failed."), s_pm.panic_what); | ||
} | ||
ets_putc('\n'); | ||
} | ||
else if (s_panic_file) { | ||
ets_printf_P(PSTR("\nPanic %S\n"), s_panic_file); | ||
else if (s_pm.panic_file) { | ||
ets_printf_P(PSTR("\nPanic %S\n"), s_pm.panic_file); | ||
} | ||
else if (s_unhandled_exception) { | ||
ets_printf_P(PSTR("\nUnhandled C++ exception: %S\n"), s_unhandled_exception); | ||
else if (s_pm.unhandled_exception) { | ||
ets_printf_P(PSTR("\nUnhandled C++ exception: %S\n"), s_pm.unhandled_exception); | ||
} | ||
else if (s_abort_called) { | ||
else if (s_pm.abort_called) { | ||
ets_printf_P(PSTR("\nAbort called\n")); | ||
} | ||
else if (rst_info.reason == REASON_EXCEPTION_RST) { | ||
|
@@ -199,7 +209,7 @@ static void postmortem_report(uint32_t sp_dump) { | |
rst_info.exccause, /* Address executing at time of Soft WDT level-1 interrupt */ rst_info.epc1, 0, 0, 0, 0); | ||
} | ||
else if (rst_info.reason == REASON_USER_STACK_SMASH) { | ||
ets_printf_P(PSTR("\nStack smashing detected at 0x%08x\n"), s_stack_chk_addr); | ||
ets_printf_P(PSTR("\nStack smashing detected at 0x%08x\n"), s_pm.stack_chk_addr); | ||
} | ||
else if (rst_info.reason == REASON_USER_STACK_OVERFLOW) { | ||
ets_printf_P(PSTR("\nStack overflow detected\n")); | ||
|
@@ -210,7 +220,7 @@ static void postmortem_report(uint32_t sp_dump) { | |
|
||
uint32_t cont_stack_start; | ||
if (rst_info.reason == REASON_USER_STACK_SMASH) { | ||
cont_stack_start = s_stack_chk_addr; | ||
cont_stack_start = s_pm.stack_chk_addr; | ||
} else { | ||
cont_stack_start = (uint32_t) (&g_pcont->stack[0]); | ||
} | ||
|
@@ -229,7 +239,7 @@ static void postmortem_report(uint32_t sp_dump) { | |
// 16 ?unnamed? - index into a table, pull out pointer, and call if non-zero | ||
// appears near near wDev_ProcessFiq | ||
// 32 pp_soft_wdt_feed_local - gather the specifics and call __wrap_system_restart_local | ||
offset = 32 + 16 + 48 + 256; | ||
offset = 32 + 16 + 48 + 256; | ||
} | ||
else if (rst_info.reason == REASON_EXCEPTION_RST) { | ||
// Stack Tally | ||
|
@@ -280,22 +290,29 @@ static void postmortem_report(uint32_t sp_dump) { | |
ets_printf_P(PSTR("<<<stack<<<\n")); | ||
|
||
// Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address | ||
if (umm_last_fail_alloc_addr) { | ||
if (_umm_last_fail_alloc.addr) { | ||
#if defined(DEBUG_ESP_OOM) | ||
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)@%S:%d\n"), | ||
(uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size, | ||
umm_last_fail_alloc_file, umm_last_fail_alloc_line); | ||
ets_printf_P(PSTR("\nlast failed alloc call: 0x%08X(%d), File: %S:%d\n"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No special case in newlib here? Implementation thinks of both 's' and 'S' as exactly same printf(3) says it is '%ls', which is wide-char ('wchar_t') and non-applicable here
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have always been confused about this duality. Some print functions give an error (or is it a warning?) about %S and PROGMEM strings while other functions don't, like Serial.printf_P.
The comment for the code you pointed out reads to me that Arduino intends to have %S for PROGMEM strings. case 'S': // TODO: Verify cap-S under Arduino is "PROGMEM char*", not wchar_t There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 6280e98 / #5376
as-is both '%s' and even '%.*s' work seamlessly, no need for extra care |
||
(uint32_t)_umm_last_fail_alloc.addr, | ||
_umm_last_fail_alloc.size, | ||
(_umm_last_fail_alloc.file) ? _umm_last_fail_alloc.file : "??", | ||
_umm_last_fail_alloc.line); | ||
#else | ||
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)\n"), (uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size); | ||
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)\n"), (uint32_t)_umm_last_fail_alloc.addr, _umm_last_fail_alloc.size); | ||
#endif | ||
} | ||
|
||
cut_here(); | ||
|
||
if (s_unhandled_exception && umm_last_fail_alloc_addr) { | ||
if (s_pm.unhandled_exception && _umm_last_fail_alloc.addr) { | ||
// now outside from the "cut-here" zone, print correctly the `new` caller address, | ||
// idf-monitor.py will be able to decode this one and show exact location in sources | ||
ets_printf_P(PSTR("\nlast failed alloc caller: 0x%08x\n"), (uint32_t)umm_last_fail_alloc_addr); | ||
ets_printf_P(PSTR("\nlast failed alloc caller: 0x%08x\n"), (uint32_t)_umm_last_fail_alloc.addr); | ||
} | ||
|
||
size_t oom_count = umm_get_oom_count(); | ||
if (oom_count) { | ||
ets_printf_P(PSTR("\nOOM Count: %u\n"), oom_count); | ||
} | ||
|
||
custom_crash_callback( &rst_info, sp_dump + offset, stack_end ); | ||
|
@@ -353,7 +370,7 @@ static void raise_exception() { | |
if (gdb_present()) | ||
__asm__ __volatile__ ("syscall"); // triggers GDB when enabled | ||
|
||
s_user_reset_reason = REASON_USER_SWEXCEPTION_RST; | ||
s_pm.user_reset_reason = REASON_USER_SWEXCEPTION_RST; | ||
ets_printf_P(PSTR("\nUser exception (panic/abort/assert)")); | ||
uint32_t sp; | ||
__asm__ __volatile__ ("mov %0, a1\n\t" : "=r"(sp) :: "memory"); | ||
|
@@ -362,37 +379,37 @@ static void raise_exception() { | |
} | ||
|
||
void abort() { | ||
s_abort_called = true; | ||
s_pm.abort_called = true; | ||
raise_exception(); | ||
} | ||
|
||
void __unhandled_exception(const char *str) { | ||
s_unhandled_exception = str; | ||
s_pm.unhandled_exception = str; | ||
raise_exception(); | ||
} | ||
|
||
void __assert_func(const char *file, int line, const char *func, const char *what) { | ||
s_panic_file = file; | ||
s_panic_line = line; | ||
s_panic_func = func; | ||
s_panic_what = what; | ||
s_pm.panic_file = file; | ||
s_pm.panic_line = line; | ||
s_pm.panic_func = func; | ||
s_pm.panic_what = what; | ||
gdb_do_break(); /* if GDB is not present, this is a no-op */ | ||
raise_exception(); | ||
} | ||
|
||
void __panic_func(const char* file, int line, const char* func) { | ||
s_panic_file = file; | ||
s_panic_line = line; | ||
s_panic_func = func; | ||
s_panic_what = 0; | ||
s_pm.panic_file = file; | ||
s_pm.panic_line = line; | ||
s_pm.panic_func = func; | ||
s_pm.panic_what = 0; | ||
gdb_do_break(); /* if GDB is not present, this is a no-op */ | ||
raise_exception(); | ||
} | ||
|
||
uintptr_t __stack_chk_guard = 0x08675309 ^ RANDOM_REG32; | ||
void __stack_chk_fail(void) { | ||
s_user_reset_reason = REASON_USER_STACK_SMASH; | ||
s_stack_chk_addr = (uint32_t)__builtin_return_address(0); | ||
s_pm.user_reset_reason = REASON_USER_STACK_SMASH; | ||
s_pm.stack_chk_addr = (uint32_t)__builtin_return_address(0); | ||
|
||
if (gdb_present()) | ||
__asm__ __volatile__ ("syscall"); // triggers GDB when enabled | ||
|
@@ -405,8 +422,8 @@ void __stack_chk_fail(void) { | |
} | ||
|
||
void __stack_overflow(cont_t* cont, uint32_t* sp) { | ||
s_user_reset_reason = REASON_USER_STACK_OVERFLOW; | ||
s_stack_chk_addr = (uint32_t)&cont->stack[0]; | ||
s_pm.user_reset_reason = REASON_USER_STACK_OVERFLOW; | ||
s_pm.stack_chk_addr = (uint32_t)&cont->stack[0]; | ||
|
||
if (gdb_present()) | ||
__asm__ __volatile__ ("syscall"); // triggers GDB when enabled | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't 'new_handler' be independent of debugging?
since the idea is to allow plain new to sometimes avoid throwing (regardless of whether it builds w/ -fexceptions or without). exception-less builds obviously just call abort for would-be exception
ref new_op.cc and new_opnt.cc, nothrow variant just wraps the other in try catch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been a while since I did this, I think the increased build size for non-debug builds was a consideration.
It would add 136 bytes of IROM to the non-debug build so that it always builds with our revised
new_handler
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will have to recheck too, then. Debug=enabled without exceptions are not handled though?
Plus there are more missing overloads from -std=c++17
https://en.cppreference.com/w/cpp/memory/new/operator_new
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current master does not support "new" align operations. Compiler reports:
/workdir/repo/gcc-gnu/libstdc++-v3/libsupc++/new_opa.cc:86: undefined reference to
memalign'`No hits on a quick issue check for
memalign
.The library umm_malloc needs an enhancement to support an allocate-aligned option. Also, things will get complicated when incorporating the poison check feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've worked through adding an alignment option to the
umm_malloc
library. In the process, I have addressed an unreported alignment issue.umm_malloc
allocated data addresses always landed on an 8-byte aligned - 4-byte.Now I need to finish working through all the build options in
abi.cpp
andheap.cpp
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(edit: comment thread from above)
Can this be a config error? GCC / newlib / hal / etc.
Reading cppreference and some of the internal GCC stuff from '-faligned-new=X', 'Fundamental alignment' and max_align_t seem to come from 'long double' and 'long long' which are afaik 4-byte ones.
Currently, it does indeed show 8-byte
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...which is sort-of expected, as run-of-the-mill xtensa isa pdf does specify alignment req for long double and long long as 8. compiler here works according to spec, stack and heap alignment would stay consistent vs. current approach
no mention / explicit alignment things mentioned in the machine config, besides the
BIGGEST_ALIGNMENT
of 128 (bits)https://github.com/gcc-mirror/gcc/tree/master/gcc/config/xtensa
/ALIGN/
esp32 variants adhere to a similar standard. however, esp8266 rtos does use 4-byte heap alignment
(which I would assume works just fine for the most part)