diff --git a/Kconfig b/Kconfig index c641882f8..7e2596594 100644 --- a/Kconfig +++ b/Kconfig @@ -93,7 +93,7 @@ config OPTIMIZATION_ASSERTIONS_DISABLED endchoice # assertions -config CXX_EXCEPTIONS +menuconfig CXX_EXCEPTIONS bool "Enable C++ exceptions" default n help @@ -102,8 +102,16 @@ config CXX_EXCEPTIONS Disabling this option disables C++ exception support in all compiled files, and any libstdc++ code which throws an exception will abort instead. - Enabling this option currently adds an additional 20KB of heap overhead, and 4KB of additional heap is allocated - the first time an exception is thrown in user code. + Enabling this option currently adds an additional ~500 bytes of heap overhead + when an exception is thrown in user code for the first time. + +config CXX_EXCEPTIONS_EMG_POOL_SIZE + int "Emergency Pool Size" + default 0 + depends on CXX_EXCEPTIONS + help + Size (in bytes) of the emergency memory pool for C++ exceptions. This pool will be used to allocate + memory for thrown exceptions when there is not enough memory on the heap. endmenu # Compiler Options diff --git a/components/cxx/test/test_cxx.cpp b/components/cxx/test/test_cxx.cpp index 743a6869d..64bf1f986 100644 --- a/components/cxx/test/test_cxx.cpp +++ b/components/cxx/test/test_cxx.cpp @@ -20,7 +20,7 @@ class Base { public: virtual ~Base() {} - virtual void foo() = 0; + virtual void foo() = 0; }; class Derived : public Base @@ -192,8 +192,13 @@ TEST_CASE("before scheduler has started, static initializers work correctly", "[ TEST_CASE("c++ exceptions work", "[cxx]") { - /* Note: This test currently trips the memory leak threshold - as libunwind allocates ~4KB of data on first exception. */ + /* Note: When first exception (in system) is thrown this test produces memory leaks report (~500 bytes): + - 392 bytes (can vary) as libunwind allocates memory to keep stack frames info to handle exceptions. + This info is kept until global destructors are called by __do_global_dtors_aux() + - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals + - 16 bytes are allocated by pthread_setspecific() which is called by __cxa_get_globals() to init TLS var for __cxa_eh_globals + - 88 bytes are allocated by pthread_setspecific() to init internal lock + */ int thrown_value; try { @@ -207,6 +212,60 @@ TEST_CASE("c++ exceptions work", "[cxx]") printf("OK?\n"); } +TEST_CASE("c++ exceptions emergency pool", "[cxx] [ignore]") +{ + /* Note: When first exception (in system) is thrown this test produces memory leaks report (~500 bytes): + - 392 bytes (can vary) as libunwind allocates memory to keep stack frames info to handle exceptions. + This info is kept until global destructors are called by __do_global_dtors_aux() + - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals + - 16 bytes are allocated by pthread_setspecific() which is called by __cxa_get_globals() to init TLS var for __cxa_eh_globals + - 88 bytes are allocated by pthread_setspecific() to init internal lock + */ + void **p, **pprev = NULL; + int thrown_value = 0; + // throw first exception to ensure that all initial allocations are made + try + { + throw 33; + } + catch (int e) + { + thrown_value = e; + } + TEST_ASSERT_EQUAL(33, thrown_value); + // consume all dynamic memory + while ((p = (void **)malloc(sizeof(void *)))) { + if (pprev) { + *p = pprev; + } else { + *p = NULL; + } + pprev = p; + } + try + { + throw 20; + } + catch (int e) + { + thrown_value = e; + printf("Got exception %d\n", thrown_value); + } +#if CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE > 0 + // free all memory + while (pprev) { + p = (void **)(*pprev); + free(pprev); + pprev = p; + } + TEST_ASSERT_EQUAL(20, thrown_value); +#else + // if emergency pool is disabled we should never get here, + // expect abort() due to lack of memory for new exception + TEST_ASSERT_TRUE(0 == 1); +#endif +} + #endif /* These test cases pull a lot of code from libstdc++ and are disabled for now diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 0feed97bd..01fba08a0 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -397,6 +397,13 @@ void start_cpu1_default(void) } #endif //!CONFIG_FREERTOS_UNICORE +#ifdef CONFIG_CXX_EXCEPTIONS +size_t __cxx_eh_arena_size_get() +{ + return CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE; +} +#endif + static void do_global_ctors(void) { #ifdef CONFIG_CXX_EXCEPTIONS