Path: blob/main/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
39644 views
//===-- CompileUnitIndex.cpp ----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "CompileUnitIndex.h"910#include "PdbIndex.h"11#include "PdbUtil.h"1213#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"14#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"15#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"16#include "llvm/DebugInfo/MSF/MappedBlockStream.h"17#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"18#include "llvm/DebugInfo/PDB/Native/DbiStream.h"19#include "llvm/DebugInfo/PDB/Native/InfoStream.h"20#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"21#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"22#include "llvm/DebugInfo/PDB/Native/TpiStream.h"23#include "llvm/Support/Path.h"2425#include "lldb/Utility/LLDBAssert.h"2627using namespace lldb;28using namespace lldb_private;29using namespace lldb_private::npdb;30using namespace llvm::codeview;31using namespace llvm::pdb;3233static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) {34if (main == other)35return true;3637// If the files refer to the local file system, we can just ask the file38// system if they're equivalent. But if the source isn't present on disk39// then we still want to try.40if (llvm::sys::fs::equivalent(main, other))41return true;4243llvm::SmallString<64> normalized(other);44llvm::sys::path::native(normalized);45return main.equals_insensitive(normalized);46}4748static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {49cci.m_compile_opts.emplace();50llvm::cantFail(51SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts));52}5354static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {55cci.m_obj_name.emplace();56llvm::cantFail(57SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name));58}5960static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym,61CompilandIndexItem &cci) {62BuildInfoSym bis(SymbolRecordKind::BuildInfoSym);63llvm::cantFail(SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis));6465// S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do66// a little extra work to pull out the LF_BUILDINFO.67LazyRandomTypeCollection &types = index.ipi().typeCollection();68std::optional<CVType> cvt = types.tryGetType(bis.BuildId);6970if (!cvt || cvt->kind() != LF_BUILDINFO)71return;7273BuildInfoRecord bir;74llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir));75cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end());76}7778static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) {79const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray();8081// This is a private function, it shouldn't be called if the information82// has already been parsed.83lldbassert(!item.m_obj_name);84lldbassert(!item.m_compile_opts);85lldbassert(item.m_build_info.empty());8687// We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO.88int found = 0;89for (const CVSymbol &sym : syms) {90switch (sym.kind()) {91case S_COMPILE3:92ParseCompile3(sym, item);93break;94case S_OBJNAME:95ParseObjname(sym, item);96break;97case S_BUILDINFO:98ParseBuildInfo(index, sym, item);99break;100default:101continue;102}103if (++found >= 3)104break;105}106}107108static void ParseInlineeLineTableForCompileUnit(CompilandIndexItem &item) {109for (const auto &ss : item.m_debug_stream.getSubsectionsArray()) {110if (ss.kind() != DebugSubsectionKind::InlineeLines)111continue;112113DebugInlineeLinesSubsectionRef inlinee_lines;114llvm::BinaryStreamReader reader(ss.getRecordData());115if (llvm::Error error = inlinee_lines.initialize(reader)) {116consumeError(std::move(error));117continue;118}119120for (const InlineeSourceLine &Line : inlinee_lines) {121item.m_inline_map[Line.Header->Inlinee] = Line;122}123}124}125126CompilandIndexItem::CompilandIndexItem(127PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream,128llvm::pdb::DbiModuleDescriptor descriptor)129: m_id(id), m_debug_stream(std::move(debug_stream)),130m_module_descriptor(std::move(descriptor)) {}131132CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {133auto result = m_comp_units.try_emplace(modi, nullptr);134if (!result.second)135return *result.first->second;136137// Find the module list and load its debug information stream and cache it138// since we need to use it for almost all interesting operations.139const DbiModuleList &modules = m_index.dbi().modules();140llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);141uint16_t stream = descriptor.getModuleStreamIndex();142std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =143m_index.pdb().createIndexedStream(stream);144145146std::unique_ptr<CompilandIndexItem>& cci = result.first->second;147148if (!stream_data) {149llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr);150cci = std::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor));151return *cci;152}153154llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,155std::move(stream_data));156157cantFail(debug_stream.reload());158159cci = std::make_unique<CompilandIndexItem>(160PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor));161ParseExtendedInfo(m_index, *cci);162ParseInlineeLineTableForCompileUnit(*cci);163164auto strings = m_index.pdb().getStringTable();165if (strings) {166cci->m_strings.initialize(cci->m_debug_stream.getSubsectionsArray());167cci->m_strings.setStrings(strings->getStringTable());168} else {169consumeError(strings.takeError());170}171172// We want the main source file to always comes first. Note that we can't173// just push_back the main file onto the front because `GetMainSourceFile`174// computes it in such a way that it doesn't own the resulting memory. So we175// have to iterate the module file list comparing each one to the main file176// name until we find it, and we can cache that one since the memory is backed177// by a contiguous chunk inside the mapped PDB.178llvm::SmallString<64> main_file = GetMainSourceFile(*cci);179std::string s = std::string(main_file.str());180llvm::sys::path::native(main_file);181182uint32_t file_count = modules.getSourceFileCount(modi);183cci->m_file_list.reserve(file_count);184bool found_main_file = false;185for (llvm::StringRef file : modules.source_files(modi)) {186if (!found_main_file && IsMainFile(main_file, file)) {187cci->m_file_list.insert(cci->m_file_list.begin(), file);188found_main_file = true;189continue;190}191cci->m_file_list.push_back(file);192}193194return *cci;195}196197const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const {198auto iter = m_comp_units.find(modi);199if (iter == m_comp_units.end())200return nullptr;201return iter->second.get();202}203204CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) {205auto iter = m_comp_units.find(modi);206if (iter == m_comp_units.end())207return nullptr;208return iter->second.get();209}210211llvm::SmallString<64>212CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const {213// LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID214// records in the IPI stream. The order of the arg indices is as follows:215// [0] - working directory where compiler was invoked.216// [1] - absolute path to compiler binary217// [2] - source file name218// [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets219// added even when using /Z7)220// [4] - full command line invocation.221//222// We need to form the path [0]\[2] to generate the full path to the main223// file.source224if (item.m_build_info.size() < 3)225return {""};226227LazyRandomTypeCollection &types = m_index.ipi().typeCollection();228229StringIdRecord working_dir;230StringIdRecord file_name;231CVType dir_cvt = types.getType(item.m_build_info[0]);232CVType file_cvt = types.getType(item.m_build_info[2]);233llvm::cantFail(234TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir));235llvm::cantFail(236TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name));237238llvm::sys::path::Style style = working_dir.String.starts_with("/")239? llvm::sys::path::Style::posix240: llvm::sys::path::Style::windows;241if (llvm::sys::path::is_absolute(file_name.String, style))242return file_name.String;243244llvm::SmallString<64> absolute_path = working_dir.String;245llvm::sys::path::append(absolute_path, file_name.String);246return absolute_path;247}248249250