-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Heap: Improve debug caller address reporting #8720
base: master
Are you sure you want to change the base?
Conversation
lite refactoring of DEBUG_ESP_OOM printing Overall refactoring to make easier to follow. Added ABI changes includes atomic save of OOM debug info Added debug support for C++ "delete" operator Avoid identifying the wrapper code as the source of the Heap call. Each outer wrapper passes the caller's address down to the next level. Improves "caller" address reporting for OOM and Poison events. Previously, some OOM and Poison events reported info pointing to the thick heap wrapper rather than back to the caller. Update abi.cpp, heap.cpp and umm_local.c (umm_poison) to support caller parameter.
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.
Added DEBUG_ESP_WITHINISR to provide notification for ISRs using C++ 'new'/'delete' operators or LIBC calls using _malloc_r, ... Moved block of port related changes for UMM_POISON... from `umm_malloc_cfg.h` to `umm_malloc_cfgport.h`. Support for specifying both UMM_POISON_CHECK and UMM_POISON_CHECK_LITE at the same time did not work as expected. UMM_POISON_CHECK_LITE dominated in the build. Fix: Keep things simple prevent combining UMM_POISON_CHECK and UMM_POISON_CHECK_LITE. Defines for UMM_POISON_CHECK, UMM_POISON_CHECK_LITE, and UMM_POISON_NONE are checked for mutual exclusivity. For sketches with extremely limited resources, added UMM_POISON_NONE to prevent UMM_POISON_CHECK_LITE from being added to debug builds. Make UMM_POISON_CHECK_LITE fail messages more specific about the location. At free distinguishes between neighbor and allocation. And, identify the caller's address.
pvPortMalloc(,,,true) case. Improved comments.
…nto pr-heap-refactor3
…nto pr-heap-refactor3
…nto pr-heap-refactor3
…nto pr-heap-refactor3
Let me know if you prefer I factor out experimental |
If it is still a draft, probably makes sense to leave it out of the PR to reduce loc |
Updated umm_poison move from umm_malloc_cfg.h to umm_malloc_cfgport.h
in postmortem and printing OOM from Heap wrappers. Fixed crash within postmortem when OOM file is NULL Fixed printing OOM file name when not in ISR.
removed experimental debug extension as requested added forgotten OOM count to the postmortem report
resolved conflicts with umm_malloc_cfg.h and umm_malloc_cfgport.h
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated this description to reflect the current PR state: not ready
Still true?
cores/esp8266/abi.cpp
Outdated
void * operator new (std::size_t size) | ||
{ | ||
void *p; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(not necessary here, whole file-wide)
There is discrepancy in ptr *
style
void* a();
void *b();
void * c();
...
void foo() {
void *ptr;
}
...
Can feed this file to clang-format afterwards, though.
Original style prefers TYPE* NAME
, like most C++ libs in general
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be corrected now
cores/esp8266/heap.cpp
Outdated
// The sized deletes are defined in other files. | ||
#pragma GCC diagnostic ignored "-Wsized-deallocation" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is just below, though. I don't see a warning here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I finally remembered to remove this one. Some comments I found for Clang indicated that it was needed for older compilers.
ets_printf_P(PSTR("\nlast failed alloc call: %08X(%d)@%S:%d\n"), | ||
(uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size, | ||
umm_last_fail_alloc_file, umm_last_fail_alloc_line); | ||
ets_printf_P(PSTR("\nlast failed alloc call: 0x%08X(%d), File: %S:%d\n"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%s
not %S
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%S
, umm_last_fail_alloc_file
can be a PROGMEM
string
At least some SDK file name strings are in .irom.text
.
cores/esp8266/heap.cpp
Outdated
#define UMM_MALLOC_FL(s,f,l,c) umm_poison_malloc(s) | ||
#define UMM_CALLOC_FL(n,s,f,l,c) umm_poison_calloc(n,s) | ||
#define UMM_REALLOC_FL(p,s,f,l,c) umm_poison_realloc_flc(p,s,f,l,c) | ||
#define UMM_FREE_FL(p,f,l,c) umm_poison_free_flc(p,f,l,c) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_FL
-> _FLC
? same as standalone funcs named now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the backport remnants. Cleanup UMM_MALLOC_FL and UMM_CALLOC_FL.
should now be fixed.
cores/esp8266/heap.cpp
Outdated
#define UMM_MALLOC_FL(s,f,l,c) umm_poison_malloc(s) | ||
#define UMM_CALLOC_FL(n,s,f,l,c) umm_poison_calloc(n,s) | ||
#define UMM_REALLOC_FL(p,s,f,l,c) umm_poison_realloc_flc(p,s,f,l,c) | ||
#define UMM_FREE_FL(p,f,l,c) umm_poison_free_flc(p,f,l,c) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense to use struct à la source_location
to the reduce number of args?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! That would be convenient.
Thoughts and Concerns:
- Can
source_location
be used from.c
modules? - Will function names be stored in
PROGMEM
or DRAM? - Their example shows a longer (full definition) string than we have used for the function name, which could cause build size issues for debug builds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I am not able to build with #include <source_location>
Detecting libraries used...
/home/mhightow/Arduino/hardware/esp8266com/esp8266/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-g++ -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -D_GNU_SOURCE -DESP8266 -Os @/tmp/arduino_build_311930/core/build.opt -I/home/mhightow/Arduino/hardware/esp8266com/esp8266/tools/sdk/include -I/home/mhightow/Arduino/hardware/esp8266com/esp8266/tools/sdk/lwip2/include -I/home/mhightow/Arduino/hardware/esp8266com/esp8266/tools/sdk/libc/xtensa-lx106-elf/include -I/tmp/arduino_build_311930/core -c @/home/mhightow/Arduino/hardware/esp8266com/esp8266/tools/warnings/none-cppflags -g -free -fipa-pta -Werror=return-type -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 -std=gnu++17 -ffunction-sections -fdata-sections -fexceptions -DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DFP_IN_IROM -w -x c++ -E -CC -DNONOSDK22x_190703=1 -DF_CPU=80000000L -DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 -DDEBUG_ESP_PORT=Serial -DARDUINO=10819 -DARDUINO_ESP8266_NODEMCU_ESP12E -DARDUINO_ARCH_ESP8266 "-DARDUINO_BOARD=\"ESP8266_NODEMCU_ESP12E\"" "-DARDUINO_BOARD_ID=\"nodemcuv2\"" -DLED_BUILTIN=2 -DFLASHMODE_DIO -I/home/mhightow/Arduino/hardware/esp8266com/esp8266/cores/esp8266 -I/home/mhightow/Arduino/hardware/esp8266com/esp8266/variants/nodemcu /tmp/arduino_build_311930/sketch/SrcLoc.ino.cpp -o /dev/null
SrcLoc:3:10: fatal error: source_location: No such file or directoryAlternatives for source_location: []
3 | #include <source_location>
| ^~~~~~~~~~~~~~~~~
compilation terminated.
I also noticed that path ... esp8266com/esp8266/tools/sdk/libc/xtensa-lx106-elf/include
does not exist on my machine, even after rerunning ./get.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not available in C, as it would need default-arg constructed by the caller.
Not available in -std=c++17, part of the -std=c++2a
What I meant was copying the behaviour, at least in structure which is then passed to the func. May be significantly larger in size, on second though :/
i.e. static struct foo { .file = ..., .lineno = ...}; foo.caller = __builtin__(); func(..., &foo);
Plus, we do have a hack around __FILE__
which is not applied to source_location to put strings in progmem as default .rodata
is still in ram
cores/esp8266/heap.cpp
Outdated
[[maybe_unused]] const char *file = NULL; | ||
[[maybe_unused]] const int line = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Macros below missing 'no-file-line' variants so these can be left out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should now be fixed.
cores/esp8266/heap.cpp
Outdated
// | ||
#include <bits/c++config.h> | ||
#include <cstdlib> | ||
#include "new" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#include "new" | |
#include <new> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should now be fixed.
uint32_t saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL); | ||
_umm_last_fail_alloc.addr = caller; | ||
_umm_last_fail_alloc.size = size; | ||
_umm_last_fail_alloc.file = file; | ||
_umm_last_fail_alloc.line = line; | ||
print_loc(withinISR(saved_ps), size, file, line, caller); | ||
xt_wsr_ps(saved_ps); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a refresher, I may be forgetting order of operation rules here...
Since we are between func calls, no need to explicitly put mem barrier to force variable assignment to happen after xt_rsil and before xt_wsr_ps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is my understanding as well.
Also, both macros xt_rsil
and xt_wst_ps
include a memory barrier, ... : "memory")
, so I think we are good.
cores/esp8266/abi.cpp
Outdated
|
||
extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__)); | ||
extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); | ||
|
||
#if defined(__cpp_exceptions) && (defined(DEBUG_ESP_OOM) || defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_WITHINISR)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't 'new_handler' be independent of debugging?
since the idea is to allow plain new to sometimes avoid throwing (regardless of whether it builds w/ -fexceptions or without). exception-less builds obviously just call abort for would-be exception
ref new_op.cc and new_opnt.cc, nothrow variant just wraps the other in try catch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't 'new_handler' be independent of debugging?
It has been a while since I did this, I think the increased build size for non-debug builds was a consideration.
It would add 136 bytes of IROM to the non-debug build so that it always builds with our revised new_handler
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will have to recheck too, then. Debug=enabled without exceptions are not handled though?
Plus there are more missing overloads from -std=c++17
https://en.cppreference.com/w/cpp/memory/new/operator_new
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plus there are more missing overloads from -std=c++17
The current master does not support "new" align operations. Compiler reports:
/workdir/repo/gcc-gnu/libstdc++-v3/libsupc++/new_opa.cc:86: undefined reference to
memalign'`
No hits on a quick issue check for memalign
.
The library umm_malloc needs an enhancement to support an allocate-aligned option. Also, things will get complicated when incorporating the poison check feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've worked through adding an alignment option to the umm_malloc
library. In the process, I have addressed an unreported alignment issue. umm_malloc
allocated data addresses always landed on an 8-byte aligned - 4-byte.
Now I need to finish working through all the build options in abi.cpp
and heap.cpp
.
Cleanup remnants from backport
Fixed postmortem build issue around optional oom count function Made corrections for build macro DEBUG_ESP_WITHINISR
PR Status: ready for review
Heap: Improve debug caller address reporting to indicate the address in the sketch or library.
Lite refactoring of
heap.cpp
and expanded commentsDEBUG_ESP_OOM
printingOverall avoid identifying the wrapper code as the source of the Heap call. Each outer wrapper passes the caller's address down to the next level.
Improves "caller" address reporting for OOM and Poison events. Previously, some OOM and Poison events reported info pointing to the thick heap wrapper rather than back to the caller. Update
abi.cpp
,heap.cpp
andumm_local.c
(umm_poison
) to support caller parameters.Enhanced
DEBUG_ESP_OOM
to track OOM events when C++ Exceptions: "enabled" is selected. Report more interesting higher-level code addresses as the caller instead of GCC C++ module.Added
DEBUG_ESP_WITHINISR
to provide notification for ISRs using C++ "new"/"delete" operators orLIBC
calls using_malloc_r
, ...Enhanced poison neighbor reporting to distinguish between allocation vs. neighbor failing poison check.
Support for specifying both UMM_POISON_CHECK and UMM_POISON_CHECK_LITE at the same time did not work as expected. UMM_POISON_CHECK_LITE dominated in the build. Fix: Keep things simple and prevent combining UMM_POISON_CHECK and UMM_POISON_CHECK_LITE. Defines for UMM_POISON_CHECK, UMM_POISON_CHECK_LITE, and UMM_POISON_NONE are checked for mutual exclusivity.
For sketches with extremely limited resources, added UMM_POISON_NONE to prevent UMM_POISON_CHECK_LITE from being added to debug builds.
Moved port related changes for
UMM_POISON...
fromumm_malloc_cfg.h
toumm_malloc_cfgport.h
.Draft version remnants removed.
Updated this description to reflect the current PR