Document DXIL Debugger memory allocation and pointer implementation

Renamed some containers to distinguish between real memory allocations and pointers to within an existing memory allocation

Replace some asserts with if's as part of defensive coding
This commit is contained in:
Jake Turner
2025-01-08 15:58:57 +00:00
parent 4b35fbcc96
commit 6b1b2fb7da
2 changed files with 108 additions and 84 deletions
+95 -79
View File
@@ -1635,15 +1635,14 @@ void MemoryTracking::AllocateMemoryForType(const DXIL::Type *type, Id allocId, b
RDCASSERTEQUAL(type->type, Type::TypeKind::Pointer);
ConvertDXILTypeToShaderVariable(type->inner, var);
// Add the SSA to m_MemoryAllocs with its backing memory and size
// Add the SSA to m_Allocations with its backing memory and size
size_t byteSize = ComputeDXILTypeByteSize(type->inner);
void *backingMem = malloc(byteSize);
memset(backingMem, 0, byteSize);
MemoryTracking::Alloc &alloc = m_Allocs[allocId];
alloc = {backingMem, byteSize, global};
m_Allocations[allocId] = {backingMem, byteSize, global};
// set the backing memory
m_AllocPointers[allocId] = {allocId, backingMem, byteSize};
// Create a pointer to represent this allocation
m_Pointers[allocId] = {allocId, backingMem, byteSize};
}
ThreadState::ThreadState(uint32_t workgroupIndex, Debugger &debugger,
@@ -1670,7 +1669,7 @@ ThreadState::ThreadState(uint32_t workgroupIndex, Debugger &debugger,
ThreadState::~ThreadState()
{
for(auto it : m_Memory.m_Allocs)
for(auto it : m_Memory.m_Allocations)
{
if(!it.second.global)
free(it.second.backingMemory);
@@ -4236,21 +4235,25 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
if(ptrId == DXILDebug::INVALID_ID)
break;
auto itPtr = m_Memory.m_AllocPointers.find(ptrId);
if(itPtr == m_Memory.m_AllocPointers.end())
auto itPtr = m_Memory.m_Pointers.find(ptrId);
if(itPtr == m_Memory.m_Pointers.end())
{
RDCWARN("Unknown memory pointer Id %u", ptrId);
RDCERR("Unknown memory pointer Id %u", ptrId);
break;
}
const MemoryTracking::AllocPointer &ptr = itPtr->second;
const MemoryTracking::Pointer &ptr = itPtr->second;
Id baseMemoryId = ptr.baseMemoryId;
auto itAlloc = m_Memory.m_Allocs.find(baseMemoryId);
RDCASSERT(itAlloc != m_Memory.m_Allocs.end());
const MemoryTracking::Alloc &alloc = itAlloc->second;
auto itAlloc = m_Memory.m_Allocations.find(baseMemoryId);
if(itAlloc == m_Memory.m_Allocations.end())
{
RDCERR("Unknown memory allocation Id %u", baseMemoryId);
break;
}
const MemoryTracking::Allocation &allocation = itAlloc->second;
ShaderVariable arg;
if(alloc.global)
if(allocation.global)
{
RDCASSERT(IsVariableAssigned(baseMemoryId));
arg = m_Variables[baseMemoryId];
@@ -4266,44 +4269,42 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
case Operation::StoreAtomic:
{
// Store(ptr, value)
Id baseMemoryId = DXILDebug::INVALID_ID;
void *baseMemoryBackingPtr = NULL;
uint64_t allocSize = 0;
void *allocMemoryBackingPtr = NULL;
Id ptrId = GetArgumentId(0);
if(ptrId == DXILDebug::INVALID_ID)
break;
auto itPtr = m_Memory.m_AllocPointers.find(ptrId);
if(itPtr == m_Memory.m_AllocPointers.end())
auto itPtr = m_Memory.m_Pointers.find(ptrId);
if(itPtr == m_Memory.m_Pointers.end())
{
RDCWARN("Unknown memory pointer Id %u", ptrId);
RDCERR("Unknown memory pointer Id %u", ptrId);
break;
}
const MemoryTracking::AllocPointer &ptr = itPtr->second;
baseMemoryId = ptr.baseMemoryId;
baseMemoryBackingPtr = ptr.backingMemory;
allocSize = ptr.size;
const MemoryTracking::Pointer &ptr = itPtr->second;
Id baseMemoryId = ptr.baseMemoryId;
void *memory = ptr.memory;
uint64_t allocSize = ptr.size;
auto itAlloc = m_Memory.m_Allocs.find(baseMemoryId);
RDCASSERT(itAlloc != m_Memory.m_Allocs.end());
const MemoryTracking::Alloc &alloc = itAlloc->second;
allocMemoryBackingPtr = alloc.backingMemory;
RDCASSERT(baseMemoryBackingPtr);
RDCASSERT(memory);
RDCASSERTNOTEQUAL(baseMemoryId, DXILDebug::INVALID_ID);
ShaderVariable val;
RDCASSERT(GetShaderVariable(inst.args[1], opCode, dxOpCode, val));
RDCASSERTEQUAL(resultId, DXILDebug::INVALID_ID);
UpdateBackingMemoryFromVariable(baseMemoryBackingPtr, allocSize, val);
UpdateBackingMemoryFromVariable(memory, allocSize, val);
ShaderVariableChange change;
RDCASSERT(IsVariableAssigned(baseMemoryId));
change.before = m_Variables[baseMemoryId];
UpdateMemoryVariableFromBackingMemory(baseMemoryId, allocMemoryBackingPtr);
auto itAlloc = m_Memory.m_Allocations.find(baseMemoryId);
if(itAlloc == m_Memory.m_Allocations.end())
{
RDCERR("Unknown memory allocation Id %u", baseMemoryId);
break;
}
const MemoryTracking::Allocation &allocation = itAlloc->second;
UpdateMemoryVariableFromBackingMemory(baseMemoryId, allocation.backingMemory);
// record the change to the base memory variable
change.after = m_Variables[baseMemoryId];
@@ -4338,7 +4339,11 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
}
RDCASSERT(IsVariableAssigned(ptrId));
RDCASSERT(m_Memory.m_Allocs.count(ptrId) == 1);
if(m_Memory.m_Allocations.count(ptrId) == 0)
{
RDCERR("Unknown memory allocation Id %u", ptrId);
break;
}
// arg[1..] : indices 1...N
rdcarray<uint64_t> indexes;
@@ -4367,12 +4372,12 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
size_t size = countElems * GetElementByteSize(baseType);
// Copy from the backing memory to the result
const MemoryTracking::Alloc &alloc = m_Memory.m_Allocs[ptrId];
uint8_t *backingMemory = (uint8_t *)alloc.backingMemory;
const MemoryTracking::Allocation &allocation = m_Memory.m_Allocations[ptrId];
uint8_t *memory = (uint8_t *)allocation.backingMemory;
// Ensure global variables use global memory
// Ensure non-global variables do not use global memory
if(alloc.global)
if(allocation.global)
RDCASSERT(cast<GlobalVar>(inst.args[0]));
else
RDCASSERT(!cast<GlobalVar>(inst.args[0]));
@@ -4380,21 +4385,21 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
result.type = baseType;
result.rows = (uint8_t)countElems;
RDCASSERT(offset + size <= alloc.size);
if(offset + size <= alloc.size)
RDCASSERT(offset + size <= allocation.size);
if(offset + size <= allocation.size)
{
backingMemory += offset;
m_Memory.m_AllocPointers[resultId] = {ptrId, backingMemory, size};
memory += offset;
m_Memory.m_Pointers[resultId] = {ptrId, memory, size};
RDCASSERT(size <= sizeof(ShaderValue));
if(size <= sizeof(ShaderValue))
memcpy(&result.value, backingMemory, size);
memcpy(&result.value, memory, size);
else
RDCERR("Size %u too large MAX %u for GetElementPtr", size, sizeof(ShaderValue));
}
else
{
RDCERR("Invalid GEP offset %u size %u for alloc size %u", offset, size, alloc.size);
RDCERR("Invalid GEP offset %u size %u for allocation size %u", offset, size, allocation.size);
}
break;
}
@@ -5251,31 +5256,35 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
case Operation::AtomicUMin:
case Operation::CompareExchange:
{
uint64_t allocSize = 0;
void *allocMemoryBackingPtr = NULL;
void *baseMemoryBackingPtr = NULL;
Id baseMemoryId = DXILDebug::INVALID_ID;
Id ptrId = GetArgumentId(0);
if(ptrId == DXILDebug::INVALID_ID)
break;
auto itPtr = m_Memory.m_Pointers.find(ptrId);
if(itPtr == m_Memory.m_Pointers.end())
{
auto itPtr = m_Memory.m_AllocPointers.find(ptrId);
RDCASSERT(itPtr != m_Memory.m_AllocPointers.end());
const MemoryTracking::AllocPointer &ptr = itPtr->second;
baseMemoryId = ptr.baseMemoryId;
baseMemoryBackingPtr = ptr.backingMemory;
allocSize = ptr.size;
auto itAlloc = m_Memory.m_Allocs.find(baseMemoryId);
RDCASSERT(itAlloc != m_Memory.m_Allocs.end());
const MemoryTracking::Alloc &alloc = itAlloc->second;
allocMemoryBackingPtr = alloc.backingMemory;
RDCERR("Unknown memory pointer Id %u", ptrId);
break;
}
RDCASSERT(baseMemoryBackingPtr);
const MemoryTracking::Pointer &ptr = itPtr->second;
Id baseMemoryId = ptr.baseMemoryId;
RDCASSERTNOTEQUAL(baseMemoryId, DXILDebug::INVALID_ID);
void *memory = ptr.memory;
RDCASSERT(memory);
uint64_t allocSize = ptr.size;
auto itAlloc = m_Memory.m_Allocations.find(baseMemoryId);
if(itAlloc == m_Memory.m_Allocations.end())
{
RDCERR("Unknown memory allocation Id %u", ptrId);
break;
}
const MemoryTracking::Allocation &allocation = itAlloc->second;
void *allocMemoryBackingPtr = allocation.backingMemory;
RDCASSERTNOTEQUAL(resultId, DXILDebug::INVALID_ID);
RDCASSERT(IsVariableAssigned(ptrId));
const ShaderVariable a = m_Variables[ptrId];
@@ -5389,8 +5398,8 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
RDCERR("Unhandled opCode %s", ToStr(opCode).c_str());
}
// Save the result back
UpdateBackingMemoryFromVariable(baseMemoryBackingPtr, allocSize, res);
// Save the result back to the backing memory of the pointer
UpdateBackingMemoryFromVariable(memory, allocSize, res);
ShaderVariableChange change;
if(m_State)
@@ -6263,7 +6272,7 @@ ShaderValue ThreadState::DDY(bool fine, Operation opCode, DXOp dxOpCode,
GlobalState::~GlobalState()
{
for(auto it : memory.m_Allocs)
for(auto it : memory.m_Allocations)
{
RDCASSERT(it.second.global);
free(it.second.backingMemory);
@@ -7687,13 +7696,19 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
{
RDCASSERT(ConvertDXILConstantToShaderVariable(initialData, globalVar.var));
// Write ShaderVariable data back to memory
auto itAlloc = globalMemory.m_Allocs.find(globalVar.id);
RDCASSERT(itAlloc != globalMemory.m_Allocs.end());
const MemoryTracking::Alloc &alloc = itAlloc->second;
void *allocMemoryBackingPtr = alloc.backingMemory;
uint64_t allocSize = alloc.size;
state.UpdateBackingMemoryFromVariable(allocMemoryBackingPtr, allocSize, globalVar.var);
RDCASSERTEQUAL(allocSize, 0);
auto itAlloc = globalMemory.m_Allocations.find(globalVar.id);
if(itAlloc != globalMemory.m_Allocations.end())
{
const MemoryTracking::Allocation &allocation = itAlloc->second;
void *allocMemoryBackingPtr = allocation.backingMemory;
uint64_t allocSize = allocation.size;
state.UpdateBackingMemoryFromVariable(allocMemoryBackingPtr, allocSize, globalVar.var);
RDCASSERTEQUAL(allocSize, 0);
}
else
{
RDCERR("Unknown memory allocation for GlobalVariable Id %u", globalVar.id);
}
}
}
}
@@ -7736,20 +7751,21 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
// Decode the pointer allocation: ptrId, offset, size
RDCASSERT(DecodePointer(ptrId, offset, size, var));
auto it = globalMemory.m_Allocs.find(ptrId);
if(it != globalMemory.m_Allocs.end())
auto it = globalMemory.m_Allocations.find(ptrId);
if(it != globalMemory.m_Allocations.end())
{
const MemoryTracking::Alloc &alloc = it->second;
uint8_t *backingMemory = (uint8_t *)alloc.backingMemory;
RDCASSERT(offset + size <= alloc.size);
if(offset + size <= alloc.size)
const MemoryTracking::Allocation &allocation = it->second;
uint8_t *memory = (uint8_t *)allocation.backingMemory;
RDCASSERT(offset + size <= allocation.size);
if(offset + size <= allocation.size)
{
backingMemory += offset;
globalMemory.m_AllocPointers[id] = {ptrId, backingMemory, size};
memory += offset;
globalMemory.m_Pointers[id] = {ptrId, memory, size};
}
else
{
RDCERR("Invalid GEP offset %u size %u for alloc size %u", offset, size, alloc.size);
RDCERR("Invalid GEP offset %u size %u for allocation size %u", offset, size,
allocation.size);
}
}
else
+13 -5
View File
@@ -189,22 +189,30 @@ struct MemoryTracking
{
void AllocateMemoryForType(const DXIL::Type *type, Id allocId, bool global, ShaderVariable &var);
struct Alloc
// Represents actual memory allocations (think of it like a memory heap)
struct Allocation
{
// the allocated memory
void *backingMemory;
uint64_t size;
bool global;
};
struct AllocPointer
// Represents pointers within a Allocation memory allocation (heap)
struct Pointer
{
// the Allocation that owns the memory pointed to
Id baseMemoryId;
void *backingMemory;
// the memory pointer which will be within the Allocation backing memory
void *memory;
// size of the data the pointer
uint64_t size;
};
std::map<Id, Alloc> m_Allocs;
std::map<Id, AllocPointer> m_AllocPointers;
// Memory allocations with backing memory (heaps)
std::map<Id, Allocation> m_Allocations;
// Pointers within a Allocation memory allocation, the allocated memory will be in m_Allocations
std::map<Id, Pointer> m_Pointers;
};
struct ThreadState