[Lldb-commits] [lldb] Reland "[lldb] Initial plugin and test for SymbolLocatorSymStore" (PR #185658)
Stefan Gränitz via lldb-commits
lldb-commits at lists.llvm.org
Thu Mar 12 08:14:43 PDT 2026
https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/185658
>From 069d77c58181e8e9dde30b97434bcbd97e6ff40e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Tue, 10 Mar 2026 13:43:53 +0100
Subject: [PATCH 1/7] Reland "[lldb] Initial plugin and test for
SymbolLocatorSymStore"
---
.../ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 21 +++
.../ObjectFile/PECOFF/ObjectFilePECOFF.h | 2 +
.../Plugins/SymbolLocator/CMakeLists.txt | 1 +
.../SymbolLocator/SymStore/CMakeLists.txt | 20 +++
.../SymStore/SymbolLocatorSymStore.cpp | 147 ++++++++++++++++++
.../SymStore/SymbolLocatorSymStore.h | 50 ++++++
.../SymbolLocatorSymStoreProperties.td | 7 +
.../PECOFF/SymbolVendorPECOFF.cpp | 48 +++---
lldb/test/API/symstore/Makefile | 2 +
lldb/test/API/symstore/TestSymStoreLocal.py | 121 ++++++++++++++
lldb/test/API/symstore/main.c | 5 +
11 files changed, 401 insertions(+), 23 deletions(-)
create mode 100644 lldb/source/Plugins/SymbolLocator/SymStore/CMakeLists.txt
create mode 100644 lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.cpp
create mode 100644 lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h
create mode 100644 lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStoreProperties.td
create mode 100644 lldb/test/API/symstore/Makefile
create mode 100644 lldb/test/API/symstore/TestSymStoreLocal.py
create mode 100644 lldb/test/API/symstore/main.c
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
index 3a17b4c46a788..cec47d96b33d2 100644
--- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
@@ -1108,6 +1108,27 @@ std::optional<FileSpec> ObjectFilePECOFF::GetDebugLink() {
return std::nullopt;
}
+std::optional<FileSpec> ObjectFilePECOFF::GetPDBPath() {
+ llvm::StringRef pdb_file;
+ const llvm::codeview::DebugInfo *pdb_info = nullptr;
+ if (llvm::Error Err = m_binary->getDebugPDBInfo(pdb_info, pdb_file)) {
+ // DebugInfo section is corrupt.
+ Log *log = GetLog(LLDBLog::Object);
+ llvm::StringRef file = m_binary->getFileName();
+ LLDB_LOG_ERROR(
+ log, std::move(Err),
+ "Failed to read Codeview record for PDB debug info file ({1}): {0}",
+ file);
+ return std::nullopt;
+ }
+ if (pdb_file.empty()) {
+ // No DebugInfo section present.
+ return std::nullopt;
+ }
+ return FileSpec(pdb_file, FileSpec::GuessPathStyle(pdb_file).value_or(
+ FileSpec::Style::native));
+}
+
uint32_t ObjectFilePECOFF::ParseDependentModules() {
ModuleSP module_sp(GetModule());
if (!module_sp)
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
index 8002e70e604bb..30bd672dc68f8 100644
--- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
@@ -130,6 +130,8 @@ class ObjectFilePECOFF : public lldb_private::ObjectFile {
/// contains it.
std::optional<lldb_private::FileSpec> GetDebugLink();
+ std::optional<lldb_private::FileSpec> GetPDBPath();
+
uint32_t GetDependentModules(lldb_private::FileSpecList &files) override;
lldb_private::Address GetEntryPointAddress() override;
diff --git a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
index 3b466f71dca58..9b9ec470b86a9 100644
--- a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
+++ b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
@@ -6,6 +6,7 @@ set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND SymbolLocator)
# prevents an unstripped binary from being requested from the Debuginfod
# provider.
add_subdirectory(Debuginfod)
+add_subdirectory(SymStore)
add_subdirectory(Default)
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_subdirectory(DebugSymbols)
diff --git a/lldb/source/Plugins/SymbolLocator/SymStore/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/SymStore/CMakeLists.txt
new file mode 100644
index 0000000000000..b0da27f26c6a8
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/SymStore/CMakeLists.txt
@@ -0,0 +1,20 @@
+lldb_tablegen(SymbolLocatorSymStoreProperties.inc -gen-lldb-property-defs
+ SOURCE SymbolLocatorSymStoreProperties.td
+ TARGET LLDBPluginSymbolLocatorSymStorePropertiesGen)
+
+lldb_tablegen(SymbolLocatorSymStorePropertiesEnum.inc -gen-lldb-property-enum-defs
+ SOURCE SymbolLocatorSymStoreProperties.td
+ TARGET LLDBPluginSymbolLocatorSymStorePropertiesEnumGen)
+
+add_lldb_library(lldbPluginSymbolLocatorSymStore PLUGIN
+ SymbolLocatorSymStore.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbHost
+ lldbSymbol
+ )
+
+add_dependencies(lldbPluginSymbolLocatorSymStore
+ LLDBPluginSymbolLocatorSymStorePropertiesGen
+ LLDBPluginSymbolLocatorSymStorePropertiesEnumGen)
diff --git a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.cpp b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.cpp
new file mode 100644
index 0000000000000..d008a7d3e8e9a
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.cpp
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "SymbolLocatorSymStore.h"
+
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/UUID.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(SymbolLocatorSymStore)
+
+namespace {
+
+#define LLDB_PROPERTIES_symbollocatorsymstore
+#include "SymbolLocatorSymStoreProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_symbollocatorsymstore
+#include "SymbolLocatorSymStorePropertiesEnum.inc"
+};
+
+class PluginProperties : public Properties {
+public:
+ static llvm::StringRef GetSettingName() {
+ return SymbolLocatorSymStore::GetPluginNameStatic();
+ }
+
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_symbollocatorsymstore_properties_def);
+ }
+
+ Args GetURLs() const {
+ Args urls;
+ m_collection_sp->GetPropertyAtIndexAsArgs(ePropertySymStoreURLs, urls);
+ return urls;
+ }
+};
+
+} // namespace
+
+static PluginProperties &GetGlobalPluginProperties() {
+ static PluginProperties g_settings;
+ return g_settings;
+}
+
+SymbolLocatorSymStore::SymbolLocatorSymStore() : SymbolLocator() {}
+
+void SymbolLocatorSymStore::Initialize() {
+ // First version can only locate PDB in local SymStore (no download yet).
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
+ nullptr, LocateExecutableSymbolFile, nullptr, nullptr,
+ SymbolLocatorSymStore::DebuggerInitialize);
+}
+
+void SymbolLocatorSymStore::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForSymbolLocatorPlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ constexpr bool is_global_setting = true;
+ PluginManager::CreateSettingForSymbolLocatorPlugin(
+ debugger, GetGlobalPluginProperties().GetValueProperties(),
+ "Properties for the SymStore Symbol Locator plug-in.",
+ is_global_setting);
+ }
+}
+
+void SymbolLocatorSymStore::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+llvm::StringRef SymbolLocatorSymStore::GetPluginDescriptionStatic() {
+ return "Symbol locator for PDB in SymStore";
+}
+
+SymbolLocator *SymbolLocatorSymStore::CreateInstance() {
+ return new SymbolLocatorSymStore();
+}
+
+// RSDS entries store identity as a 20-byte UUID composed of 16-byte GUID and
+// 4-byte age:
+// 12345678-1234-5678-9ABC-DEF012345678-00000001
+//
+// SymStore key is a string with no separators and age as decimal:
+// 12345678123456789ABCDEF0123456781
+//
+static std::string formatSymStoreKey(const UUID &uuid) {
+ llvm::ArrayRef<uint8_t> bytes = uuid.GetBytes();
+ uint32_t age = llvm::support::endian::read32be(bytes.data() + 16);
+ constexpr bool LowerCase = false;
+ return llvm::toHex(bytes.slice(0, 16), LowerCase) + std::to_string(age);
+}
+
+std::optional<FileSpec> SymbolLocatorSymStore::LocateExecutableSymbolFile(
+ const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
+ const UUID &uuid = module_spec.GetUUID();
+ if (!uuid.IsValid() ||
+ !ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup())
+ return {};
+
+ Log *log = GetLog(LLDBLog::Symbols);
+ std::string pdb_name =
+ module_spec.GetSymbolFileSpec().GetFilename().GetStringRef().str();
+ if (pdb_name.empty()) {
+ LLDB_LOGV(log, "Failed to resolve symbol PDB module: PDB name empty");
+ return {};
+ }
+
+ LLDB_LOGV(log, "LocateExecutableSymbolFile {0} with UUID {1}", pdb_name,
+ uuid.GetAsString());
+ if (uuid.GetBytes().size() != 20) {
+ LLDB_LOGV(log, "Failed to resolve symbol PDB module: UUID invalid");
+ return {};
+ }
+
+ std::string key = formatSymStoreKey(uuid);
+ Args sym_store_urls = GetGlobalPluginProperties().GetURLs();
+ for (const Args::ArgEntry &url : sym_store_urls) {
+ llvm::SmallString<256> path;
+ llvm::sys::path::append(path, url.ref(), pdb_name, key, pdb_name);
+ FileSpec spec(path);
+ if (FileSystem::Instance().Exists(spec)) {
+ LLDB_LOGV(log, "Found {0} in SymStore {1}", pdb_name, url.ref());
+ return spec;
+ }
+ }
+
+ return {};
+}
diff --git a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h
new file mode 100644
index 0000000000000..52ec04cae387b
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStore.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_SYMSTORE_SYMBOLLOCATORSYMSTORE_H
+#define LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_SYMSTORE_SYMBOLLOCATORSYMSTORE_H
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Symbol/SymbolLocator.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// This plugin implements lookup in Microsoft SymStore instances. This can work
+/// cross-platform and for arbitrary debug info formats, but the focus is on PDB
+/// with PE/COFF binaries on Windows.
+class SymbolLocatorSymStore : public SymbolLocator {
+public:
+ SymbolLocatorSymStore();
+
+ static void Initialize();
+ static void Terminate();
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static llvm::StringRef GetPluginNameStatic() { return "symstore"; }
+ static llvm::StringRef GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolLocator *CreateInstance();
+
+ /// PluginInterface protocol.
+ /// \{
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+ /// \}
+
+ // Locate the symbol file given a module specification.
+ //
+ // Locating the file should happen only on the local computer or using the
+ // current computers global settings.
+ static std::optional<FileSpec>
+ LocateExecutableSymbolFile(const ModuleSpec &module_spec,
+ const FileSpecList &default_search_paths);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_SYMSTORE_SYMBOLLOCATORSYMSTORE_H
diff --git a/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStoreProperties.td b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStoreProperties.td
new file mode 100644
index 0000000000000..0cd631a80b90b
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/SymStore/SymbolLocatorSymStoreProperties.td
@@ -0,0 +1,7 @@
+include "../../../../include/lldb/Core/PropertiesBase.td"
+
+let Definition = "symbollocatorsymstore", Path = "plugin.symbol-locator.symstore" in {
+ def SymStoreURLs : Property<"urls", "Array">,
+ ElementType<"String">,
+ Desc<"List of local symstore directories to query for symbols">;
+}
diff --git a/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp b/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
index 20ccfa54a106c..1797e5b7677ee 100644
--- a/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
+++ b/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
@@ -71,6 +71,9 @@ SymbolVendorPECOFF::CreateInstance(const lldb::ModuleSP &module_sp,
// If the module specified a filespec, use that.
FileSpec fspec = module_sp->GetSymbolFileFileSpec();
+ // Otherwise, use the PDB path from CodeView.
+ if (!fspec)
+ fspec = obj_file->GetPDBPath().value_or(FileSpec());
// Otherwise, try gnu_debuglink, if one exists.
if (!fspec)
fspec = obj_file->GetDebugLink().value_or(FileSpec());
@@ -101,31 +104,30 @@ SymbolVendorPECOFF::CreateInstance(const lldb::ModuleSP &module_sp,
// This objfile is for debugging purposes.
dsym_objfile_sp->SetType(ObjectFile::eTypeDebugInfo);
- // Get the module unified section list and add our debug sections to
- // that.
+ // For DWARF get the module unified section list and add our debug sections
+ // to that.
SectionList *module_section_list = module_sp->GetSectionList();
SectionList *objfile_section_list = dsym_objfile_sp->GetSectionList();
- if (!objfile_section_list || !module_section_list)
- return nullptr;
-
- static const SectionType g_sections[] = {
- eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAranges,
- eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo,
- eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLoc,
- eSectionTypeDWARFDebugLocLists, eSectionTypeDWARFDebugMacInfo,
- eSectionTypeDWARFDebugNames, eSectionTypeDWARFDebugPubNames,
- eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges,
- eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugTypes,
- };
- for (SectionType section_type : g_sections) {
- if (SectionSP section_sp =
- objfile_section_list->FindSectionByType(section_type, true)) {
- if (SectionSP module_section_sp =
- module_section_list->FindSectionByType(section_type, true))
- module_section_list->ReplaceSection(module_section_sp->GetID(),
- section_sp);
- else
- module_section_list->AddSection(section_sp);
+ if (objfile_section_list && module_section_list) {
+ static const SectionType g_sections[] = {
+ eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAranges,
+ eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo,
+ eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLoc,
+ eSectionTypeDWARFDebugLocLists, eSectionTypeDWARFDebugMacInfo,
+ eSectionTypeDWARFDebugNames, eSectionTypeDWARFDebugPubNames,
+ eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges,
+ eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugTypes,
+ };
+ for (SectionType section_type : g_sections) {
+ if (SectionSP section_sp =
+ objfile_section_list->FindSectionByType(section_type, true)) {
+ if (SectionSP module_section_sp =
+ module_section_list->FindSectionByType(section_type, true))
+ module_section_list->ReplaceSection(module_section_sp->GetID(),
+ section_sp);
+ else
+ module_section_list->AddSection(section_sp);
+ }
}
}
diff --git a/lldb/test/API/symstore/Makefile b/lldb/test/API/symstore/Makefile
new file mode 100644
index 0000000000000..c9319d6e6888a
--- /dev/null
+++ b/lldb/test/API/symstore/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/symstore/TestSymStoreLocal.py b/lldb/test/API/symstore/TestSymStoreLocal.py
new file mode 100644
index 0000000000000..154af876fea3f
--- /dev/null
+++ b/lldb/test/API/symstore/TestSymStoreLocal.py
@@ -0,0 +1,121 @@
+import os
+import shutil
+import tempfile
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+"""
+Test debug symbol acquisition from a local SymStore repository. This can work
+cross-platform and for arbitrary debug info formats. We only support PDB
+currently.
+"""
+
+
+class MockedSymStore:
+ """
+ Context Manager to populate a file structure equivalent to SymStore.exe in a
+ temporary directory.
+ """
+
+ def __init__(self, test, exe, pdb):
+ self._test = test
+ self._exe = exe
+ self._pdb = pdb
+ self._tmp = None
+
+ def get_key_pdb(self, exe):
+ """
+ Module UUID: 12345678-1234-5678-9ABC-DEF012345678-00000001
+ To SymStore key: 12345678123456789ABCDEF0123456781
+ """
+ spec = lldb.SBModuleSpec()
+ spec.SetFileSpec(lldb.SBFileSpec(self._test.getBuildArtifact(exe)))
+ module = lldb.SBModule(spec)
+ raw = module.GetUUIDString().replace("-", "").upper()
+ if len(raw) != 40:
+ raise RuntimeError("Unexpected number of bytes in embedded UUID")
+ guid_hex = raw[:32]
+ age = int(raw[32:], 16)
+ return guid_hex + str(age)
+
+ def __enter__(self):
+ """
+ Mock local symstore directory tree, move PDB there and report path.
+ """
+ key = None
+ if self._test.getDebugInfo() == "pdb":
+ key = self.get_key_pdb(self._exe)
+ self._test.assertIsNotNone(key)
+ self._tmp = tempfile.mkdtemp()
+ pdb_dir = os.path.join(self._tmp, self._pdb, key)
+ os.makedirs(pdb_dir)
+ shutil.move(
+ self._test.getBuildArtifact(self._pdb),
+ os.path.join(pdb_dir, self._pdb),
+ )
+ return self._tmp
+
+ def __exit__(self, *exc_info):
+ """
+ Clean up and delete original exe so next make won't skip link command.
+ """
+ shutil.rmtree(self._tmp)
+ self._test.runCmd("settings clear plugin.symbol-locator.symstore")
+ os.remove(self._test.getBuildArtifact(self._exe))
+
+
+class SymStoreLocalTests(TestBase):
+ TEST_WITH_PDB_DEBUG_INFO = True
+
+ def build_inferior(self):
+ if self.getDebugInfo() != "pdb":
+ self.skipTest("Non-PDB debug info variants not yet supported")
+ self.build()
+ exe_file = "a.out"
+ sym_file = "a.pdb"
+ self.assertTrue(os.path.isfile(self.getBuildArtifact(exe_file)))
+ self.assertTrue(os.path.isfile(self.getBuildArtifact(sym_file)))
+ return exe_file, sym_file
+
+ def try_breakpoint(self, exe, should_have_loc, ext_lookup=True):
+ enable = "true" if ext_lookup else "false"
+ self.runCmd(f"settings set symbols.enable-external-lookup {enable}")
+ target = self.dbg.CreateTarget(self.getBuildArtifact(exe))
+ self.assertTrue(target and target.IsValid(), "Target is valid")
+ bp = target.BreakpointCreateByName("func")
+ self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
+ self.assertEqual(bp.GetNumLocations(), 1 if should_have_loc else 0)
+ self.dbg.DeleteTarget(target)
+
+ def test_no_symstore_url(self):
+ """
+ Check that breakpoint doesn't resolve without SymStore.
+ """
+ exe, sym = self.build_inferior()
+ with MockedSymStore(self, exe, sym):
+ self.try_breakpoint(exe, should_have_loc=False)
+
+ def test_external_lookup_off(self):
+ """
+ Check that breakpoint doesn't resolve with external lookup disabled.
+ """
+ exe, sym = self.build_inferior()
+ with MockedSymStore(self, exe, sym) as symstore_dir:
+ self.runCmd(
+ f"settings set plugin.symbol-locator.symstore.urls {symstore_dir}"
+ )
+ self.try_breakpoint(exe, ext_lookup=False, should_have_loc=False)
+
+ def test_basic(self):
+ """
+ Check that breakpoint resolves with local SymStore.
+ """
+ exe, sym = self.build_inferior()
+ with MockedSymStore(self, exe, sym) as symstore_dir:
+ self.runCmd(
+ f"settings set plugin.symbol-locator.symstore.urls {symstore_dir}"
+ )
+ self.try_breakpoint(exe, should_have_loc=True)
diff --git a/lldb/test/API/symstore/main.c b/lldb/test/API/symstore/main.c
new file mode 100644
index 0000000000000..a95762e80ea44
--- /dev/null
+++ b/lldb/test/API/symstore/main.c
@@ -0,0 +1,5 @@
+int func(int argc, const char *argv[]) {
+ return (argc + 1) * (argv[argc][0] + 2);
+}
+
+int main(int argc, const char *argv[]) { return func(0, argv); }
>From f9d329ce25fca6f60967dce8e1aa8540672b2b35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Tue, 10 Mar 2026 14:08:26 +0100
Subject: [PATCH 2/7] Create mock symstore in build directory and not in
mkdtemp
---
lldb/test/API/symstore/TestSymStoreLocal.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lldb/test/API/symstore/TestSymStoreLocal.py b/lldb/test/API/symstore/TestSymStoreLocal.py
index 154af876fea3f..cedc76ac4354c 100644
--- a/lldb/test/API/symstore/TestSymStoreLocal.py
+++ b/lldb/test/API/symstore/TestSymStoreLocal.py
@@ -49,7 +49,7 @@ def __enter__(self):
if self._test.getDebugInfo() == "pdb":
key = self.get_key_pdb(self._exe)
self._test.assertIsNotNone(key)
- self._tmp = tempfile.mkdtemp()
+ self._tmp = self._test.getBuildArtifact("tmp")
pdb_dir = os.path.join(self._tmp, self._pdb, key)
os.makedirs(pdb_dir)
shutil.move(
@@ -63,8 +63,8 @@ def __exit__(self, *exc_info):
Clean up and delete original exe so next make won't skip link command.
"""
shutil.rmtree(self._tmp)
- self._test.runCmd("settings clear plugin.symbol-locator.symstore")
os.remove(self._test.getBuildArtifact(self._exe))
+ self._test.runCmd("settings clear plugin.symbol-locator.symstore")
class SymStoreLocalTests(TestBase):
@@ -109,7 +109,7 @@ def test_external_lookup_off(self):
)
self.try_breakpoint(exe, ext_lookup=False, should_have_loc=False)
- def test_basic(self):
+ def test_local_dir(self):
"""
Check that breakpoint resolves with local SymStore.
"""
>From a0f995fbea11999b4ce8a603b958243e9a06d833 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Wed, 11 Mar 2026 14:35:37 +0100
Subject: [PATCH 3/7] Stop checking log output in
NativePDB/find-pdb-next-to-exe.test
---
.../Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test | 5 -----
1 file changed, 5 deletions(-)
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
index c35c82ad84d2f..8e71225a5470e 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
+++ b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
@@ -16,14 +16,12 @@
# Regular setup - PDB is at the original path
# RUN: %lldb -S %t/init.input -s %t/check.input %t/build/a.exe | FileCheck --check-prefix=BOTH-ORIG %s
# BOTH-ORIG: (lldb) target create
-# BOTH-ORIG-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}build{{[/\\]}}a.exe
# BOTH-ORIG: (A) a = (x = 47)
# Move the executable to a different directory but keep the PDB.
# RUN: mv %t/build/a.exe %t/dir1
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=PDB-ORIG %s
# PDB-ORIG: (lldb) target create
-# PDB-ORIG-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
# PDB-ORIG: (A) a = (x = 47)
# Copy the PDB to the same directory and all search dirs. LLDB should prefer the original PDB.
@@ -36,21 +34,18 @@
# RUN: rm %t/build/a.pdb
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=NEXT-TO-EXE %s
# NEXT-TO-EXE: (lldb) target create
-# NEXT-TO-EXE-NEXT: Loading {{.*[/\\]}}dir1{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
# NEXT-TO-EXE: (A) a = (x = 47)
# Remove the PDB next to the exe. LLDB should now use the one in dir2 (first in list).
# RUN: rm %t/dir1/a.pdb
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=DIR2 %s
# DIR2: (lldb) target create
-# DIR2-NEXT: Loading {{.*[/\\]}}dir2{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
# DIR2: (A) a = (x = 47)
# Remove the PDB in dir2. LLDB should now use the one in dir3 (second in list).
# RUN: rm %t/dir2/a.pdb
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=DIR3 %s
# DIR3: (lldb) target create
-# DIR3-NEXT: Loading {{.*[/\\]}}dir3{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
# DIR3: (A) a = (x = 47)
# Remove the last PDB in dir3. Now, there's no matching PDB anymore.
>From 841e342ddc5e07dd8db2e9879305d32e37fb3a49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Wed, 11 Mar 2026 15:12:15 +0100
Subject: [PATCH 4/7] Access object file through module once more
---
lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
index e35195fec2efc..26b89eaefd37a 100644
--- a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
@@ -239,7 +239,8 @@ uint32_t SymbolFilePDB::CalculateAbilities() {
if (!m_session_up) {
// Lazily load and match the PDB file, but only do this once.
- std::string exePath = m_objfile_sp->GetFileSpec().GetPath();
+ std::string exePath =
+ m_objfile_sp->GetModule()->GetObjectFile()->GetFileSpec().GetPath();
auto error = loadDataForEXE(PDB_ReaderType::DIA, llvm::StringRef(exePath),
m_session_up);
if (error) {
>From 4a2facdb024dd8ed18e3045e647e84d419d4c4be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 12 Mar 2026 15:06:12 +0100
Subject: [PATCH 5/7] Move Loading log from loadMatchingPDBFile() to
CalculateAbilities()
---
.../Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index bed15839be1cd..b094e7afc3dac 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -169,8 +169,6 @@ loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
if (expected_info->getGuid() != guid)
return nullptr;
- LLDB_LOG(GetLog(LLDBLog::Symbols), "Loading {0} for {1}", pdb->getFilePath(),
- exe_path);
return pdb;
}
@@ -398,6 +396,9 @@ uint32_t SymbolFileNativePDB::CalculateAbilities() {
if (!pdb_file)
return 0;
+ LLDB_LOG(GetLog(LLDBLog::Symbols), "Loading {0} for {1}", 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());
>From e9f2e08c4f9b46681bbd7c60df6e1b4d47e81450 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 12 Mar 2026 15:06:33 +0100
Subject: [PATCH 6/7] Revert "Stop checking log output in
NativePDB/find-pdb-next-to-exe.test"
This reverts commit a0f995fbea11999b4ce8a603b958243e9a06d833.
---
.../Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
index 8e71225a5470e..c35c82ad84d2f 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
+++ b/lldb/test/Shell/SymbolFile/NativePDB/find-pdb-next-to-exe.test
@@ -16,12 +16,14 @@
# Regular setup - PDB is at the original path
# RUN: %lldb -S %t/init.input -s %t/check.input %t/build/a.exe | FileCheck --check-prefix=BOTH-ORIG %s
# BOTH-ORIG: (lldb) target create
+# BOTH-ORIG-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}build{{[/\\]}}a.exe
# BOTH-ORIG: (A) a = (x = 47)
# Move the executable to a different directory but keep the PDB.
# RUN: mv %t/build/a.exe %t/dir1
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=PDB-ORIG %s
# PDB-ORIG: (lldb) target create
+# PDB-ORIG-NEXT: Loading {{.*[/\\]}}build{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
# PDB-ORIG: (A) a = (x = 47)
# Copy the PDB to the same directory and all search dirs. LLDB should prefer the original PDB.
@@ -34,18 +36,21 @@
# RUN: rm %t/build/a.pdb
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=NEXT-TO-EXE %s
# NEXT-TO-EXE: (lldb) target create
+# NEXT-TO-EXE-NEXT: Loading {{.*[/\\]}}dir1{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
# NEXT-TO-EXE: (A) a = (x = 47)
# Remove the PDB next to the exe. LLDB should now use the one in dir2 (first in list).
# RUN: rm %t/dir1/a.pdb
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=DIR2 %s
# DIR2: (lldb) target create
+# DIR2-NEXT: Loading {{.*[/\\]}}dir2{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
# DIR2: (A) a = (x = 47)
# Remove the PDB in dir2. LLDB should now use the one in dir3 (second in list).
# RUN: rm %t/dir2/a.pdb
# RUN: %lldb -S %t/init.input -s %t/check.input %t/dir1/a.exe | FileCheck --check-prefix=DIR3 %s
# DIR3: (lldb) target create
+# DIR3-NEXT: Loading {{.*[/\\]}}dir3{{[/\\]}}a.pdb for {{.*[/\\]}}dir1{{[/\\]}}a.exe
# DIR3: (A) a = (x = 47)
# Remove the last PDB in dir3. Now, there's no matching PDB anymore.
>From 6c09e2d10223b847ade3e8c287889ce35941053c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 12 Mar 2026 15:14:12 +0100
Subject: [PATCH 7/7] clang-format
---
.../Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index b094e7afc3dac..d083768c3cdd0 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -396,8 +396,10 @@ uint32_t SymbolFileNativePDB::CalculateAbilities() {
if (!pdb_file)
return 0;
- LLDB_LOG(GetLog(LLDBLog::Symbols), "Loading {0} for {1}", pdb_file->getFilePath(),
- m_objfile_sp->GetModule()->GetObjectFile()->GetFileSpec().GetPath());
+ LLDB_LOG(
+ GetLog(LLDBLog::Symbols), "Loading {0} for {1}",
+ pdb_file->getFilePath(),
+ m_objfile_sp->GetModule()->GetObjectFile()->GetFileSpec().GetPath());
auto expected_index = PdbIndex::create(pdb_file);
if (!expected_index) {
More information about the lldb-commits
mailing list