Update DqnMemStack to use struct for metadata

This commit is contained in:
Doyle T 2018-07-22 23:53:08 +10:00
parent efe015017a
commit c282097a1f
2 changed files with 75 additions and 101 deletions

View File

@ -2340,6 +2340,7 @@ FILE_SCOPE void DqnMemStack_Test()
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN4); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN4);
DQN_ASSERT(result1 == result2); DQN_ASSERT(result1 == result2);
stack.Pop(result1); stack.Pop(result1);
DQN_ASSERT(stack.block->head == stack.block->memory);
} }
if (1) if (1)
@ -2348,6 +2349,7 @@ FILE_SCOPE void DqnMemStack_Test()
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN16); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN16);
DQN_ASSERT(result1 == result2); DQN_ASSERT(result1 == result2);
stack.Pop(result1); stack.Pop(result1);
DQN_ASSERT(stack.block->head == stack.block->memory);
} }
if (1) if (1)
@ -2356,6 +2358,7 @@ FILE_SCOPE void DqnMemStack_Test()
u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN64); u8 *result2 = (u8 *)DQN_ALIGN_POW_N(result1, ALIGN64);
DQN_ASSERT(result1 == result2); DQN_ASSERT(result1 == result2);
stack.Pop(result1); stack.Pop(result1);
DQN_ASSERT(stack.block->head == stack.block->memory);
} }
stack.Free(); stack.Free();

155
dqn.h
View File

