Skip to content

Commit 6088147

Browse files
committed
Improve caller address reporting to indicate address in
sketch or library. Enhanced DEBUG_ESP_OOM to track OOM events when C++ Exceptions: "enabled" is selected. Report more interesting higher level code address as caller instead of GCC C++ module. Enhanced poison neighbor reporting to distinguish between allocation vs. neighbor failing poison check. For draft version, added debug macro HEAP_DEBUG_PROBE_PSFLC_CB to aid in evaluating caller address results.
1 parent 4ec3c81 commit 6088147

File tree

4 files changed

+83
-40
lines changed

4 files changed

+83
-40
lines changed

cores/esp8266/abi.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,46 @@
2424
using __cxxabiv1::__guard;
2525

2626
// Debugging helper, last allocation which returned NULL
27-
extern "C" void *_heap_abi_malloc(size_t size, bool unhandle, const void* const caller);
27+
extern "C" void *_heap_abi_malloc(size_t size, bool unhandled, const void* const caller);
2828

2929
extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__));
3030
extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__));
3131

32-
#if !defined(__cpp_exceptions)
32+
#if defined(__cpp_exceptions) && (defined(DEBUG_ESP_OOM) || defined(DEBUG_ESP_PORT))
33+
/*
34+
When built with C++ Exceptions: "enabled", track caller address of Last OOM.
35+
* For debug build, force enable Last OOM tracking.
36+
* With the option "DEBUG_ESP_OOM," always do Last OOM tracking.
37+
* Otherwise, disable Last OOM tracking. The build relies on the weak link to
38+
the default C++ exception handler.
39+
*/
40+
41+
// Debug replacement adaptation from ".../new_op.cc".
42+
using std::new_handler;
43+
using std::bad_alloc;
44+
45+
void * operator new (std::size_t size)
46+
{
47+
void *p;
48+
49+
/* malloc (0) is unpredictable; avoid it. */
50+
if (__builtin_expect(size == 0, false)) {
51+
size = 1;
52+
}
53+
54+
while (0 == (p = _heap_abi_malloc(size, false, __builtin_return_address(0)))) {
55+
new_handler handler = std::get_new_handler();
56+
if (!handler) {
57+
throw(bad_alloc());
58+
}
59+
handler();
60+
}
61+
62+
return p;
63+
}
64+
#elif !defined(__cpp_exceptions)
65+
// When doing builds with C++ Exceptions "disabled", always save details of
66+
// the last OOM event.
3367

3468
// overwrite weak operators new/new[] definitions
3569

