You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
8.1 KiB
226 lines
8.1 KiB
//===-- CompileUnitIndex.cpp ----------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CompileUnitIndex.h"
|
|
|
|
#include "PdbIndex.h"
|
|
#include "PdbUtil.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
|
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
|
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
|
|
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
|
|
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#include "lldb/Utility/LLDBAssert.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::npdb;
|
|
using namespace llvm::codeview;
|
|
using namespace llvm::pdb;
|
|
|
|
static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) {
|
|
if (main == other)
|
|
return true;
|
|
|
|
// If the files refer to the local file system, we can just ask the file
|
|
// system if they're equivalent. But if the source isn't present on disk
|
|
// then we still want to try.
|
|
if (llvm::sys::fs::equivalent(main, other))
|
|
return true;
|
|
|
|
llvm::SmallString<64> normalized(other);
|
|
llvm::sys::path::native(normalized);
|
|
return main.equals_lower(normalized);
|
|
}
|
|
|
|
static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {
|
|
cci.m_compile_opts.emplace();
|
|
llvm::cantFail(
|
|
SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts));
|
|
}
|
|
|
|
static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {
|
|
cci.m_obj_name.emplace();
|
|
llvm::cantFail(
|
|
SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name));
|
|
}
|
|
|
|
static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym,
|
|
CompilandIndexItem &cci) {
|
|
BuildInfoSym bis(SymbolRecordKind::BuildInfoSym);
|
|
llvm::cantFail(SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis));
|
|
|
|
// S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do
|
|
// a little extra work to pull out the LF_BUILDINFO.
|
|
LazyRandomTypeCollection &types = index.ipi().typeCollection();
|
|
llvm::Optional<CVType> cvt = types.tryGetType(bis.BuildId);
|
|
|
|
if (!cvt || cvt->kind() != LF_BUILDINFO)
|
|
return;
|
|
|
|
BuildInfoRecord bir;
|
|
llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir));
|
|
cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end());
|
|
}
|
|
|
|
static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) {
|
|
const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray();
|
|
|
|
// This is a private function, it shouldn't be called if the information
|
|
// has already been parsed.
|
|
lldbassert(!item.m_obj_name);
|
|
lldbassert(!item.m_compile_opts);
|
|
lldbassert(item.m_build_info.empty());
|
|
|
|
// We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO.
|
|
int found = 0;
|
|
for (const CVSymbol &sym : syms) {
|
|
switch (sym.kind()) {
|
|
case S_COMPILE3:
|
|
ParseCompile3(sym, item);
|
|
break;
|
|
case S_OBJNAME:
|
|
ParseObjname(sym, item);
|
|
break;
|
|
case S_BUILDINFO:
|
|
ParseBuildInfo(index, sym, item);
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
if (++found >= 3)
|
|
break;
|
|
}
|
|
}
|
|
|
|
CompilandIndexItem::CompilandIndexItem(
|
|
PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream,
|
|
llvm::pdb::DbiModuleDescriptor descriptor)
|
|
: m_id(id), m_debug_stream(std::move(debug_stream)),
|
|
m_module_descriptor(std::move(descriptor)) {}
|
|
|
|
CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {
|
|
auto result = m_comp_units.try_emplace(modi, nullptr);
|
|
if (!result.second)
|
|
return *result.first->second;
|
|
|
|
// Find the module list and load its debug information stream and cache it
|
|
// since we need to use it for almost all interesting operations.
|
|
const DbiModuleList &modules = m_index.dbi().modules();
|
|
llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);
|
|
uint16_t stream = descriptor.getModuleStreamIndex();
|
|
std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
|
|
m_index.pdb().createIndexedStream(stream);
|
|
|
|
|
|
std::unique_ptr<CompilandIndexItem>& cci = result.first->second;
|
|
|
|
if (!stream_data) {
|
|
llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr);
|
|
cci = std::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor));
|
|
return *cci;
|
|
}
|
|
|
|
llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
|
|
std::move(stream_data));
|
|
|
|
cantFail(debug_stream.reload());
|
|
|
|
cci = std::make_unique<CompilandIndexItem>(
|
|
PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor));
|
|
ParseExtendedInfo(m_index, *cci);
|
|
|
|
cci->m_strings.initialize(debug_stream.getSubsectionsArray());
|
|
PDBStringTable &strings = cantFail(m_index.pdb().getStringTable());
|
|
cci->m_strings.setStrings(strings.getStringTable());
|
|
|
|
// We want the main source file to always comes first. Note that we can't
|
|
// just push_back the main file onto the front because `GetMainSourceFile`
|
|
// computes it in such a way that it doesn't own the resulting memory. So we
|
|
// have to iterate the module file list comparing each one to the main file
|
|
// name until we find it, and we can cache that one since the memory is backed
|
|
// by a contiguous chunk inside the mapped PDB.
|
|
llvm::SmallString<64> main_file = GetMainSourceFile(*cci);
|
|
std::string s = std::string(main_file.str());
|
|
llvm::sys::path::native(main_file);
|
|
|
|
uint32_t file_count = modules.getSourceFileCount(modi);
|
|
cci->m_file_list.reserve(file_count);
|
|
bool found_main_file = false;
|
|
for (llvm::StringRef file : modules.source_files(modi)) {
|
|
if (!found_main_file && IsMainFile(main_file, file)) {
|
|
cci->m_file_list.insert(cci->m_file_list.begin(), file);
|
|
found_main_file = true;
|
|
continue;
|
|
}
|
|
cci->m_file_list.push_back(file);
|
|
}
|
|
|
|
return *cci;
|
|
}
|
|
|
|
const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const {
|
|
auto iter = m_comp_units.find(modi);
|
|
if (iter == m_comp_units.end())
|
|
return nullptr;
|
|
return iter->second.get();
|
|
}
|
|
|
|
CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) {
|
|
auto iter = m_comp_units.find(modi);
|
|
if (iter == m_comp_units.end())
|
|
return nullptr;
|
|
return iter->second.get();
|
|
}
|
|
|
|
llvm::SmallString<64>
|
|
CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const {
|
|
// LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID
|
|
// records in the IPI stream. The order of the arg indices is as follows:
|
|
// [0] - working directory where compiler was invoked.
|
|
// [1] - absolute path to compiler binary
|
|
// [2] - source file name
|
|
// [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets
|
|
// added even when using /Z7)
|
|
// [4] - full command line invocation.
|
|
//
|
|
// We need to form the path [0]\[2] to generate the full path to the main
|
|
// file.source
|
|
if (item.m_build_info.size() < 3)
|
|
return {""};
|
|
|
|
LazyRandomTypeCollection &types = m_index.ipi().typeCollection();
|
|
|
|
StringIdRecord working_dir;
|
|
StringIdRecord file_name;
|
|
CVType dir_cvt = types.getType(item.m_build_info[0]);
|
|
CVType file_cvt = types.getType(item.m_build_info[2]);
|
|
llvm::cantFail(
|
|
TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir));
|
|
llvm::cantFail(
|
|
TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name));
|
|
|
|
llvm::sys::path::Style style = working_dir.String.startswith("/")
|
|
? llvm::sys::path::Style::posix
|
|
: llvm::sys::path::Style::windows;
|
|
if (llvm::sys::path::is_absolute(file_name.String, style))
|
|
return file_name.String;
|
|
|
|
llvm::SmallString<64> absolute_path = working_dir.String;
|
|
llvm::sys::path::append(absolute_path, file_name.String);
|
|
return absolute_path;
|
|
}
|