DxilDiaSession.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // DxilDiaSession.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. // //
  8. // DIA API implementation for DXIL modules. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #include "DxilDiaSession.h"
  12. #include "dxc/DxilPIXPasses/DxilPIXPasses.h"
  13. #include "dxc/DxilPIXPasses/DxilPIXVirtualRegisters.h"
  14. #include "llvm/ADT/STLExtras.h"
  15. #include "llvm/IR/Function.h"
  16. #include "llvm/IR/InstIterator.h"
  17. #include "llvm/IR/Instruction.h"
  18. #include "llvm/IR/Instructions.h"
  19. #include "llvm/IR/Metadata.h"
  20. #include "llvm/IR/Module.h"
  21. #include "llvm/IR/LegacyPassManager.h"
  22. #include "llvm/PassRegistry.h"
  23. #include "DxilDia.h"
  24. #include "DxilDiaEnumTables.h"
  25. #include "DxilDiaTable.h"
  26. #include "DxilDiaTableInjectedSources.h"
  27. #include "DxilDiaTableLineNumbers.h"
  28. #include "DxilDiaTableSourceFiles.h"
  29. #include "DxilDiaTableSymbols.h"
  30. void dxil_dia::Session::Init(
  31. std::shared_ptr<llvm::LLVMContext> context,
  32. std::shared_ptr<llvm::Module> mod,
  33. std::shared_ptr<llvm::DebugInfoFinder> finder) {
  34. m_pEnumTables = nullptr;
  35. m_module = mod;
  36. m_context = context;
  37. m_finder = finder;
  38. m_dxilModule = llvm::make_unique<hlsl::DxilModule>(mod.get());
  39. llvm::legacy::PassManager PM;
  40. llvm::initializeDxilDbgValueToDbgDeclarePass(*llvm::PassRegistry::getPassRegistry());
  41. llvm::initializeDxilAnnotateWithVirtualRegisterPass(*llvm::PassRegistry::getPassRegistry());
  42. PM.add(llvm::createDxilDbgValueToDbgDeclarePass());
  43. PM.add(llvm::createDxilAnnotateWithVirtualRegisterPass());
  44. PM.run(*m_module);
  45. // Extract HLSL metadata.
  46. m_dxilModule->LoadDxilMetadata();
  47. // Get file contents.
  48. m_contents =
  49. m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceContentsMDName);
  50. if (!m_contents)
  51. m_contents = m_module->getNamedMetadata("llvm.dbg.contents");
  52. m_defines =
  53. m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceDefinesMDName);
  54. if (!m_defines)
  55. m_defines = m_module->getNamedMetadata("llvm.dbg.defines");
  56. m_mainFileName =
  57. m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceMainFileNameMDName);
  58. if (!m_mainFileName)
  59. m_mainFileName = m_module->getNamedMetadata("llvm.dbg.mainFileName");
  60. m_arguments =
  61. m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceArgsMDName);
  62. if (!m_arguments)
  63. m_arguments = m_module->getNamedMetadata("llvm.dbg.args");
  64. // Build up a linear list of instructions. The index will be used as the
  65. // RVA.
  66. for (llvm::Function &fn : m_module->functions()) {
  67. for (llvm::inst_iterator it = inst_begin(fn), end = inst_end(fn); it != end; ++it) {
  68. llvm::Instruction &i = *it;
  69. RVA rva;
  70. if (!pix_dxil::PixDxilInstNum::FromInst(&i, &rva)) {
  71. continue;
  72. }
  73. m_rvaMap.insert({ &i, rva });
  74. m_instructions.insert({ rva, &i});
  75. if (llvm::DebugLoc DL = i.getDebugLoc()) {
  76. auto result = m_lineToInfoMap.emplace(DL.getLine(), LineInfo(DL.getCol(), rva, rva + 1));
  77. if (!result.second) {
  78. result.first->second.StartCol = std::min(result.first->second.StartCol, DL.getCol());
  79. result.first->second.Last = rva + 1;
  80. }
  81. m_instructionLines.push_back(&i);
  82. }
  83. }
  84. }
  85. // Sanity check to make sure rva map is same as instruction index.
  86. for (auto It = m_instructions.begin(); It != m_instructions.end(); ++It) {
  87. DXASSERT(m_rvaMap.find(It->second) != m_rvaMap.end(), "instruction not mapped to rva");
  88. DXASSERT(m_rvaMap[It->second] == It->first, "instruction mapped to wrong rva");
  89. }
  90. // Initialize symbols
  91. try {
  92. m_symsMgr.Init(this);
  93. } catch (const hlsl::Exception &) {
  94. m_symsMgr = std::move(dxil_dia::SymbolManager());
  95. }
  96. }
  97. HRESULT dxil_dia::Session::getSourceFileIdByName(
  98. llvm::StringRef fileName,
  99. DWORD *pRetVal) {
  100. if (Contents() != nullptr) {
  101. for (unsigned i = 0; i < Contents()->getNumOperands(); ++i) {
  102. llvm::StringRef fn =
  103. llvm::dyn_cast<llvm::MDString>(Contents()->getOperand(i)->getOperand(0))
  104. ->getString();
  105. if (fn.equals(fileName)) {
  106. *pRetVal = i;
  107. return S_OK;
  108. }
  109. }
  110. }
  111. *pRetVal = 0;
  112. return S_FALSE;
  113. }
  114. STDMETHODIMP dxil_dia::Session::get_loadAddress(
  115. /* [retval][out] */ ULONGLONG *pRetVal) {
  116. *pRetVal = 0;
  117. return S_OK;
  118. }
  119. STDMETHODIMP dxil_dia::Session::get_globalScope(
  120. /* [retval][out] */ IDiaSymbol **pRetVal) {
  121. DxcThreadMalloc TM(m_pMalloc);
  122. if (pRetVal == nullptr) {
  123. return E_INVALIDARG;
  124. }
  125. *pRetVal = nullptr;
  126. Symbol *ret;
  127. IFR(m_symsMgr.GetGlobalScope(&ret));
  128. *pRetVal = ret;
  129. return S_OK;
  130. }
  131. STDMETHODIMP dxil_dia::Session::getEnumTables(
  132. /* [out] */ _COM_Outptr_ IDiaEnumTables **ppEnumTables) {
  133. if (!m_pEnumTables) {
  134. DxcThreadMalloc TM(m_pMalloc);
  135. IFR(EnumTables::Create(this, &m_pEnumTables));
  136. }
  137. m_pEnumTables.p->AddRef();
  138. *ppEnumTables = m_pEnumTables;
  139. return S_OK;
  140. }
  141. STDMETHODIMP dxil_dia::Session::findFileById(
  142. /* [in] */ DWORD uniqueId,
  143. /* [out] */ IDiaSourceFile **ppResult) {
  144. if (!m_pEnumTables) {
  145. return E_INVALIDARG;
  146. }
  147. CComPtr<IDiaTable> pTable;
  148. VARIANT vtIndex;
  149. vtIndex.vt = VT_UI4;
  150. vtIndex.uintVal = (int)Table::Kind::SourceFiles;
  151. IFR(m_pEnumTables->Item(vtIndex, &pTable));
  152. CComPtr<IUnknown> pElt;
  153. IFR(pTable->Item(uniqueId, &pElt));
  154. return pElt->QueryInterface(ppResult);
  155. }
  156. STDMETHODIMP dxil_dia::Session::findFile(
  157. /* [in] */ IDiaSymbol *pCompiland,
  158. /* [in] */ LPCOLESTR name,
  159. /* [in] */ DWORD compareFlags,
  160. /* [out] */ IDiaEnumSourceFiles **ppResult) {
  161. if (!m_pEnumTables) {
  162. return E_INVALIDARG;
  163. }
  164. // TODO: properly support compareFlags.
  165. auto namecmp = &_wcsicmp;
  166. if (compareFlags & nsCaseSensitive) {
  167. namecmp = &wcscmp;
  168. }
  169. DxcThreadMalloc TM(m_pMalloc);
  170. CComPtr<IDiaTable> pTable;
  171. VARIANT vtIndex;
  172. vtIndex.vt = VT_UI4;
  173. vtIndex.uintVal = (int)Table::Kind::SourceFiles;
  174. IFR(m_pEnumTables->Item(vtIndex, &pTable));
  175. CComPtr<IDiaEnumSourceFiles> pSourceTable;
  176. IFR(pTable->QueryInterface(&pSourceTable));
  177. HRESULT hr;
  178. CComPtr<IDiaSourceFile> src;
  179. ULONG cnt;
  180. std::vector<CComPtr<IDiaSourceFile>> sources;
  181. pSourceTable->Reset();
  182. while (SUCCEEDED(hr = pSourceTable->Next(1, &src, &cnt)) && hr == S_OK && cnt == 1) {
  183. CComBSTR currName;
  184. IFR(src->get_fileName(&currName));
  185. if (namecmp(name, currName) == 0) {
  186. sources.emplace_back(src);
  187. }
  188. src.Release();
  189. }
  190. *ppResult = CreateOnMalloc<SourceFilesTable>(
  191. GetMallocNoRef(),
  192. this,
  193. std::move(sources));
  194. if (*ppResult == nullptr) {
  195. return E_OUTOFMEMORY;
  196. }
  197. (*ppResult)->AddRef();
  198. return S_OK;
  199. }
  200. namespace dxil_dia {
  201. static HRESULT DxcDiaFindLineNumbersByRVA(
  202. Session *pSession,
  203. DWORD rva,
  204. DWORD length,
  205. IDiaEnumLineNumbers **ppResult)
  206. {
  207. if (!ppResult)
  208. return E_POINTER;
  209. std::vector<const llvm::Instruction*> instructions;
  210. auto &allInstructions = pSession->InstructionsRef();
  211. // Gather the list of insructions that map to the given rva range.
  212. for (DWORD i = rva; i < rva + length; ++i) {
  213. auto It = allInstructions.find(i);
  214. if (It == allInstructions.end())
  215. return E_INVALIDARG;
  216. // Only include the instruction if it has debug info for line mappings.
  217. const llvm::Instruction *inst = It->second;
  218. if (inst->getDebugLoc())
  219. instructions.push_back(inst);
  220. }
  221. // Create line number table from explicit instruction list.
  222. IMalloc *pMalloc = pSession->GetMallocNoRef();
  223. *ppResult = CreateOnMalloc<LineNumbersTable>(pMalloc, pSession, std::move(instructions));
  224. if (*ppResult == nullptr)
  225. return E_OUTOFMEMORY;
  226. (*ppResult)->AddRef();
  227. return S_OK;
  228. }
  229. } // namespace dxil_dia
  230. STDMETHODIMP dxil_dia::Session::findLinesByAddr(
  231. /* [in] */ DWORD seg,
  232. /* [in] */ DWORD offset,
  233. /* [in] */ DWORD length,
  234. /* [out] */ IDiaEnumLineNumbers **ppResult) {
  235. DxcThreadMalloc TM(m_pMalloc);
  236. return DxcDiaFindLineNumbersByRVA(this, offset, length, ppResult);
  237. }
  238. STDMETHODIMP dxil_dia::Session::findLinesByRVA(
  239. /* [in] */ DWORD rva,
  240. /* [in] */ DWORD length,
  241. /* [out] */ IDiaEnumLineNumbers **ppResult) {
  242. DxcThreadMalloc TM(m_pMalloc);
  243. return DxcDiaFindLineNumbersByRVA(this, rva, length, ppResult);
  244. }
  245. STDMETHODIMP dxil_dia::Session::findInlineeLinesByAddr(
  246. /* [in] */ IDiaSymbol *parent,
  247. /* [in] */ DWORD isect,
  248. /* [in] */ DWORD offset,
  249. /* [in] */ DWORD length,
  250. /* [out] */ IDiaEnumLineNumbers **ppResult) {
  251. DxcThreadMalloc TM(m_pMalloc);
  252. return DxcDiaFindLineNumbersByRVA(this, offset, length, ppResult);
  253. }
  254. STDMETHODIMP dxil_dia::Session::findLinesByLinenum(
  255. /* [in] */ IDiaSymbol *compiland,
  256. /* [in] */ IDiaSourceFile *file,
  257. /* [in] */ DWORD linenum,
  258. /* [in] */ DWORD column,
  259. /* [out] */ IDiaEnumLineNumbers **ppResult) {
  260. if (!m_pEnumTables) {
  261. return E_INVALIDARG;
  262. }
  263. *ppResult = nullptr;
  264. DxcThreadMalloc TM(m_pMalloc);
  265. CComPtr<IDiaTable> pTable;
  266. VARIANT vtIndex;
  267. vtIndex.vt = VT_UI4;
  268. vtIndex.uintVal = (int)Table::Kind::LineNumbers;
  269. IFR(m_pEnumTables->Item(vtIndex, &pTable));
  270. CComPtr<IDiaEnumLineNumbers> pLineTable;
  271. IFR(pTable->QueryInterface(&pLineTable));
  272. HRESULT hr;
  273. CComPtr<IDiaLineNumber> line;
  274. ULONG cnt;
  275. std::vector<const llvm::Instruction *> lines;
  276. std::function<bool(DWORD, DWORD)>column_matches = [column](DWORD colStart, DWORD colEnd) -> bool {
  277. return true;
  278. };
  279. if (column != 0) {
  280. column_matches = [column](DWORD colStart, DWORD colEnd) -> bool {
  281. return colStart < column && column < colEnd;
  282. };
  283. }
  284. pLineTable->Reset();
  285. while (SUCCEEDED(hr = pLineTable->Next(1, &line, &cnt)) && hr == S_OK && cnt == 1) {
  286. CComPtr<IDiaSourceFile> f;
  287. DWORD ln, lnEnd, cn, cnEnd;
  288. IFR(line->get_lineNumber(&ln));
  289. IFR(line->get_lineNumberEnd(&lnEnd));
  290. IFR(line->get_columnNumber(&cn));
  291. IFR(line->get_columnNumberEnd(&cnEnd));
  292. IFR(line->get_sourceFile(&f));
  293. if (file == f && (ln <= linenum && linenum <= lnEnd) && column_matches(cn, cnEnd)) {
  294. lines.emplace_back(reinterpret_cast<LineNumber*>(line.p)->Inst());
  295. }
  296. line.Release();
  297. }
  298. HRESULT result = lines.empty() ? S_FALSE : S_OK;
  299. *ppResult = CreateOnMalloc<LineNumbersTable>(
  300. GetMallocNoRef(),
  301. this,
  302. std::move(lines));
  303. if (*ppResult == nullptr) {
  304. return E_OUTOFMEMORY;
  305. }
  306. (*ppResult)->AddRef();
  307. return result;
  308. }
  309. STDMETHODIMP dxil_dia::Session::findInjectedSource(
  310. /* [in] */ LPCOLESTR srcFile,
  311. /* [out] */ IDiaEnumInjectedSources **ppResult) {
  312. if (Contents() != nullptr) {
  313. CW2A pUtf8FileName(srcFile, CP_UTF8);
  314. DxcThreadMalloc TM(m_pMalloc);
  315. IDiaTable *pTable;
  316. IFT(Table::Create(this, Table::Kind::InjectedSource, &pTable));
  317. auto *pInjectedSource =
  318. reinterpret_cast<InjectedSourcesTable *>(pTable);
  319. pInjectedSource->Init(pUtf8FileName.m_psz);
  320. *ppResult = pInjectedSource;
  321. return S_OK;
  322. }
  323. return S_FALSE;
  324. }
  325. static constexpr DWORD kD3DCodeSection = 1;
  326. STDMETHODIMP dxil_dia::Session::findInlineFramesByAddr(
  327. /* [in] */ IDiaSymbol *parent,
  328. /* [in] */ DWORD isect,
  329. /* [in] */ DWORD offset,
  330. /* [out] */ IDiaEnumSymbols **ppResult) {
  331. if (parent != nullptr || isect != kD3DCodeSection || ppResult == nullptr) {
  332. return E_INVALIDARG;
  333. }
  334. *ppResult = nullptr;
  335. DxcThreadMalloc TM(m_pMalloc);
  336. auto &allInstructions = InstructionsRef();
  337. auto It = allInstructions.find(offset);
  338. if (It == allInstructions.end()) {
  339. return E_INVALIDARG;
  340. }
  341. HRESULT hr;
  342. SymbolChildrenEnumerator *ChildrenEnum;
  343. IFR(hr = m_symsMgr.DbgScopeOf(It->second, &ChildrenEnum));
  344. *ppResult = ChildrenEnum;
  345. return hr;
  346. }