1307f5ae8SZachary Turner //===-- CompileUnitIndex.cpp ------------------------------------*- C++ -*-===//
2307f5ae8SZachary Turner //
3307f5ae8SZachary Turner //                     The LLVM Compiler Infrastructure
4307f5ae8SZachary Turner //
5307f5ae8SZachary Turner // This file is distributed under the University of Illinois Open Source
6307f5ae8SZachary Turner // License. See LICENSE.TXT for details.
7307f5ae8SZachary Turner //
8307f5ae8SZachary Turner //===----------------------------------------------------------------------===//
9307f5ae8SZachary Turner 
10307f5ae8SZachary Turner #include "CompileUnitIndex.h"
11307f5ae8SZachary Turner 
12307f5ae8SZachary Turner #include "PdbIndex.h"
13307f5ae8SZachary Turner #include "PdbUtil.h"
14307f5ae8SZachary Turner 
15307f5ae8SZachary Turner #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
16307f5ae8SZachary Turner #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
17307f5ae8SZachary Turner #include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
18307f5ae8SZachary Turner #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
19307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
20307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
21307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
22307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
23307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
24307f5ae8SZachary Turner #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
25307f5ae8SZachary Turner #include "llvm/Support/Path.h"
26307f5ae8SZachary Turner 
27307f5ae8SZachary Turner #include "lldb/Utility/LLDBAssert.h"
28307f5ae8SZachary Turner 
29307f5ae8SZachary Turner using namespace lldb;
30307f5ae8SZachary Turner using namespace lldb_private;
31307f5ae8SZachary Turner using namespace lldb_private::npdb;
32307f5ae8SZachary Turner using namespace llvm::codeview;
33307f5ae8SZachary Turner using namespace llvm::pdb;
34307f5ae8SZachary Turner 
35307f5ae8SZachary Turner static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) {
36307f5ae8SZachary Turner   if (main == other)
37307f5ae8SZachary Turner     return true;
38307f5ae8SZachary Turner 
39307f5ae8SZachary Turner   // If the files refer to the local file system, we can just ask the file
40307f5ae8SZachary Turner   // system if they're equivalent.  But if the source isn't present on disk
41307f5ae8SZachary Turner   // then we still want to try.
42307f5ae8SZachary Turner   if (llvm::sys::fs::equivalent(main, other))
43307f5ae8SZachary Turner     return true;
44307f5ae8SZachary Turner 
45307f5ae8SZachary Turner   llvm::SmallString<64> normalized(other);
46307f5ae8SZachary Turner   llvm::sys::path::native(normalized);
47307f5ae8SZachary Turner   return main.equals_lower(normalized);
48307f5ae8SZachary Turner }
49307f5ae8SZachary Turner 
50307f5ae8SZachary Turner static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {
51307f5ae8SZachary Turner   cci.m_compile_opts.emplace();
52307f5ae8SZachary Turner   llvm::cantFail(
53307f5ae8SZachary Turner       SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts));
54307f5ae8SZachary Turner }
55307f5ae8SZachary Turner 
56307f5ae8SZachary Turner static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {
57307f5ae8SZachary Turner   cci.m_obj_name.emplace();
58307f5ae8SZachary Turner   llvm::cantFail(
59307f5ae8SZachary Turner       SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name));
60307f5ae8SZachary Turner }
61307f5ae8SZachary Turner 
62307f5ae8SZachary Turner static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym,
63307f5ae8SZachary Turner                            CompilandIndexItem &cci) {
64307f5ae8SZachary Turner   BuildInfoSym bis(SymbolRecordKind::BuildInfoSym);
65307f5ae8SZachary Turner   llvm::cantFail(SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis));
66307f5ae8SZachary Turner 
67307f5ae8SZachary Turner   // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream.  Let's do
68307f5ae8SZachary Turner   // a little extra work to pull out the LF_BUILDINFO.
69307f5ae8SZachary Turner   LazyRandomTypeCollection &types = index.ipi().typeCollection();
70307f5ae8SZachary Turner   llvm::Optional<CVType> cvt = types.tryGetType(bis.BuildId);
71307f5ae8SZachary Turner 
72307f5ae8SZachary Turner   if (!cvt || cvt->kind() != LF_BUILDINFO)
73307f5ae8SZachary Turner     return;
74307f5ae8SZachary Turner 
75307f5ae8SZachary Turner   BuildInfoRecord bir;
76307f5ae8SZachary Turner   llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir));
77307f5ae8SZachary Turner   cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end());
78307f5ae8SZachary Turner }
79307f5ae8SZachary Turner 
80307f5ae8SZachary Turner static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) {
81307f5ae8SZachary Turner   const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray();
82307f5ae8SZachary Turner 
83307f5ae8SZachary Turner   // This is a private function, it shouldn't be called if the information
84307f5ae8SZachary Turner   // has already been parsed.
85307f5ae8SZachary Turner   lldbassert(!item.m_obj_name);
86307f5ae8SZachary Turner   lldbassert(!item.m_compile_opts);
87307f5ae8SZachary Turner   lldbassert(item.m_build_info.empty());
88307f5ae8SZachary Turner 
89307f5ae8SZachary Turner   // We're looking for 3 things.  S_COMPILE3, S_OBJNAME, and S_BUILDINFO.
90307f5ae8SZachary Turner   int found = 0;
91307f5ae8SZachary Turner   for (const CVSymbol &sym : syms) {
92307f5ae8SZachary Turner     switch (sym.kind()) {
93307f5ae8SZachary Turner     case S_COMPILE3:
94307f5ae8SZachary Turner       ParseCompile3(sym, item);
95307f5ae8SZachary Turner       break;
96307f5ae8SZachary Turner     case S_OBJNAME:
97307f5ae8SZachary Turner       ParseObjname(sym, item);
98307f5ae8SZachary Turner       break;
99307f5ae8SZachary Turner     case S_BUILDINFO:
100307f5ae8SZachary Turner       ParseBuildInfo(index, sym, item);
101307f5ae8SZachary Turner       break;
102307f5ae8SZachary Turner     default:
103307f5ae8SZachary Turner       continue;
104307f5ae8SZachary Turner     }
105307f5ae8SZachary Turner     if (++found >= 3)
106307f5ae8SZachary Turner       break;
107307f5ae8SZachary Turner   }
108307f5ae8SZachary Turner }
109307f5ae8SZachary Turner 
110307f5ae8SZachary Turner CompilandIndexItem::CompilandIndexItem(
111*6284aee9SZachary Turner     PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream,
112307f5ae8SZachary Turner     llvm::pdb::DbiModuleDescriptor descriptor)
113*6284aee9SZachary Turner     : m_id(id), m_debug_stream(std::move(debug_stream)),
114307f5ae8SZachary Turner       m_module_descriptor(std::move(descriptor)) {}
115307f5ae8SZachary Turner 
116307f5ae8SZachary Turner CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {
117*6284aee9SZachary Turner   auto result = m_comp_units.try_emplace(modi, nullptr);
118307f5ae8SZachary Turner   if (!result.second)
119307f5ae8SZachary Turner     return *result.first->second;
120307f5ae8SZachary Turner 
121307f5ae8SZachary Turner   // Find the module list and load its debug information stream and cache it
122307f5ae8SZachary Turner   // since we need to use it for almost all interesting operations.
123307f5ae8SZachary Turner   const DbiModuleList &modules = m_index.dbi().modules();
124307f5ae8SZachary Turner   llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);
125307f5ae8SZachary Turner   uint16_t stream = descriptor.getModuleStreamIndex();
126307f5ae8SZachary Turner   std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
127307f5ae8SZachary Turner       m_index.pdb().createIndexedStream(stream);
128307f5ae8SZachary Turner   llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
129307f5ae8SZachary Turner                                                std::move(stream_data));
130307f5ae8SZachary Turner   cantFail(debug_stream.reload());
131307f5ae8SZachary Turner 
132307f5ae8SZachary Turner   std::unique_ptr<CompilandIndexItem> &cci = result.first->second;
133307f5ae8SZachary Turner 
134307f5ae8SZachary Turner   cci = llvm::make_unique<CompilandIndexItem>(
135*6284aee9SZachary Turner       PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor));
136307f5ae8SZachary Turner   ParseExtendedInfo(m_index, *cci);
137307f5ae8SZachary Turner 
138307f5ae8SZachary Turner   cci->m_strings.initialize(debug_stream.getSubsectionsArray());
139307f5ae8SZachary Turner   PDBStringTable &strings = cantFail(m_index.pdb().getStringTable());
140307f5ae8SZachary Turner   cci->m_strings.setStrings(strings.getStringTable());
141307f5ae8SZachary Turner 
142307f5ae8SZachary Turner   // We want the main source file to always comes first.  Note that we can't
143307f5ae8SZachary Turner   // just push_back the main file onto the front because `GetMainSourceFile`
144307f5ae8SZachary Turner   // computes it in such a way that it doesn't own the resulting memory.  So we
145307f5ae8SZachary Turner   // have to iterate the module file list comparing each one to the main file
146307f5ae8SZachary Turner   // name until we find it, and we can cache that one since the memory is backed
147307f5ae8SZachary Turner   // by a contiguous chunk inside the mapped PDB.
148307f5ae8SZachary Turner   llvm::SmallString<64> main_file = GetMainSourceFile(*cci);
149307f5ae8SZachary Turner   std::string s = main_file.str();
150307f5ae8SZachary Turner   llvm::sys::path::native(main_file);
151307f5ae8SZachary Turner 
152307f5ae8SZachary Turner   uint32_t file_count = modules.getSourceFileCount(modi);
153307f5ae8SZachary Turner   cci->m_file_list.reserve(file_count);
154307f5ae8SZachary Turner   bool found_main_file = false;
155307f5ae8SZachary Turner   for (llvm::StringRef file : modules.source_files(modi)) {
156307f5ae8SZachary Turner     if (!found_main_file && IsMainFile(main_file, file)) {
157307f5ae8SZachary Turner       cci->m_file_list.insert(cci->m_file_list.begin(), file);
158307f5ae8SZachary Turner       found_main_file = true;
159307f5ae8SZachary Turner       continue;
160307f5ae8SZachary Turner     }
161307f5ae8SZachary Turner     cci->m_file_list.push_back(file);
162307f5ae8SZachary Turner   }
163307f5ae8SZachary Turner 
164307f5ae8SZachary Turner   return *cci;
165307f5ae8SZachary Turner }
166307f5ae8SZachary Turner 
167307f5ae8SZachary Turner const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const {
168*6284aee9SZachary Turner   auto iter = m_comp_units.find(modi);
169307f5ae8SZachary Turner   if (iter == m_comp_units.end())
170307f5ae8SZachary Turner     return nullptr;
171307f5ae8SZachary Turner   return iter->second.get();
172307f5ae8SZachary Turner }
173307f5ae8SZachary Turner 
174307f5ae8SZachary Turner CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) {
175*6284aee9SZachary Turner   auto iter = m_comp_units.find(modi);
176307f5ae8SZachary Turner   if (iter == m_comp_units.end())
177307f5ae8SZachary Turner     return nullptr;
178307f5ae8SZachary Turner   return iter->second.get();
179307f5ae8SZachary Turner }
180307f5ae8SZachary Turner 
181307f5ae8SZachary Turner llvm::SmallString<64>
182307f5ae8SZachary Turner CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const {
183307f5ae8SZachary Turner   // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID
184307f5ae8SZachary Turner   // records in the IPI stream.  The order of the arg indices is as follows:
185307f5ae8SZachary Turner   // [0] - working directory where compiler was invoked.
186307f5ae8SZachary Turner   // [1] - absolute path to compiler binary
187307f5ae8SZachary Turner   // [2] - source file name
188307f5ae8SZachary Turner   // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets
189307f5ae8SZachary Turner   //       added even when using /Z7)
190307f5ae8SZachary Turner   // [4] - full command line invocation.
191307f5ae8SZachary Turner   //
192307f5ae8SZachary Turner   // We need to form the path [0]\[2] to generate the full path to the main
193307f5ae8SZachary Turner   // file.source
194307f5ae8SZachary Turner   if (item.m_build_info.size() < 3)
195307f5ae8SZachary Turner     return {""};
196307f5ae8SZachary Turner 
197307f5ae8SZachary Turner   LazyRandomTypeCollection &types = m_index.ipi().typeCollection();
198307f5ae8SZachary Turner 
199307f5ae8SZachary Turner   StringIdRecord working_dir;
200307f5ae8SZachary Turner   StringIdRecord file_name;
201307f5ae8SZachary Turner   CVType dir_cvt = types.getType(item.m_build_info[0]);
202307f5ae8SZachary Turner   CVType file_cvt = types.getType(item.m_build_info[2]);
203307f5ae8SZachary Turner   llvm::cantFail(
204307f5ae8SZachary Turner       TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir));
205307f5ae8SZachary Turner   llvm::cantFail(
206307f5ae8SZachary Turner       TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name));
207307f5ae8SZachary Turner 
208307f5ae8SZachary Turner   llvm::SmallString<64> absolute_path = working_dir.String;
209307f5ae8SZachary Turner   llvm::sys::path::append(absolute_path, file_name.String);
210307f5ae8SZachary Turner   return absolute_path;
211307f5ae8SZachary Turner }
212