[Lldb-commits] [lldb] [lldb][NativePDB] Defer PdbIndex creation until the symbol file is queried (PR #211178)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 22 07:05:24 PDT 2026
https://github.com/cherleey updated https://github.com/llvm/llvm-project/pull/211178
>From bfac9baabc50f00dfca7b50167747255571fd492 Mon Sep 17 00:00:00 2001
From: cherleey <31400524+cherleey at users.noreply.github.com>
Date: Wed, 22 Jul 2026 22:42:59 +0900
Subject: [PATCH] [lldb][NativePDB] Defer PdbIndex creation until the symbol
file is queried
---
.../NativePDB/SymbolFileNativePDB.cpp | 93 ++++++++++++++++---
.../NativePDB/SymbolFileNativePDB.h | 14 ++-
2 files changed, 92 insertions(+), 15 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index ec6e89b10e776..214ba98d3446f 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -34,6 +34,7 @@
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
@@ -173,6 +174,34 @@ loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
return pdb;
}
+// Reads only the DBI stream header to answer isStripped(). Parsing the full
+// DBI stream (let alone PdbIndex::create, which also pulls in the type and
+// symbol-record streams) materializes most of a large PDB on the private
+// heap; abilities probing runs for every candidate module at attach/launch,
+// so it must stay O(header).
+static std::optional<bool> IsDbiStripped(llvm::pdb::PDBFile &pdb) {
+ using namespace llvm::pdb;
+ if (!pdb.hasPDBDbiStream())
+ return std::nullopt;
+ auto stream_or_err = pdb.safelyCreateIndexedStream(
+ static_cast<uint32_t>(SpecialStream::StreamDBI));
+ if (!stream_or_err) {
+ llvm::consumeError(stream_or_err.takeError());
+ return std::nullopt;
+ }
+ std::unique_ptr<llvm::msf::MappedBlockStream> stream =
+ std::move(*stream_or_err);
+ if (stream->getLength() < sizeof(DbiStreamHeader))
+ return std::nullopt;
+ llvm::BinaryStreamReader reader(*stream);
+ const DbiStreamHeader *header = nullptr;
+ if (auto ec = reader.readObject(header)) {
+ llvm::consumeError(std::move(ec));
+ return std::nullopt;
+ }
+ return (header->Flags & DbiFlags::FlagStrippedMask) != 0;
+}
+
static bool IsFunctionPrologue(const CompilandIndexItem &cci,
lldb::addr_t addr) {
// FIXME: Implement this.
@@ -383,7 +412,7 @@ uint32_t SymbolFileNativePDB::CalculateAbilities() {
if (!m_objfile_sp)
return 0;
- if (!m_index) {
+ if (!m_pdb_file) {
// Lazily load and match the PDB file, but only do this once.
PDBFile *pdb_file;
if (auto *pdb = llvm::dyn_cast<ObjectFilePDB>(m_objfile_sp.get())) {
@@ -402,26 +431,53 @@ uint32_t SymbolFileNativePDB::CalculateAbilities() {
pdb_file->getFilePath(),
m_objfile_sp->GetModule()->GetObjectFile()->GetFileSpec().GetPath());
- auto expected_index = PdbIndex::create(pdb_file);
- if (!expected_index) {
- llvm::consumeError(expected_index.takeError());
- return 0;
- }
- m_index = std::move(*expected_index);
+ m_pdb_file = pdb_file;
}
- if (!m_index)
- return 0;
// We don't especially have to be precise here. We only distinguish between
- // stripped and not stripped.
- abilities = kAllAbilities;
+ // stripped and not stripped. Building the PdbIndex here would eagerly parse
+ // the type and symbol-record streams of every candidate module, so the
+ // stripped check reads just the DBI stream header instead — the index is
+ // built on demand in GetOrCreateIndex().
+ //
+ // PdbIndex::create requires the DBI/TPI/IPI streams; reject PDBs lacking
+ // them here so an unusable PDB does not win plugin selection only to fail
+ // when the index is materialized later.
+ if (!m_pdb_file->hasPDBTpiStream() || !m_pdb_file->hasPDBIpiStream())
+ return 0;
- if (m_index->dbi().isStripped())
+ std::optional<bool> stripped = IsDbiStripped(*m_pdb_file);
+ if (!stripped)
+ return 0;
+
+ abilities = kAllAbilities;
+ if (*stripped)
abilities &= ~(Blocks | LocalVariables);
return abilities;
}
+PdbIndex *SymbolFileNativePDB::GetOrCreateIndex() {
+ if (m_index)
+ return m_index.get();
+ if (!m_pdb_file)
+ return nullptr;
+
+ LLDB_LOG(GetLog(LLDBLog::Symbols), "Building PDB index for {0}",
+ m_objfile_sp->GetFileSpec().GetPath());
+
+ auto expected_index = PdbIndex::create(m_pdb_file);
+ if (!expected_index) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), expected_index.takeError(),
+ "Failed to build PDB index: {0}");
+ return nullptr;
+ }
+ m_index = std::move(*expected_index);
+ return m_index.get();
+}
+
void SymbolFileNativePDB::InitializeObject() {
+ if (!GetOrCreateIndex())
+ return;
m_obj_load_address = m_objfile_sp->GetModule()
->GetObjectFile()
->GetBaseAddress()
@@ -442,6 +498,8 @@ void SymbolFileNativePDB::InitializeObject() {
}
uint32_t SymbolFileNativePDB::CalculateNumCompileUnits() {
+ if (!GetOrCreateIndex())
+ return 0;
const DbiModuleList &modules = m_index->dbi().modules();
uint32_t count = modules.getModuleCount();
if (count == 0)
@@ -1245,6 +1303,11 @@ lldb::LanguageType SymbolFileNativePDB::ParseLanguage(CompileUnit &comp_unit) {
}
void SymbolFileNativePDB::AddSymbols(Symtab &symtab) {
+ // Symtab construction is reachable before InitializeObject (e.g. the
+ // on-demand wrapper serves pre-hydration lookups from the symtab), so the
+ // index must be materialized here as well.
+ if (!GetOrCreateIndex())
+ return;
auto *section_list =
m_objfile_sp->GetModule()->GetObjectFile()->GetSectionList();
if (!section_list)
@@ -2759,7 +2822,11 @@ SymbolFileNativePDB::GetTypeSystemForLanguage(lldb::LanguageType language) {
uint64_t SymbolFileNativePDB::GetDebugInfoSize(bool load_all_debug_info) {
// PDB files are a separate file that contains all debug info.
- return m_index->pdb().getFileSize();
+ // Reachable before the index is materialized (e.g. `statistics dump` with
+ // on-demand symbol loading), so use the PDB file directly.
+ if (!m_pdb_file)
+ return 0;
+ return m_pdb_file->getFileSize();
}
void SymbolFileNativePDB::BuildParentMap() {
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index 4d5d9fb58bcac..5707b1acc72b2 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -158,8 +158,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
- llvm::pdb::PDBFile &GetPDBFile() { return m_index->pdb(); }
- const llvm::pdb::PDBFile &GetPDBFile() const { return m_index->pdb(); }
+ llvm::pdb::PDBFile &GetPDBFile() { return *m_pdb_file; }
+ const llvm::pdb::PDBFile &GetPDBFile() const { return *m_pdb_file; }
PdbIndex &GetIndex() { return *m_index; };
@@ -301,7 +301,17 @@ class SymbolFileNativePDB : public SymbolFileCommon {
// pdb debug info.
lldb::user_id_t anonymous_id = LLDB_INVALID_UID - 1;
+ /// Builds m_index on first use. PdbIndex::create eagerly parses the DBI,
+ /// type (TPI/IPI) and symbol-record streams — for large PDBs that is most
+ /// of the file materialized on the private heap — so it must not run
+ /// during abilities probing, only when debug info is actually consumed
+ /// (InitializeObject / symtab construction).
+ PdbIndex *GetOrCreateIndex();
+
std::unique_ptr<llvm::pdb::PDBFile> m_file_up;
+ /// The matched PDB file (owned by m_file_up, or by the ObjectFilePDB when
+ /// the module's object file IS the PDB). Set by CalculateAbilities.
+ llvm::pdb::PDBFile *m_pdb_file = nullptr;
std::unique_ptr<PdbIndex> m_index;
llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_global_vars;
More information about the lldb-commits
mailing list