Again, I use malloc() and new() all the time, but never use free(). If you want to track your malloc operations, I recommend the following.
Malloc() typically calls the routine sbrk() to actually perform the low-level heap allocation. The standard sbrk() call has a useful side-effect: if you ask it to allocate 0 bytes, it will return a pointer to the address that will be allocated next, i.e. a pointer to the current top-of-heap. In other words:
// Find out where the top of the heap is right now:
void* topOfHeap = (void*)sbrk(0);
If you know the heap’s starting address, you can trivially calculate how much space has been allocated to this point. If sbrk(0) returns NULL, it means that your heap is absolutely full.
If you want to go deeper than just knowing where the top of the heap is, it is really easy to supply your own sbrk(). Here is an example from something I did a long time ago. You will see why sbrk(0) returns the current top of heap:
// Allocate memory from the heap.
// All allocation requests are forced to begin on a word-aligned address.
extern caddr_t sbrk(int incr)
{
static unsigned char* endOfHeap = NULL;
unsigned char *prev_heap;
// Adjust incr (if necessary) such that our heap pointer always stays word aligned
incr = ((incr+3) & ~3);
if (endOfHeap == NULL) {
// Force the initial heap pointer to be word-aligned.
// This should be unnecessary given the instructions for how the heap is defined to the linker, but this is safe:
endOfHeap = (unsigned char *)(((uint32_t)(&_sheap)+3) & ~3);
}
prev_heap = endOfHeap;
// Check if this allocation request will take us past the end of the heap:
if ((endOfHeap + incr) >= (unsigned char *)(&_eheap)) {
// Not enough room! This allocation fails:
return(NULL);
}
// If we get here, we know the allocation will succeed:
endOfHeap += incr;
return (caddr_t) prev_heap;
}
In this example, I got the start and end addresses of the heap created by the linker script as the symbols _sheap and _eheap. Your symbols may be different. But an even easier way to do it is to leave the linker out of the equation and declare the heap as a static array. This means that the heap will get stored in amongst the rest of your global variables , but since sbrk() is written to never go outside its bounds, it’s all perfectly safe:
#define HEAPSIZE 4096
uint8_t heap[HEAPSIZE];
Now, change the sample code above to refer to the start of the heap as &heap[0] and the end of the heap as &heap[HEAPSIZE] and you are good to go.
The beauty of supplying your own sbrk() is that your code becomes completely responsible for allocating all heap memory. No heap allocations occur without you knowing about it!