| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // DxilDiaSession.cpp // |
| // Copyright (C) Microsoft Corporation. All rights reserved. // |
| // This file is distributed under the University of Illinois Open Source // |
| // License. See LICENSE.TXT for details. // |
| // // |
| // DIA API implementation for DXIL modules. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "DxilDiaSession.h" |
| |
| #include "dxc/DxilPIXPasses/DxilPIXVirtualRegisters.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/InstIterator.h" |
| #include "llvm/IR/Instruction.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/Metadata.h" |
| #include "llvm/IR/Module.h" |
| |
| #include "..\DxilPIXPasses\PixPassHelpers.h" |
| #include "DxilDia.h" |
| #include "DxilDiaEnumTables.h" |
| #include "DxilDiaTable.h" |
| #include "DxilDiaTableInjectedSources.h" |
| #include "DxilDiaTableLineNumbers.h" |
| #include "DxilDiaTableSourceFiles.h" |
| #include "DxilDiaTableSymbols.h" |
| |
| void dxil_dia::Session::Init(std::shared_ptr<llvm::LLVMContext> context, |
| std::shared_ptr<llvm::Module> mod, |
| std::shared_ptr<llvm::DebugInfoFinder> finder) { |
| m_pEnumTables = nullptr; |
| m_module = mod; |
| m_context = context; |
| m_finder = finder; |
| m_dxilModule = llvm::make_unique<hlsl::DxilModule>(mod.get()); |
| |
| // Extract HLSL metadata. |
| m_dxilModule->LoadDxilMetadata(); |
| |
| // Get file contents. |
| m_contents = |
| m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceContentsMDName); |
| if (!m_contents) |
| m_contents = m_module->getNamedMetadata("llvm.dbg.contents"); |
| |
| m_defines = |
| m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceDefinesMDName); |
| if (!m_defines) |
| m_defines = m_module->getNamedMetadata("llvm.dbg.defines"); |
| |
| m_mainFileName = m_module->getNamedMetadata( |
| hlsl::DxilMDHelper::kDxilSourceMainFileNameMDName); |
| if (!m_mainFileName) |
| m_mainFileName = m_module->getNamedMetadata("llvm.dbg.mainFileName"); |
| |
| m_arguments = |
| m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceArgsMDName); |
| if (!m_arguments) |
| m_arguments = m_module->getNamedMetadata("llvm.dbg.args"); |
| |
| // Build up a linear list of instructions. The index will be used as the |
| // RVA. |
| std::vector<llvm::Function *> allInstrumentableFunctions = |
| PIXPassHelpers::GetAllInstrumentableFunctions(*m_dxilModule.get()); |
| for (auto fn : allInstrumentableFunctions) { |
| for (llvm::inst_iterator it = inst_begin(fn), end = inst_end(fn); it != end; |
| ++it) { |
| llvm::Instruction &i = *it; |
| RVA rva; |
| if (!pix_dxil::PixDxilInstNum::FromInst(&i, &rva)) { |
| continue; |
| } |
| m_rvaMap.insert({&i, rva}); |
| m_instructions.insert({rva, &i}); |
| if (llvm::DebugLoc DL = i.getDebugLoc()) { |
| auto result = m_lineToInfoMap.emplace( |
| DL.getLine(), LineInfo(DL.getCol(), rva, rva + 1)); |
| if (!result.second) { |
| result.first->second.StartCol = |
| std::min(result.first->second.StartCol, DL.getCol()); |
| result.first->second.Last = rva + 1; |
| } |
| m_instructionLines.push_back(&i); |
| } |
| } |
| } |
| |
| // Sanity check to make sure rva map is same as instruction index. |
| for (auto It = m_instructions.begin(); It != m_instructions.end(); ++It) { |
| DXASSERT(m_rvaMap.find(It->second) != m_rvaMap.end(), |
| "instruction not mapped to rva"); |
| DXASSERT(m_rvaMap[It->second] == It->first, |
| "instruction mapped to wrong rva"); |
| } |
| } |
| |
| const dxil_dia::SymbolManager &dxil_dia::Session::SymMgr() { |
| if (!m_symsMgr) { |
| try { |
| m_symsMgr.reset(new dxil_dia::SymbolManager()); |
| m_symsMgr->Init(this); |
| } catch (const hlsl::Exception &) { |
| m_symsMgr.reset(new dxil_dia::SymbolManager()); |
| } |
| } |
| return *m_symsMgr; |
| } |
| |
| HRESULT dxil_dia::Session::getSourceFileIdByName(llvm::StringRef fileName, |
| DWORD *pRetVal) { |
| if (Contents() != nullptr) { |
| for (unsigned i = 0; i < Contents()->getNumOperands(); ++i) { |
| llvm::StringRef fn = llvm::dyn_cast<llvm::MDString>( |
| Contents()->getOperand(i)->getOperand(0)) |
| ->getString(); |
| if (fn.equals(fileName)) { |
| *pRetVal = i; |
| return S_OK; |
| } |
| } |
| } |
| *pRetVal = 0; |
| return S_FALSE; |
| } |
| |
| STDMETHODIMP dxil_dia::Session::get_loadAddress( |
| /* [retval][out] */ ULONGLONG *pRetVal) { |
| *pRetVal = 0; |
| return S_OK; |
| } |
| |
| STDMETHODIMP dxil_dia::Session::get_globalScope( |
| /* [retval][out] */ IDiaSymbol **pRetVal) { |
| DxcThreadMalloc TM(m_pMalloc); |
| |
| if (pRetVal == nullptr) { |
| return E_INVALIDARG; |
| } |
| *pRetVal = nullptr; |
| |
| Symbol *ret; |
| IFR(SymMgr().GetGlobalScope(&ret)); |
| *pRetVal = ret; |
| return S_OK; |
| } |
| |
| STDMETHODIMP dxil_dia::Session::getEnumTables(IDiaEnumTables **ppEnumTables) { |
| if (!m_pEnumTables) { |
| DxcThreadMalloc TM(m_pMalloc); |
| IFR(EnumTables::Create(this, &m_pEnumTables)); |
| } |
| m_pEnumTables.p->AddRef(); |
| *ppEnumTables = m_pEnumTables; |
| return S_OK; |
| } |
| |
| STDMETHODIMP dxil_dia::Session::findFileById( |
| /* [in] */ DWORD uniqueId, |
| /* [out] */ IDiaSourceFile **ppResult) { |
| if (!m_pEnumTables) { |
| return E_INVALIDARG; |
| } |
| CComPtr<IDiaTable> pTable; |
| VARIANT vtIndex; |
| vtIndex.vt = VT_UI4; |
| vtIndex.uintVal = (int)Table::Kind::SourceFiles; |
| IFR(m_pEnumTables->Item(vtIndex, &pTable)); |
| CComPtr<IUnknown> pElt; |
| IFR(pTable->Item(uniqueId, &pElt)); |
| return pElt->QueryInterface(ppResult); |
| } |
| |
| STDMETHODIMP dxil_dia::Session::findFile( |
| /* [in] */ IDiaSymbol *pCompiland, |
| /* [in] */ LPCOLESTR name, |
| /* [in] */ DWORD compareFlags, |
| /* [out] */ IDiaEnumSourceFiles **ppResult) { |
| if (!m_pEnumTables) { |
| return E_INVALIDARG; |
| } |
| |
| // TODO: properly support compareFlags. |
| auto namecmp = &_wcsicmp; |
| if (compareFlags & nsCaseSensitive) { |
| namecmp = &wcscmp; |
| } |
| |
| DxcThreadMalloc TM(m_pMalloc); |
| CComPtr<IDiaTable> pTable; |
| VARIANT vtIndex; |
| vtIndex.vt = VT_UI4; |
| vtIndex.uintVal = (int)Table::Kind::SourceFiles; |
| IFR(m_pEnumTables->Item(vtIndex, &pTable)); |
| |
| CComPtr<IDiaEnumSourceFiles> pSourceTable; |
| IFR(pTable->QueryInterface(&pSourceTable)); |
| HRESULT hr; |
| CComPtr<IDiaSourceFile> src; |
| ULONG cnt; |
| std::vector<CComPtr<IDiaSourceFile>> sources; |
| |
| pSourceTable->Reset(); |
| while (SUCCEEDED(hr = pSourceTable->Next(1, &src, &cnt)) && hr == S_OK && |
| cnt == 1) { |
| CComBSTR currName; |
| IFR(src->get_fileName(&currName)); |
| if (namecmp(name, currName) == 0) { |
| sources.emplace_back(src); |
| } |
| src.Release(); |
| } |
| |
| *ppResult = CreateOnMalloc<SourceFilesTable>(GetMallocNoRef(), this, |
| std::move(sources)); |
| |
| if (*ppResult == nullptr) { |
| return E_OUTOFMEMORY; |
| } |
| (*ppResult)->AddRef(); |
| return S_OK; |
| } |
| |
| namespace dxil_dia { |
| static HRESULT DxcDiaFindLineNumbersByRVA(Session *pSession, DWORD rva, |
| DWORD length, |
| IDiaEnumLineNumbers **ppResult) { |
| if (!ppResult) |
| return E_POINTER; |
| |
| std::vector<const llvm::Instruction *> instructions; |
| auto &allInstructions = pSession->InstructionsRef(); |
| |
| // Gather the list of insructions that map to the given rva range. |
| for (DWORD i = rva; i < rva + length; ++i) { |
| auto It = allInstructions.find(i); |
| if (It == allInstructions.end()) |
| return E_INVALIDARG; |
| |
| // Only include the instruction if it has debug info for line mappings. |
| const llvm::Instruction *inst = It->second; |
| if (inst->getDebugLoc()) |
| instructions.push_back(inst); |
| } |
| |
| // Create line number table from explicit instruction list. |
| IMalloc *pMalloc = pSession->GetMallocNoRef(); |
| *ppResult = CreateOnMalloc<LineNumbersTable>(pMalloc, pSession, |
| std::move(instructions)); |
| if (*ppResult == nullptr) |
| return E_OUTOFMEMORY; |
| (*ppResult)->AddRef(); |
| return S_OK; |
| } |
| } // namespace dxil_dia |
| |
| STDMETHODIMP dxil_dia::Session::findLinesByAddr( |
| /* [in] */ DWORD seg, |
| /* [in] */ DWORD offset, |
| /* [in] */ DWORD length, |
| /* [out] */ IDiaEnumLineNumbers **ppResult) { |
| DxcThreadMalloc TM(m_pMalloc); |
| return DxcDiaFindLineNumbersByRVA(this, offset, length, ppResult); |
| } |
| |
| STDMETHODIMP dxil_dia::Session::findLinesByRVA( |
| /* [in] */ DWORD rva, |
| /* [in] */ DWORD length, |
| /* [out] */ IDiaEnumLineNumbers **ppResult) { |
| DxcThreadMalloc TM(m_pMalloc); |
| return DxcDiaFindLineNumbersByRVA(this, rva, length, ppResult); |
| } |
| |
| STDMETHODIMP dxil_dia::Session::findInlineeLinesByAddr( |
| /* [in] */ IDiaSymbol *parent, |
| /* [in] */ DWORD isect, |
| /* [in] */ DWORD offset, |
| /* [in] */ DWORD length, |
| /* [out] */ IDiaEnumLineNumbers **ppResult) { |
| DxcThreadMalloc TM(m_pMalloc); |
| return DxcDiaFindLineNumbersByRVA(this, offset, length, ppResult); |
| } |
| |
| STDMETHODIMP dxil_dia::Session::findLinesByLinenum( |
| /* [in] */ IDiaSymbol *compiland, |
| /* [in] */ IDiaSourceFile *file, |
| /* [in] */ DWORD linenum, |
| /* [in] */ DWORD column, |
| /* [out] */ IDiaEnumLineNumbers **ppResult) { |
| if (!m_pEnumTables) { |
| return E_INVALIDARG; |
| } |
| *ppResult = nullptr; |
| |
| DxcThreadMalloc TM(m_pMalloc); |
| CComPtr<IDiaTable> pTable; |
| VARIANT vtIndex; |
| vtIndex.vt = VT_UI4; |
| vtIndex.uintVal = (int)Table::Kind::LineNumbers; |
| IFR(m_pEnumTables->Item(vtIndex, &pTable)); |
| |
| CComPtr<IDiaEnumLineNumbers> pLineTable; |
| IFR(pTable->QueryInterface(&pLineTable)); |
| HRESULT hr; |
| CComPtr<IDiaLineNumber> line; |
| ULONG cnt; |
| std::vector<const llvm::Instruction *> lines; |
| |
| std::function<bool(DWORD, DWORD)> column_matches = |
| [](DWORD colStart, DWORD colEnd) -> bool { return true; }; |
| |
| if (column != 0) { |
| column_matches = [column](DWORD colStart, DWORD colEnd) -> bool { |
| return colStart < column && column < colEnd; |
| }; |
| } |
| |
| pLineTable->Reset(); |
| while (SUCCEEDED(hr = pLineTable->Next(1, &line, &cnt)) && hr == S_OK && |
| cnt == 1) { |
| CComPtr<IDiaSourceFile> f; |
| DWORD ln, lnEnd, cn, cnEnd; |
| IFR(line->get_lineNumber(&ln)); |
| IFR(line->get_lineNumberEnd(&lnEnd)); |
| IFR(line->get_columnNumber(&cn)); |
| IFR(line->get_columnNumberEnd(&cnEnd)); |
| IFR(line->get_sourceFile(&f)); |
| |
| if (file == f && (ln <= linenum && linenum <= lnEnd) && |
| column_matches(cn, cnEnd)) { |
| lines.emplace_back(reinterpret_cast<LineNumber *>(line.p)->Inst()); |
| } |
| line.Release(); |
| } |
| |
| HRESULT result = lines.empty() ? S_FALSE : S_OK; |
| *ppResult = CreateOnMalloc<LineNumbersTable>(GetMallocNoRef(), this, |
| std::move(lines)); |
| |
| if (*ppResult == nullptr) { |
| return E_OUTOFMEMORY; |
| } |
| (*ppResult)->AddRef(); |
| return result; |
| } |
| |
| STDMETHODIMP dxil_dia::Session::findInjectedSource( |
| /* [in] */ LPCOLESTR srcFile, |
| /* [out] */ IDiaEnumInjectedSources **ppResult) { |
| if (Contents() != nullptr) { |
| CW2A pUtf8FileName(srcFile, CP_UTF8); |
| DxcThreadMalloc TM(m_pMalloc); |
| IDiaTable *pTable; |
| IFT(Table::Create(this, Table::Kind::InjectedSource, &pTable)); |
| auto *pInjectedSource = reinterpret_cast<InjectedSourcesTable *>(pTable); |
| pInjectedSource->Init(pUtf8FileName.m_psz); |
| *ppResult = pInjectedSource; |
| return S_OK; |
| } |
| return S_FALSE; |
| } |
| |
| static constexpr DWORD kD3DCodeSection = 1; |
| STDMETHODIMP dxil_dia::Session::findInlineFramesByAddr( |
| /* [in] */ IDiaSymbol *parent, |
| /* [in] */ DWORD isect, |
| /* [in] */ DWORD offset, |
| /* [out] */ IDiaEnumSymbols **ppResult) { |
| if (parent != nullptr || isect != kD3DCodeSection || ppResult == nullptr) { |
| return E_INVALIDARG; |
| } |
| *ppResult = nullptr; |
| |
| DxcThreadMalloc TM(m_pMalloc); |
| auto &allInstructions = InstructionsRef(); |
| auto It = allInstructions.find(offset); |
| if (It == allInstructions.end()) { |
| return E_INVALIDARG; |
| } |
| |
| HRESULT hr; |
| SymbolChildrenEnumerator *ChildrenEnum; |
| IFR(hr = SymMgr().DbgScopeOf(It->second, &ChildrenEnum)); |
| |
| *ppResult = ChildrenEnum; |
| return hr; |
| } |