LLDB mainline
CompileUnitIndex.cpp
Go to the documentation of this file.
1//===-- CompileUnitIndex.cpp ----------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "CompileUnitIndex.h"
10
11#include "PdbIndex.h"
12#include "PdbUtil.h"
13
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"
25
28#include "lldb/Utility/Log.h"
29
30using namespace lldb;
31using namespace lldb_private;
32using namespace lldb_private::npdb;
33using namespace llvm::codeview;
34using namespace llvm::pdb;
35
36static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) {
37 if (main == other)
38 return true;
39
40 // If the files refer to the local file system, we can just ask the file
41 // system if they're equivalent. But if the source isn't present on disk
42 // then we still want to try.
43 if (llvm::sys::fs::equivalent(main, other))
44 return true;
45
46 llvm::SmallString<64> normalized(other);
47 llvm::sys::path::native(normalized);
48 return main.equals_insensitive(normalized);
49}
50
51static llvm::Error ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {
52 cci.m_compile_opts.emplace();
53 if (auto err = SymbolDeserializer::deserializeAs<Compile3Sym>(
54 sym, *cci.m_compile_opts)) {
55 cci.m_compile_opts.reset();
56 return err;
57 }
58 return llvm::Error::success();
59}
60
61static llvm::Error ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {
62 cci.m_obj_name.emplace();
63 if (auto err =
64 SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name)) {
65 cci.m_obj_name.reset();
66 return err;
67 }
68 return llvm::Error::success();
69}
70
71static llvm::Error ParseBuildInfo(PdbIndex &index, const CVSymbol &sym,
72 CompilandIndexItem &cci) {
73 BuildInfoSym bis(SymbolRecordKind::BuildInfoSym);
74 if (auto err = SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis))
75 return err;
76
77 // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do
78 // a little extra work to pull out the LF_BUILDINFO.
79 LazyRandomTypeCollection &types = index.ipi().typeCollection();
80 std::optional<CVType> cvt = types.tryGetType(bis.BuildId);
81
82 if (!cvt || cvt->kind() != LF_BUILDINFO)
83 return llvm::Error::success();
84
85 BuildInfoRecord bir;
86 if (auto err = TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir))
87 return err;
88 cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end());
89 return llvm::Error::success();
90}
91
92static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) {
93 const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray();
94
95 // This is a private function, it shouldn't be called if the information
96 // has already been parsed.
99 lldbassert(item.m_build_info.empty());
100
102 // We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO.
103 int found = 0;
104 for (const CVSymbol &sym : syms) {
105 switch (sym.kind()) {
106 case S_COMPILE3:
107 if (auto err = ParseCompile3(sym, item))
108 LLDB_LOG_ERROR(log, std::move(err),
109 "Failed to parse S_COMPILE3 record: {0}");
110 break;
111 case S_OBJNAME:
112 if (auto err = ParseObjname(sym, item))
113 LLDB_LOG_ERROR(log, std::move(err),
114 "Failed to parse S_OBJNAME record: {0}");
115 break;
116 case S_BUILDINFO:
117 if (auto err = ParseBuildInfo(index, sym, item))
118 LLDB_LOG_ERROR(log, std::move(err),
119 "Failed to parse S_BUILDINFO record: {0}");
120 break;
121 default:
122 continue;
123 }
124 if (++found >= 3)
125 break;
126 }
127}
128
131 for (const auto &ss : item.m_debug_stream.getSubsectionsArray()) {
132 if (ss.kind() != DebugSubsectionKind::InlineeLines)
133 continue;
134
135 DebugInlineeLinesSubsectionRef inlinee_lines;
136 llvm::BinaryStreamReader reader(ss.getRecordData());
137 if (llvm::Error error = inlinee_lines.initialize(reader)) {
138 LLDB_LOG_ERROR(log, std::move(error),
139 "Failed to initialize inlinee lines subsection: {0}");
140 continue;
141 }
142
143 for (const InlineeSourceLine &Line : inlinee_lines) {
144 item.m_inline_map[Line.Header->Inlinee] = Line;
145 }
146 }
147}
148
150 PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream,
151 llvm::pdb::DbiModuleDescriptor descriptor)
152 : m_id(id), m_debug_stream(std::move(debug_stream)),
153 m_module_descriptor(std::move(descriptor)) {}
154
156 auto result = m_comp_units.try_emplace(modi, nullptr);
157 if (!result.second)
158 return *result.first->second;
159
160 // Find the module list and load its debug information stream and cache it
161 // since we need to use it for almost all interesting operations.
162 const DbiModuleList &modules = m_index.dbi().modules();
163 llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);
164 uint16_t stream = descriptor.getModuleStreamIndex();
165 std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
166 m_index.pdb().createIndexedStream(stream);
167
168
169 std::unique_ptr<CompilandIndexItem>& cci = result.first->second;
170
171 if (!stream_data) {
172 llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr);
173 cci = std::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor));
174 return *cci;
175 }
176
177 llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
178 std::move(stream_data));
179
180 if (llvm::Error err = debug_stream.reload()) {
181 LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err),
182 "Failed to reload debug stream for module {1}: {0}", modi);
183 llvm::pdb::ModuleDebugStreamRef empty_stream(descriptor, nullptr);
184 cci = std::make_unique<CompilandIndexItem>(
185 PdbCompilandId{modi}, empty_stream, std::move(descriptor));
186 return *cci;
187 }
188
189 cci = std::make_unique<CompilandIndexItem>(
190 PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor));
193
194 auto strings = m_index.pdb().getStringTable();
195 if (strings) {
196 cci->m_strings.initialize(cci->m_debug_stream.getSubsectionsArray());
197 cci->m_strings.setStrings(strings->getStringTable());
198 } else {
199 LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), strings.takeError(),
200 "Failed to get PDB string table: {0}");
201 }
202
203 // We want the main source file to always comes first. Note that we can't
204 // just push_back the main file onto the front because `GetMainSourceFile`
205 // computes it in such a way that it doesn't own the resulting memory. So we
206 // have to iterate the module file list comparing each one to the main file
207 // name until we find it, and we can cache that one since the memory is backed
208 // by a contiguous chunk inside the mapped PDB.
209 llvm::SmallString<64> main_file;
210 if (auto main_file_or_err = GetMainSourceFile(*cci)) {
211 main_file = std::move(*main_file_or_err);
212 } else {
213 LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), main_file_or_err.takeError(),
214 "Failed to determine main source file for module {1}: {0}",
215 modi);
216 }
217 llvm::sys::path::native(main_file);
218
219 uint32_t file_count = modules.getSourceFileCount(modi);
220 cci->m_file_list.reserve(file_count);
221 bool found_main_file = false;
222 for (llvm::StringRef file : modules.source_files(modi)) {
223 if (!found_main_file && IsMainFile(main_file, file)) {
224 cci->m_file_list.insert(cci->m_file_list.begin(), file);
225 found_main_file = true;
226 continue;
227 }
228 cci->m_file_list.push_back(file);
229 }
230
231 return *cci;
232}
233
235 auto iter = m_comp_units.find(modi);
236 if (iter == m_comp_units.end())
237 return nullptr;
238 return iter->second.get();
239}
240
242 auto iter = m_comp_units.find(modi);
243 if (iter == m_comp_units.end())
244 return nullptr;
245 return iter->second.get();
246}
247
248llvm::Expected<llvm::SmallString<64>>
250 // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID
251 // records in the IPI stream. The order of the arg indices is as follows:
252 // [0] - working directory where compiler was invoked.
253 // [1] - absolute path to compiler binary
254 // [2] - source file name
255 // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets
256 // added even when using /Z7)
257 // [4] - full command line invocation.
258 //
259 // We need to form the path [0]\[2] to generate the full path to the main
260 // file.source
261 if (item.m_build_info.size() < 3)
262 return llvm::SmallString<64>("");
263
264 LazyRandomTypeCollection &types = m_index.ipi().typeCollection();
265
266 StringIdRecord working_dir;
267 StringIdRecord file_name;
268 CVType dir_cvt = types.getType(item.m_build_info[0]);
269 CVType file_cvt = types.getType(item.m_build_info[2]);
270 if (auto err =
271 TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir))
272 return std::move(err);
273 if (auto err =
274 TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name))
275 return std::move(err);
276
277 llvm::sys::path::Style style = working_dir.String.starts_with("/")
278 ? llvm::sys::path::Style::posix
279 : llvm::sys::path::Style::windows;
280 if (llvm::sys::path::is_absolute(file_name.String, style))
281 return file_name.String;
282
283 llvm::SmallString<64> absolute_path = working_dir.String;
284 llvm::sys::path::append(absolute_path, file_name.String);
285 return absolute_path;
286}
static llvm::raw_ostream & error(Stream &strm)
static llvm::Error ParseBuildInfo(PdbIndex &index, const CVSymbol &sym, CompilandIndexItem &cci)
static llvm::Error ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci)
static bool IsMainFile(llvm::StringRef main, llvm::StringRef other)
static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item)
static void ParseInlineeLineTableForCompileUnit(CompilandIndexItem &item)
static llvm::Error ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci)
#define lldbassert(x)
Definition LLDBAssert.h:16
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:399
llvm::DenseMap< uint16_t, std::unique_ptr< CompilandIndexItem > > m_comp_units
CompilandIndexItem & GetOrCreateCompiland(uint16_t modi)
const CompilandIndexItem * GetCompiland(uint16_t modi) const
llvm::Expected< llvm::SmallString< 64 > > GetMainSourceFile(const CompilandIndexItem &item) const
PdbIndex - Lazy access to the important parts of a PDB file.
Definition PdbIndex.h:47
llvm::pdb::TpiStream & ipi()
Definition PdbIndex.h:127
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:332
Represents a single compile unit.
llvm::pdb::DbiModuleDescriptor m_module_descriptor
std::map< llvm::codeview::TypeIndex, llvm::codeview::InlineeSourceLine > m_inline_map
llvm::SmallVector< llvm::codeview::TypeIndex, 5 > m_build_info
CompilandIndexItem(PdbCompilandId m_id, llvm::pdb::ModuleDebugStreamRef debug_stream, llvm::pdb::DbiModuleDescriptor descriptor)
std::optional< llvm::codeview::ObjNameSym > m_obj_name
std::optional< llvm::codeview::Compile3Sym > m_compile_opts
llvm::pdb::ModuleDebugStreamRef m_debug_stream