So, it looks like I have to do my own tracking.
In the allocation system, so far, every allocation has a (private) 16-byte header that immediately precedes the allocated data. There may be some waste data before the header due to cache alignment code:
struct MemAllocation {
uint32 identifier; // used to identify this as an allocation from our system
uint32 allocationSize;
uint32 allocationFlags;
void* realAddress; // the real base address that was returned from AllocPooled()
};
The allocation routine aligns the allocation to a cacheline boundary, so this structure is cache aligned, as is the space following it.
The identifier is a unique ID that allows the corresponding free method to ascertain if it is legal to release the memory pointed to. Basically if the uint32 value 16 bytes before the returned address (having done the obvious null checks first) matches this id, it is a block you can free. Of course, the free routine zeroes out this field so that if you ever did try to free it twice, the second free will fail gracefully.
I guess I can extend this and add MemAllocation* prev and MemAllocation* next, but that leaves a further 8 bytes to fill with useful data or rejig the alignment code. I suppose a 32-64 bytes overhead (inclusive of alignment padding) per allocation isn't so much these days.
Besides, I could always conditinally compile them in/out once the entire thing is sufficiently debugged.