Skip to content

Commit 831ef52

Browse files
earlephilhowerbryceschober
authored andcommitted
Record last failed alloc, dump on a panic (esp8266#4220)
At runtime, whenever a realloc, malloc, calloc, or new call fails to find enough memory, record the calling function and size requested. Does not print anything or call any heavyweight functions on this, as it is legal to return NULL to an alloc-type call. If the main application handles this NULL properly, there should be no panic and nothing different will happen. If the main application panics() at any later point in time, this record will be printed out as part of the crash log for processing with an updated EspExceptionDecoder.java. This adds 2-3 instructions overhead in the normal case, and around 10-12 instructions in the failing case, and requires an additional 8 bytes of .BSS to function. Only a single address is kept, the final failing malloc-type function call before a panic, but it is trivial to extend to a fifo with little overhead in the common, non-failing case. (cherry picked from commit 4a958c8)
1 parent 06bfc08 commit 831ef52

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

cores/esp8266/abi.cpp

+16-2
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,28 @@
2424

2525
using __cxxabiv1::__guard;
2626

27+
// Debugging helper, last allocation which returned NULL
28+
extern void *umm_last_fail_alloc_addr;
29+
extern int umm_last_fail_alloc_size;
30+
2731
void *operator new(size_t size)
2832
{
29-
return malloc(size);
33+
void *ret = malloc(size);
34+
if (0 != size && 0 == ret) {
35+
umm_last_fail_alloc_addr = __builtin_return_address(0);
36+
umm_last_fail_alloc_size = size;
37+
}
38+
return ret;
3039
}
3140

3241
void *operator new[](size_t size)
3342
{
34-
return malloc(size);
43+
void *ret = malloc(size);
44+
if (0 != size && 0 == ret) {
45+
umm_last_fail_alloc_addr = __builtin_return_address(0);
46+
umm_last_fail_alloc_size = size;
47+
}
48+
return ret;
3549
}
3650

3751
void operator delete(void * ptr)

cores/esp8266/core_esp8266_postmortem.c

+10
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ static void uart_write_char_d(char c);
4545
static void uart0_write_char_d(char c);
4646
static void uart1_write_char_d(char c);
4747
static void print_stack(uint32_t start, uint32_t end);
48+
49+
// From UMM, the last caller of a malloc/realloc/calloc which failed:
50+
extern void *umm_last_fail_alloc_addr;
51+
extern int umm_last_fail_alloc_size;
52+
4853
static void raise_exception() __attribute__((noreturn));
4954

5055
extern void __custom_crash_callback( struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) {
@@ -137,6 +142,11 @@ void __wrap_system_restart_local() {
137142

138143
print_stack(sp + offset, stack_end);
139144

145+
// Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address
146+
if (umm_last_fail_alloc_addr) {
147+
ets_printf("\nlast failed alloc call: %08X(%d)\n", (uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size);
148+
}
149+
140150
custom_crash_callback( &rst_info, sp + offset, stack_end );
141151

142152
delayMicroseconds(10000);

cores/esp8266/heap.c

+22-3
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@
88
#include <c_types.h>
99
#include <sys/reent.h>
1010

11+
// Debugging helper, last allocation which returned NULL
12+
void *umm_last_fail_alloc_addr = NULL;
13+
int umm_last_fail_alloc_size = 0;
14+
1115
void* _malloc_r(struct _reent* unused, size_t size)
1216
{
1317
(void) unused;
14-
return malloc(size);
18+
void *ret = malloc(size);
19+
if (0 != size && 0 == ret) {
20+
umm_last_fail_alloc_addr = __builtin_return_address(0);
21+
umm_last_fail_alloc_size = size;
22+
}
23+
return ret;
1524
}
1625

1726
void _free_r(struct _reent* unused, void* ptr)
@@ -23,13 +32,23 @@ void _free_r(struct _reent* unused, void* ptr)
2332
void* _realloc_r(struct _reent* unused, void* ptr, size_t size)
2433
{
2534
(void) unused;
26-
return realloc(ptr, size);
35+
void *ret = realloc(ptr, size);
36+
if (0 != size && 0 == ret) {
37+
umm_last_fail_alloc_addr = __builtin_return_address(0);
38+
umm_last_fail_alloc_size = size;
39+
}
40+
return ret;
2741
}
2842

2943
void* _calloc_r(struct _reent* unused, size_t count, size_t size)
3044
{
3145
(void) unused;
32-
return calloc(count, size);
46+
void *ret = calloc(count, size);
47+
if (0 != (count * size) && 0 == ret) {
48+
umm_last_fail_alloc_addr = __builtin_return_address(0);
49+
umm_last_fail_alloc_size = count * size;
50+
}
51+
return ret;
3352
}
3453

3554
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line)

cores/esp8266/umm_malloc/umm_malloc.c

+16
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,10 @@
499499

500500
#include "umm_malloc_cfg.h" /* user-dependent */
501501

502+
// From UMM, the last caller of a malloc/realloc/calloc which failed:
503+
extern void *umm_last_fail_alloc_addr;
504+
extern int umm_last_fail_alloc_size;
505+
502506
#ifndef UMM_FIRST_FIT
503507
# ifndef UMM_BEST_FIT
504508
# define UMM_BEST_FIT
@@ -1661,6 +1665,10 @@ void *umm_malloc( size_t size ) {
16611665
size += POISON_SIZE(size);
16621666

16631667
ret = _umm_malloc( size );
1668+
if (0 != size && 0 == ret) {
1669+
umm_last_fail_alloc_addr = __builtin_return_address(0);
1670+
umm_last_fail_alloc_size = size;
1671+
}
16641672

16651673
ret = GET_POISONED(ret, size);
16661674

@@ -1688,6 +1696,10 @@ void *umm_calloc( size_t num, size_t item_size ) {
16881696
if (ret) {
16891697
memset(ret, 0x00, size);
16901698
}
1699+
if (0 != size && 0 == ret) {
1700+
umm_last_fail_alloc_addr = __builtin_return_address(0);
1701+
umm_last_fail_alloc_size = size;
1702+
}
16911703

16921704
ret = GET_POISONED(ret, size);
16931705

@@ -1713,6 +1725,10 @@ void *umm_realloc( void *ptr, size_t size ) {
17131725

17141726
size += POISON_SIZE(size);
17151727
ret = _umm_realloc( ptr, size );
1728+
if (0 != size && 0 == ret) {
1729+
umm_last_fail_alloc_addr = __builtin_return_address(0);
1730+
umm_last_fail_alloc_size = size;
1731+
}
17161732

17171733
ret = GET_POISONED(ret, size);
17181734

0 commit comments

Comments
 (0)