]> granicus.if.org Git - esp-idf/commitdiff
esp32: Adds C++ exceptions emergency pool size menuconfig option
authorAlexey Gerenkov <alexey@espressif.com>
Fri, 17 Nov 2017 09:38:19 +0000 (12:38 +0300)
committerAlexey Gerenkov <alexey@espressif.com>
Thu, 23 Nov 2017 03:14:11 +0000 (06:14 +0300)
Kconfig
components/cxx/test/test_cxx.cpp
components/esp32/cpu_start.c

diff --git a/Kconfig b/Kconfig
index c641882f840048b9cc0a49c48f1082604ecbb532..7e2596594b7a0112da9bc7eb3704efaacfa951bc 100644 (file)
--- 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
 
index 743a6869ded0076305b34723b35e31d3aeab58b2..64bf1f98600f4da87756b0e9c2c1f926eabf7b05 100644 (file)
@@ -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
index 0feed97bdf832d6e84f68e3c468ade0d9b9f2068..01fba08a0cfdd17c993f23bd0e1f27cc7a41f07b 100644 (file)
@@ -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