From 4b5db1c7656031d6c08c7c853585767070153ea5 Mon Sep 17 00:00:00 2001 From: Jeff Noyle Date: Wed, 6 Dec 2023 09:33:40 -0800 Subject: [PATCH] PIX: Fix scope ascension for variable lookup (#6091) In DxcPixLiveVariables.cpp, UniqueScopeForInlinedFunctions::AscendScopeHierarchy tries to keep track of a variable's scope and the scope at which its containing function was inlined, if any. This scope duo has to be the same in two places for things to work out: the scopes inferred from dbg.declare, and then again when the scope hierarchy is ascended in order to discover all variables accessible from a given point in the program. AscendScopeHierarchy was forgetting to update the inlined part of the scope, resulting in, for example, PIX's debugger losing track of variables outside of, for example, a for loop, even though the code can see those variables at that point. Just setting the inlined scope to be the same as the function scope fixes this problem. (In addition to these new tests, existing PIX tests exercise this situation happily) --- include/dxc/Test/HlslTestUtils.h | 20 ++ include/dxc/WinAdapter.h | 5 + lib/DxilDia/DxcPixLiveVariables.cpp | 11 +- tools/clang/unittests/HLSL/PixDiaTest.cpp | 282 ++++++++++++++++++-- tools/clang/unittests/HLSL/PixTestUtils.cpp | 60 +++++ tools/clang/unittests/HLSL/PixTestUtils.h | 18 +- 6 files changed, 366 insertions(+), 30 deletions(-) diff --git a/include/dxc/Test/HlslTestUtils.h b/include/dxc/Test/HlslTestUtils.h index 666feb9dcd..b1903b9c08 100644 --- a/include/dxc/Test/HlslTestUtils.h +++ b/include/dxc/Test/HlslTestUtils.h @@ -115,6 +115,7 @@ using namespace std; #endif // VERIFY_ARE_EQUAL static constexpr char whitespaceChars[] = " \t\r\n"; +static constexpr wchar_t wideWhitespaceChars[] = L" \t\r\n"; inline std::string strltrim(const std::string &value) { size_t first = value.find_first_not_of(whitespaceChars); @@ -157,6 +158,25 @@ strtok(const std::string &value, const char *delimiters = whitespaceChars) { return tokens; } +inline std::vector +strtok(const std::wstring &value, + const wchar_t *delimiters = wideWhitespaceChars) { + size_t searchOffset = 0; + std::vector tokens; + while (searchOffset != value.size()) { + size_t tokenStartIndex = value.find_first_not_of(delimiters, searchOffset); + if (tokenStartIndex == std::string::npos) + break; + size_t tokenEndIndex = value.find_first_of(delimiters, tokenStartIndex); + if (tokenEndIndex == std::string::npos) + tokenEndIndex = value.size(); + tokens.emplace_back( + value.substr(tokenStartIndex, tokenEndIndex - tokenStartIndex)); + searchOffset = tokenEndIndex; + } + return tokens; +} + // strreplace will replace all instances of lookFors with replacements at the // same index. Will log an error if the string is not found, unless the first // character is ? marking it optional. diff --git a/include/dxc/WinAdapter.h b/include/dxc/WinAdapter.h index fe4cb14ffa..b2eb35b61c 100644 --- a/include/dxc/WinAdapter.h +++ b/include/dxc/WinAdapter.h @@ -1015,6 +1015,11 @@ class CComBSTR { m_str = NULL; return s; } + + void Empty() throw() { + SysFreeString(m_str); + m_str = NULL; + } }; //===--------- Convert argv to wchar ----------------===// diff --git a/lib/DxilDia/DxcPixLiveVariables.cpp b/lib/DxilDia/DxcPixLiveVariables.cpp index 9c1bc38c90..725c2078c0 100644 --- a/lib/DxilDia/DxcPixLiveVariables.cpp +++ b/lib/DxilDia/DxcPixLiveVariables.cpp @@ -49,12 +49,19 @@ class UniqueScopeForInlinedFunctions { // is at file-level scope, it will return nullptr. if (auto Namespace = llvm::dyn_cast(FunctionScope)) { if (auto *ContainingScope = Namespace->getScope()) { - FunctionScope = ContainingScope; + if (FunctionScope == InlinedAtScope) + InlinedAtScope = FunctionScope = ContainingScope; + else + FunctionScope = ContainingScope; return; } } const llvm::DITypeIdentifierMap EmptyMap; - FunctionScope = FunctionScope->getScope().resolve(EmptyMap); + if (FunctionScope == InlinedAtScope) + InlinedAtScope = FunctionScope = + FunctionScope->getScope().resolve(EmptyMap); + else + FunctionScope = FunctionScope->getScope().resolve(EmptyMap); } static UniqueScopeForInlinedFunctions Create(llvm::DebugLoc const &DbgLoc, diff --git a/tools/clang/unittests/HLSL/PixDiaTest.cpp b/tools/clang/unittests/HLSL/PixDiaTest.cpp index e288df6a33..dede2386df 100644 --- a/tools/clang/unittests/HLSL/PixDiaTest.cpp +++ b/tools/clang/unittests/HLSL/PixDiaTest.cpp @@ -14,7 +14,6 @@ #include "dxc/DxilContainer/DxilContainer.h" #include "dxc/Support/WinIncludes.h" -#include "dxc/dxcapi.h" #include "dxc/Test/DxcTestUtils.h" #include "dxc/Test/HLSLTestData.h" @@ -177,8 +176,14 @@ class PixDiaTest { TEST_METHOD(DxcPixDxilDebugInfo_UnnamedField) TEST_METHOD(DxcPixDxilDebugInfo_SubProgramsInNamespaces) TEST_METHOD(DxcPixDxilDebugInfo_SubPrograms) - TEST_METHOD(DxcPixDxilDebugInfo_InlinedFunctions_TwiceInlinedFunctions) - TEST_METHOD(DxcPixDxilDebugInfo_InlinedFunctions_CalledTwiceInSameCaller) + TEST_METHOD( + DxcPixDxilDebugInfo_VariableScopes_InlinedFunctions_TwiceInlinedFunctions) + TEST_METHOD( + DxcPixDxilDebugInfo_VariableScopes_InlinedFunctions_CalledTwiceInSameCaller) + TEST_METHOD(DxcPixDxilDebugInfo_VariableScopes_ForScopes) + TEST_METHOD(DxcPixDxilDebugInfo_VariableScopes_ScopeBraces) + TEST_METHOD(DxcPixDxilDebugInfo_VariableScopes_Function) + TEST_METHOD(DxcPixDxilDebugInfo_VariableScopes_Member) dxc::DxcDllSupport m_dllSupport; VersionSupportInfo m_ver; @@ -638,7 +643,8 @@ class PixDiaTest { const char *hlsl, const wchar_t *profile, const char *lineAtWhichToExamineVariables, std::vector const &ExpectedVariables); - CComPtr + + DebuggerInterfaces CompileAndCreateDxcDebug(const char *hlsl, const wchar_t *profile, IDxcIncludeHandler *includer = nullptr); @@ -769,7 +775,7 @@ void PixDiaTest::CompileAndRunAnnotationAndGetDebugPart( *ppDebugPart = GetDebugPart(dllSupport, pNewContainer).Detach(); } -CComPtr +DebuggerInterfaces PixDiaTest::CompileAndCreateDxcDebug(const char *hlsl, const wchar_t *profile, IDxcIncludeHandler *includer) { @@ -783,9 +789,10 @@ PixDiaTest::CompileAndCreateDxcDebug(const char *hlsl, const wchar_t *profile, CComPtr Factory; VERIFY_SUCCEEDED(session->QueryInterface(IID_PPV_ARGS(&Factory))); - CComPtr dxilDebugger; - VERIFY_SUCCEEDED(Factory->NewDxcPixDxilDebugInfo(&dxilDebugger)); - return dxilDebugger; + DebuggerInterfaces ret; + VERIFY_SUCCEEDED(Factory->NewDxcPixDxilDebugInfo(&ret.debugInfo)); + VERIFY_SUCCEEDED(Factory->NewDxcPixCompilationInfo(&ret.compilationInfo)); + return ret; } CComPtr @@ -845,7 +852,7 @@ void PixDiaTest::TestGlobalStaticCase( const char *lineAtWhichToExamineVariables, std::vector const &ExpectedVariables) { - auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, profile); + auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, profile).debugInfo; auto liveVariables = GetLiveVariablesAt(hlsl, lineAtWhichToExamineVariables, dxilDebugger); @@ -2239,7 +2246,7 @@ void main() } )"; - auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_6"); + auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_6").debugInfo; auto liveVariables = GetLiveVariablesAt(hlsl, "InterestingLine", dxilDebugger); @@ -2300,7 +2307,7 @@ void main() } )"; - auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_6"); + auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_6").debugInfo; auto liveVariables = GetLiveVariablesAt(hlsl, "InterestingLine", dxilDebugger); @@ -2358,7 +2365,7 @@ void main() } )"; - auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_6"); + auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_6").debugInfo; auto liveVariables = GetLiveVariablesAt(hlsl, "InterestingLine", dxilDebugger); @@ -2418,7 +2425,7 @@ void main() } )"; - auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_6"); + auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_6").debugInfo; auto liveVariables = GetLiveVariablesAt(hlsl, "InterestingLine", dxilDebugger); @@ -2451,7 +2458,7 @@ void PixDiaTest::TestUnnamedTypeCase(const char *hlsl, const wchar_t *expectedTypeName) { if (m_ver.SkipDxilVersion(1, 2)) return; - auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_0"); + auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_0").debugInfo; auto liveVariables = GetLiveVariablesAt(hlsl, "InterestingLine", dxilDebugger); DWORD count; @@ -2562,7 +2569,7 @@ void main() if (m_ver.SkipDxilVersion(1, 2)) return; - auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_0"); + auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"cs_6_0").debugInfo; auto liveVariables = GetLiveVariablesAt(hlsl, "InterestingLine", dxilDebugger); DWORD count; @@ -2656,7 +2663,7 @@ float4 fn2( float3 f3, float d, bool sanitize = true ) )"}}); auto dxilDebugger = - CompileAndCreateDxcDebug(hlsl, L"cs_6_0", pIncludeHandler); + CompileAndCreateDxcDebug(hlsl, L"cs_6_0", pIncludeHandler).debugInfo; struct SourceLocations { CComBSTR Filename; @@ -2780,7 +2787,7 @@ static DWORD AdvanceUntilFunctionEntered(IDxcPixDxilDebugInfo *dxilDebugger, static DWORD GetRegisterNumberForVariable(IDxcPixDxilDebugInfo *dxilDebugger, DWORD instructionOffset, wchar_t const *variableName, - wchar_t const *memberName) { + wchar_t const *memberName = nullptr) { CComPtr DxcPixDxilLiveVariables; if (SUCCEEDED(dxilDebugger->GetLiveVariablesAt(instructionOffset, &DxcPixDxilLiveVariables))) { @@ -2795,12 +2802,14 @@ static DWORD GetRegisterNumberForVariable(IDxcPixDxilDebugInfo *dxilDebugger, if (Name == variableName) { CComPtr DxcPixDxilStorage; VERIFY_SUCCEEDED(DxcPixVariable->GetStorage(&DxcPixDxilStorage)); - CComPtr DxcPixDxilMemberStorage; - VERIFY_SUCCEEDED(DxcPixDxilStorage->AccessField( - memberName, &DxcPixDxilMemberStorage)); + if (memberName != nullptr) { + CComPtr DxcPixDxilMemberStorage; + VERIFY_SUCCEEDED(DxcPixDxilStorage->AccessField( + memberName, &DxcPixDxilMemberStorage)); + DxcPixDxilStorage = DxcPixDxilMemberStorage; + } DWORD RegisterNumber = 42; - VERIFY_SUCCEEDED( - DxcPixDxilMemberStorage->GetRegisterNumber(&RegisterNumber)); + VERIFY_SUCCEEDED(DxcPixDxilStorage->GetRegisterNumber(&RegisterNumber)); return RegisterNumber; } } @@ -2809,7 +2818,37 @@ static DWORD GetRegisterNumberForVariable(IDxcPixDxilDebugInfo *dxilDebugger, return -1; } -TEST_F(PixDiaTest, DxcPixDxilDebugInfo_InlinedFunctions_TwiceInlinedFunctions) { +static void +CheckVariableExistsAtThisInstruction(IDxcPixDxilDebugInfo *dxilDebugger, + DWORD instructionOffset, + wchar_t const *variableName) { + // It's sufficient to know that there _is_ a register number the var: + (void)GetRegisterNumberForVariable(dxilDebugger, instructionOffset, + variableName); +} + +static void +CheckVariableDoesNOTExistsAtThisInstruction(IDxcPixDxilDebugInfo *dxilDebugger, + DWORD instructionOffset, + wchar_t const *variableName) { + CComPtr DxcPixDxilLiveVariables; + VERIFY_SUCCEEDED(dxilDebugger->GetLiveVariablesAt(instructionOffset, + &DxcPixDxilLiveVariables)); + DWORD count = 42; + VERIFY_SUCCEEDED(DxcPixDxilLiveVariables->GetCount(&count)); + for (DWORD i = 0; i < count; ++i) { + CComPtr DxcPixVariable; + VERIFY_SUCCEEDED( + DxcPixDxilLiveVariables->GetVariableByIndex(i, &DxcPixVariable)); + CComBSTR Name; + VERIFY_SUCCEEDED(DxcPixVariable->GetName(&Name)); + VERIFY_ARE_NOT_EQUAL(Name, variableName); + } +} + +TEST_F( + PixDiaTest, + DxcPixDxilDebugInfo_VariableScopes_InlinedFunctions_TwiceInlinedFunctions) { if (m_ver.SkipDxilVersion(1, 6)) return; @@ -2880,7 +2919,7 @@ float4 DeeperInlinedFunction(in BuiltInTriangleIntersectionAttributes attr, int )"}}); auto dxilDebugger = - CompileAndCreateDxcDebug(hlsl, L"lib_6_6", pIncludeHandler); + CompileAndCreateDxcDebug(hlsl, L"lib_6_6", pIncludeHandler).debugInfo; // Case: same functions called from two different top-level callers DWORD instructionOffset = @@ -2923,8 +2962,9 @@ float4 DeeperInlinedFunction(in BuiltInTriangleIntersectionAttributes attr, int ColorRegisterNumberWhenCalledFromOuterForDeeper); } -TEST_F(PixDiaTest, - DxcPixDxilDebugInfo_InlinedFunctions_CalledTwiceInSameCaller) { +TEST_F( + PixDiaTest, + DxcPixDxilDebugInfo_VariableScopes_InlinedFunctions_CalledTwiceInSameCaller) { if (m_ver.SkipDxilVersion(1, 6)) return; @@ -2953,7 +2993,7 @@ void ClosestHitShader3(inout RayPayload payload, in BuiltInTriangleIntersectionA } )"; - auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"lib_6_6"); + auto dxilDebugger = CompileAndCreateDxcDebug(hlsl, L"lib_6_6").debugInfo; // Case: same function called from two places in same top-level function. // In this case, we expect the storage for the variable to be in the same @@ -2976,4 +3016,192 @@ void ClosestHitShader3(inout RayPayload payload, in BuiltInTriangleIntersectionA VERIFY_ARE_EQUAL(callsite0, callsite1); } +TEST_F(PixDiaTest, DxcPixDxilDebugInfo_VariableScopes_ForScopes) { + if (m_ver.SkipDxilVersion(1, 6)) + return; + + const char *hlsl = + R"(/*01*/RWStructuredBuffer intRWUAV: register(u0); +/*02*/[shader("compute")] +/*03*/[numthreads(1,1,1)] +/*04*/void CSMain() +/*05*/{ +/*06*/ int zero = intRWUAV.Load(0); +/*07*/ int two = zero * 2; // debug-loc(CheckVariableExistsHere) +/*08*/ int three = zero * 3; +/*09*/ for(int i =0; i < two; ++ i) +/*10*/ { +/*11*/ int one = intRWUAV.Load(i); +/*12*/ three += one; // debug-loc(Stop inside loop) +/*13*/ } +/*14*/ intRWUAV[0] = three; // debug-loc(Stop here) +/*15*/} +)"; + + auto debugInterfaces = CompileAndCreateDxcDebug(hlsl, L"lib_6_6"); + auto dxilDebugger = debugInterfaces.debugInfo; + auto Labels = GatherDebugLocLabelsFromDxcUtils(debugInterfaces); + + // Case: same function called from two places in same top-level function. + // In this case, we expect the storage for the variable to be in the same + // place for both "instances" of the function: as a thread proceeds through + // the caller, it will write new values into the variable's storage during + // the second or subsequent invocations of the inlined function. + DWORD instructionOffset = + AdvanceUntilFunctionEntered(dxilDebugger, 0, L"CSMain"); + + instructionOffset = + Labels->FindInstructionOffsetForLabel(L"CheckVariableExistsHere"); + CheckVariableExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"zero"); + + instructionOffset = + Labels->FindInstructionOffsetForLabel(L"Stop inside loop"); + CheckVariableExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"zero"); + + instructionOffset = Labels->FindInstructionOffsetForLabel(L"Stop here"); + CheckVariableDoesNOTExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"one"); +} + +TEST_F(PixDiaTest, DxcPixDxilDebugInfo_VariableScopes_ScopeBraces) { + if (m_ver.SkipDxilVersion(1, 6)) + return; + + const char *hlsl = + R"(/*01*/RWStructuredBuffer intRWUAV: register(u0); +/*02*/[shader("compute")] +/*03*/[numthreads(1,1,1)] +/*04*/void CSMain() +/*05*/{ +/*06*/ int zero = intRWUAV.Load(0); +/*07*/ int two = zero * 2; // debug-loc(CheckVariableExistsHere) +/*08*/ { +/*09*/ int one = intRWUAV.Load(1); +/*10*/ two += one; // debug-loc(Stop inside loop) +/*11*/ } +/*12*/ intRWUAV[0] = two; // debug-loc(Stop here) +/*13*/} +)"; + + auto debugInterfaces = CompileAndCreateDxcDebug(hlsl, L"lib_6_6"); + auto Labels = GatherDebugLocLabelsFromDxcUtils(debugInterfaces); + auto dxilDebugger = debugInterfaces.debugInfo; + + // Case: same function called from two places in same top-level function. + // In this case, we expect the storage for the variable to be in the same + // place for both "instances" of the function: as a thread proceeds through + // the caller, it will write new values into the variable's storage during + // the second or subsequent invocations of the inlined function. + DWORD instructionOffset = + AdvanceUntilFunctionEntered(dxilDebugger, 0, L"CSMain"); + + instructionOffset = + Labels->FindInstructionOffsetForLabel(L"CheckVariableExistsHere"); + CheckVariableExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"zero"); + + instructionOffset = + Labels->FindInstructionOffsetForLabel(L"Stop inside loop"); + CheckVariableExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"zero"); + + instructionOffset = Labels->FindInstructionOffsetForLabel(L"Stop here"); + CheckVariableDoesNOTExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"one"); +} + +TEST_F(PixDiaTest, DxcPixDxilDebugInfo_VariableScopes_Function) { + if (m_ver.SkipDxilVersion(1, 6)) + return; + + const char *hlsl = + R"(/*01*/RWStructuredBuffer intRWUAV: register(u0); +/*02*/int Square(int i) { +/*03*/ int i2 = i * i; // debug-loc(Stop in subroutine) +/*04*/ return i2; +/*05*/} +/*06*/[shader("compute")] +/*07*/[numthreads(1,1,1)] +/*08*/void CSMain() +/*09*/{ +/*10*/ int zero = intRWUAV.Load(0); +/*11*/ int two = Square(zero); +/*12*/ intRWUAV[0] = two; // debug-loc(Stop here) +/*13*/} +)"; + + auto debugInterfaces = CompileAndCreateDxcDebug(hlsl, L"lib_6_6"); + auto Labels = GatherDebugLocLabelsFromDxcUtils(debugInterfaces); + auto dxilDebugger = debugInterfaces.debugInfo; + + // Case: same function called from two places in same top-level function. + // In this case, we expect the storage for the variable to be in the same + // place for both "instances" of the function: as a thread proceeds through + // the caller, it will write new values into the variable's storage during + // the second or subsequent invocations of the inlined function. + DWORD instructionOffset = + AdvanceUntilFunctionEntered(dxilDebugger, 0, L"CSMain"); + + instructionOffset = + Labels->FindInstructionOffsetForLabel(L"Stop in subroutine"); + CheckVariableDoesNOTExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"zero"); + + instructionOffset = Labels->FindInstructionOffsetForLabel(L"Stop here"); + CheckVariableDoesNOTExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"i2"); + CheckVariableExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"zero"); +} + +TEST_F(PixDiaTest, DxcPixDxilDebugInfo_VariableScopes_Member) { + if (m_ver.SkipDxilVersion(1, 6)) + return; + + const char *hlsl = + R"(/*01*/RWStructuredBuffer intRWUAV: register(u0); +struct Struct { + int i; + int Getter() { + int q = i; + return q; //debug-loc(inside member fn) + } +}; +[shader("compute")] +[numthreads(1,1,1)] +void CSMain() +{ + Struct s; + s.i = intRWUAV.Load(0); + int j = s.Getter(); + intRWUAV[0] = j; // debug-loc(Stop here) +} +)"; + + auto debugInterfaces = CompileAndCreateDxcDebug(hlsl, L"lib_6_6"); + auto Labels = GatherDebugLocLabelsFromDxcUtils(debugInterfaces); + auto dxilDebugger = debugInterfaces.debugInfo; + + // Case: same function called from two places in same top-level function. + // In this case, we expect the storage for the variable to be in the same + // place for both "instances" of the function: as a thread proceeds through + // the caller, it will write new values into the variable's storage during + // the second or subsequent invocations of the inlined function. + DWORD instructionOffset = + AdvanceUntilFunctionEntered(dxilDebugger, 0, L"CSMain"); + + instructionOffset = + Labels->FindInstructionOffsetForLabel(L"inside member fn"); + CheckVariableDoesNOTExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"s"); + CheckVariableExistsAtThisInstruction(dxilDebugger, instructionOffset, L"q"); + + instructionOffset = Labels->FindInstructionOffsetForLabel(L"Stop here"); + CheckVariableDoesNOTExistsAtThisInstruction(dxilDebugger, instructionOffset, + L"i"); + CheckVariableExistsAtThisInstruction(dxilDebugger, instructionOffset, L"j"); +} + #endif diff --git a/tools/clang/unittests/HLSL/PixTestUtils.cpp b/tools/clang/unittests/HLSL/PixTestUtils.cpp index a4a8033099..4d4a8f2bb7 100644 --- a/tools/clang/unittests/HLSL/PixTestUtils.cpp +++ b/tools/clang/unittests/HLSL/PixTestUtils.cpp @@ -274,6 +274,66 @@ PassOutput RunAnnotationPasses(dxc::DxcDllSupport &dllSupport, IDxcBlob *dxil, Tokenize(outputText.c_str(), "\n")}; } +class InstructionOffsetSeekerImpl : public InstructionOffsetSeeker { + + std::map m_labelToInstructionOffset; + +public: + InstructionOffsetSeekerImpl(DebuggerInterfaces &debuggerInterfaces) { + DWORD SourceFileOrdinal = 0; + CComBSTR fileName; + CComBSTR fileContent; + while (SUCCEEDED(debuggerInterfaces.compilationInfo->GetSourceFile( + SourceFileOrdinal, &fileName, &fileContent))) { + auto lines = strtok(std::wstring(fileContent), L"\n"); + for (size_t line = 0; line < lines.size(); ++line) { + const wchar_t DebugLocLabel[] = L"debug-loc("; + auto DebugLocPos = lines[line].find(DebugLocLabel); + if (DebugLocPos != std::wstring::npos) { + auto StartLabelPos = DebugLocPos + (_countof(DebugLocLabel) - 1); + auto CloseDebugLocPos = lines[line].find(L")", StartLabelPos); + if (CloseDebugLocPos == std::string::npos) { + VERIFY_ARE_NOT_EQUAL(CloseDebugLocPos, std::string::npos); + } + auto Label = lines[line].substr(StartLabelPos, + CloseDebugLocPos - StartLabelPos); + CComPtr InstructionOffsets; + if (FAILED((debuggerInterfaces.debugInfo + ->InstructionOffsetsFromSourceLocation( + fileName, line + 1, 0, &InstructionOffsets)))) { + VERIFY_FAIL(L"InstructionOffsetsFromSourceLocation failed"); + } + auto InstructionOffsetCount = InstructionOffsets->GetCount(); + if (InstructionOffsetCount == 0) { + VERIFY_FAIL(L"Instruction offset count was zero"); + } + if (m_labelToInstructionOffset.find(Label) != + m_labelToInstructionOffset.end()) { + VERIFY_FAIL(L"Duplicate label found!"); + } + // Just the last offset is sufficient: + m_labelToInstructionOffset[Label] = + InstructionOffsets->GetOffsetByIndex(InstructionOffsetCount - 1); + } + } + SourceFileOrdinal++; + fileName.Empty(); + fileContent.Empty(); + } + } + + virtual DWORD FindInstructionOffsetForLabel(const wchar_t *label) override { + VERIFY_ARE_NOT_EQUAL(m_labelToInstructionOffset.find(label), + m_labelToInstructionOffset.end()); + return m_labelToInstructionOffset[label]; + } +}; + +std::unique_ptr +GatherDebugLocLabelsFromDxcUtils(DebuggerInterfaces &debuggerInterfaces) { + return std::make_unique(debuggerInterfaces); +} + CComPtr GetDebugPart(dxc::DxcDllSupport &dllSupport, IDxcBlob *container) { diff --git a/tools/clang/unittests/HLSL/PixTestUtils.h b/tools/clang/unittests/HLSL/PixTestUtils.h index fd9ad9d736..8f7d0cdf45 100644 --- a/tools/clang/unittests/HLSL/PixTestUtils.h +++ b/tools/clang/unittests/HLSL/PixTestUtils.h @@ -12,8 +12,9 @@ #pragma once #include "dxc/Support/WinIncludes.h" -#include "dxc/dxcapi.h" +#include "dxc/dxcpix.h" +#include #include #include @@ -57,4 +58,19 @@ struct PassOutput { PassOutput RunAnnotationPasses(dxc::DxcDllSupport &dllSupport, IDxcBlob *dxil, int startingLineNumber = 0); + +struct DebuggerInterfaces { + CComPtr debugInfo; + CComPtr compilationInfo; +}; + +class InstructionOffsetSeeker { +public: + virtual ~InstructionOffsetSeeker() {} + virtual DWORD FindInstructionOffsetForLabel(const wchar_t *label) = 0; +}; + +std::unique_ptr +GatherDebugLocLabelsFromDxcUtils(DebuggerInterfaces &debuggerInterfaces); + } // namespace pix_test