From 8deefe43ca0c0e6cba70e339c833a24a4b487d42 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Tue, 7 Jan 2025 17:41:30 +0000 Subject: [PATCH] Improve DXIL callstack generation Resolve debug scopes in a separate pass before callstacks Walk callstacks using DILocation (DebugLocation) data (dwarf scopes and inlinedAt data) For each DILocation Walk scope upwards to fill in the callstack (always a dwarf scope) Make new DILocation from inlinedAt and walk that DILocation --- renderdoc/driver/shaders/dxil/dxil_debug.cpp | 118 +++++++++++-------- 1 file changed, 72 insertions(+), 46 deletions(-) diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.cpp b/renderdoc/driver/shaders/dxil/dxil_debug.cpp index 3630c0d756..e35de3fb88 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_debug.cpp @@ -7922,7 +7922,7 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain } } - // Generate scopes and callstacks + // Generate scopes for(const Function *f : m_Program->m_Functions) { if(!f->external) @@ -7930,76 +7930,102 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain FunctionInfo &info = m_FunctionInfos[f]; uint32_t countInstructions = (uint32_t)f->instructions.size(); - rdcarray scopeHierarchy; ScopedDebugData *currentScope = NULL; for(uint32_t i = 0; i < countInstructions; ++i) { uint32_t instructionIndex = i + info.globalInstructionOffset; const Instruction &inst = *f->instructions[i]; ScopedDebugData *thisScope = NULL; - if(!DXIL::IsLLVMDebugCall(inst)) + // Use DebugLoc data for building up the list of scopes + uint32_t dbgLoc = ShouldIgnoreSourceMapping(inst) ? ~0U : inst.debugLoc; + if(dbgLoc != ~0U) { - // Use DebugLoc data for building up the list of scopes - uint32_t dbgLoc = ShouldIgnoreSourceMapping(inst) ? ~0U : inst.debugLoc; - if(dbgLoc != ~0U) - { - const DebugLocation &debugLoc = m_Program->m_DebugLocations[dbgLoc]; - thisScope = AddScopedDebugData(debugLoc.scope); - } + const DebugLocation &debugLoc = m_Program->m_DebugLocations[dbgLoc]; + thisScope = AddScopedDebugData(debugLoc.scope); } + if(!thisScope) + continue; + if(currentScope) currentScope->maxInstruction = instructionIndex - 1; - if(thisScope == currentScope) - continue; + currentScope = thisScope; + thisScope->maxInstruction = instructionIndex; + } + } + } - if(!thisScope) + // Sort the scopes by instruction index + std::sort(m_DebugInfo.scopedDebugDatas.begin(), m_DebugInfo.scopedDebugDatas.end(), + [](const ScopedDebugData *a, const ScopedDebugData *b) { return *a < *b; }); + + // Generate callstacks + for(const Function *f : m_Program->m_Functions) + { + if(!f->external) + { + FunctionInfo &info = m_FunctionInfos[f]; + uint32_t countInstructions = (uint32_t)f->instructions.size(); + + rdcarray scopeHierarchy; + for(uint32_t i = 0; i < countInstructions; ++i) + { + uint32_t instructionIndex = i + info.globalInstructionOffset; + const Instruction &inst = *f->instructions[i]; + // Use DebugLoc data for building up the list of scopes + uint32_t dbgLoc = ShouldIgnoreSourceMapping(inst) ? ~0U : inst.debugLoc; + if(dbgLoc == ~0U) continue; - currentScope = thisScope; - thisScope->maxInstruction = instructionIndex; - // Walk upwards from this scope to find where to append to the scope hierarchy + FunctionInfo::Callstack callstack; + + const DebugLocation *debugLoc = &m_Program->m_DebugLocations[dbgLoc]; + // For each DILocation + while(debugLoc) { - ScopedDebugData *scope = thisScope; - while(scope) + // Walk scope upwards to make callstack : always a DebugScope + const DXIL::Metadata *scopeMD = debugLoc->scope; + while(scopeMD) { - int32_t index = scopeHierarchy.indexOf(thisScope); - if(index >= 0) + DXIL::DIBase *dwarf = scopeMD->dwarf; + if(dwarf) { - scopeHierarchy.erase(index, scopeHierarchy.count() - index); - break; + // Walk upwards through all the functions + if(dwarf->type == DIBase::Subprogram) + { + rdcstr funcName = m_Program->GetFunctionScopeName(dwarf); + if(!funcName.empty()) + callstack.insert(0, funcName); + scopeMD = dwarf->As()->scope; + } + else if(dwarf->type == DIBase::LexicalBlock) + { + scopeMD = dwarf->As()->scope; + } + else if(dwarf->type == DIBase::File) + { + scopeMD = NULL; + break; + } + else + { + RDCERR("Unhandled scope type %s", ToStr(dwarf->type).c_str()); + scopeMD = NULL; + break; + } } - scope = scope->parent; } + // Make new DILocation from inlinedAt and walk that DILocation + if(debugLoc->inlinedAt) + debugLoc = debugLoc->inlinedAt->debugLoc; + else + debugLoc = NULL; } - // Add the new scope to the hierarchy and generate the callstack - scopeHierarchy.push_back(thisScope); - - FunctionInfo::Callstack callstack; - for(ScopedDebugData *scope : scopeHierarchy) - { - if(!scope->functionName.empty()) - callstack.push_back(scope->functionName); - } - // If there is no callstack then use the function name - if(callstack.empty()) - callstack.push_back(f->name); info.callstacks[instructionIndex] = callstack; } - // If there is no callstack for the function then use the function name - if(info.callstacks.empty()) - { - FunctionInfo::Callstack callstack; - callstack.push_back(f->name); - info.callstacks[0] = callstack; - } } } - // Sort the scopes by instruction index - std::sort(m_DebugInfo.scopedDebugDatas.begin(), m_DebugInfo.scopedDebugDatas.end(), - [](const ScopedDebugData *a, const ScopedDebugData *b) { return *a < *b; }); - ParseDebugData(); // Extend the life time of any SSA ID which is mapped to a source variable