cores/esp8266/heap.cpp

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
* tested. This option is enabled when Tools->Debug: Serial is selected or
4646
* Tools->Debug level: "CORE" is selected. While coverage is not 100%, a
4747
* sketch is less likely to have strange behavior from heavy heap access with
48-
* interrupts disabled.
48+
* interrupts disabled. If needed, continue reading "UMM_POISON_CHECK" for
49+
* more details.
4950
*
5051
* * UMM_POISON_CHECK - Adds and presets 4 bytes of poison at the beginning and
5152
* end of each allocation. For each Heap API call, a complete sweep through
@@ -101,6 +102,8 @@ extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((
101102
#include <sys/reent.h>
102103
#include <user_interface.h>
103104

105+
#include "heap_cb.h"
106+
104107
extern "C" {
105108

106109
///////////////////////////////////////////////////////////////////////////////
@@ -133,7 +136,7 @@ extern "C" {
133136
#undef realloc
134137
#undef free
135138

136-
#elif defined(DEBUG_ESP_OOM) || defined(UMM_INTEGRITY_CHECK)
139+
#elif defined(DEBUG_ESP_OOM) || defined(UMM_INTEGRITY_CHECK) || defined(HEAP_DEBUG_PROBE_PSFLC_CB)
137140
// All other debug wrappers that do not require handling poison
138141
#define UMM_MALLOC_FL(s,f,l,c) umm_malloc(s)
139142
#define UMM_CALLOC_FL(n,s,f,l,c) umm_calloc(n,s)
@@ -197,10 +200,11 @@ extern "C" {
197200

198201

199202
///////////////////////////////////////////////////////////////////////////////
200-
// OOM - this structure variable is always in use by abi.cpp
201-
//
202-
// Always track last failed caller and size requested
203+
// OOM - this structure variable is always in use by abi.cpp - except for
204+
// C++ Exceptions "enabled" builds.
203205
//
206+
// When building with C++ Exceptions "disabled" or debug build,
207+
// always track last failed caller and size requested
204208
#if defined(DEBUG_ESP_OOM)
205209
struct umm_last_fail_alloc {
206210
const void *addr;
@@ -210,6 +214,8 @@ struct umm_last_fail_alloc {
210214
} _umm_last_fail_alloc = {NULL, 0, NULL, 0};
211215

212216
#else
217+
// Note for the least used case "(defined(__cpp_exceptions) &&
218+
// !defined(DEBUG_ESP_OOM))", we only capture details for LIBC calls.
213219
struct umm_last_fail_alloc {
214220
const void *addr;
215221
size_t size;
@@ -268,6 +274,7 @@ static bool IRAM_ATTR oom_check__log_last_fail_atomic_psflc(void *ptr, size_t si
268274
_umm_last_fail_alloc.line = line;
269275
print_loc(size, file, line, caller);
270276
xt_wsr_ps(saved_ps);
277+
_HEAP_DEBUG_PROBE_PSFLC_CB(heap_oom_cb_id, ptr, size, file, line, caller);
271278
return false;
272279
}
273280
return true;
@@ -276,18 +283,19 @@ static bool IRAM_ATTR oom_check__log_last_fail_atomic_psflc(void *ptr, size_t si
276283
#define OOM_CHECK__LOG_LAST_FAIL_LITE_FL(p, s, f, l, c) ({ (void)p, (void)s, (void)f; (void)l; (void)c; true; })
277284

278285
#elif defined(ENABLE_THICK_DEBUG_WRAPPERS)
279-
static bool IRAM_ATTR oom_check__log_last_fail_psc(void *ptr, size_t size, const void* caller) {
286+
static bool IRAM_ATTR oom_check__log_last_fail_atomic_psc(void *ptr, size_t size, const void* caller) {
280287
if (0 != (size) && 0 == ptr) {
281288
// Need to ensure changes to umm_last_fail_alloc are atomic.
282289
uint32_t saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL);
283290
_umm_last_fail_alloc.addr = caller;
284291
_umm_last_fail_alloc.size = size;
285292
xt_wsr_ps(saved_ps);
293+
_HEAP_DEBUG_PROBE_PSFLC_CB(heap_oom_cb_id, ptr, size, NULL, 0, caller);
286294
return false;
287295
}
288296
return true;
289297
}
290-
#define OOM_CHECK__LOG_LAST_FAIL_FL(p, s, f, l, c) oom_check__log_last_fail_psc(p, s, c)
298+
#define OOM_CHECK__LOG_LAST_FAIL_FL(p, s, f, l, c) oom_check__log_last_fail_atomic_psc(p, s, c)
291299
#define OOM_CHECK__LOG_LAST_FAIL_LITE_FL(p, s, f, l, c) ({ (void)p, (void)s, (void)f; (void)l; (void)c; true; })
292300

293301
#else
@@ -298,6 +306,7 @@ static bool oom_check__log_last_fail_psc(void *ptr, size_t size, const void* cal
298306
if (0 != (size) && 0 == ptr) {
299307
_umm_last_fail_alloc.addr = caller;
300308
_umm_last_fail_alloc.size = size;
309+
_HEAP_DEBUG_PROBE_PSFLC_CB(heap_oom_cb_id, ptr, size, NULL, 0, caller);
301310
return false;
302311
}
303312
return true;
@@ -356,6 +365,7 @@ void* IRAM_ATTR _heap_pvPortMalloc(size_t size, const char* file, int line, cons
356365
INTEGRITY_CHECK__PANIC_FL(file, line, caller);
357366
POISON_CHECK__PANIC_FL(file, line, caller);
358367
void* ret = UMM_MALLOC_FL(size, file, line, caller);
368+
ret = _HEAP_DEBUG_PROBE_PSFLC_CB(heap_malloc_cb_id, ret, size, file, line, caller);
359369
OOM_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line, caller);
360370
return ret;
361371
}
@@ -366,14 +376,17 @@ void* IRAM_ATTR _heap_pvPortCalloc(size_t count, size_t size, const char* file,
366376
POISON_CHECK__PANIC_FL(file, line, caller);
367377
size_t total_size = umm_umul_sat(count, size);
368378
void* ret = UMM_CALLOC_FL(1, total_size, file, line, caller);
379+
ret = _HEAP_DEBUG_PROBE_PSFLC_CB(heap_calloc_cb_id, ret, size, file, line, caller);
369380
OOM_CHECK__LOG_LAST_FAIL_FL(ret, total_size, file, line, caller);
370381
return ret;
371382
}
372383

373384
void* IRAM_ATTR _heap_pvPortRealloc(void *ptr, size_t size, const char* file, int line, const void *caller)
374385
{
375386
INTEGRITY_CHECK__PANIC_FL(file, line, caller);
387+
ptr = _HEAP_DEBUG_PROBE_PSFLC_CB(heap_realloc_in_cb_id, ptr, size, file, line, caller);
376388
void* ret = UMM_REALLOC_FL(ptr, size, file, line, caller);
389+
ret = _HEAP_DEBUG_PROBE_PSFLC_CB(heap_realloc_out_cb_id, ret, size, file, line, caller);
377390
POISON_CHECK__PANIC_FL(file, line, caller);
378391
OOM_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line, caller);
379392
return ret;
@@ -382,6 +395,7 @@ void* IRAM_ATTR _heap_pvPortRealloc(void *ptr, size_t size, const char* file, in
382395
void IRAM_ATTR _heap_vPortFree(void *ptr, const char* file, int line, [[maybe_unused]] const void *caller)
383396
{
384397
INTEGRITY_CHECK__PANIC_FL(file, line, caller);
398+
ptr = _HEAP_DEBUG_PROBE_PSFLC_CB(heap_free_cb_id, ptr, 0, file, line, caller);
385399
UMM_FREE_FL(ptr, file, line, caller);
386400
POISON_CHECK__PANIC_FL(file, line, caller);
387401
}
@@ -579,10 +593,10 @@ void system_show_malloc(void)
579593
#endif
580594
}
581595

582-
#if !defined(__cpp_exceptions)
596+
#if defined(DEBUG_ESP_OOM) || !defined(__cpp_exceptions) || defined(DEBUG_ESP_PORT)
583597
///////////////////////////////////////////////////////////////////////////////
584598
// heap allocator for "new" (ABI) - To support collecting OOM info, always defined
585-
void* _heap_abi_malloc(size_t size, bool unhandle, const void* caller)
599+
void* _heap_abi_malloc(size_t size, bool unhandled, const void* caller)
586600
{
587601
[[maybe_unused]] const char *file = NULL;
588602
[[maybe_unused]] const int line = 0;
@@ -591,16 +605,15 @@ void* _heap_abi_malloc(size_t size, bool unhandle, const void* caller)
591605
INTEGRITY_CHECK__PANIC_FL(file, line, caller);
592606
POISON_CHECK__PANIC_FL(file, line, caller);
593607
void* ret = UMM_MALLOC_FL(size, file, line, caller);
594-
if (!OOM_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line, caller) && unhandle) {
595-
__unhandled_exception(PSTR("OOM"));
596-
}
608+
bool ok = OOM_CHECK__LOG_LAST_FAIL_FL(ret, size, file, line, caller);
597609
#else
598610
void* ret = UMM_MALLOC(size);
599-
// Always do some level of OOM check
600-
if (!OOM_CHECK__LOG_LAST_FAIL_LITE_FL(ret, size, file, line, caller) && unhandle) {
611+
// minimum OOM check
612+
bool ok = OOM_CHECK__LOG_LAST_FAIL_LITE_FL(ret, size, file, line, caller)
613+
#endif
614+
if (!ok && unhandled) {
601615
__unhandled_exception(PSTR("OOM"));
602616
}
603-
#endif
604617
return ret;
605618
}
606619
#endif
@@ -617,23 +630,7 @@ void* _heap_abi_malloc(size_t size, bool unhandle, const void* caller)
617630
// Replacement C++ delete operator to capture callers address
618631
//
619632
#include <bits/c++config.h>
620-
621-
#if !_GLIBCXX_HOSTED
622-
// A freestanding C runtime may not provide "free" -- but there is no
623-
// other reasonable way to implement "operator delete".
624-
namespace std
625-
{
626-
_GLIBCXX_BEGIN_NAMESPACE_VERSION
627-
extern "C" void free(void*);
628-
_GLIBCXX_END_NAMESPACE_VERSION
629-
} // namespace
630-
#pragma message("!_GLIBCXX_HOSTED")
631-
#else
632-
// #pragma message("_GLIBCXX_HOSTED")
633-
// This is the path taken
634633
#include <cstdlib>
635-
#endif
636-
637634
#include "new"
638635

639636
// The sized deletes are defined in other files.

cores/esp8266/umm_malloc/umm_local.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ static bool check_poison_neighbors(umm_heap_context_t *_context, uint16_t cur) {
8686
#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
8787

8888
/* ------------------------------------------------------------------------ */
89+
#include "heap_cb.h"
8990

9091
static void *get_unpoisoned_check_neighbors(const void *vptr, const char *file, int line, const void *caller) {
9192
uintptr_t ptr = (uintptr_t)vptr;
@@ -97,24 +98,34 @@ static void *get_unpoisoned_check_neighbors(const void *vptr, const char *file,
9798
#if defined(UMM_POISON_CHECK_LITE)
9899
UMM_CRITICAL_DECL(id_poison);
99100
uint16_t c;
100-
bool poison = false;
101+
bool poison = true;
101102
umm_heap_context_t *_context = _umm_get_ptr_context((void *)ptr);
102103
if (_context) {
103104

104105
/* Figure out which block we're in. Note the use of truncated division... */
105106
c = (ptr - (uintptr_t)(&(_context->heap[0]))) / sizeof(umm_block);
106107

107108
UMM_CRITICAL_ENTRY(id_poison);
108-
poison =
109-
check_poison_block(&UMM_BLOCK(c)) &&
110-
check_poison_neighbors(_context, c);
109+
if (! check_poison_block(&UMM_BLOCK(c))) {
110+
DBGLOG_ERROR("Allocation address %p\n", vptr);
111+
size_t size = *(size_t *)ptr;
112+
_HEAP_DEBUG_PROBE_PSFLC_CB(heap_poison_lite_cb_id, (void *)ptr, size, file, line, caller);
113+
poison = false;
114+
} else
115+
if (! check_poison_neighbors(_context, c)) {
116+
DBGLOG_ERROR("This bad block is in a neighbor allocation near: %p\n", vptr);
117+
_HEAP_DEBUG_PROBE_PSFLC_CB(heap_poison_lite_neighbor_cb_id, (void *)ptr, 0, file, line, caller);
118+
poison = false;
119+
}
111120
UMM_CRITICAL_EXIT(id_poison);
112121
} else {
113122
DBGLOG_ERROR("\nPointer %p is not a Heap address.\n", vptr);
123+
_HEAP_DEBUG_PROBE_PSFLC_CB(heap_poison_lite_addr_cb_id, (void *)ptr, 0, file, line, caller);
124+
poison = false;
114125
}
115126

116127
if (!poison) {
117-
DBGLOG_ERROR("called from %p\n", caller);
128+
DBGLOG_ERROR("Called from %p\n", caller);
118129
if (file) {
119130
__panic_func(file, line, "");
120131
} else {

cores/esp8266/umm_malloc/umm_local.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ static size_t umm_uadd_sat(const size_t a, const size_t b);
3434
// #define DBGLOG_FORCE(force, format, ...) {if(force) {::printf(PSTR(format), ## __VA_ARGS__);}}
3535

3636

37-
#if defined(DEBUG_ESP_OOM) || defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK)
37+
#if defined(DEBUG_ESP_OOM) || defined(UMM_POISON_CHECK) \
38+
|| defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK) \
39+
|| defined(HEAP_DEBUG_PROBE_PSFLC_CB) || defined(_HEAP_DEBUG_PROBE_PSFLC_CB)
3840
#else
39-
4041
#define umm_malloc(s) malloc(s)
4142
#define umm_calloc(n,s) calloc(n,s)
4243
#define umm_realloc(p,s) realloc(p,s)

0 commit comments

Comments
 (0)