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
28 using namespace lldb;
29 using namespace lldb_private;
30 using namespace lldb_private::npdb;
31 using namespace llvm::codeview;
32 using namespace llvm::pdb;
33
IsMainFile(llvm::StringRef main,llvm::StringRef other)34 static 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(main, other))
42 return true;
43
44 llvm::SmallString<64> normalized(other);
45 llvm::sys::path::native(normalized);
46 return main.equals_lower(normalized);
47 }
48
ParseCompile3(const CVSymbol & sym,CompilandIndexItem & cci)49 static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {
50 cci.m_compile_opts.emplace();
51 llvm::cantFail(
52 SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts));
53 }
54
ParseObjname(const CVSymbol & sym,CompilandIndexItem & cci)55 static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {
56 cci.m_obj_name.emplace();
57 llvm::cantFail(
58 SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name));
59 }
60
ParseBuildInfo(PdbIndex & index,const CVSymbol & sym,CompilandIndexItem & cci)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));
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 llvm::Optional<CVType> cvt = types.tryGetType(bis.BuildId);
70
71 if (!cvt || cvt->kind() != LF_BUILDINFO)
72 return;
73
74 BuildInfoRecord bir;
75 llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir));
76 cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end());
77 }
78
ParseExtendedInfo(PdbIndex & index,CompilandIndexItem & item)79 static 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, item);
94 break;
95 case S_OBJNAME:
96 ParseObjname(sym, item);
97 break;
98 case S_BUILDINFO:
99 ParseBuildInfo(index, sym, item);
100 break;
101 default:
102 continue;
103 }
104 if (++found >= 3)
105 break;
106 }
107 }
108
CompilandIndexItem(PdbCompilandId id,llvm::pdb::ModuleDebugStreamRef debug_stream,llvm::pdb::DbiModuleDescriptor descriptor)109 CompilandIndexItem::CompilandIndexItem(
110 PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream,
111 llvm::pdb::DbiModuleDescriptor descriptor)
112 : m_id(id), m_debug_stream(std::move(debug_stream)),
113 m_module_descriptor(std::move(descriptor)) {}
114
GetOrCreateCompiland(uint16_t modi)115 CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {
116 auto result = m_comp_units.try_emplace(modi, nullptr);
117 if (!result.second)
118 return *result.first->second;
119
120 // Find the module list and load its debug information stream and cache it
121 // since we need to use it for almost all interesting operations.
122 const DbiModuleList &modules = m_index.dbi().modules();
123 llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);
124 uint16_t stream = descriptor.getModuleStreamIndex();
125 std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
126 m_index.pdb().createIndexedStream(stream);
127
128
129 std::unique_ptr<CompilandIndexItem>& cci = result.first->second;
130
131 if (!stream_data) {
132 llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr);
133 cci = std::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor));
134 return *cci;
135 }
136
137 llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
138 std::move(stream_data));
139
140 cantFail(debug_stream.reload());
141
142 cci = std::make_unique<CompilandIndexItem>(
143 PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor));
144 ParseExtendedInfo(m_index, *cci);
145
146 cci->m_strings.initialize(debug_stream.getSubsectionsArray());
147 PDBStringTable &strings = cantFail(m_index.pdb().getStringTable());
148 cci->m_strings.setStrings(strings.getStringTable());
149
150 // We want the main source file to always comes first. Note that we can't
151 // just push_back the main file onto the front because `GetMainSourceFile`
152 // computes it in such a way that it doesn't own the resulting memory. So we
153 // have to iterate the module file list comparing each one to the main file
154 // name until we find it, and we can cache that one since the memory is backed
155 // by a contiguous chunk inside the mapped PDB.
156 llvm::SmallString<64> main_file = GetMainSourceFile(*cci);
157 std::string s = std::string(main_file.str());
158 llvm::sys::path::native(main_file);
159
160 uint32_t file_count = modules.getSourceFileCount(modi);
161 cci->m_file_list.reserve(file_count);
162 bool found_main_file = false;
163 for (llvm::StringRef file : modules.source_files(modi)) {
164 if (!found_main_file && IsMainFile(main_file, file)) {
165 cci->m_file_list.insert(cci->m_file_list.begin(), file);
166 found_main_file = true;
167 continue;
168 }
169 cci->m_file_list.push_back(file);
170 }
171
172 return *cci;
173 }
174
GetCompiland(uint16_t modi) const175 const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const {
176 auto iter = m_comp_units.find(modi);
177 if (iter == m_comp_units.end())
178 return nullptr;
179 return iter->second.get();
180 }
181
GetCompiland(uint16_t modi)182 CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) {
183 auto iter = m_comp_units.find(modi);
184 if (iter == m_comp_units.end())
185 return nullptr;
186 return iter->second.get();
187 }
188
189 llvm::SmallString<64>
GetMainSourceFile(const CompilandIndexItem & item) const190 CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const {
191 // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID
192 // records in the IPI stream. The order of the arg indices is as follows:
193 // [0] - working directory where compiler was invoked.
194 // [1] - absolute path to compiler binary
195 // [2] - source file name
196 // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets
197 // added even when using /Z7)
198 // [4] - full command line invocation.
199 //
200 // We need to form the path [0]\[2] to generate the full path to the main
201 // file.source
202 if (item.m_build_info.size() < 3)
203 return {""};
204
205 LazyRandomTypeCollection &types = m_index.ipi().typeCollection();
206
207 StringIdRecord working_dir;
208 StringIdRecord file_name;
209 CVType dir_cvt = types.getType(item.m_build_info[0]);
210 CVType file_cvt = types.getType(item.m_build_info[2]);
211 llvm::cantFail(
212 TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir));
213 llvm::cantFail(
214 TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name));
215
216 llvm::sys::path::Style style = working_dir.String.startswith("/")
217 ? llvm::sys::path::Style::posix
218 : llvm::sys::path::Style::windows;
219 if (llvm::sys::path::is_absolute(file_name.String, style))
220 return file_name.String;
221
222 llvm::SmallString<64> absolute_path = working_dir.String;
223 llvm::sys::path::append(absolute_path, file_name.String);
224 return absolute_path;
225 }
226