diff --git a/o1heap/o1heap.c b/o1heap/o1heap.c index 61e6e39..2206eaf 100644 --- a/o1heap/o1heap.c +++ b/o1heap/o1heap.c @@ -240,6 +240,12 @@ O1HEAP_PRIVATE void unbin(O1HeapInstance* const handle, const Fragment* const fr } } +#ifdef O1HEAP_TRACE +// The user must implement these functions or linking will fail. +extern void o1heapAllocateTrace(O1HeapInstance* const handle, void* const allocated_memory, size_t size_bytes); +extern void o1heapFreeTrace(O1HeapInstance* const handle, void* const freed_memory, size_t size_bytes); +#endif + // ---------------------------------------- PUBLIC API IMPLEMENTATION ---------------------------------------- O1HeapInstance* o1heapInit(void* const base, const size_t size) @@ -365,9 +371,22 @@ void* o1heapAllocate(O1HeapInstance* const handle, const size_t amount) frag->header.used = true; out = ((char*) frag) + O1HEAP_ALIGNMENT; +#ifdef O1HEAP_TRACE + o1heapAllocateTrace(handle, out, frag->header.size); +#endif } } +#ifdef O1HEAP_TRACE + if (NULL == out) + { + // we didn't successfully allocate so call the trace method with a null pointer to indicate allocation + // failure. The size will be the requested size so the trace can include this information in a + // failed allocation event. + o1heapAllocateTrace(handle, out, amount); + } +#endif + // Update the diagnostics. if (O1HEAP_LIKELY(handle->diagnostics.peak_request_size < amount)) { @@ -401,6 +420,10 @@ void o1heapFree(O1HeapInstance* const handle, void* const pointer) O1HEAP_ASSERT(frag->header.size <= handle->diagnostics.capacity); O1HEAP_ASSERT((frag->header.size % FRAGMENT_SIZE_MIN) == 0U); +#ifdef O1HEAP_TRACE + o1heapFreeTrace(handle, pointer, frag->header.size); +#endif + // Even if we're going to drop the fragment later, mark it free anyway to prevent double-free. frag->header.used = false; diff --git a/o1heap/o1heap.h b/o1heap/o1heap.h index fb776b4..c3fa117 100644 --- a/o1heap/o1heap.h +++ b/o1heap/o1heap.h @@ -33,6 +33,32 @@ extern "C" { /// The guaranteed alignment depends on the platform pointer width. #define O1HEAP_ALIGNMENT (sizeof(void*) * 4U) +/// Define O1HEAP_TRACE to enable two extern symbols your application can implement when integrating with trace +/// tooling: +/// +/// Invoked from o1heapAllocate with a pointer to the newly allocated memory and the size of the +/// memory region. Note that size_bytes is not the size of the memory requested but the size of +/// the memory fragment reserved. +/// +/// if allocated_memory is NULL an allocation failure has occurred. In this case size_bytes is the +/// number of bytes requested that the allocator could not provide. +/// +/// void o1heapAllocateTrace(O1HeapInstance* const handle, void* const allocated_memory, const size_t size_bytes); +/// +/// +/// Invoked from o1heapFree with a pointer to the previously allocated memory and the size of the +/// memory region released. Note that size_bytes is not the size of the memory originally requested +/// but the size of the memory fragment that was released. +/// +/// freed_memory must not be accessed. It is provided for it's address only. +/// +/// void o1heapFreeTrace(O1HeapInstance* const handle, void* const freed_memory, size_t size_bytes); +/// +/// If you define this macro you _must_ implement these methods or the link stage of your build will fail. +/// +/// #define O1HEAP_TRACE + + /// The definition is private, so the user code can only operate on pointers. This is done to enforce encapsulation. typedef struct O1HeapInstance O1HeapInstance;