CompileUnitIndex.cpp revision 360784
1//===-- CompileUnitIndex.cpp ------------------------------------*- C++ -*-===//
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(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
49static 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
55static 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
61static 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
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, 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
109CompilandIndexItem::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
115CompilandIndexItem &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 = 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
175const 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
182CompilandIndexItem *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
189llvm::SmallString<64>
190CompileUnitIndex::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