mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
Record last failed alloc, dump on a panic (#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.
This commit is contained in:
parent
06352ab0e1
commit
4a958c8444
@ -24,14 +24,28 @@
|
||||
|
||||
using __cxxabiv1::__guard;
|
||||
|
||||
// Debugging helper, last allocation which returned NULL
|
||||
extern void *umm_last_fail_alloc_addr;
|
||||
extern int umm_last_fail_alloc_size;
|
||||
|
||||
void *operator new(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
void *ret = malloc(size);
|
||||
if (0 != size && 0 == ret) {
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *operator new[](size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
void *ret = malloc(size);
|
||||
if (0 != size && 0 == ret) {
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void operator delete(void * ptr)
|
||||
|
@ -45,6 +45,11 @@ static void uart_write_char_d(char c);
|
||||
static void uart0_write_char_d(char c);
|
||||
static void uart1_write_char_d(char c);
|
||||
static void print_stack(uint32_t start, uint32_t end);
|
||||
|
||||
// From UMM, the last caller of a malloc/realloc/calloc which failed:
|
||||
extern void *umm_last_fail_alloc_addr;
|
||||
extern int umm_last_fail_alloc_size;
|
||||
|
||||
static void raise_exception() __attribute__((noreturn));
|
||||
|
||||
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() {
|
||||
|
||||
print_stack(sp + offset, stack_end);
|
||||
|
||||
// Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address
|
||||
if (umm_last_fail_alloc_addr) {
|
||||
ets_printf("\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 );
|
||||
|
||||
delayMicroseconds(10000);
|
||||
|
@ -8,10 +8,19 @@
|
||||
#include <c_types.h>
|
||||
#include <sys/reent.h>
|
||||
|
||||
// Debugging helper, last allocation which returned NULL
|
||||
void *umm_last_fail_alloc_addr = NULL;
|
||||
int umm_last_fail_alloc_size = 0;
|
||||
|
||||
void* _malloc_r(struct _reent* unused, size_t size)
|
||||
{
|
||||
(void) unused;
|
||||
return malloc(size);
|
||||
void *ret = malloc(size);
|
||||
if (0 != size && 0 == ret) {
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _free_r(struct _reent* unused, void* ptr)
|
||||
@ -23,13 +32,23 @@ void _free_r(struct _reent* unused, void* ptr)
|
||||
void* _realloc_r(struct _reent* unused, void* ptr, size_t size)
|
||||
{
|
||||
(void) unused;
|
||||
return realloc(ptr, size);
|
||||
void *ret = realloc(ptr, size);
|
||||
if (0 != size && 0 == ret) {
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* _calloc_r(struct _reent* unused, size_t count, size_t size)
|
||||
{
|
||||
(void) unused;
|
||||
return calloc(count, size);
|
||||
void *ret = calloc(count, size);
|
||||
if (0 != (count * size) && 0 == ret) {
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = count * size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line)
|
||||
|
@ -499,6 +499,10 @@
|
||||
|
||||
#include "umm_malloc_cfg.h" /* user-dependent */
|
||||
|
||||
// From UMM, the last caller of a malloc/realloc/calloc which failed:
|
||||
extern void *umm_last_fail_alloc_addr;
|
||||
extern int umm_last_fail_alloc_size;
|
||||
|
||||
#ifndef UMM_FIRST_FIT
|
||||
# ifndef UMM_BEST_FIT
|
||||
# define UMM_BEST_FIT
|
||||
@ -1661,6 +1665,10 @@ void *umm_malloc( size_t size ) {
|
||||
size += POISON_SIZE(size);
|
||||
|
||||
ret = _umm_malloc( size );
|
||||
if (0 != size && 0 == ret) {
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
|
||||
ret = GET_POISONED(ret, size);
|
||||
|
||||
@ -1688,6 +1696,10 @@ void *umm_calloc( size_t num, size_t item_size ) {
|
||||
if (ret) {
|
||||
memset(ret, 0x00, size);
|
||||
}
|
||||
if (0 != size && 0 == ret) {
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
|
||||
ret = GET_POISONED(ret, size);
|
||||
|
||||
@ -1713,6 +1725,10 @@ void *umm_realloc( void *ptr, size_t size ) {
|
||||
|
||||
size += POISON_SIZE(size);
|
||||
ret = _umm_realloc( ptr, size );
|
||||
if (0 != size && 0 == ret) {
|
||||
umm_last_fail_alloc_addr = __builtin_return_address(0);
|
||||
umm_last_fail_alloc_size = size;
|
||||
}
|
||||
|
||||
ret = GET_POISONED(ret, size);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user