180814287SRaphael Isemann //===-- CompileUnitIndex.cpp ----------------------------------------------===//
2307f5ae8SZachary Turner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6307f5ae8SZachary Turner //
7307f5ae8SZachary Turner //===----------------------------------------------------------------------===//
8307f5ae8SZachary Turner
9307f5ae8SZachary Turner #include "CompileUnitIndex.h"
10307f5ae8SZachary Turner
11307f5ae8SZachary Turner #include "PdbIndex.h"
12307f5ae8SZachary Turner #include "PdbUtil.h"
13307f5ae8SZachary Turner
14307f5ae8SZachary Turner #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
15307f5ae8SZachary Turner #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
16307f5ae8SZachary Turner #include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
17307f5ae8SZachary Turner #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
18307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
19307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
20307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
21307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
22307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
23307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
24307f5ae8SZachary Turner #include "llvm/Support/Path.h"
25307f5ae8SZachary Turner
26307f5ae8SZachary Turner #include "lldb/Utility/LLDBAssert.h"
27307f5ae8SZachary Turner
28307f5ae8SZachary Turner using namespace lldb;
29307f5ae8SZachary Turner using namespace lldb_private;
30307f5ae8SZachary Turner using namespace lldb_private::npdb;
31307f5ae8SZachary Turner using namespace llvm::codeview;
32307f5ae8SZachary Turner using namespace llvm::pdb;
33307f5ae8SZachary Turner
IsMainFile(llvm::StringRef main,llvm::StringRef other)34307f5ae8SZachary Turner static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) {
35307f5ae8SZachary Turner if (main == other)
36307f5ae8SZachary Turner return true;
37307f5ae8SZachary Turner
38307f5ae8SZachary Turner // If the files refer to the local file system, we can just ask the file
39307f5ae8SZachary Turner // system if they're equivalent. But if the source isn't present on disk
40307f5ae8SZachary Turner // then we still want to try.
41307f5ae8SZachary Turner if (llvm::sys::fs::equivalent(main, other))
42307f5ae8SZachary Turner return true;
43307f5ae8SZachary Turner
44307f5ae8SZachary Turner llvm::SmallString<64> normalized(other);
45307f5ae8SZachary Turner llvm::sys::path::native(normalized);
46e50f9c41SMartin Storsjö return main.equals_insensitive(normalized);
47307f5ae8SZachary Turner }
48307f5ae8SZachary Turner
ParseCompile3(const CVSymbol & sym,CompilandIndexItem & cci)49307f5ae8SZachary Turner static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {
50307f5ae8SZachary Turner cci.m_compile_opts.emplace();
51307f5ae8SZachary Turner llvm::cantFail(
52307f5ae8SZachary Turner SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts));
53307f5ae8SZachary Turner }
54307f5ae8SZachary Turner
ParseObjname(const CVSymbol & sym,CompilandIndexItem & cci)55307f5ae8SZachary Turner static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {
56307f5ae8SZachary Turner cci.m_obj_name.emplace();
57307f5ae8SZachary Turner llvm::cantFail(
58307f5ae8SZachary Turner SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name));
59307f5ae8SZachary Turner }
60307f5ae8SZachary Turner
ParseBuildInfo(PdbIndex & index,const CVSymbol & sym,CompilandIndexItem & cci)61307f5ae8SZachary Turner static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym,
62307f5ae8SZachary Turner CompilandIndexItem &cci) {
63307f5ae8SZachary Turner BuildInfoSym bis(SymbolRecordKind::BuildInfoSym);
64307f5ae8SZachary Turner llvm::cantFail(SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis));
65307f5ae8SZachary Turner
66307f5ae8SZachary Turner // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do
67307f5ae8SZachary Turner // a little extra work to pull out the LF_BUILDINFO.
68307f5ae8SZachary Turner LazyRandomTypeCollection &types = index.ipi().typeCollection();
6989fab98eSFangrui Song std::optional<CVType> cvt = types.tryGetType(bis.BuildId);
70307f5ae8SZachary Turner
71307f5ae8SZachary Turner if (!cvt || cvt->kind() != LF_BUILDINFO)
72307f5ae8SZachary Turner return;
73307f5ae8SZachary Turner
74307f5ae8SZachary Turner BuildInfoRecord bir;
75307f5ae8SZachary Turner llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir));
76307f5ae8SZachary Turner cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end());
77307f5ae8SZachary Turner }
78307f5ae8SZachary Turner
ParseExtendedInfo(PdbIndex & index,CompilandIndexItem & item)79307f5ae8SZachary Turner static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) {
80307f5ae8SZachary Turner const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray();
81307f5ae8SZachary Turner
82307f5ae8SZachary Turner // This is a private function, it shouldn't be called if the information
83307f5ae8SZachary Turner // has already been parsed.
84307f5ae8SZachary Turner lldbassert(!item.m_obj_name);
85307f5ae8SZachary Turner lldbassert(!item.m_compile_opts);
86307f5ae8SZachary Turner lldbassert(item.m_build_info.empty());
87307f5ae8SZachary Turner
88307f5ae8SZachary Turner // We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO.
89307f5ae8SZachary Turner int found = 0;
90307f5ae8SZachary Turner for (const CVSymbol &sym : syms) {
91307f5ae8SZachary Turner switch (sym.kind()) {
92307f5ae8SZachary Turner case S_COMPILE3:
93307f5ae8SZachary Turner ParseCompile3(sym, item);
94307f5ae8SZachary Turner break;
95307f5ae8SZachary Turner case S_OBJNAME:
96307f5ae8SZachary Turner ParseObjname(sym, item);
97307f5ae8SZachary Turner break;
98307f5ae8SZachary Turner case S_BUILDINFO:
99307f5ae8SZachary Turner ParseBuildInfo(index, sym, item);
100307f5ae8SZachary Turner break;
101307f5ae8SZachary Turner default:
102307f5ae8SZachary Turner continue;
103307f5ae8SZachary Turner }
104307f5ae8SZachary Turner if (++found >= 3)
105307f5ae8SZachary Turner break;
106307f5ae8SZachary Turner }
107307f5ae8SZachary Turner }
108307f5ae8SZachary Turner
ParseInlineeLineTableForCompileUnit(CompilandIndexItem & item)109f00cd23cSZequan Wu static void ParseInlineeLineTableForCompileUnit(CompilandIndexItem &item) {
110f00cd23cSZequan Wu for (const auto &ss : item.m_debug_stream.getSubsectionsArray()) {
111f00cd23cSZequan Wu if (ss.kind() != DebugSubsectionKind::InlineeLines)
112f00cd23cSZequan Wu continue;
113f00cd23cSZequan Wu
114f00cd23cSZequan Wu DebugInlineeLinesSubsectionRef inlinee_lines;
115f00cd23cSZequan Wu llvm::BinaryStreamReader reader(ss.getRecordData());
116f00cd23cSZequan Wu if (llvm::Error error = inlinee_lines.initialize(reader)) {
117f00cd23cSZequan Wu consumeError(std::move(error));
118f00cd23cSZequan Wu continue;
119f00cd23cSZequan Wu }
120f00cd23cSZequan Wu
121f00cd23cSZequan Wu for (const InlineeSourceLine &Line : inlinee_lines) {
122f00cd23cSZequan Wu item.m_inline_map[Line.Header->Inlinee] = Line;
123f00cd23cSZequan Wu }
124f00cd23cSZequan Wu }
125f00cd23cSZequan Wu }
126f00cd23cSZequan Wu
CompilandIndexItem(PdbCompilandId id,llvm::pdb::ModuleDebugStreamRef debug_stream,llvm::pdb::DbiModuleDescriptor descriptor)127307f5ae8SZachary Turner CompilandIndexItem::CompilandIndexItem(
1286284aee9SZachary Turner PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream,
129307f5ae8SZachary Turner llvm::pdb::DbiModuleDescriptor descriptor)
1306284aee9SZachary Turner : m_id(id), m_debug_stream(std::move(debug_stream)),
131307f5ae8SZachary Turner m_module_descriptor(std::move(descriptor)) {}
132307f5ae8SZachary Turner
GetOrCreateCompiland(uint16_t modi)133307f5ae8SZachary Turner CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {
1346284aee9SZachary Turner auto result = m_comp_units.try_emplace(modi, nullptr);
135307f5ae8SZachary Turner if (!result.second)
136307f5ae8SZachary Turner return *result.first->second;
137307f5ae8SZachary Turner
138307f5ae8SZachary Turner // Find the module list and load its debug information stream and cache it
139307f5ae8SZachary Turner // since we need to use it for almost all interesting operations.
140307f5ae8SZachary Turner const DbiModuleList &modules = m_index.dbi().modules();
141307f5ae8SZachary Turner llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);
142307f5ae8SZachary Turner uint16_t stream = descriptor.getModuleStreamIndex();
143307f5ae8SZachary Turner std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
144307f5ae8SZachary Turner m_index.pdb().createIndexedStream(stream);
145a31347f1SZachary Turner
146307f5ae8SZachary Turner
147307f5ae8SZachary Turner std::unique_ptr<CompilandIndexItem>& cci = result.first->second;
148307f5ae8SZachary Turner
149a31347f1SZachary Turner if (!stream_data) {
150a31347f1SZachary Turner llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr);
151a8f3ae7cSJonas Devlieghere cci = std::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor));
152a31347f1SZachary Turner return *cci;
153a31347f1SZachary Turner }
154a31347f1SZachary Turner
155a31347f1SZachary Turner llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
156a31347f1SZachary Turner std::move(stream_data));
157a31347f1SZachary Turner
158a31347f1SZachary Turner cantFail(debug_stream.reload());
159a31347f1SZachary Turner
160a8f3ae7cSJonas Devlieghere cci = std::make_unique<CompilandIndexItem>(
1616284aee9SZachary Turner PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor));
162307f5ae8SZachary Turner ParseExtendedInfo(m_index, *cci);
163f00cd23cSZequan Wu ParseInlineeLineTableForCompileUnit(*cci);
164307f5ae8SZachary Turner
165960126e0SZequan Wu auto strings = m_index.pdb().getStringTable();
166960126e0SZequan Wu if (strings) {
167d0fb3eabSFangrui Song cci->m_strings.initialize(cci->m_debug_stream.getSubsectionsArray());
168960126e0SZequan Wu cci->m_strings.setStrings(strings->getStringTable());
169960126e0SZequan Wu } else {
170960126e0SZequan Wu consumeError(strings.takeError());
171960126e0SZequan Wu }
172307f5ae8SZachary Turner
173307f5ae8SZachary Turner // We want the main source file to always comes first. Note that we can't
174307f5ae8SZachary Turner // just push_back the main file onto the front because `GetMainSourceFile`
175307f5ae8SZachary Turner // computes it in such a way that it doesn't own the resulting memory. So we
176307f5ae8SZachary Turner // have to iterate the module file list comparing each one to the main file
177307f5ae8SZachary Turner // name until we find it, and we can cache that one since the memory is backed
178307f5ae8SZachary Turner // by a contiguous chunk inside the mapped PDB.
179307f5ae8SZachary Turner llvm::SmallString<64> main_file = GetMainSourceFile(*cci);
180adcd0268SBenjamin Kramer std::string s = std::string(main_file.str());
181307f5ae8SZachary Turner llvm::sys::path::native(main_file);
182307f5ae8SZachary Turner
183307f5ae8SZachary Turner uint32_t file_count = modules.getSourceFileCount(modi);
184307f5ae8SZachary Turner cci->m_file_list.reserve(file_count);
185307f5ae8SZachary Turner bool found_main_file = false;
186307f5ae8SZachary Turner for (llvm::StringRef file : modules.source_files(modi)) {
187307f5ae8SZachary Turner if (!found_main_file && IsMainFile(main_file, file)) {
188307f5ae8SZachary Turner cci->m_file_list.insert(cci->m_file_list.begin(), file);
189307f5ae8SZachary Turner found_main_file = true;
190307f5ae8SZachary Turner continue;
191307f5ae8SZachary Turner }
192307f5ae8SZachary Turner cci->m_file_list.push_back(file);
193307f5ae8SZachary Turner }
194307f5ae8SZachary Turner
195307f5ae8SZachary Turner return *cci;
196307f5ae8SZachary Turner }
197307f5ae8SZachary Turner
GetCompiland(uint16_t modi) const198307f5ae8SZachary Turner const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const {
1996284aee9SZachary Turner auto iter = m_comp_units.find(modi);
200307f5ae8SZachary Turner if (iter == m_comp_units.end())
201307f5ae8SZachary Turner return nullptr;
202307f5ae8SZachary Turner return iter->second.get();
203307f5ae8SZachary Turner }
204307f5ae8SZachary Turner
GetCompiland(uint16_t modi)205307f5ae8SZachary Turner CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) {
2066284aee9SZachary Turner auto iter = m_comp_units.find(modi);
207307f5ae8SZachary Turner if (iter == m_comp_units.end())
208307f5ae8SZachary Turner return nullptr;
209307f5ae8SZachary Turner return iter->second.get();
210307f5ae8SZachary Turner }
211307f5ae8SZachary Turner
212307f5ae8SZachary Turner llvm::SmallString<64>
GetMainSourceFile(const CompilandIndexItem & item) const213307f5ae8SZachary Turner CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const {
214307f5ae8SZachary Turner // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID
215307f5ae8SZachary Turner // records in the IPI stream. The order of the arg indices is as follows:
216307f5ae8SZachary Turner // [0] - working directory where compiler was invoked.
217307f5ae8SZachary Turner // [1] - absolute path to compiler binary
218307f5ae8SZachary Turner // [2] - source file name
219307f5ae8SZachary Turner // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets
220307f5ae8SZachary Turner // added even when using /Z7)
221307f5ae8SZachary Turner // [4] - full command line invocation.
222307f5ae8SZachary Turner //
223307f5ae8SZachary Turner // We need to form the path [0]\[2] to generate the full path to the main
224307f5ae8SZachary Turner // file.source
225307f5ae8SZachary Turner if (item.m_build_info.size() < 3)
226307f5ae8SZachary Turner return {""};
227307f5ae8SZachary Turner
228307f5ae8SZachary Turner LazyRandomTypeCollection &types = m_index.ipi().typeCollection();
229307f5ae8SZachary Turner
230307f5ae8SZachary Turner StringIdRecord working_dir;
231307f5ae8SZachary Turner StringIdRecord file_name;
232307f5ae8SZachary Turner CVType dir_cvt = types.getType(item.m_build_info[0]);
233307f5ae8SZachary Turner CVType file_cvt = types.getType(item.m_build_info[2]);
234307f5ae8SZachary Turner llvm::cantFail(
235307f5ae8SZachary Turner TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir));
236307f5ae8SZachary Turner llvm::cantFail(
237307f5ae8SZachary Turner TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name));
238307f5ae8SZachary Turner
239*744f3891SKazu Hirata llvm::sys::path::Style style = working_dir.String.starts_with("/")
240b3130b4fSZachary Turner ? llvm::sys::path::Style::posix
241b3130b4fSZachary Turner : llvm::sys::path::Style::windows;
242b3130b4fSZachary Turner if (llvm::sys::path::is_absolute(file_name.String, style))
243b3130b4fSZachary Turner return file_name.String;
244b3130b4fSZachary Turner
245307f5ae8SZachary Turner llvm::SmallString<64> absolute_path = working_dir.String;
246307f5ae8SZachary Turner llvm::sys::path::append(absolute_path, file_name.String);
247307f5ae8SZachary Turner return absolute_path;
248307f5ae8SZachary Turner }
249