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
26#include "lldb/Utility/LLDBAssert.h"
27
28using namespace lldb;
29using namespace lldb_private;
30using namespace lldb_private::npdb;
31using namespace llvm::codeview;
32using namespace llvm::pdb;
33
34static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) {
35 if (main == other)
36 return true;
37
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(A: main, B: other))
42 return true;
43
44 llvm::SmallString<64> normalized(other);
45 llvm::sys::path::native(path&: normalized);
46 return main.equals_insensitive(RHS: normalized);
47}
48
49static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {
50 cci.m_compile_opts.emplace();
51 llvm::cantFail(
52 Err: SymbolDeserializer::deserializeAs<Compile3Sym>(Symbol: sym, Record&: *cci.m_compile_opts));
53}
54
55static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {
56 cci.m_obj_name.emplace();
57 llvm::cantFail(
58 Err: SymbolDeserializer::deserializeAs<ObjNameSym>(Symbol: sym, Record&: *cci.m_obj_name));
59}
60
61static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym,
62 CompilandIndexItem &cci) {
63 BuildInfoSym bis(SymbolRecordKind::BuildInfoSym);
64 llvm::cantFail(Err: SymbolDeserializer::deserializeAs<BuildInfoSym>(Symbol: sym, Record&: bis));
65
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(Index: bis.BuildId);
70
71 if (!cvt || cvt->kind() != LF_BUILDINFO)
72 return;
73
74 BuildInfoRecord bir;
75 llvm::cantFail(Err: TypeDeserializer::deserializeAs<BuildInfoRecord>(CVT&: *cvt, Record&: bir));
76 cci.m_build_info.assign(in_start: bir.ArgIndices.begin(), in_end: bir.ArgIndices.end());
77}
78
79static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) {
80 const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray();
81
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());
87
88 // We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO.
89 int found = 0;
90 for (const CVSymbol &sym : syms) {
91 switch (sym.kind()) {
92 case S_COMPILE3:
93 ParseCompile3(sym, cci&: item);
94 break;
95 case S_OBJNAME:
96 ParseObjname(sym, cci&: item);
97 break;
98 case S_BUILDINFO:
99 ParseBuildInfo(index, sym, cci&: item);
100 break;
101 default:
102 continue;
103 }
104 if (++found >= 3)
105 break;
106 }
107}
108
109static void ParseInlineeLineTableForCompileUnit(CompilandIndexItem &item) {
110 for (const auto &ss : item.m_debug_stream.getSubsectionsArray()) {
111 if (ss.kind() != DebugSubsectionKind::InlineeLines)
112 continue;
113
114 DebugInlineeLinesSubsectionRef inlinee_lines;
115 llvm::BinaryStreamReader reader(ss.getRecordData());
116 if (llvm::Error error = inlinee_lines.initialize(Reader: reader)) {
117 consumeError(Err: std::move(error));
118 continue;
119 }
120
121 for (const InlineeSourceLine &Line : inlinee_lines) {
122 item.m_inline_map[Line.Header->Inlinee] = Line;
123 }
124 }
125}
126
127CompilandIndexItem::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)) {}
132
133CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {
134 auto result = m_comp_units.try_emplace(Key: modi, Args: nullptr);
135 if (!result.second)
136 return *result.first->second;
137
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: modi);
142 uint16_t stream = descriptor.getModuleStreamIndex();
143 std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
144 m_index.pdb().createIndexedStream(SN: stream);
145
146
147 std::unique_ptr<CompilandIndexItem>& cci = result.first->second;
148
149 if (!stream_data) {
150 llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr);
151 cci = std::make_unique<CompilandIndexItem>(args: PdbCompilandId{ .modi: modi }, args&: debug_stream, args: std::move(descriptor));
152 return *cci;
153 }
154
155 llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
156 std::move(stream_data));
157
158 cantFail(Err: debug_stream.reload());
159
160 cci = std::make_unique<CompilandIndexItem>(
161 args: PdbCompilandId{.modi: modi}, args: std::move(debug_stream), args: std::move(descriptor));
162 ParseExtendedInfo(index&: m_index, item&: *cci);
163 ParseInlineeLineTableForCompileUnit(item&: *cci);
164
165 auto strings = m_index.pdb().getStringTable();
166 if (strings) {
167 cci->m_strings.initialize(FragmentRange: cci->m_debug_stream.getSubsectionsArray());
168 cci->m_strings.setStrings(strings->getStringTable());
169 } else {
170 consumeError(Err: strings.takeError());
171 }
172
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(item: *cci);
180 std::string s = std::string(main_file.str());
181 llvm::sys::path::native(path&: main_file);
182
183 uint32_t file_count = modules.getSourceFileCount(Modi: modi);
184 cci->m_file_list.reserve(n: file_count);
185 bool found_main_file = false;
186 for (llvm::StringRef file : modules.source_files(Modi: modi)) {
187 if (!found_main_file && IsMainFile(main: main_file, other: file)) {
188 cci->m_file_list.insert(position: cci->m_file_list.begin(), x: file);
189 found_main_file = true;
190 continue;
191 }
192 cci->m_file_list.push_back(x: file);
193 }
194
195 return *cci;
196}
197
198const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const {
199 auto iter = m_comp_units.find(Val: modi);
200 if (iter == m_comp_units.end())
201 return nullptr;
202 return iter->second.get();
203}
204
205CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) {
206 auto iter = m_comp_units.find(Val: modi);
207 if (iter == m_comp_units.end())
208 return nullptr;
209 return iter->second.get();
210}
211
212llvm::SmallString<64>
213CompileUnitIndex::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.
222 //
223 // We need to form the path [0]\[2] to generate the full path to the main
224 // file.source
225 if (item.m_build_info.size() < 3)
226 return {""};
227
228 LazyRandomTypeCollection &types = m_index.ipi().typeCollection();
229
230 StringIdRecord working_dir;
231 StringIdRecord file_name;
232 CVType dir_cvt = types.getType(Index: item.m_build_info[0]);
233 CVType file_cvt = types.getType(Index: item.m_build_info[2]);
234 llvm::cantFail(
235 Err: TypeDeserializer::deserializeAs<StringIdRecord>(CVT&: dir_cvt, Record&: working_dir));
236 llvm::cantFail(
237 Err: TypeDeserializer::deserializeAs<StringIdRecord>(CVT&: file_cvt, Record&: file_name));
238
239 llvm::sys::path::Style style = working_dir.String.starts_with(Prefix: "/")
240 ? llvm::sys::path::Style::posix
241 : llvm::sys::path::Style::windows;
242 if (llvm::sys::path::is_absolute(path: file_name.String, style))
243 return file_name.String;
244
245 llvm::SmallString<64> absolute_path = working_dir.String;
246 llvm::sys::path::append(path&: absolute_path, a: file_name.String);
247 return absolute_path;
248}
249

source code of lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp