mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-07 16:23:38 +03:00
Increase free IRAM (#5499)
* Move functions out of IRAM when possible umm_init() is called in OS startup after flash is enabled, and never again. cont_get_free_stack() shouldn't be called from IRQ. Don't inline _iram_read_byte() function. * Move system fault handler to flash, use new printf The __wrap_system_restart call has been in flash for quite a while and seems to be working fine. There were some support routines that were placed in IRAM (mistakenly thinking the wrap_restart caller was also in IRAM) which are now moved to flash. Clean up the printf code to use the new stdlib which handles PGM_P strings as format and arguments without any difficulty. * Make STR macro more unique Add double-underscores to some string assistance macros to avoid conflicts with user applications. * Use function, not macro, to save code space Save ~2KB final bin size by using a ets_printf_P function and not an inline macro. IRAM and HEAP unaffected. * Don't actually touch the SP in the dump Store a copy of the incoming stack pointer in the postmortem in order to avoid actually changing the SP when a crash happend in BearSSL. * Make C++ uncaught exceptions explicit Use the term "Unhandled C++ exception" instead of just "Unhandled exception" to make it clear such crashes are caused by a C++ throw and now a system exception.
This commit is contained in:
parent
6324241e36
commit
8e46a3371d
@ -47,7 +47,8 @@ int ICACHE_RAM_ATTR cont_check(cont_t* cont) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR cont_get_free_stack(cont_t* cont) {
|
||||
// No need for this to be in IRAM, not expected to be IRQ called
|
||||
int cont_get_free_stack(cont_t* cont) {
|
||||
uint32_t *head = cont->stack;
|
||||
int freeWords = 0;
|
||||
|
||||
|
@ -62,32 +62,25 @@ extern void __custom_crash_callback( struct rst_info * rst_info, uint32_t stack,
|
||||
|
||||
extern void custom_crash_callback( struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) __attribute__ ((weak, alias("__custom_crash_callback")));
|
||||
|
||||
// Single, non-inlined copy of pgm_read_byte to save IRAM space (as this is not timing critical)
|
||||
static char ICACHE_RAM_ATTR iram_read_byte (const char *addr) {
|
||||
return pgm_read_byte(addr);
|
||||
}
|
||||
|
||||
// Required to output the s_panic_file, it's stored in PMEM
|
||||
#define ets_puts_P(pstr) \
|
||||
{ \
|
||||
char c; \
|
||||
do { \
|
||||
c = iram_read_byte(pstr++); \
|
||||
if (c) ets_putc(c); \
|
||||
} while (c); \
|
||||
}
|
||||
|
||||
// Place these strings in .text because the SPI interface may be in bad shape during an exception.
|
||||
#define ets_printf_P(str, ...) \
|
||||
{ \
|
||||
static const char istr[] ICACHE_RAM_ATTR = (str); \
|
||||
char mstr[sizeof(str)]; \
|
||||
for (size_t i=0; i < sizeof(str); i++) mstr[i] = iram_read_byte(&istr[i]); \
|
||||
ets_printf(mstr, ##__VA_ARGS__); \
|
||||
// Prints need to use our library function to allow for file and function
|
||||
// to be safely accessed from flash. This function encapsulates snprintf()
|
||||
// [which by definition will 0-terminate] and dumping to the UART
|
||||
static void ets_printf_P(const char *str, ...) {
|
||||
char destStr[160];
|
||||
char *c = destStr;
|
||||
va_list argPtr;
|
||||
va_start(argPtr, str);
|
||||
vsnprintf(destStr, sizeof(destStr), str, argPtr);
|
||||
va_end(argPtr);
|
||||
while (*c) {
|
||||
ets_putc(*(c++));
|
||||
}
|
||||
}
|
||||
|
||||
void __wrap_system_restart_local() {
|
||||
register uint32_t sp asm("a1");
|
||||
uint32_t sp_dump = sp;
|
||||
|
||||
if (gdb_present()) {
|
||||
/* When GDBStub is present, exceptions are handled by GDBStub,
|
||||
@ -110,28 +103,24 @@ void __wrap_system_restart_local() {
|
||||
ets_install_putc1(&uart_write_char_d);
|
||||
|
||||
if (s_panic_line) {
|
||||
ets_printf_P("\nPanic ");
|
||||
ets_puts_P(s_panic_file); // This is in PROGMEM, need special output because ets_printf can't handle ROM parameters
|
||||
ets_printf_P(":%d %s", s_panic_line, s_panic_func);
|
||||
ets_printf_P(PSTR("\nPanic %S:%d %S"), s_panic_file, s_panic_line, s_panic_func);
|
||||
if (s_panic_what) {
|
||||
ets_printf_P(": Assertion '");
|
||||
ets_puts_P(s_panic_what); // This is also in PMEM
|
||||
ets_printf_P("' failed.");
|
||||
ets_printf_P(PSTR(": Assertion '%S' failed."), s_panic_what);
|
||||
}
|
||||
ets_putc('\n');
|
||||
}
|
||||
else if (s_unhandled_exception) {
|
||||
ets_printf_P("\nUnhandled exception: %s\n", s_unhandled_exception);
|
||||
ets_printf_P(PSTR("\nUnhandled C++ exception: %S\n"), s_unhandled_exception);
|
||||
}
|
||||
else if (s_abort_called) {
|
||||
ets_printf_P("\nAbort called\n");
|
||||
ets_printf_P(PSTR("\nAbort called\n"));
|
||||
}
|
||||
else if (rst_info.reason == REASON_EXCEPTION_RST) {
|
||||
ets_printf_P("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n",
|
||||
ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"),
|
||||
rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc);
|
||||
}
|
||||
else if (rst_info.reason == REASON_SOFT_WDT_RST) {
|
||||
ets_printf_P("\nSoft WDT reset\n");
|
||||
ets_printf_P(PSTR("\nSoft WDT reset\n"));
|
||||
}
|
||||
|
||||
uint32_t cont_stack_start = (uint32_t) &(g_pcont->stack);
|
||||
@ -152,54 +141,53 @@ void __wrap_system_restart_local() {
|
||||
offset = 0x10;
|
||||
}
|
||||
|
||||
ets_printf_P("\n>>>stack>>>\n");
|
||||
ets_printf_P(PSTR("\n>>>stack>>>\n"));
|
||||
|
||||
if (sp > stack_thunk_get_stack_bot() && sp <= stack_thunk_get_stack_top()) {
|
||||
if (sp_dump > stack_thunk_get_stack_bot() && sp_dump <= stack_thunk_get_stack_top()) {
|
||||
// BearSSL we dump the BSSL second stack and then reset SP back to the main cont stack
|
||||
ets_printf_P("\nctx: bearssl \n");
|
||||
ets_printf_P("sp: %08x end: %08x offset: %04x\n", sp, stack_thunk_get_stack_top(), offset);
|
||||
print_stack(sp + offset, stack_thunk_get_stack_top());
|
||||
ets_printf_P(PSTR("\nctx: bearssl\nsp: %08x end: %08x offset: %04x\n"), sp_dump, stack_thunk_get_stack_top(), offset);
|
||||
print_stack(sp_dump + offset, stack_thunk_get_stack_top());
|
||||
offset = 0; // No offset needed anymore, the exception info was stored in the bssl stack
|
||||
sp = stack_thunk_get_cont_sp();
|
||||
sp_dump = stack_thunk_get_cont_sp();
|
||||
}
|
||||
|
||||
if (sp > cont_stack_start && sp < cont_stack_end) {
|
||||
ets_printf_P("\nctx: cont \n");
|
||||
if (sp_dump > cont_stack_start && sp_dump < cont_stack_end) {
|
||||
ets_printf_P(PSTR("\nctx: cont\n"));
|
||||
stack_end = cont_stack_end;
|
||||
}
|
||||
else {
|
||||
ets_printf_P("\nctx: sys \n");
|
||||
ets_printf_P(PSTR("\nctx: sys\n"));
|
||||
stack_end = 0x3fffffb0;
|
||||
// it's actually 0x3ffffff0, but the stuff below ets_run
|
||||
// is likely not really relevant to the crash
|
||||
}
|
||||
|
||||
ets_printf_P("sp: %08x end: %08x offset: %04x\n", sp, stack_end, offset);
|
||||
ets_printf_P(PSTR("sp: %08x end: %08x offset: %04x\n"), sp_dump, stack_end, offset);
|
||||
|
||||
print_stack(sp + offset, stack_end);
|
||||
print_stack(sp_dump + offset, stack_end);
|
||||
|
||||
ets_printf_P("<<<stack<<<\n");
|
||||
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) {
|
||||
ets_printf_P("\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);
|
||||
}
|
||||
|
||||
custom_crash_callback( &rst_info, sp + offset, stack_end );
|
||||
custom_crash_callback( &rst_info, sp_dump + offset, stack_end );
|
||||
|
||||
delayMicroseconds(10000);
|
||||
__real_system_restart_local();
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_RAM_ATTR print_stack(uint32_t start, uint32_t end) {
|
||||
static void print_stack(uint32_t start, uint32_t end) {
|
||||
for (uint32_t pos = start; pos < end; pos += 0x10) {
|
||||
uint32_t* values = (uint32_t*)(pos);
|
||||
|
||||
// rough indicator: stack frames usually have SP saved as the second word
|
||||
bool looksLikeStackFrame = (values[2] == pos + 0x10);
|
||||
|
||||
ets_printf_P("%08x: %08x %08x %08x %08x %c\n",
|
||||
ets_printf_P(PSTR("%08x: %08x %08x %08x %08x %c\n"),
|
||||
pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame)?'<':' ');
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
|
||||
#include <core_version.h>
|
||||
|
||||
#define STRHELPER(x) #x
|
||||
#define STR(x) STRHELPER(x)
|
||||
#define __STRHELPER(x) #x
|
||||
#define __STR(x) __STRHELPER(x)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C++"
|
||||
@ -111,7 +111,7 @@ namespace esp8266 {
|
||||
constexpr
|
||||
int coreVersionMajor ()
|
||||
{
|
||||
return conststr::parseNthInteger(STR(ARDUINO_ESP8266_GIT_DESC), 0);
|
||||
return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -120,7 +120,7 @@ int coreVersionMajor ()
|
||||
constexpr
|
||||
int coreVersionMinor ()
|
||||
{
|
||||
return conststr::parseNthInteger(STR(ARDUINO_ESP8266_GIT_DESC), 1);
|
||||
return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -129,7 +129,7 @@ int coreVersionMinor ()
|
||||
constexpr
|
||||
int coreVersionRevision ()
|
||||
{
|
||||
return conststr::parseNthInteger(STR(ARDUINO_ESP8266_GIT_DESC), 2);
|
||||
return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 2);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -139,7 +139,7 @@ int coreVersionRevision ()
|
||||
constexpr
|
||||
int coreVersionSubRevision ()
|
||||
{
|
||||
return conststr::parseNthInteger(STR(ARDUINO_ESP8266_GIT_DESC), 3);
|
||||
return conststr::parseNthInteger(__STR(ARDUINO_ESP8266_GIT_DESC), 3);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1202,8 +1202,9 @@ static unsigned short int umm_assimilate_down( unsigned short int c, unsigned sh
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void umm_init( void ) {
|
||||
/* This function called only one time during OS startup after flash is */
|
||||
/* enabled. No need to keep it in IRAM. */
|
||||
void ICACHE_FLASH_ATTR umm_init( void ) {
|
||||
/* init heap pointer and size, and memset it to 0 */
|
||||
umm_heap = (umm_block *)UMM_MALLOC_CFG__HEAP_ADDR;
|
||||
umm_numblocks = (UMM_MALLOC_CFG__HEAP_SIZE / sizeof(umm_block));
|
||||
|
Loading…
x
Reference in New Issue
Block a user