[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