1 //===-- CompileUnitIndex.cpp ----------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "CompileUnitIndex.h"
14 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
15 #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
16 #include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
17 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
18 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
19 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
20 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
21 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
22 #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
23 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
24 #include "llvm/Support/Path.h"
26 #include "lldb/Utility/LLDBAssert.h"
29 using namespace lldb_private
;
30 using namespace lldb_private::npdb
;
31 using namespace llvm::codeview
;
32 using namespace llvm::pdb
;
34 static bool IsMainFile(llvm::StringRef main
, llvm::StringRef other
) {
38 // If the files refer to the local file system, we can just ask the file
39 // system if they're equivalent. But if the source isn't present on disk
40 // then we still want to try.
41 if (llvm::sys::fs::equivalent(main
, other
))
44 llvm::SmallString
<64> normalized(other
);
45 llvm::sys::path::native(normalized
);
46 return main
.equals_insensitive(normalized
);
49 static void ParseCompile3(const CVSymbol
&sym
, CompilandIndexItem
&cci
) {
50 cci
.m_compile_opts
.emplace();
52 SymbolDeserializer::deserializeAs
<Compile3Sym
>(sym
, *cci
.m_compile_opts
));
55 static void ParseObjname(const CVSymbol
&sym
, CompilandIndexItem
&cci
) {
56 cci
.m_obj_name
.emplace();
58 SymbolDeserializer::deserializeAs
<ObjNameSym
>(sym
, *cci
.m_obj_name
));
61 static void ParseBuildInfo(PdbIndex
&index
, const CVSymbol
&sym
,
62 CompilandIndexItem
&cci
) {
63 BuildInfoSym
bis(SymbolRecordKind::BuildInfoSym
);
64 llvm::cantFail(SymbolDeserializer::deserializeAs
<BuildInfoSym
>(sym
, bis
));
66 // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do
67 // a little extra work to pull out the LF_BUILDINFO.
68 LazyRandomTypeCollection
&types
= index
.ipi().typeCollection();
69 std::optional
<CVType
> cvt
= types
.tryGetType(bis
.BuildId
);
71 if (!cvt
|| cvt
->kind() != LF_BUILDINFO
)
75 llvm::cantFail(TypeDeserializer::deserializeAs
<BuildInfoRecord
>(*cvt
, bir
));
76 cci
.m_build_info
.assign(bir
.ArgIndices
.begin(), bir
.ArgIndices
.end());
79 static void ParseExtendedInfo(PdbIndex
&index
, CompilandIndexItem
&item
) {
80 const CVSymbolArray
&syms
= item
.m_debug_stream
.getSymbolArray();
82 // This is a private function, it shouldn't be called if the information
83 // has already been parsed.
84 lldbassert(!item
.m_obj_name
);
85 lldbassert(!item
.m_compile_opts
);
86 lldbassert(item
.m_build_info
.empty());
88 // We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO.
90 for (const CVSymbol
&sym
: syms
) {
93 ParseCompile3(sym
, item
);
96 ParseObjname(sym
, item
);
99 ParseBuildInfo(index
, sym
, item
);
109 static void ParseInlineeLineTableForCompileUnit(CompilandIndexItem
&item
) {
110 for (const auto &ss
: item
.m_debug_stream
.getSubsectionsArray()) {
111 if (ss
.kind() != DebugSubsectionKind::InlineeLines
)
114 DebugInlineeLinesSubsectionRef inlinee_lines
;
115 llvm::BinaryStreamReader
reader(ss
.getRecordData());
116 if (llvm::Error error
= inlinee_lines
.initialize(reader
)) {
117 consumeError(std::move(error
));
121 for (const InlineeSourceLine
&Line
: inlinee_lines
) {
122 item
.m_inline_map
[Line
.Header
->Inlinee
] = Line
;
127 CompilandIndexItem::CompilandIndexItem(
128 PdbCompilandId id
, llvm::pdb::ModuleDebugStreamRef debug_stream
,
129 llvm::pdb::DbiModuleDescriptor descriptor
)
130 : m_id(id
), m_debug_stream(std::move(debug_stream
)),
131 m_module_descriptor(std::move(descriptor
)) {}
133 CompilandIndexItem
&CompileUnitIndex::GetOrCreateCompiland(uint16_t modi
) {
134 auto result
= m_comp_units
.try_emplace(modi
, nullptr);
136 return *result
.first
->second
;
138 // Find the module list and load its debug information stream and cache it
139 // since we need to use it for almost all interesting operations.
140 const DbiModuleList
&modules
= m_index
.dbi().modules();
141 llvm::pdb::DbiModuleDescriptor descriptor
= modules
.getModuleDescriptor(modi
);
142 uint16_t stream
= descriptor
.getModuleStreamIndex();
143 std::unique_ptr
<llvm::msf::MappedBlockStream
> stream_data
=
144 m_index
.pdb().createIndexedStream(stream
);
147 std::unique_ptr
<CompilandIndexItem
>& cci
= result
.first
->second
;
150 llvm::pdb::ModuleDebugStreamRef
debug_stream(descriptor
, nullptr);
151 cci
= std::make_unique
<CompilandIndexItem
>(PdbCompilandId
{ modi
}, debug_stream
, std::move(descriptor
));
155 llvm::pdb::ModuleDebugStreamRef
debug_stream(descriptor
,
156 std::move(stream_data
));
158 cantFail(debug_stream
.reload());
160 cci
= std::make_unique
<CompilandIndexItem
>(
161 PdbCompilandId
{modi
}, std::move(debug_stream
), std::move(descriptor
));
162 ParseExtendedInfo(m_index
, *cci
);
163 ParseInlineeLineTableForCompileUnit(*cci
);
165 auto strings
= m_index
.pdb().getStringTable();
167 cci
->m_strings
.initialize(cci
->m_debug_stream
.getSubsectionsArray());
168 cci
->m_strings
.setStrings(strings
->getStringTable());
170 consumeError(strings
.takeError());
173 // We want the main source file to always comes first. Note that we can't
174 // just push_back the main file onto the front because `GetMainSourceFile`
175 // computes it in such a way that it doesn't own the resulting memory. So we
176 // have to iterate the module file list comparing each one to the main file
177 // name until we find it, and we can cache that one since the memory is backed
178 // by a contiguous chunk inside the mapped PDB.
179 llvm::SmallString
<64> main_file
= GetMainSourceFile(*cci
);
180 std::string s
= std::string(main_file
.str());
181 llvm::sys::path::native(main_file
);
183 uint32_t file_count
= modules
.getSourceFileCount(modi
);
184 cci
->m_file_list
.reserve(file_count
);
185 bool found_main_file
= false;
186 for (llvm::StringRef file
: modules
.source_files(modi
)) {
187 if (!found_main_file
&& IsMainFile(main_file
, file
)) {
188 cci
->m_file_list
.insert(cci
->m_file_list
.begin(), file
);
189 found_main_file
= true;
192 cci
->m_file_list
.push_back(file
);
198 const CompilandIndexItem
*CompileUnitIndex::GetCompiland(uint16_t modi
) const {
199 auto iter
= m_comp_units
.find(modi
);
200 if (iter
== m_comp_units
.end())
202 return iter
->second
.get();
205 CompilandIndexItem
*CompileUnitIndex::GetCompiland(uint16_t modi
) {
206 auto iter
= m_comp_units
.find(modi
);
207 if (iter
== m_comp_units
.end())
209 return iter
->second
.get();
212 llvm::SmallString
<64>
213 CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem
&item
) const {
214 // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID
215 // records in the IPI stream. The order of the arg indices is as follows:
216 // [0] - working directory where compiler was invoked.
217 // [1] - absolute path to compiler binary
218 // [2] - source file name
219 // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets
220 // added even when using /Z7)
221 // [4] - full command line invocation.
223 // We need to form the path [0]\[2] to generate the full path to the main
225 if (item
.m_build_info
.size() < 3)
228 LazyRandomTypeCollection
&types
= m_index
.ipi().typeCollection();
230 StringIdRecord working_dir
;
231 StringIdRecord file_name
;
232 CVType dir_cvt
= types
.getType(item
.m_build_info
[0]);
233 CVType file_cvt
= types
.getType(item
.m_build_info
[2]);
235 TypeDeserializer::deserializeAs
<StringIdRecord
>(dir_cvt
, working_dir
));
237 TypeDeserializer::deserializeAs
<StringIdRecord
>(file_cvt
, file_name
));
239 llvm::sys::path::Style style
= working_dir
.String
.startswith("/")
240 ? llvm::sys::path::Style::posix
241 : llvm::sys::path::Style::windows
;
242 if (llvm::sys::path::is_absolute(file_name
.String
, style
))
243 return file_name
.String
;
245 llvm::SmallString
<64> absolute_path
= working_dir
.String
;
246 llvm::sys::path::append(absolute_path
, file_name
.String
);
247 return absolute_path
;