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