[Lldb-commits] [lldb] [lldb] Add ScriptedSymbolLocator plugin for source file resolution (PR #181334)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Feb 13 01:39:47 PST 2026
https://github.com/rchamala updated https://github.com/llvm/llvm-project/pull/181334
>From 5f1923e3504767426044864d90228d23b25e3e3b Mon Sep 17 00:00:00 2001
From: Rahul Reddy Chamala <rachamal at fb.com>
Date: Thu, 12 Feb 2026 16:58:25 -0800
Subject: [PATCH] Add ScriptedSymbolLocator plugin for source file resolution
---
lldb/include/lldb/Core/PluginManager.h | 4 +
.../ScriptedSymbolLocatorInterface.h | 57 ++++
.../lldb/Interpreter/ScriptInterpreter.h | 6 +
lldb/include/lldb/Symbol/LineEntry.h | 2 +-
lldb/include/lldb/lldb-forward.h | 3 +
lldb/include/lldb/lldb-private-interfaces.h | 2 +
lldb/source/Core/Module.cpp | 2 +-
lldb/source/Core/PluginManager.cpp | 22 +-
.../Python/Interfaces/CMakeLists.txt | 3 +-
.../ScriptInterpreterPythonInterfaces.h | 1 +
.../Interfaces/ScriptedPythonInterface.h | 4 +
.../ScriptedSymbolLocatorPythonInterface.cpp | 278 ++++++++++++++++++
.../ScriptedSymbolLocatorPythonInterface.h | 69 +++++
.../Python/ScriptInterpreterPython.cpp | 5 +
.../Python/ScriptInterpreterPythonImpl.h | 7 +-
.../Plugins/SymbolLocator/CMakeLists.txt | 1 +
.../Debuginfod/SymbolLocatorDebuginfod.cpp | 2 +-
.../SymbolLocator/Scripted/CMakeLists.txt | 24 ++
.../Scripted/SymbolLocatorScripted.cpp | 271 +++++++++++++++++
.../Scripted/SymbolLocatorScripted.h | 57 ++++
.../SymbolLocatorScriptedProperties.td | 7 +
lldb/source/Symbol/LineEntry.cpp | 22 +-
lldb/source/Target/StackFrame.cpp | 2 +-
lldb/source/Target/StackFrameList.cpp | 3 +-
lldb/source/Target/ThreadPlanStepRange.cpp | 10 +-
.../scripted_symbol_locator/Makefile | 4 +
.../TestScriptedSymbolLocator.py | 156 ++++++++++
.../scripted_symbol_locator/main.c | 5 +
.../scripted_symbol_locator/source_locator.py | 53 ++++
29 files changed, 1065 insertions(+), 17 deletions(-)
create mode 100644 lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h
create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp
create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h
create mode 100644 lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt
create mode 100644 lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp
create mode 100644 lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h
create mode 100644 lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScriptedProperties.td
create mode 100644 lldb/test/API/functionalities/scripted_symbol_locator/Makefile
create mode 100644 lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py
create mode 100644 lldb/test/API/functionalities/scripted_symbol_locator/main.c
create mode 100644 lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py
diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h
index 4d116f52460ff..3fd3e6177afa0 100644
--- a/lldb/include/lldb/Core/PluginManager.h
+++ b/lldb/include/lldb/Core/PluginManager.h
@@ -455,6 +455,7 @@ class PluginManager {
SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file =
nullptr,
SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle = nullptr,
+ SymbolLocatorLocateSourceFile locate_source_file = nullptr,
DebuggerInitializeCallback debugger_init_callback = nullptr);
static bool UnregisterPlugin(SymbolLocatorCreateInstance create_callback);
@@ -479,6 +480,9 @@ class PluginManager {
const UUID *uuid,
const ArchSpec *arch);
+ static FileSpec LocateSourceFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &original_source_file);
+
// Trace
static bool RegisterPlugin(
llvm::StringRef name, llvm::StringRef description,
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h
new file mode 100644
index 0000000000000..a9edb23d164c0
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h
@@ -0,0 +1,57 @@
+//===-- ScriptedSymbolLocatorInterface.h -------------------------*- C++
+//-*-===//
+//
+// 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_INTERPRETER_INTERFACES_SCRIPTEDSYMBOLLOCATORINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDSYMBOLLOCATORINTERFACE_H
+
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Interpreter/Interfaces/ScriptedInterface.h"
+#include "lldb/Utility/Status.h"
+
+#include "lldb/lldb-private.h"
+
+#include <optional>
+#include <string>
+
+namespace lldb_private {
+class ScriptedSymbolLocatorInterface : virtual public ScriptedInterface {
+public:
+ virtual llvm::Expected<StructuredData::GenericSP>
+ CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
+ StructuredData::DictionarySP args_sp,
+ StructuredData::Generic *script_obj = nullptr) = 0;
+
+ virtual std::optional<ModuleSpec>
+ LocateExecutableObjectFile(const ModuleSpec &module_spec, Status &error) {
+ return {};
+ }
+
+ virtual std::optional<FileSpec>
+ LocateExecutableSymbolFile(const ModuleSpec &module_spec,
+ const FileSpecList &default_search_paths,
+ Status &error) {
+ return {};
+ }
+
+ virtual bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
+ Status &error, bool force_lookup,
+ bool copy_executable) {
+ return false;
+ }
+
+ virtual std::optional<FileSpec>
+ LocateSourceFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &original_source_file, Status &error) {
+ return {};
+ }
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDSYMBOLLOCATORINTERFACE_H
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 557d73a415452..bbd669fd767d3 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -33,6 +33,7 @@
#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
#include "lldb/Interpreter/ScriptObject.h"
#include "lldb/Symbol/SymbolContext.h"
@@ -545,6 +546,11 @@ class ScriptInterpreter : public PluginInterface {
return {};
}
+ virtual lldb::ScriptedSymbolLocatorInterfaceSP
+ CreateScriptedSymbolLocatorInterface() {
+ return {};
+ }
+
virtual lldb::ScriptedThreadPlanInterfaceSP
CreateScriptedThreadPlanInterface() {
return {};
diff --git a/lldb/include/lldb/Symbol/LineEntry.h b/lldb/include/lldb/Symbol/LineEntry.h
index adf2e989e3e34..e023eda6d89a4 100644
--- a/lldb/include/lldb/Symbol/LineEntry.h
+++ b/lldb/include/lldb/Symbol/LineEntry.h
@@ -128,7 +128,7 @@ struct LineEntry {
///
/// \param[in] target_sp
/// Shared pointer to the target this LineEntry belongs to.
- void ApplyFileMappings(lldb::TargetSP target_sp);
+ void ApplyFileMappings(lldb::TargetSP target_sp, const Address &address);
/// Helper to access the file.
const FileSpec &GetFile() const { return file_sp->GetSpecOnly(); }
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index ccfe5efa19e1d..c0f65b09616a3 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -196,6 +196,7 @@ class ScriptedProcessInterface;
class ScriptedStopHookInterface;
class ScriptedThreadInterface;
class ScriptedThreadPlanInterface;
+class ScriptedSymbolLocatorInterface;
class ScriptedSyntheticChildren;
class SearchFilter;
class Section;
@@ -431,6 +432,8 @@ typedef std::shared_ptr<lldb_private::ScriptedThreadPlanInterface>
ScriptedThreadPlanInterfaceSP;
typedef std::shared_ptr<lldb_private::ScriptedBreakpointInterface>
ScriptedBreakpointInterfaceSP;
+typedef std::shared_ptr<lldb_private::ScriptedSymbolLocatorInterface>
+ ScriptedSymbolLocatorInterfaceSP;
typedef std::shared_ptr<lldb_private::Section> SectionSP;
typedef std::unique_ptr<lldb_private::SectionList> SectionListUP;
typedef std::weak_ptr<lldb_private::Section> SectionWP;
diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h
index a87e01769c555..6d71b8d671b71 100644
--- a/lldb/include/lldb/lldb-private-interfaces.h
+++ b/lldb/include/lldb/lldb-private-interfaces.h
@@ -110,6 +110,8 @@ typedef std::optional<FileSpec> (*SymbolLocatorLocateExecutableSymbolFile)(
typedef bool (*SymbolLocatorDownloadObjectAndSymbolFile)(
ModuleSpec &module_spec, Status &error, bool force_lookup,
bool copy_executable);
+typedef std::optional<FileSpec> (*SymbolLocatorLocateSourceFile)(
+ const lldb::ModuleSP &module_sp, const FileSpec &original_source_file);
using BreakpointHitCallback =
std::function<bool(void *baton, StoppointCallbackContext *context,
lldb::user_id_t break_id, lldb::user_id_t break_loc_id)>;
diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
index 659190833c20d..fc17daf86a901 100644
--- a/lldb/source/Core/Module.cpp
+++ b/lldb/source/Core/Module.cpp
@@ -476,7 +476,7 @@ uint32_t Module::ResolveSymbolContextForAddress(
symfile->ResolveSymbolContext(so_addr, resolve_scope, sc);
if ((resolve_scope & eSymbolContextLineEntry) && sc.line_entry.IsValid())
- sc.line_entry.ApplyFileMappings(sc.target_sp);
+ sc.line_entry.ApplyFileMappings(sc.target_sp, so_addr);
}
// Resolve the symbol if requested, but don't re-look it up if we've
diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp
index 64130d700a006..5b8bcc7cc68ef 100644
--- a/lldb/source/Core/PluginManager.cpp
+++ b/lldb/source/Core/PluginManager.cpp
@@ -1476,18 +1476,21 @@ struct SymbolLocatorInstance
SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file,
SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file,
SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle,
+ SymbolLocatorLocateSourceFile locate_source_file,
DebuggerInitializeCallback debugger_init_callback)
: PluginInstance<SymbolLocatorCreateInstance>(
name, description, create_callback, debugger_init_callback),
locate_executable_object_file(locate_executable_object_file),
locate_executable_symbol_file(locate_executable_symbol_file),
download_object_symbol_file(download_object_symbol_file),
- find_symbol_file_in_bundle(find_symbol_file_in_bundle) {}
+ find_symbol_file_in_bundle(find_symbol_file_in_bundle),
+ locate_source_file(locate_source_file) {}
SymbolLocatorLocateExecutableObjectFile locate_executable_object_file;
SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file;
SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file;
SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle;
+ SymbolLocatorLocateSourceFile locate_source_file;
};
typedef PluginInstances<SymbolLocatorInstance> SymbolLocatorInstances;
@@ -1503,11 +1506,12 @@ bool PluginManager::RegisterPlugin(
SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file,
SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file,
SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle,
+ SymbolLocatorLocateSourceFile locate_source_file,
DebuggerInitializeCallback debugger_init_callback) {
return GetSymbolLocatorInstances().RegisterPlugin(
name, description, create_callback, locate_executable_object_file,
locate_executable_symbol_file, download_object_symbol_file,
- find_symbol_file_in_bundle, debugger_init_callback);
+ find_symbol_file_in_bundle, locate_source_file, debugger_init_callback);
}
bool PluginManager::UnregisterPlugin(
@@ -1591,6 +1595,20 @@ FileSpec PluginManager::FindSymbolFileInBundle(const FileSpec &symfile_bundle,
return {};
}
+FileSpec PluginManager::LocateSourceFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &original_source_file) {
+ auto instances = GetSymbolLocatorInstances().GetSnapshot();
+ for (auto &instance : instances) {
+ if (instance.locate_source_file) {
+ std::optional<FileSpec> result =
+ instance.locate_source_file(module_sp, original_source_file);
+ if (result)
+ return *result;
+ }
+ }
+ return {};
+}
+
#pragma mark Trace
struct TraceInstance
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
index 50569cdefaafa..ddffff08095fb 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
@@ -26,6 +26,7 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
ScriptedFrameProviderPythonInterface.cpp
ScriptedPlatformPythonInterface.cpp
ScriptedProcessPythonInterface.cpp
+ ScriptedSymbolLocatorPythonInterface.cpp
ScriptedPythonInterface.cpp
ScriptedStopHookPythonInterface.cpp
ScriptedBreakpointPythonInterface.cpp
@@ -42,5 +43,3 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
${Python3_LIBRARIES}
${LLDB_LIBEDIT_LIBS}
)
-
-
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
index 721902ec1e253..52827d01b2495 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
@@ -22,6 +22,7 @@
#include "ScriptedPlatformPythonInterface.h"
#include "ScriptedProcessPythonInterface.h"
#include "ScriptedStopHookPythonInterface.h"
+#include "ScriptedSymbolLocatorPythonInterface.h"
#include "ScriptedThreadPlanPythonInterface.h"
namespace lldb_private {
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index 4aadee584b2e2..82e6f239a3b68 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -632,6 +632,10 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return python::SWIGBridge::ToSWIGWrapper(arg);
}
+ python::PythonObject Transform(lldb::ModuleSP arg) {
+ return python::SWIGBridge::ToSWIGWrapper(arg);
+ }
+
python::PythonObject Transform(Event *arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp
new file mode 100644
index 0000000000000..f0027bb7d386c
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp
@@ -0,0 +1,278 @@
+//===-- ScriptedSymbolLocatorPythonInterface.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 "lldb/Host/Config.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/lldb-enumerations.h"
+
+#if LLDB_ENABLE_PYTHON
+
+// clang-format off
+// LLDB Python header must be included first
+#include "../lldb-python.h"
+// clang-format on
+
+#include "../SWIGPythonBridge.h"
+#include "../ScriptInterpreterPythonImpl.h"
+#include "ScriptedSymbolLocatorPythonInterface.h"
+
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBModuleSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+ScriptedSymbolLocatorPythonInterface::ScriptedSymbolLocatorPythonInterface(
+ ScriptInterpreterPythonImpl &interpreter)
+ : ScriptedSymbolLocatorInterface(), ScriptedPythonInterface(interpreter) {}
+
+llvm::Expected<StructuredData::GenericSP>
+ScriptedSymbolLocatorPythonInterface::CreatePluginObject(
+ const llvm::StringRef class_name, ExecutionContext &exe_ctx,
+ StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) {
+ ExecutionContextRefSP exe_ctx_ref_sp =
+ std::make_shared<ExecutionContextRef>(exe_ctx);
+ StructuredDataImpl sd_impl(args_sp);
+ return ScriptedPythonInterface::CreatePluginObject(class_name, script_obj,
+ exe_ctx_ref_sp, sd_impl);
+}
+
+/// Helper to convert an internal ModuleSpec to a Python SBModuleSpec object.
+/// Must be called with the GIL held.
+static PythonObject ToSWIGModuleSpec(const ModuleSpec &module_spec) {
+ // Build an SBModuleSpec using public API setters since the constructor
+ // from ModuleSpec is private.
+ SBModuleSpec sb_module_spec;
+
+ const UUID &uuid = module_spec.GetUUID();
+ if (uuid.IsValid())
+ sb_module_spec.SetUUIDBytes(uuid.GetBytes().data(), uuid.GetBytes().size());
+
+ const FileSpec &file = module_spec.GetFileSpec();
+ if (file)
+ sb_module_spec.SetFileSpec(SBFileSpec(file.GetPath().c_str(), false));
+
+ const FileSpec &platform_file = module_spec.GetPlatformFileSpec();
+ if (platform_file)
+ sb_module_spec.SetPlatformFileSpec(
+ SBFileSpec(platform_file.GetPath().c_str(), false));
+
+ const FileSpec &symbol_file = module_spec.GetSymbolFileSpec();
+ if (symbol_file)
+ sb_module_spec.SetSymbolFileSpec(
+ SBFileSpec(symbol_file.GetPath().c_str(), false));
+
+ const ArchSpec &arch = module_spec.GetArchitecture();
+ if (arch.IsValid())
+ sb_module_spec.SetTriple(arch.GetTriple().getTriple().c_str());
+
+ ConstString object_name = module_spec.GetObjectName();
+ if (object_name)
+ sb_module_spec.SetObjectName(object_name.GetCString());
+
+ sb_module_spec.SetObjectOffset(module_spec.GetObjectOffset());
+ sb_module_spec.SetObjectSize(module_spec.GetObjectSize());
+
+ return SWIGBridge::ToSWIGWrapper(
+ std::make_unique<SBModuleSpec>(sb_module_spec));
+}
+
+/// Helper to convert an internal FileSpec to a Python SBFileSpec object.
+/// Must be called with the GIL held.
+static PythonObject ToSWIGFileSpec(const FileSpec &file_spec) {
+ return SWIGBridge::ToSWIGWrapper(
+ std::make_unique<SBFileSpec>(file_spec.GetPath().c_str(), false));
+}
+
+std::optional<ModuleSpec>
+ScriptedSymbolLocatorPythonInterface::LocateExecutableObjectFile(
+ const ModuleSpec &module_spec, Status &error) {
+ if (!m_object_instance_sp)
+ return {};
+
+ // Acquire the GIL before creating any Python objects.
+ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)m_object_instance_sp->GetValue());
+ if (!implementor.IsAllocated())
+ return {};
+
+ PythonObject py_module_spec = ToSWIGModuleSpec(module_spec);
+
+ auto expected =
+ implementor.CallMethod("locate_executable_object_file", py_module_spec);
+ if (!expected) {
+ // Consume the PythonException while the GIL is held. Converting to string
+ // forces PythonException destruction before the GIL is released.
+ std::string msg = llvm::toString(expected.takeError());
+ error = Status(msg);
+ return {};
+ }
+
+ PythonObject py_return = std::move(*expected);
+ if (!py_return.IsAllocated() || py_return.IsNone())
+ return {};
+
+ auto obj = py_return.CreateStructuredObject();
+ if (!obj)
+ return {};
+
+ llvm::StringRef value = obj->GetStringValue();
+ if (value.empty())
+ return {};
+
+ ModuleSpec result_spec(module_spec);
+ result_spec.GetFileSpec().SetPath(value);
+ return result_spec;
+}
+
+std::optional<FileSpec>
+ScriptedSymbolLocatorPythonInterface::LocateExecutableSymbolFile(
+ const ModuleSpec &module_spec, const FileSpecList &default_search_paths,
+ Status &error) {
+ if (!m_object_instance_sp)
+ return {};
+
+ // Acquire the GIL before creating any Python objects.
+ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)m_object_instance_sp->GetValue());
+ if (!implementor.IsAllocated())
+ return {};
+
+ PythonObject py_module_spec = ToSWIGModuleSpec(module_spec);
+
+ // Convert FileSpecList to a Python list of SBFileSpec.
+ PythonList py_paths(default_search_paths.GetSize());
+ for (size_t i = 0; i < default_search_paths.GetSize(); i++) {
+ py_paths.SetItemAtIndex(
+ i, ToSWIGFileSpec(default_search_paths.GetFileSpecAtIndex(i)));
+ }
+
+ auto expected = implementor.CallMethod("locate_executable_symbol_file",
+ py_module_spec, py_paths);
+ if (!expected) {
+ std::string msg = llvm::toString(expected.takeError());
+ error = Status(msg);
+ return {};
+ }
+
+ PythonObject py_return = std::move(*expected);
+ if (!py_return.IsAllocated() || py_return.IsNone())
+ return {};
+
+ auto obj = py_return.CreateStructuredObject();
+ if (!obj)
+ return {};
+
+ llvm::StringRef value = obj->GetStringValue();
+ if (value.empty())
+ return {};
+
+ FileSpec file_spec;
+ file_spec.SetPath(value);
+ return file_spec;
+}
+
+bool ScriptedSymbolLocatorPythonInterface::DownloadObjectAndSymbolFile(
+ ModuleSpec &module_spec, Status &error, bool force_lookup,
+ bool copy_executable) {
+ if (!m_object_instance_sp)
+ return false;
+
+ // Acquire the GIL before creating any Python objects.
+ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)m_object_instance_sp->GetValue());
+ if (!implementor.IsAllocated())
+ return false;
+
+ PythonObject py_module_spec = ToSWIGModuleSpec(module_spec);
+
+ auto expected = implementor.CallMethod(
+ "download_object_and_symbol_file", py_module_spec,
+ PythonBoolean(force_lookup), PythonBoolean(copy_executable));
+ if (!expected) {
+ std::string msg = llvm::toString(expected.takeError());
+ error = Status(msg);
+ return false;
+ }
+
+ PythonObject py_return = std::move(*expected);
+ if (!py_return.IsAllocated() || py_return.IsNone())
+ return false;
+
+ auto obj = py_return.CreateStructuredObject();
+ if (!obj)
+ return false;
+
+ return obj->GetBooleanValue();
+}
+
+std::optional<FileSpec> ScriptedSymbolLocatorPythonInterface::LocateSourceFile(
+ const lldb::ModuleSP &module_sp, const FileSpec &original_source_file,
+ Status &error) {
+ if (!m_object_instance_sp)
+ return {};
+
+ std::optional<FileSpec> result;
+
+ {
+ // Acquire the GIL before creating any Python objects. All Python objects
+ // (including error objects) must be destroyed within this scope.
+ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)m_object_instance_sp->GetValue());
+ if (!implementor.IsAllocated())
+ return {};
+
+ PythonObject py_module = SWIGBridge::ToSWIGWrapper(module_sp);
+ std::string source_path = original_source_file.GetPath();
+ PythonString py_source_path(source_path);
+
+ auto expected =
+ implementor.CallMethod("locate_source_file", py_module, py_source_path);
+ if (!expected) {
+ // Consume the error (which may contain PythonException) while the GIL
+ // is still held. Convert to string to force PythonException destruction
+ // before the GIL is released.
+ std::string msg = llvm::toString(expected.takeError());
+ error = Status(msg);
+ return {};
+ }
+
+ PythonObject py_return = std::move(*expected);
+ if (py_return.IsAllocated() && !py_return.IsNone()) {
+ auto obj = py_return.CreateStructuredObject();
+ if (obj) {
+ llvm::StringRef value = obj->GetStringValue();
+ if (!value.empty()) {
+ FileSpec file_spec;
+ file_spec.SetPath(value);
+ result = file_spec;
+ }
+ }
+ }
+ } // GIL released here, after all Python objects are destroyed.
+
+ return result;
+}
+
+#endif // LLDB_ENABLE_PYTHON
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h
new file mode 100644
index 0000000000000..35b02a0e56c65
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h
@@ -0,0 +1,69 @@
+//===-- ScriptedSymbolLocatorPythonInterface.h -------------------*- C++
+//-*-===//
+//
+// 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_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSYMBOLLOCATORPYTHONINTERFACE_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSYMBOLLOCATORPYTHONINTERFACE_H
+
+#include "lldb/Host/Config.h"
+#include "lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "ScriptedPythonInterface.h"
+
+#include <optional>
+
+namespace lldb_private {
+class ScriptedSymbolLocatorPythonInterface
+ : public ScriptedSymbolLocatorInterface,
+ public ScriptedPythonInterface,
+ public PluginInterface {
+public:
+ ScriptedSymbolLocatorPythonInterface(
+ ScriptInterpreterPythonImpl &interpreter);
+
+ llvm::Expected<StructuredData::GenericSP>
+ CreatePluginObject(const llvm::StringRef class_name,
+ ExecutionContext &exe_ctx,
+ StructuredData::DictionarySP args_sp,
+ StructuredData::Generic *script_obj = nullptr) override;
+
+ llvm::SmallVector<AbstractMethodRequirement>
+ GetAbstractMethodRequirements() const override {
+ return llvm::SmallVector<AbstractMethodRequirement>(
+ {{"locate_source_file", 2}});
+ }
+
+ std::optional<ModuleSpec>
+ LocateExecutableObjectFile(const ModuleSpec &module_spec,
+ Status &error) override;
+
+ std::optional<FileSpec>
+ LocateExecutableSymbolFile(const ModuleSpec &module_spec,
+ const FileSpecList &default_search_paths,
+ Status &error) override;
+
+ bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec, Status &error,
+ bool force_lookup,
+ bool copy_executable) override;
+
+ std::optional<FileSpec> LocateSourceFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &original_source_file,
+ Status &error) override;
+
+ static llvm::StringRef GetPluginNameStatic() {
+ return "ScriptedSymbolLocatorPythonInterface";
+ }
+
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+};
+} // namespace lldb_private
+
+#endif // LLDB_ENABLE_PYTHON
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSYMBOLLOCATORPYTHONINTERFACE_H
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 35a772c1454df..1346f496b0e07 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1532,6 +1532,11 @@ ScriptInterpreterPythonImpl::CreateScriptedFrameProviderInterface() {
return std::make_shared<ScriptedFrameProviderPythonInterface>(*this);
}
+ScriptedSymbolLocatorInterfaceSP
+ScriptInterpreterPythonImpl::CreateScriptedSymbolLocatorInterface() {
+ return std::make_shared<ScriptedSymbolLocatorPythonInterface>(*this);
+}
+
ScriptedThreadPlanInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() {
return std::make_shared<ScriptedThreadPlanPythonInterface>(*this);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index 1eac78e6360f2..b301add60d754 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -104,6 +104,9 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
lldb::ScriptedFrameProviderInterfaceSP
CreateScriptedFrameProviderInterface() override;
+ lldb::ScriptedSymbolLocatorInterfaceSP
+ CreateScriptedSymbolLocatorInterface() override;
+
lldb::ScriptedThreadPlanInterfaceSP
CreateScriptedThreadPlanInterface() override;
@@ -202,7 +205,7 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
bool GetLongHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp,
std::string &dest) override;
-
+
StructuredData::ObjectSP
GetOptionsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override;
@@ -211,7 +214,7 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
bool SetOptionValueForCommandObject(StructuredData::GenericSP cmd_obj_sp,
ExecutionContext *exe_ctx,
- llvm::StringRef long_option,
+ llvm::StringRef long_option,
llvm::StringRef value) override;
void OptionParsingStartedForCommandObject(
diff --git a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
index 3b466f71dca58..bf7f6046eed9d 100644
--- a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
+++ b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
@@ -7,6 +7,7 @@ set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND SymbolLocator)
# provider.
add_subdirectory(Debuginfod)
add_subdirectory(Default)
+add_subdirectory(Scripted)
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_subdirectory(DebugSymbols)
endif()
diff --git a/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp b/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp
index a09bb356e3a8c..bdef57f0671e1 100644
--- a/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp
+++ b/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp
@@ -111,7 +111,7 @@ void SymbolLocatorDebuginfod::Initialize() {
PluginManager::RegisterPlugin(
GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
LocateExecutableObjectFile, LocateExecutableSymbolFile, nullptr,
- nullptr, SymbolLocatorDebuginfod::DebuggerInitialize);
+ nullptr, nullptr, SymbolLocatorDebuginfod::DebuggerInitialize);
llvm::HTTPClient::initialize();
});
}
diff --git a/lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt
new file mode 100644
index 0000000000000..79eff43dd5e22
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt
@@ -0,0 +1,24 @@
+lldb_tablegen(SymbolLocatorScriptedProperties.inc -gen-lldb-property-defs
+ SOURCE SymbolLocatorScriptedProperties.td
+ TARGET LLDBPluginSymbolLocatorScriptedPropertiesGen)
+
+lldb_tablegen(SymbolLocatorScriptedPropertiesEnum.inc -gen-lldb-property-enum-defs
+ SOURCE SymbolLocatorScriptedProperties.td
+ TARGET LLDBPluginSymbolLocatorScriptedPropertiesEnumGen)
+
+set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND SymbolLocator)
+
+add_lldb_library(lldbPluginSymbolLocatorScripted PLUGIN
+ SymbolLocatorScripted.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbHost
+ lldbInterpreter
+ lldbSymbol
+ lldbUtility
+ )
+
+add_dependencies(lldbPluginSymbolLocatorScripted
+ LLDBPluginSymbolLocatorScriptedPropertiesGen
+ LLDBPluginSymbolLocatorScriptedPropertiesEnumGen)
diff --git a/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp
new file mode 100644
index 0000000000000..2a65e1159d9bc
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp
@@ -0,0 +1,271 @@
+//===-- SymbolLocatorScripted.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 "SymbolLocatorScripted.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include <unordered_map>
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(SymbolLocatorScripted)
+
+namespace {
+
+#define LLDB_PROPERTIES_symbollocatorscripted
+#include "SymbolLocatorScriptedProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_symbollocatorscripted
+#include "SymbolLocatorScriptedPropertiesEnum.inc"
+};
+
+class PluginProperties : public Properties {
+public:
+ static llvm::StringRef GetSettingName() {
+ return SymbolLocatorScripted::GetPluginNameStatic();
+ }
+
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_symbollocatorscripted_properties_def);
+
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyScriptClass, [this] { ScriptClassChangedCallback(); });
+ }
+
+ llvm::StringRef GetScriptClassName() const {
+ const OptionValueString *s =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueString(
+ ePropertyScriptClass);
+ if (s)
+ return s->GetCurrentValueAsRef();
+ return {};
+ }
+
+ ScriptedSymbolLocatorInterfaceSP GetInterface() const {
+ return m_interface_sp;
+ }
+
+ void SetInterface(ScriptedSymbolLocatorInterfaceSP interface_sp) {
+ m_interface_sp = interface_sp;
+ }
+
+ /// Look up a previously cached source file resolution result.
+ /// Returns true if a cached entry exists (even if the result is nullopt).
+ bool LookupSourceFileCache(const std::string &key,
+ std::optional<FileSpec> &result) {
+ auto it = m_source_file_cache.find(key);
+ if (it != m_source_file_cache.end()) {
+ result = it->second;
+ return true;
+ }
+ return false;
+ }
+
+ void InsertSourceFileCache(const std::string &key,
+ const std::optional<FileSpec> &result) {
+ m_source_file_cache[key] = result;
+ }
+
+private:
+ void ScriptClassChangedCallback() {
+ // Invalidate the cached interface and source file cache when the user
+ // changes the script class.
+ m_interface_sp.reset();
+ m_source_file_cache.clear();
+ }
+
+ ScriptedSymbolLocatorInterfaceSP m_interface_sp;
+ std::unordered_map<std::string, std::optional<FileSpec>> m_source_file_cache;
+};
+
+} // namespace
+
+static PluginProperties &GetGlobalPluginProperties() {
+ static PluginProperties g_settings;
+ return g_settings;
+}
+
+static ScriptedSymbolLocatorInterfaceSP GetOrCreateInterface() {
+ PluginProperties &props = GetGlobalPluginProperties();
+
+ llvm::StringRef class_name = props.GetScriptClassName();
+ if (class_name.empty())
+ return {};
+
+ // Return the cached interface if available.
+ auto interface_sp = props.GetInterface();
+ if (interface_sp)
+ return interface_sp;
+
+ // Find a debugger with a script interpreter.
+ DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(0);
+ if (!debugger_sp)
+ return {};
+
+ ScriptInterpreter *interpreter = debugger_sp->GetScriptInterpreter();
+ if (!interpreter)
+ return {};
+
+ interface_sp = interpreter->CreateScriptedSymbolLocatorInterface();
+ if (!interface_sp)
+ return {};
+
+ // Create the Python script object from the user's class.
+ ExecutionContext exe_ctx;
+ StructuredData::DictionarySP args_sp;
+ auto obj_or_err =
+ interface_sp->CreatePluginObject(class_name, exe_ctx, args_sp);
+
+ if (!obj_or_err) {
+ Log *log = GetLog(LLDBLog::Symbols);
+ LLDB_LOG_ERROR(log, obj_or_err.takeError(),
+ "SymbolLocatorScripted: failed to create Python object for "
+ "class '{0}': {1}",
+ class_name);
+ return {};
+ }
+
+ props.SetInterface(interface_sp);
+ return interface_sp;
+}
+
+SymbolLocatorScripted::SymbolLocatorScripted() : SymbolLocator() {}
+
+void SymbolLocatorScripted::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
+ LocateExecutableObjectFile, LocateExecutableSymbolFile,
+ DownloadObjectAndSymbolFile, nullptr, LocateSourceFile,
+ SymbolLocatorScripted::DebuggerInitialize);
+}
+
+void SymbolLocatorScripted::Terminate() {
+ GetGlobalPluginProperties().SetInterface(nullptr);
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+void SymbolLocatorScripted::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForSymbolLocatorPlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForSymbolLocatorPlugin(
+ debugger, GetGlobalPluginProperties().GetValueProperties(),
+ "Properties for the Scripted Symbol Locator plug-in.",
+ is_global_setting);
+ }
+}
+
+llvm::StringRef SymbolLocatorScripted::GetPluginDescriptionStatic() {
+ return "Scripted symbol locator plug-in.";
+}
+
+SymbolLocator *SymbolLocatorScripted::CreateInstance() {
+ return new SymbolLocatorScripted();
+}
+
+std::optional<ModuleSpec> SymbolLocatorScripted::LocateExecutableObjectFile(
+ const ModuleSpec &module_spec) {
+ auto interface_sp = GetOrCreateInterface();
+ if (!interface_sp)
+ return {};
+ Status error;
+ auto result = interface_sp->LocateExecutableObjectFile(module_spec, error);
+ if (!error.Success()) {
+ Log *log = GetLog(LLDBLog::Symbols);
+ LLDB_LOG(log,
+ "SymbolLocatorScripted: locate_executable_object_file "
+ "failed: {0}",
+ error);
+ }
+ return result;
+}
+
+std::optional<FileSpec> SymbolLocatorScripted::LocateExecutableSymbolFile(
+ const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
+ auto interface_sp = GetOrCreateInterface();
+ if (!interface_sp)
+ return {};
+ Status error;
+ auto result = interface_sp->LocateExecutableSymbolFile(
+ module_spec, default_search_paths, error);
+ if (!error.Success()) {
+ Log *log = GetLog(LLDBLog::Symbols);
+ LLDB_LOG(log,
+ "SymbolLocatorScripted: locate_executable_symbol_file "
+ "failed: {0}",
+ error);
+ }
+ return result;
+}
+
+bool SymbolLocatorScripted::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
+ Status &error,
+ bool force_lookup,
+ bool copy_executable) {
+ auto interface_sp = GetOrCreateInterface();
+ if (!interface_sp)
+ return false;
+ return interface_sp->DownloadObjectAndSymbolFile(
+ module_spec, error, force_lookup, copy_executable);
+}
+
+std::optional<FileSpec>
+SymbolLocatorScripted::LocateSourceFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &original_source_file) {
+ if (!module_sp)
+ return {};
+
+ PluginProperties &props = GetGlobalPluginProperties();
+
+ // Cache resolved source files to avoid repeated Python calls for the same
+ // (module, source_file) pair.
+ std::string cache_key =
+ module_sp->GetUUID().GetAsString() + ":" + original_source_file.GetPath();
+
+ std::optional<FileSpec> cached;
+ if (props.LookupSourceFileCache(cache_key, cached))
+ return cached;
+
+ auto interface_sp = GetOrCreateInterface();
+ if (!interface_sp) {
+ props.InsertSourceFileCache(cache_key, std::nullopt);
+ return {};
+ }
+
+ Status error;
+ auto located =
+ interface_sp->LocateSourceFile(module_sp, original_source_file, error);
+
+ if (!error.Success()) {
+ Log *log = GetLog(LLDBLog::Symbols);
+ LLDB_LOG(log, "SymbolLocatorScripted: locate_source_file failed: {0}",
+ error);
+ }
+
+ props.InsertSourceFileCache(cache_key, located);
+
+ if (located) {
+ Log *log = GetLog(LLDBLog::Symbols);
+ LLDB_LOGF(log,
+ "SymbolLocatorScripted::%s: resolved source file '%s' to '%s'",
+ __FUNCTION__, original_source_file.GetPath().c_str(),
+ located->GetPath().c_str());
+ }
+ return located;
+}
diff --git a/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h
new file mode 100644
index 0000000000000..845200c2c715f
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h
@@ -0,0 +1,57 @@
+//===-- SymbolLocatorScripted.h --------------------------------*- C++ -*-===//
+//
+// 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_SCRIPTED_SYMBOLLOCATORSCRIPTED_H
+#define LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_SCRIPTED_SYMBOLLOCATORSCRIPTED_H
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Symbol/SymbolLocator.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class SymbolLocatorScripted : public SymbolLocator {
+public:
+ SymbolLocatorScripted();
+
+ static void Initialize();
+ static void Terminate();
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static llvm::StringRef GetPluginNameStatic() { return "scripted"; }
+ static llvm::StringRef GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolLocator *CreateInstance();
+
+ /// PluginInterface protocol.
+ /// \{
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+ /// \}
+
+ // Locate the executable file given a module specification.
+ static std::optional<ModuleSpec>
+ LocateExecutableObjectFile(const ModuleSpec &module_spec);
+
+ // Locate the symbol file given a module specification.
+ static std::optional<FileSpec>
+ LocateExecutableSymbolFile(const ModuleSpec &module_spec,
+ const FileSpecList &default_search_paths);
+
+ static bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
+ Status &error, bool force_lookup,
+ bool copy_executable);
+
+ // Locate the source file given a module and original source file path.
+ static std::optional<FileSpec>
+ LocateSourceFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &original_source_file);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_SCRIPTED_SYMBOLLOCATORSCRIPTED_H
diff --git a/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScriptedProperties.td b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScriptedProperties.td
new file mode 100644
index 0000000000000..daec59eb07c20
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScriptedProperties.td
@@ -0,0 +1,7 @@
+include "../../../../include/lldb/Core/PropertiesBase.td"
+
+let Definition = "symbollocatorscripted", Path = "plugin.symbol-locator.scripted" in {
+ def ScriptClass : Property<"script-class", "String">,
+ DefaultStringValue<"">,
+ Desc<"The name of the Python class that implements the scripted symbol locator. The class should implement methods like locate_source_file(module_spec, original_source_file) to resolve source files.">;
+}
diff --git a/lldb/source/Symbol/LineEntry.cpp b/lldb/source/Symbol/LineEntry.cpp
index dcfbac8789863..d246b4f82efc8 100644
--- a/lldb/source/Symbol/LineEntry.cpp
+++ b/lldb/source/Symbol/LineEntry.cpp
@@ -7,6 +7,9 @@
//===----------------------------------------------------------------------===//
#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
@@ -242,8 +245,25 @@ AddressRange LineEntry::GetSameLineContiguousAddressRange(
return complete_line_range;
}
-void LineEntry::ApplyFileMappings(lldb::TargetSP target_sp) {
+void LineEntry::ApplyFileMappings(lldb::TargetSP target_sp,
+ const Address &address) {
if (target_sp) {
+ // Try to resolve the source file via SymbolLocator plugins (e.g.,
+ // ScriptedSymbolLocator). This allows users to fetch source files
+ // by build ID from remote servers.
+ // Use Address::GetModule() directly to avoid re-entering
+ // ResolveSymbolContextForAddress which would cause infinite recursion.
+ lldb::ModuleSP module_sp = address.GetModule();
+ if (module_sp) {
+ FileSpec resolved = PluginManager::LocateSourceFile(
+ module_sp, original_file_sp->GetSpecOnly());
+ if (resolved) {
+ original_file_sp = std::make_shared<SupportFile>(resolved);
+ file_sp = std::make_shared<SupportFile>(resolved);
+ return;
+ }
+ }
+
// Apply any file remappings to our file.
if (auto new_file_spec = target_sp->GetSourcePathMap().FindFile(
original_file_sp->GetSpecOnly())) {
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 340607e14abed..656e68a9dd511 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -412,7 +412,7 @@ StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) {
if ((resolved & eSymbolContextLineEntry) &&
!m_sc.line_entry.IsValid()) {
m_sc.line_entry = sc.line_entry;
- m_sc.line_entry.ApplyFileMappings(m_sc.target_sp);
+ m_sc.line_entry.ApplyFileMappings(m_sc.target_sp, lookup_addr);
}
}
} else {
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index 4f4b06f30460b..b46916a9af35e 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -562,7 +562,8 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx,
while (unwind_sc.GetParentOfInlinedScope(
curr_frame_address, next_frame_sc, next_frame_address)) {
- next_frame_sc.line_entry.ApplyFileMappings(target_sp);
+ next_frame_sc.line_entry.ApplyFileMappings(target_sp,
+ curr_frame_address);
behaves_like_zeroth_frame = false;
StackFrameSP frame_sp(new StackFrame(
m_thread.shared_from_this(), m_frames.size(), idx,
diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp
index 3a9deb6f5c6fd..a536d0ace7e53 100644
--- a/lldb/source/Target/ThreadPlanStepRange.cpp
+++ b/lldb/source/Target/ThreadPlanStepRange.cpp
@@ -106,7 +106,7 @@ bool ThreadPlanStepRange::InRange() {
size_t num_ranges = m_address_ranges.size();
for (size_t i = 0; i < num_ranges; i++) {
- ret_value =
+ ret_value =
m_address_ranges[i].ContainsLoadAddress(pc_load_addr, &GetTarget());
if (ret_value)
break;
@@ -340,7 +340,7 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() {
// clear the m_found_calls, we'll rediscover it for this range.
m_found_calls = false;
-
+
lldb::addr_t cur_addr = GetThread().GetRegisterContext()->GetPC();
// Find the current address in our address ranges, and fetch the disassembly
// if we haven't already:
@@ -432,8 +432,8 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() {
std::make_shared<SupportFile>(call_site_file_spec);
top_most_line_entry.range = range;
top_most_line_entry.file_sp = std::make_shared<SupportFile>();
- top_most_line_entry.ApplyFileMappings(
- GetThread().CalculateTarget());
+ top_most_line_entry.ApplyFileMappings(GetThread().CalculateTarget(),
+ range.GetBaseAddress());
if (!top_most_line_entry.file_sp->GetSpecOnly())
top_most_line_entry.file_sp =
top_most_line_entry.original_file_sp;
@@ -551,7 +551,7 @@ bool ThreadPlanStepRange::IsPlanStale() {
lldb::addr_t addr = GetThread().GetRegisterContext()->GetPC() - 1;
size_t num_ranges = m_address_ranges.size();
for (size_t i = 0; i < num_ranges; i++) {
- bool in_range =
+ bool in_range =
m_address_ranges[i].ContainsLoadAddress(addr, &GetTarget());
if (in_range) {
SetPlanComplete();
diff --git a/lldb/test/API/functionalities/scripted_symbol_locator/Makefile b/lldb/test/API/functionalities/scripted_symbol_locator/Makefile
new file mode 100644
index 0000000000000..36728cd2e597b
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_symbol_locator/Makefile
@@ -0,0 +1,4 @@
+C_SOURCES := main.c
+USE_SYSTEM_STDLIB := 1
+LD_EXTRAS := -Wl,--build-id
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py b/lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py
new file mode 100644
index 0000000000000..7bded9bdbab96
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py
@@ -0,0 +1,156 @@
+"""
+Test the ScriptedSymbolLocator plugin for source file resolution.
+"""
+
+import os
+import shutil
+import tempfile
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class ScriptedSymbolLocatorTestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.main_source_file = lldb.SBFileSpec("main.c")
+
+ def import_locator(self):
+ self.runCmd(
+ "command script import "
+ + os.path.join(self.getSourceDir(), "source_locator.py")
+ )
+
+ def set_locator_class(self, class_name):
+ self.runCmd(
+ "settings set plugin.symbol-locator.scripted.script-class " + class_name
+ )
+
+ def clear_locator_class(self):
+ self.runCmd('settings set plugin.symbol-locator.scripted.script-class ""')
+
+ def script(self, expr):
+ """Execute a Python expression in LLDB's script interpreter and return
+ the result as a string."""
+ ret = lldb.SBCommandReturnObject()
+ self.dbg.GetCommandInterpreter().HandleCommand("script " + expr, ret)
+ return ret.GetOutput().strip() if ret.Succeeded() else ""
+
+ @skipUnlessPlatform(["linux", "freebsd"])
+ def test_locate_source_file(self):
+ """Test that the scripted locator resolves source files and receives
+ an SBModule with a valid UUID."""
+ self.build()
+
+ # Copy main.c to a temp directory so the locator can "resolve" to it.
+ tmp_dir = tempfile.mkdtemp()
+ self.addTearDownHook(lambda: shutil.rmtree(tmp_dir))
+ shutil.copy(os.path.join(self.getSourceDir(), "main.c"), tmp_dir)
+
+ # Create the target BEFORE setting the script class, so module loading
+ # (which may run on worker threads) does not trigger the Python locator.
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target and target.IsValid(), VALID_TARGET)
+
+ # Now set up the scripted locator. LocateSourceFile will only be called
+ # from the main thread when we access a frame's line entry.
+ self.import_locator()
+ self.script("source_locator.SourceLocator.resolved_dir = '%s'" % tmp_dir)
+ self.set_locator_class("source_locator.SourceLocator")
+ self.addTearDownHook(lambda: self.clear_locator_class())
+
+ bp = target.BreakpointCreateByName("func")
+ self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
+ self.assertEqual(bp.GetNumLocations(), 1)
+
+ # Launch and stop at the breakpoint so ApplyFileMappings runs on
+ # the main thread via StackFrame::GetSymbolContext.
+ process = target.LaunchSimple(None, None, os.getcwd())
+ self.assertIsNotNone(process)
+ self.assertState(process.GetState(), lldb.eStateStopped)
+
+ thread = process.GetSelectedThread()
+ frame = thread.GetSelectedFrame()
+ line_entry = frame.GetLineEntry()
+ self.assertTrue(line_entry and line_entry.IsValid(), "Line entry is valid")
+ self.assertEqual(line_entry.GetFileSpec().GetFilename(), "main.c")
+
+ # Verify the resolved path points to our temp directory.
+ resolved_dir = line_entry.GetFileSpec().GetDirectory()
+ self.assertEqual(resolved_dir, tmp_dir)
+
+ # Verify the locator was called with a valid UUID by reading calls
+ # from LLDB's Python namespace.
+ calls_str = self.script("source_locator.SourceLocator.calls")
+ self.assertIn("main.c", calls_str, "Locator should have been called")
+
+ self.dbg.DeleteTarget(target)
+
+ @skipUnlessPlatform(["linux", "freebsd"])
+ def test_locate_source_file_none_fallthrough(self):
+ """Test that returning None falls through to normal LLDB resolution,
+ and that having no script class set also works normally."""
+ self.build()
+
+ # First: test with NoneLocator -- should fall through.
+ self.import_locator()
+ self.set_locator_class("source_locator.NoneLocator")
+ self.addTearDownHook(lambda: self.clear_locator_class())
+
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target and target.IsValid(), VALID_TARGET)
+
+ bp = target.BreakpointCreateByName("func")
+ self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
+ self.assertEqual(bp.GetNumLocations(), 1)
+
+ loc = bp.GetLocationAtIndex(0)
+ line_entry = loc.GetAddress().GetLineEntry()
+ self.assertTrue(line_entry and line_entry.IsValid(), "Line entry is valid")
+ self.assertEqual(line_entry.GetFileSpec().GetFilename(), "main.c")
+
+ self.dbg.DeleteTarget(target)
+
+ # Second: test with no script class set -- should also work normally.
+ self.clear_locator_class()
+
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target and target.IsValid(), VALID_TARGET)
+
+ bp = target.BreakpointCreateByName("func")
+ self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
+ self.assertEqual(bp.GetNumLocations(), 1)
+
+ loc = bp.GetLocationAtIndex(0)
+ line_entry = loc.GetAddress().GetLineEntry()
+ self.assertTrue(line_entry and line_entry.IsValid(), "Line entry is valid")
+ self.assertEqual(line_entry.GetFileSpec().GetFilename(), "main.c")
+
+ self.dbg.DeleteTarget(target)
+
+ @skipUnlessPlatform(["linux", "freebsd"])
+ def test_invalid_script_class(self):
+ """Test that an invalid script class name is handled gracefully
+ without crashing, and breakpoints still resolve."""
+ self.build()
+
+ self.set_locator_class("nonexistent_module.NonexistentClass")
+ self.addTearDownHook(lambda: self.clear_locator_class())
+
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target and target.IsValid(), VALID_TARGET)
+
+ # Should not crash -- breakpoint should still resolve via normal path.
+ bp = target.BreakpointCreateByName("func")
+ self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
+ self.assertEqual(bp.GetNumLocations(), 1)
+
+ loc = bp.GetLocationAtIndex(0)
+ line_entry = loc.GetAddress().GetLineEntry()
+ self.assertTrue(line_entry and line_entry.IsValid(), "Line entry is valid")
+
+ self.dbg.DeleteTarget(target)
diff --git a/lldb/test/API/functionalities/scripted_symbol_locator/main.c b/lldb/test/API/functionalities/scripted_symbol_locator/main.c
new file mode 100644
index 0000000000000..71a79cd90f5b2
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_symbol_locator/main.c
@@ -0,0 +1,5 @@
+int func(int argc) {
+ return argc + 1; // break here
+}
+
+int main(int argc, const char *argv[]) { return func(argc); }
diff --git a/lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py b/lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py
new file mode 100644
index 0000000000000..0d314f3914766
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py
@@ -0,0 +1,53 @@
+import os
+
+import lldb
+
+
+class SourceLocator:
+ """Test locator that records calls and returns a configured resolved path."""
+
+ calls = []
+ resolved_dir = None
+
+ def __init__(self, exe_ctx, args):
+ SourceLocator.calls = []
+
+ def locate_source_file(self, module, original_source_file):
+ uuid = module.GetUUIDString()
+ SourceLocator.calls.append((uuid, original_source_file))
+ if SourceLocator.resolved_dir:
+ basename = os.path.basename(original_source_file)
+ return os.path.join(SourceLocator.resolved_dir, basename)
+ return None
+
+ def locate_executable_object_file(self, module_spec):
+ return None
+
+ def locate_executable_symbol_file(self, module_spec, default_search_paths):
+ return None
+
+ def download_object_and_symbol_file(
+ self, module_spec, force_lookup, copy_executable
+ ):
+ return False
+
+
+class NoneLocator:
+ """Locator that always returns None."""
+
+ def __init__(self, exe_ctx, args):
+ pass
+
+ def locate_source_file(self, module, original_source_file):
+ return None
+
+ def locate_executable_object_file(self, module_spec):
+ return None
+
+ def locate_executable_symbol_file(self, module_spec, default_search_paths):
+ return None
+
+ def download_object_and_symbol_file(
+ self, module_spec, force_lookup, copy_executable
+ ):
+ return False
More information about the lldb-commits
mailing list