@ -1415,16 +1415,28 @@ template <typename T> void DqnArray<T>::Reserve(isize newMax)
// Allocation Layout // Allocation Layout
// +-------------------------------------------------------------------------+ +-----------------+ // +-------------------------------------------------------------------------+ +-----------------+
// | Allocation Head | | Allocation Tail | // | Allocation Head | | Allocation Tail |
// +--------------------+-------------------------------------------------------------------------------------+-----------------------------------+ // +--------------------+-------------------------------------------------------------------------+-----------------------------+-----------------+
// | Ptr From Allocator | Offset To Src | Alignment | Alloc Type | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) | // | Ptr From Allocator | Offset To Src | Alignment | Alloc Type | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) |
// +----------------------------------------------------------------------------------------------------------------------------------------------+ // +--------------------+-------------------------------------------------------------------------+-----------------------------+-----------------+
// Ptr From Allocator: The pointer returned by the allocator, not aligned // Ptr From Allocator: The pointer returned by the allocator, not aligned
// Alignment: The pointer given to the client is aligned to this power of two boundary // Alignment: The pointer given to the client is aligned to this power of two boundary
// Alloc Type: If the allocation was allocated from the head or tail of the memory block (mainly for memstacks). // Alloc Type: If the allocation was allocated from the head or tail of the memory block (mainly for memstacks).
// Alloc Amount: The requested allocation amount by the client (so does not include metadata) // Alloc Amount: The requested allocation amount by the client (so does not include metadata)
// B. Guard: Bounds Guard value. // B. Guard: Bounds Guard value.
// Aligned Ptr For Client: The allocated memory for the client. // Aligned Ptr For Client: The allocated memory for the client.
// Offset To Src: Number of bytes to subtract from the "Aligned Ptr For Client" to return to the "Ptr From ALlocator" // Offset To Src: Number of bytes to subtract from the "Aligned Ptr For Client" to return to the "Ptr From Allocator"
//
#pragma pack(push, 1)
struct DqnPtrHeader
{
u8 offsetToSrcPtr; // Offset to subtract from the client ptr to receive the allocation ptr
u8 alignment;
u8 allocType;
usize allocAmount;
};
#pragma pack(pop)
struct DqnMemTracker struct DqnMemTracker
{ {
static u32 const HEAD_GUARD_VALUE = 0xCAFEBABE; static u32 const HEAD_GUARD_VALUE = 0xCAFEBABE;
@ -1445,19 +1457,15 @@ struct DqnMemTracker
void RemoveAllocation (char *ptr); void RemoveAllocation (char *ptr);
void CheckAllocations () const; void CheckAllocations () const;
isize GetAllocationSize (isize size, u8 alignment) const { return allocHeadSize + size + allocTailSize + (alignment - 1); } isize GetAllocationSize (isize size, u8 alignment) const
{
return sizeof(DqnPtrHeader) + boundsGuardSize + (alignment - 1) + size + boundsGuardSize;
}
// ptr: The ptr given to the client when allocating. // ptr: The ptr given to the client when allocating.
u32 *PtrToHeadGuard (char *ptr) const { union { char *charPtr; u32 *u32Ptr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE + ALLOC_TYPE_SIZE + ALLOC_AMOUNT_SIZE; return u32Ptr; } u32 *PtrToHeadGuard (char *ptr) const { union { char *charPtr; u32 *u32Ptr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE + ALLOC_TYPE_SIZE + ALLOC_AMOUNT_SIZE; return u32Ptr; }
u32 *PtrToTailGuard (char *ptr) const { return reinterpret_cast<u32 *>(ptr + PtrToHeader(ptr)->allocAmount); }
// IMPORTANT: Getting the tail uses "Alloc Amount" metadata DqnPtrHeader *PtrToHeader (char *ptr) const { return reinterpret_cast<DqnPtrHeader *>(ptr - boundsGuardSize - sizeof(DqnPtrHeader)); }
u32 *PtrToTailGuard (char *ptr) const { union { char *charPtr; u32 *u32Ptr; }; charPtr = ptr + *PtrToAllocAmount(ptr); return u32Ptr; }
u8 *PtrToAlignment (char *ptr) const { union { char *charPtr; u8 *u8Ptr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE; return u8Ptr; }
u8 *PtrToOffsetToSrc(char *ptr) const { union { char *charPtr; u8 *u8Ptr; }; charPtr = ptr - allocHeadSize; return u8Ptr; }
// 0 if Pushed to Head on memstack, 1 if Pushed to Tail on memstack
u8 *PtrToAllocType (char *ptr) const { union { char *charPtr; u8 *u8Ptr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE; return u8Ptr; }
isize *PtrToAllocAmount(char *ptr) const { union { char *charPtr; isize *isizePtr; }; charPtr = ptr - allocHeadSize + OFFSET_TO_SRC_SIZE + ALIGNMENT_SIZE + ALLOC_TYPE_SIZE; return isizePtr; }
}; };
// #DqnMemStack API // #DqnMemStack API
@ -3328,46 +3336,20 @@ DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, b
void *result = nullptr; void *result = nullptr;
bool success = false; bool success = false;
enum class PtrType auto PtrIsLastAllocationInBlock =
{ [](DqnMemTracker const *tracker, DqnMemStack::Block const *block, char *ptr) -> bool {
NotInCurrentBlock,
Head,
Tail,
};
// TODO(doyle): Should use the metadata in the ptr head DqnPtrHeader *header = tracker->PtrToHeader(ptr);
auto ClassifyPtr = [](DqnMemStack::Block const *block, char const *ptr) -> PtrType {
PtrType result = PtrType::NotInCurrentBlock;
char const *const blockEnd = block->memory + block->size;
if (ptr >= block->memory && ptr < block->head)
{
result = PtrType::Head;
}
else if (ptr >= block->tail && ptr < blockEnd)
{
result = PtrType::Tail;
}
return result;
};
auto PtrIsLastAllocationInBlock = [&ClassifyPtr](DqnMemTracker const *tracker,
DqnMemStack::Block const *block,
char *ptr) -> bool {
PtrType type = ClassifyPtr(block, ptr);
bool result = false; bool result = false;
if (type == PtrType::Head) if (header->allocType == 0)
{ {
isize const oldMemSize = *(tracker->PtrToAllocAmount(ptr)); char const *ptrEnd = ptr - header->offsetToSrcPtr + tracker->GetAllocationSize(header->allocAmount, header->alignment);
char const *ptrEnd = ptr + oldMemSize + tracker->allocTailSize; result = ptrEnd == block->head;
result = (ptrEnd == (block->head - 1));
} }
else if (type == PtrType::Tail) else
{ {
u8 offsetToSrc = *(tracker->PtrToOffsetToSrc(ptr)); auto *actualPtr = ptr - header->offsetToSrcPtr;
auto *actualPtr = ptr - offsetToSrc; result = actualPtr == block->tail;
result = (actualPtr == block->tail);
} }
return result; return result;
@ -3388,22 +3370,23 @@ DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, b
// IMPORTANT: This is a _naive_ realloc scheme for stack allocation. // IMPORTANT: This is a _naive_ realloc scheme for stack allocation.
auto *request = &request_.e.realloc; auto *request = &request_.e.realloc;
char *ptr = static_cast<char *>(request->oldMemPtr); char *ptr = static_cast<char *>(request->oldMemPtr);
DqnPtrHeader *header = stack->tracker.PtrToHeader(static_cast<char *>(request->oldMemPtr));
for (DqnMemStack::Block *block = stack->block; block; block = block->prevBlock) for (DqnMemStack::Block *block = stack->block; block; block = block->prevBlock)
{ {
DQN_ASSERT(ptr >= block->memory && ptr <= (block->memory + block->size)); DQN_ASSERT(ptr >= block->memory && ptr <= (block->memory + block->size));
} }
DqnMemStack::Block *const block = stack->block; DqnMemStack::Block *const block = stack->block;
isize const oldMemSize = *stack->tracker.PtrToAllocAmount(ptr); isize const oldMemSize = header->allocAmount;
isize const extraBytesReq = request->newSize - oldMemSize; isize const extraBytesReq = request->newSize - oldMemSize;
u8 alignment = *stack->tracker.PtrToAlignment(ptr); u8 alignment = header->alignment;
DQN_ASSERT(extraBytesReq > 0); DQN_ASSERT(extraBytesReq > 0);
PtrType type = ClassifyPtr(block, ptr);
if (PtrIsLastAllocationInBlock(&stack->tracker, block, ptr)) if (PtrIsLastAllocationInBlock(&stack->tracker, block, ptr))
{ {
bool enoughSpace = false; bool enoughSpace = false;
if (type == PtrType::Head) if (header->allocType == 0)
{ {
DQN_ASSERT((block->head + extraBytesReq) >= block->memory); DQN_ASSERT((block->head + extraBytesReq) >= block->memory);
@ -3418,9 +3401,7 @@ DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, b
} }
else else
{ {
DQN_ASSERT(type == PtrType::Tail);
DQN_ASSERT((block->tail - extraBytesReq) < (block->memory + block->size)); DQN_ASSERT((block->tail - extraBytesReq) < (block->memory + block->size));
enoughSpace = (block->tail - extraBytesReq) > block->head; enoughSpace = (block->tail - extraBytesReq) > block->head;
if (enoughSpace) if (enoughSpace)
{ {
@ -3440,13 +3421,12 @@ DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, b
// Else, last allocation but not enough space in block. Create a new block and // Else, last allocation but not enough space in block. Create a new block and
// copy // copy
DqnMemStack::Block *oldBlock = block; DqnMemStack::Block *oldBlock = block;
if (type == PtrType::Head) if (header->allocType == 0)
{ {
result = static_cast<char *>(stack->Push(request->newSize, DqnMemStack::AllocTo::Head, alignment)); result = static_cast<char *>(stack->Push(request->newSize, DqnMemStack::AllocTo::Head, alignment));
} }
else else
{ {
DQN_ASSERT(type == PtrType::Tail);
result = static_cast<char *>(stack->Push(request->newSize, DqnMemStack::AllocTo::Tail, alignment)); result = static_cast<char *>(stack->Push(request->newSize, DqnMemStack::AllocTo::Tail, alignment));
} }
@ -3479,13 +3459,12 @@ DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, b
{ {
DQN_LOGE("Lost %$_d, the ptr to realloc is sandwiched between other allocations (LIFO)", oldMemSize); DQN_LOGE("Lost %$_d, the ptr to realloc is sandwiched between other allocations (LIFO)", oldMemSize);
if (type == PtrType::Head) if (header->allocType == 0)
{ {
result = (u8 *)stack->Push(request->newSize, DqnMemStack::AllocTo::Head, alignment); result = (u8 *)stack->Push(request->newSize, DqnMemStack::AllocTo::Head, alignment);
} }
else else
{ {
DQN_ASSERT(type == PtrType::Tail);
result = (u8 *)stack->Push(request->newSize, DqnMemStack::AllocTo::Tail, alignment); result = (u8 *)stack->Push(request->newSize, DqnMemStack::AllocTo::Tail, alignment);
} }
@ -3504,6 +3483,7 @@ DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, b
DqnMemStack::Block *block = stack->block; DqnMemStack::Block *block = stack->block;
char *ptr = static_cast<char *>(request->ptrToFree); char *ptr = static_cast<char *>(request->ptrToFree);
DqnPtrHeader *header = stack->tracker.PtrToHeader(ptr);
if (PtrIsLastAllocationInBlock(&stack->tracker, block, ptr)) if (PtrIsLastAllocationInBlock(&stack->tracker, block, ptr))
{ {
@ -3511,8 +3491,7 @@ DqnMemAPI__StackAllocatorCallback(DqnMemAPI *api, DqnMemAPI::Request request_, b
} }
else else
{ {
isize const oldMemSize = *(stack->tracker.PtrToAllocAmount(ptr)); DQN_LOGE("Lost %$_d, the ptr to free is sandwiched between other allocations (LIFO)", header->allocAmount);
DQN_LOGE("Lost %$_d, the ptr to free is sandwiched between other allocations (LIFO)", oldMemSize);
} }
} }
@ -3740,11 +3719,12 @@ void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment)
// Calculate Ptr To Give Client // Calculate Ptr To Give Client
// ============================================================================================= // =============================================================================================
char *currPtr = (pushToHead) ? (this->block->head) : (this->block->tail - sizeToAllocate); char *srcPtr = (pushToHead) ? (this->block->head) : (this->block->tail - sizeToAllocate);
char *result = reinterpret_cast<char *>(DQN_ALIGN_POW_N(currPtr + this->tracker.allocHeadSize, alignment)); char *unalignedResult = srcPtr + sizeof(DqnPtrHeader) + this->tracker.boundsGuardSize;
char *alignedResult = reinterpret_cast<char *>(DQN_ALIGN_POW_N(unalignedResult, alignment));
isize const offsetToSrc = result - currPtr; isize const offsetToPtrHeader = alignedResult - unalignedResult;
DQN_ASSERT(offsetToSrc > 0 && offsetToSrc < (u8)-1); DQN_ASSERT(offsetToPtrHeader >= 0 && offsetToPtrHeader <= (alignment - 1));
if (pushToHead) if (pushToHead)
{ {
@ -3760,22 +3740,16 @@ void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment)
// Instrument allocation with guards and tracker // Instrument allocation with guards and tracker
// ============================================================================================= // =============================================================================================
{ {
auto *myOffsetToSrc = this->tracker.PtrToOffsetToSrc(result); auto *ptrHeader = reinterpret_cast<DqnPtrHeader *>(srcPtr + offsetToPtrHeader);
*myOffsetToSrc = (u8)offsetToSrc; ptrHeader->offsetToSrcPtr = static_cast<u8>(alignedResult - srcPtr);
ptrHeader->alignment = alignment;
auto *myAlignment = this->tracker.PtrToAlignment(result); ptrHeader->allocType = (pushToHead) ? 0 : 1;
*myAlignment = alignment; ptrHeader->allocAmount = size;
auto *allocAmount = this->tracker.PtrToAllocAmount(result);
*allocAmount = size;
auto *allocType = this->tracker.PtrToAllocType(result);
*allocType = (pushToHead) ? 0 : 1;
if (Dqn_BitIsSet(this->flags, DqnMemStack::Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, DqnMemStack::Flag::BoundsGuard))
{ {
auto *headGuard = this->tracker.PtrToHeadGuard(result); u32 *headGuard = reinterpret_cast<u32 *>(alignedResult - sizeof(DqnMemTracker::HEAD_GUARD_VALUE));
auto *tailGuard = this->tracker.PtrToTailGuard(result); u32 *tailGuard = reinterpret_cast<u32 *>(alignedResult + ptrHeader->allocAmount);
*headGuard = DqnMemTracker::HEAD_GUARD_VALUE; *headGuard = DqnMemTracker::HEAD_GUARD_VALUE;
*tailGuard = DqnMemTracker::TAIL_GUARD_VALUE; *tailGuard = DqnMemTracker::TAIL_GUARD_VALUE;
} }
@ -3784,17 +3758,17 @@ void *DqnMemStack::Push(isize size, AllocTo allocTo, u8 alignment)
// Debug check (alignment, bounds guard) // Debug check (alignment, bounds guard)
// ============================================================================================= // =============================================================================================
{ {
char *checkAlignment = reinterpret_cast<char *>(DQN_ALIGN_POW_N(result, alignment)); char *checkAlignment = reinterpret_cast<char *>(DQN_ALIGN_POW_N(alignedResult, alignment));
DQN_ASSERTM(checkAlignment == result, "Adding bounds guard should not destroy alignment! %p != %p", result, checkAlignment); DQN_ASSERTM(checkAlignment == alignedResult, "Adding bounds guard should not destroy alignment! %p != %p", alignedResult, checkAlignment);
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
{ {
this->tracker.AddAllocation(result); this->tracker.AddAllocation(alignedResult);
this->tracker.CheckAllocations(); this->tracker.CheckAllocations();
} }
} }
return result; return alignedResult;
} }
FILE_SCOPE void DqnMemStack__KillTrackedPtrsInRange(DqnMemTracker *tracker, char const *start, char const *end) FILE_SCOPE void DqnMemStack__KillTrackedPtrsInRange(DqnMemTracker *tracker, char const *start, char const *end)
@ -3824,34 +3798,31 @@ void DqnMemStack::Pop(void *ptr, Dqn::ZeroClear clear)
if (!ptr) return; if (!ptr) return;
char *bytePtr = static_cast<char *>(ptr); char *bytePtr = static_cast<char *>(ptr);
DqnPtrHeader *ptrHeader = reinterpret_cast<DqnPtrHeader *>(bytePtr - sizeof(*ptrHeader));
// Check instrumented data // Check instrumented data
if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard))
{ {
this->tracker.CheckAllocations(); this->tracker.CheckAllocations();
this->tracker.RemoveAllocation(bytePtr); this->tracker.RemoveAllocation(bytePtr);
ptrHeader = reinterpret_cast<DqnPtrHeader *>(reinterpret_cast<char *>(ptrHeader) - this->tracker.boundsGuardSize);
} }
bool const popHead = (*(this->tracker.PtrToAllocType(bytePtr)) == 0); isize fullAllocationSize = this->tracker.GetAllocationSize(ptrHeader->allocAmount, ptrHeader->alignment);
isize const size = *(this->tracker.PtrToAllocAmount(bytePtr)); char *start = bytePtr - ptrHeader->offsetToSrcPtr;
u8 const alignment = *(this->tracker.PtrToAlignment(bytePtr)); char *end = start + fullAllocationSize;
u8 const offsetToSrc = *(this->tracker.PtrToOffsetToSrc(bytePtr));
isize actualSize = this->tracker.GetAllocationSize(size, alignment);
char *start = bytePtr - offsetToSrc;
char *end = start + actualSize;
char const *blockEnd = this->block->memory + this->block->size; char const *blockEnd = this->block->memory + this->block->size;
if (popHead) if (ptrHeader->allocType == 0)
{ {
DQN_ASSERTM(end == this->block->head, "Pointer to pop was not the last allocation! %p != %p", end, this->block->head); DQN_ASSERTM(end == this->block->head, "Pointer to pop was not the last allocation! %p != %p", end, this->block->head);
this->block->head -= actualSize; this->block->head -= fullAllocationSize;
DQN_ASSERT(this->block->head >= this->block->memory); DQN_ASSERT(this->block->head >= this->block->memory);
} }
else else
{ {
DQN_ASSERTM(start == this->block->tail, "Pointer to pop was not the last allocation! %p != %p", start, this->block->tail); DQN_ASSERTM(start == this->block->tail, "Pointer to pop was not the last allocation! %p != %p", start, this->block->tail);
this->block->tail += actualSize; this->block->tail += fullAllocationSize;
DQN_ASSERT(this->block->tail <= blockEnd); DQN_ASSERT(this->block->tail <= blockEnd);
} }