heap: Add heap_caps_dump() / heap_caps_dump_all() functions

Dump the structure of the heap for debugging purposes.
This commit is contained in:
Angus Gratton 2017-10-18 16:25:17 +08:00 committed by Angus Gratton
parent b19fe80baf
commit 47aaf402b8
4 changed files with 57 additions and 14 deletions

View file

@ -79,7 +79,6 @@ Routine to allocate a bit of memory with certain capabilities. caps is a bitfiel
IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps )
{
void *ret = NULL;
uint32_t remCaps;
if (caps & MALLOC_CAP_EXEC) {
//MALLOC_CAP_EXEC forces an alloc from IRAM. There is a region which has both this as well as the following
@ -102,13 +101,7 @@ IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps )
if ((heap->caps[prio] & caps) != 0) {
//Heap has at least one of the caps requested. If caps has other bits set that this prio
//doesn't cover, see if they're available in other prios.
remCaps = caps & (~heap->caps[prio]); //Remaining caps to be fulfilled
int j = prio + 1;
while (remCaps != 0 && j < SOC_MEMORY_TYPE_NO_PRIOS) {
remCaps = remCaps & (~heap->caps[j]);
j++;
}
if (remCaps == 0) {
if ((get_all_caps(heap) & caps) == caps) {
//This heap can satisfy all the requested capabilities. See if we can grab some memory using it.
if ((caps & MALLOC_CAP_EXEC) && heap->start >= SOC_DIRAM_DRAM_LOW && heap->start < SOC_DIRAM_DRAM_HIGH) {
//This is special, insofar that what we're going to get back is a DRAM address. If so,
@ -437,3 +430,20 @@ bool heap_caps_check_integrity_addr(intptr_t addr, bool print_errors)
}
return multi_heap_check(heap->heap, print_errors);
}
void heap_caps_dump(uint32_t caps)
{
bool all_heaps = caps & MALLOC_CAP_INVALID;
heap_t *heap;
SLIST_FOREACH(heap, &registered_heaps, next) {
if (heap->heap != NULL
&& (all_heaps || (get_all_caps(heap) & caps) == caps)) {
multi_heap_dump(heap->heap);
}
}
}
void heap_caps_dump_all()
{
heap_caps_dump(MALLOC_CAP_INVALID);
}

View file

@ -276,3 +276,33 @@ void *heap_caps_realloc_prefer( void *ptr, size_t size, size_t num, ... );
* @return A pointer to the memory allocated on success, NULL on failure
*/
void *heap_caps_calloc_prefer( size_t n, size_t size, size_t num, ... );
/**
* @brief Dump the full structure of all heaps with matching capabilities.
*
* Prints a large amount of output to serial (because of locking limitations,
* the output bypasses stdout/stderr). For each (variable sized) block
* in each matching heap, the following output is printed on a single line:
*
* - Block address (the data buffer returned by malloc is 4 bytes after this
* if heap debugging is set to Basic, or 8 bytes otherwise).
* - Data size (the data size may be larger than the size requested by malloc,
* either due to heap fragmentation or because of heap debugging level).
* - Address of next block in the heap.
* - If the block is free, the address of the next free block is also printed.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
*/
void heap_caps_dump(uint32_t caps);
/**
* @brief Dump the full structure of all heaps.
*
* Covers all registered heaps. Prints a large amount of output to serial.
*
* Output is the same as for heap_caps_dump.
*
*/
void heap_caps_dump_all();

View file

@ -623,13 +623,13 @@ void multi_heap_dump(multi_heap_handle_t heap)
assert(heap != NULL);
multi_heap_internal_lock(heap);
printf("Heap start %p end %p\nFirst free block %p\n", &heap->first_block, heap->last_block, heap->first_block.next_free);
MULTI_HEAP_STDERR_PRINTF("Heap start %p end %p\nFirst free block %p\n", &heap->first_block, heap->last_block, heap->first_block.next_free);
for(heap_block_t *b = &heap->first_block; b != NULL; b = get_next_block(b)) {
printf("Block %p data size 0x%08zx bytes next block %p", b, block_data_size(b), get_next_block(b));
MULTI_HEAP_STDERR_PRINTF("Block %p data size 0x%08x bytes next block %p", b, block_data_size(b), get_next_block(b));
if (is_free(b)) {
printf(" FREE. Next free %p\n", b->next_free);
MULTI_HEAP_STDERR_PRINTF(" FREE. Next free %p\n", b->next_free);
} else {
printf("\n");
MULTI_HEAP_STDERR_PRINTF("%s", "\n"); /* C macros & optional __VA_ARGS__ */
}
}
multi_heap_internal_unlock(heap);

View file

@ -19,7 +19,9 @@ To obtain information about the state of the heap:
- :cpp:func:`heap_caps_get_free_size` can also be used to return the current free memory for different memory capabilities.
- :cpp:func:`heap_caps_get_largest_free_block` can be used to return the largest free block in the heap. This is the largest single allocation which is currently possible. Tracking this value and comparing to total free heap allows you to detect heap fragmentation.
- :cpp:func:`xPortGetMinimumEverFreeHeapSize` and the related :cpp:func:`heap_caps_get_minimum_free_size` can be used to track the heap "low water mark" since boot.
- :cpp:func:`heap_caps_get_info` returns a :cpp:class:`multi_heap_info_t` structure which contains the information from the above functions, plus some additional heap-specific data (number of allocations, etc.)
- :cpp:func:`heap_caps_get_info` returns a :cpp:class:`multi_heap_info_t` structure which contains the information from the above functions, plus some additional heap-specific data (number of allocations, etc.).
- :cpp:func:`heap_caps_print_heap_info` prints a summary to stdout of the information returned by :cpp:func:`heap_caps_get_info`.
- :cpp:func:`heap_caps_dump` and :cpp:func:`heap_caps_dump_all` will output detailed information about the structure of each block in the heap. Note that this can be large amount of output.
.. _heap-corruption:
@ -52,7 +54,8 @@ Memory corruption can be one of the hardest classes of bugs to find and fix, as
- Adding regular calls to :cpp:func:`heap_caps_check_integrity_all` or :cpp:func:`heap_caps_check_integrity_addr` in your code will help you pin down the exact time that the corruption happened. You can move these checks around to "close in on" the section of code that corrupted the heap.
- Based on the memory address which is being corrupted, you can use :ref:`JTAG debugging <jtag-debugging-introduction>` to set a watchpoint on this address and have the CPU halt when it is written to.
- If you don't have JTAG, but you do know roughly when the corruption happens, then you can set a watchpoint in software just beforehand via :cpp:func:`esp_set_watchpoint`. A fatal exception will occur when the watchpoint triggers. For example ``esp_set_watchpoint(0, (void *)addr, 4, ESP_WATCHPOINT_STORE``. Note that watchpoints are per-CPU and are set on the current running CPU only, so if you don't know which CPU is corrupting memory then you will need to call this function on both CPUs.
- For buffer overflows, `heap tracing`_ in ``HEAP_TRACE_ALL`` mode lets you see which callers are allocating which addresses from the heap. See `Heap Tracing To Find Heap Corruption` for more details.
- For buffer overflows, `heap tracing`_ in ``HEAP_TRACE_ALL`` mode lets you see which callers are allocating which addresses from the heap. See `Heap Tracing To Find Heap Corruption` for more details. If you can find the function which allocates memory with an address immediately before the address which is corrupted, this will probably be the function which overflows the buffer.
- Calling :cpp:func:`heap_caps_dump` or :cpp:func:`heap_caps_dump_all` can give an indication of what heap blocks are surrounding the corrupted region and may have overflowed/underflowed/etc.
Configuration
^^^^^^^^^^^^^