[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 17:32:23 PST 2026
https://github.com/rchamala updated https://github.com/llvm/llvm-project/pull/181334
>From 776d71339f270d9a4b5691ff980291328ab9fee7 Mon Sep 17 00:00:00 2001
From: Rahul Reddy Chamala <rachamal at fb.com>
Date: Fri, 13 Feb 2026 10:55:09 -0800
Subject: [PATCH] Add ScriptedSymbolLocator plugin with per-target registration
Addresses all review comments:
- Per-target registration via target symbols scripted register/clear/info
- Dispatch pattern instead of manual GIL management
- Base class template, scripting extension, tutorial docs
- ClearStackFrames on registration for post-load locator support
- SB API: RegisterScriptedSymbolLocator / ClearScriptedSymbolLocator
---
lldb/bindings/python/CMakeLists.txt | 1 +
lldb/bindings/python/python-wrapper.swig | 39 ++++
lldb/docs/use/python-reference.rst | 1 +
.../use/tutorials/scripted-symbol-locator.md | 165 +++++++++++++
.../templates/scripted_symbol_locator.py | 220 ++++++++++++++++++
lldb/include/lldb/API/SBFileSpec.h | 5 +
lldb/include/lldb/API/SBModule.h | 1 +
lldb/include/lldb/API/SBModuleSpec.h | 1 +
lldb/include/lldb/API/SBTarget.h | 16 ++
lldb/include/lldb/Core/PluginManager.h | 4 +
.../ScriptedSymbolLocatorInterface.h | 56 +++++
.../lldb/Interpreter/ScriptInterpreter.h | 19 ++
lldb/include/lldb/Symbol/LineEntry.h | 2 +-
lldb/include/lldb/Target/Target.h | 26 +++
lldb/include/lldb/lldb-forward.h | 3 +
lldb/include/lldb/lldb-private-interfaces.h | 2 +
lldb/source/API/SBTarget.cpp | 30 +++
lldb/source/Commands/CommandObjectTarget.cpp | 130 +++++++++++
lldb/source/Core/Module.cpp | 2 +-
lldb/source/Core/PluginManager.cpp | 22 +-
lldb/source/Interpreter/ScriptInterpreter.cpp | 25 ++
.../Python/Interfaces/CMakeLists.txt | 3 +-
.../ScriptInterpreterPythonInterfaces.cpp | 2 +
.../ScriptInterpreterPythonInterfaces.h | 1 +
.../Interfaces/ScriptedPythonInterface.cpp | 61 +++++
.../Interfaces/ScriptedPythonInterface.h | 58 +++++
.../ScriptedSymbolLocatorPythonInterface.cpp | 120 ++++++++++
.../ScriptedSymbolLocatorPythonInterface.h | 71 ++++++
.../Python/SWIGPythonBridge.h | 3 +
.../Python/ScriptInterpreterPython.cpp | 5 +
.../Python/ScriptInterpreterPythonImpl.h | 3 +
.../Plugins/SymbolLocator/CMakeLists.txt | 1 +
.../Debuginfod/SymbolLocatorDebuginfod.cpp | 2 +-
.../SymbolLocator/Scripted/CMakeLists.txt | 13 ++
.../Scripted/SymbolLocatorScripted.cpp | 202 ++++++++++++++++
.../Scripted/SymbolLocatorScripted.h | 55 +++++
lldb/source/Symbol/LineEntry.cpp | 22 +-
lldb/source/Target/StackFrame.cpp | 2 +-
lldb/source/Target/StackFrameList.cpp | 3 +-
lldb/source/Target/Target.cpp | 84 +++++++
lldb/source/Target/ThreadPlanStepRange.cpp | 4 +-
.../scripted_symbol_locator/Makefile | 9 +
.../TestScriptedSymbolLocator.py | 195 ++++++++++++++++
.../scripted_symbol_locator/main.c | 5 +
.../scripted_symbol_locator/source_locator.py | 74 ++++++
.../Python/PythonTestSuite.cpp | 15 ++
46 files changed, 1771 insertions(+), 12 deletions(-)
create mode 100644 lldb/docs/use/tutorials/scripted-symbol-locator.md
create mode 100644 lldb/examples/python/templates/scripted_symbol_locator.py
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/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/bindings/python/CMakeLists.txt b/lldb/bindings/python/CMakeLists.txt
index 2ebcf5a8e7aca..058b3ceb9b038 100644
--- a/lldb/bindings/python/CMakeLists.txt
+++ b/lldb/bindings/python/CMakeLists.txt
@@ -116,6 +116,7 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_platform.py"
"${LLDB_SOURCE_DIR}/examples/python/templates/operating_system.py"
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_thread_plan.py"
+ "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_symbol_locator.py"
)
if(APPLE)
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index bf59569920470..2e391ade67121 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -595,6 +595,45 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *d
return sb_ptr;
}
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFileSpec(PyObject *
+ data) {
+ lldb::SBFileSpec *sb_ptr = NULL;
+
+ int valid_cast = SWIG_ConvertPtr(data, (void **)&sb_ptr,
+ SWIGTYPE_p_lldb__SBFileSpec, 0);
+
+ if (valid_cast == -1)
+ return NULL;
+
+ return sb_ptr;
+}
+
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBModuleSpec(PyObject *
+ data) {
+ lldb::SBModuleSpec *sb_ptr = NULL;
+
+ int valid_cast = SWIG_ConvertPtr(data, (void **)&sb_ptr,
+ SWIGTYPE_p_lldb__SBModuleSpec, 0);
+
+ if (valid_cast == -1)
+ return NULL;
+
+ return sb_ptr;
+}
+
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBModule(PyObject *
+ data) {
+ lldb::SBModule *sb_ptr = NULL;
+
+ int valid_cast = SWIG_ConvertPtr(data, (void **)&sb_ptr,
+ SWIGTYPE_p_lldb__SBModule, 0);
+
+ if (valid_cast == -1)
+ return NULL;
+
+ return sb_ptr;
+}
+
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommand(
const char *python_function_name, const char *session_dictionary_name,
lldb::DebuggerSP debugger, const char *args,
diff --git a/lldb/docs/use/python-reference.rst b/lldb/docs/use/python-reference.rst
index afca07520d8ad..2b345a7cd3bbf 100644
--- a/lldb/docs/use/python-reference.rst
+++ b/lldb/docs/use/python-reference.rst
@@ -28,3 +28,4 @@ The following tutorials and documentation demonstrate various Python capabilitie
tutorials/implementing-standalone-scripts
tutorials/custom-frame-recognizers
tutorials/extending-target-stop-hooks
+ tutorials/scripted-symbol-locator
diff --git a/lldb/docs/use/tutorials/scripted-symbol-locator.md b/lldb/docs/use/tutorials/scripted-symbol-locator.md
new file mode 100644
index 0000000000000..eaba91d9b444b
--- /dev/null
+++ b/lldb/docs/use/tutorials/scripted-symbol-locator.md
@@ -0,0 +1,165 @@
+# Scripted Symbol Locator Tutorial
+
+The **Scripted Symbol Locator** lets you write a Python class that tells LLDB
+where to find executables, symbol files, and source files for your debug
+targets. This is useful when your build artifacts live in a custom location,
+such as a symbol server or a local build-ID-indexed cache.
+
+## Quick Start
+
+1. **Write a locator class.** Create a Python file (e.g., `my_locator.py`)
+ with a class that implements the methods you need:
+
+ ```python
+ import os
+ import lldb
+ from lldb.plugins.scripted_symbol_locator import ScriptedSymbolLocator
+
+ class MyLocator(ScriptedSymbolLocator):
+ def __init__(self, exe_ctx, args):
+ super().__init__(exe_ctx, args)
+ self.cache_dir = None
+ if self.args and self.args.IsValid():
+ d = self.args.GetValueForKey("cache_dir")
+ if d and d.IsValid():
+ self.cache_dir = d.GetStringValue(4096)
+
+ def locate_source_file(self, module, original_source_file):
+ """Return the resolved file spec, or None to fall through."""
+ if not self.cache_dir:
+ return None
+ uuid = module.GetUUIDString()
+ basename = os.path.basename(original_source_file)
+ candidate = os.path.join(self.cache_dir, uuid, "src", basename)
+ if os.path.exists(candidate):
+ return lldb.SBFileSpec(candidate, True)
+ return None
+ ```
+
+2. **Import the script and register the locator on a target:**
+
+ ```
+ (lldb) command script import /path/to/my_locator.py
+ (lldb) target symbols scripted register \
+ -C my_locator.MyLocator \
+ -k cache_dir -v /path/to/cache
+ ```
+
+3. **Debug normally.** When LLDB resolves source files for that target,
+ your `locate_source_file` method will be called automatically.
+
+## Available Methods
+
+Your locator class can implement any combination of these methods. All are
+optional except `__init__` and `locate_source_file` (which is the abstract
+method that must be present).
+
+| Method | Called When |
+|--------|------------|
+| `locate_source_file(module, path)` | LLDB resolves a source file path in debug info |
+| `locate_executable_object_file(module_spec)` | LLDB needs the binary for a module |
+| `locate_executable_symbol_file(module_spec, search_paths)` | LLDB needs separate debug symbols |
+| `download_object_and_symbol_file(module_spec, force, copy)` | Last-resort download from a remote source |
+
+### Method Signatures
+
+```python
+def __init__(self, exe_ctx: lldb.SBExecutionContext,
+ args: lldb.SBStructuredData) -> None:
+ ...
+
+def locate_source_file(self, module: lldb.SBModule,
+ original_source_file: str) -> Optional[lldb.SBFileSpec]:
+ ...
+
+def locate_executable_object_file(
+ self, module_spec: lldb.SBModuleSpec) -> Optional[lldb.SBFileSpec]:
+ ...
+
+def locate_executable_symbol_file(
+ self, module_spec: lldb.SBModuleSpec,
+ default_search_paths: list) -> Optional[lldb.SBFileSpec]:
+ ...
+
+def download_object_and_symbol_file(
+ self, module_spec: lldb.SBModuleSpec,
+ force_lookup: bool, copy_executable: bool) -> bool:
+ ...
+```
+
+## Per-Target Registration
+
+The scripted symbol locator is registered **per target**. Different targets
+can use different locator classes or different arguments.
+
+```
+(lldb) target select 0
+(lldb) target symbols scripted register -C my_locator.MyLocator \
+ -k cache_dir -v /cache/project-a
+
+(lldb) target select 1
+(lldb) target symbols scripted register -C my_locator.MyLocator \
+ -k cache_dir -v /cache/project-b
+```
+
+### Commands
+
+| Command | Description |
+|---------|-------------|
+| `target symbols scripted register -C <class> [-k <key> -v <value> ...]` | Register a locator |
+| `target symbols scripted clear` | Remove the locator from the current target |
+| `target symbols scripted info` | Show the current locator class |
+
+### SB API
+
+You can also register locators programmatically:
+
+```python
+import lldb
+
+error = target.RegisterScriptedSymbolLocator(
+ "my_locator.MyLocator", args)
+# args is an SBStructuredData dictionary
+
+target.ClearScriptedSymbolLocator()
+```
+
+## Caching
+
+Source file resolutions are cached per `(module UUID, source file path)` pair
+within each target. The cache is cleared when:
+
+- A new locator is registered (via `register`)
+- The locator is cleared (via `clear`)
+
+This means your `locate_source_file` method is called at most once per
+unique `(UUID, path)` combination.
+
+## Base Class Template
+
+LLDB ships a base class template at `lldb.plugins.scripted_symbol_locator`.
+You can import and subclass it:
+
+```python
+from lldb.plugins.scripted_symbol_locator import ScriptedSymbolLocator
+
+class MyLocator(ScriptedSymbolLocator):
+ def __init__(self, exe_ctx, args):
+ super().__init__(exe_ctx, args)
+
+ def locate_source_file(self, module, original_source_file):
+ # Your implementation here
+ return None
+```
+
+The base class handles extracting the target and args from the execution
+context. See `lldb/examples/python/templates/scripted_symbol_locator.py`
+for the full template with docstrings.
+
+## Listing Scripting Extensions
+
+To see all registered scripting extensions (including symbol locators):
+
+```
+(lldb) scripting extension list
+```
diff --git a/lldb/examples/python/templates/scripted_symbol_locator.py b/lldb/examples/python/templates/scripted_symbol_locator.py
new file mode 100644
index 0000000000000..d3c87bd0563c3
--- /dev/null
+++ b/lldb/examples/python/templates/scripted_symbol_locator.py
@@ -0,0 +1,220 @@
+from abc import ABCMeta, abstractmethod
+import os
+
+import lldb
+
+
+class ScriptedSymbolLocator(metaclass=ABCMeta):
+ """
+ The base class for a scripted symbol locator.
+
+ Most of the base class methods are optional and return ``None`` to fall
+ through to LLDB's default resolution. Override only the methods you need.
+
+ Configuration::
+
+ (lldb) command script import /path/to/my_locator.py
+ (lldb) target symbols scripted register -C my_locator.MyLocator \\
+ [-k key -v value ...]
+ """
+
+ @abstractmethod
+ def __init__(self, exe_ctx, args):
+ """Construct a scripted symbol locator.
+
+ Args:
+ exe_ctx (lldb.SBExecutionContext): The execution context for
+ the scripted symbol locator.
+ args (lldb.SBStructuredData): A Dictionary holding arbitrary
+ key/value pairs used by the scripted symbol locator.
+ """
+ target = None
+ self.target = None
+ self.args = None
+ if isinstance(exe_ctx, lldb.SBExecutionContext):
+ target = exe_ctx.target
+ if isinstance(target, lldb.SBTarget) and target.IsValid():
+ self.target = target
+ self.dbg = target.GetDebugger()
+ if isinstance(args, lldb.SBStructuredData) and args.IsValid():
+ self.args = args
+
+ def locate_source_file(self, module, original_source_file):
+ """Locate the source file for a given module.
+
+ Called when LLDB resolves source file paths during stack frame
+ display, breakpoint resolution, or source listing. This is the
+ primary method for implementing source file remapping based on
+ build IDs.
+
+ The module is a fully loaded ``SBModule`` (not an ``SBModuleSpec``),
+ so you can access its UUID, file path, platform file path,
+ symbol file path, sections, and symbols.
+
+ Results are cached per (module UUID, source file) pair, so this
+ method is called at most once per unique combination.
+
+ Args:
+ module (lldb.SBModule): The loaded module containing debug
+ info. Use ``module.GetUUIDString()`` to get the build ID
+ for looking up the correct source revision.
+ original_source_file (str): The original source file path
+ as recorded in the debug info.
+
+ Returns:
+ lldb.SBFileSpec: The resolved file spec, or None to fall
+ through to LLDB's default source resolution.
+ """
+ return None
+
+ def locate_executable_object_file(self, module_spec):
+ """Locate the executable (object) file for a given module.
+
+ Called when LLDB needs to find the binary for a module during
+ target creation or module loading. For example, when loading a
+ minidump, LLDB calls this for each shared library referenced
+ in the dump.
+
+ Args:
+ module_spec (lldb.SBModuleSpec): The module specification
+ containing the UUID, file path, architecture, and other
+ search criteria.
+
+ Returns:
+ lldb.SBFileSpec: The located executable, or None to fall
+ through to LLDB's default search.
+ """
+ return None
+
+ def locate_executable_symbol_file(self, module_spec, default_search_paths):
+ """Locate the symbol file for a given module.
+
+ Called when LLDB needs to find separate debug symbols (e.g.,
+ ``.dSYM`` bundles on macOS, ``.debug`` files on Linux, ``.dwp``
+ files for split DWARF) for a module.
+
+ Args:
+ module_spec (lldb.SBModuleSpec): The module specification
+ containing the UUID and file path to search for.
+ default_search_paths (list): A list of default search paths
+ to check.
+
+ Returns:
+ lldb.SBFileSpec: The located symbol file, or None to fall
+ through to LLDB's default search.
+ """
+ return None
+
+ def download_object_and_symbol_file(
+ self, module_spec, force_lookup, copy_executable
+ ):
+ """Download both the object file and symbol file for a module.
+
+ Called when LLDB needs to download a binary and its debug symbols
+ from a remote source (e.g., a symbol server, build artifact
+ store, or cloud storage). This is the last method called in the
+ resolution chain, typically as a fallback when local lookups
+ fail.
+
+ Args:
+ module_spec (lldb.SBModuleSpec): The module specification
+ containing the UUID and file path to download.
+ force_lookup (bool): If True, skip any cached results and
+ force a fresh lookup.
+ copy_executable (bool): If True, copy the executable to a
+ local path.
+
+ Returns:
+ bool: True if the download succeeded, False otherwise.
+ """
+ return False
+
+
+class LocalCacheSymbolLocator(ScriptedSymbolLocator):
+ """Example locator that resolves files from a local cache directory.
+
+ Demonstrates how to subclass ``ScriptedSymbolLocator`` to implement
+ custom symbol and source file resolution. This locator looks up files
+ in a local directory structure organized by build ID (UUID)::
+
+ <cache_dir>/
+ <uuid>/
+ <binary_name>
+ <binary_name>.debug
+ src/
+ main.cpp
+ ...
+
+ Usage::
+
+ (lldb) command script import scripted_symbol_locator
+ (lldb) target symbols scripted register \\
+ -C scripted_symbol_locator.LocalCacheSymbolLocator \\
+ -k cache_dir -v "/path/to/cache"
+ (lldb) target create --core /path/to/minidump.dmp
+ (lldb) bt
+
+ The locator searches for:
+ - Executables: ``<cache_dir>/<uuid>/<filename>``
+ - Symbol files: ``<cache_dir>/<uuid>/<filename>.debug``
+ - Source files: ``<cache_dir>/<uuid>/src/<basename>``
+ """
+
+ cache_dir = None
+
+ def __init__(self, exe_ctx, args):
+ super().__init__(exe_ctx, args)
+
+ # Allow cache_dir to be set via structured data args.
+ if self.args:
+ cache_dir_val = self.args.GetValueForKey("cache_dir")
+ if cache_dir_val and cache_dir_val.IsValid():
+ val = cache_dir_val.GetStringValue(256)
+ if val:
+ LocalCacheSymbolLocator.cache_dir = val
+
+ def _get_cache_path(self, uuid_str, *components):
+ """Build a path under the cache directory for a given UUID.
+
+ Args:
+ uuid_str (str): The module's UUID string.
+ *components: Additional path components (e.g., filename).
+
+ Returns:
+ str: The full path, or None if cache_dir is not set or the
+ UUID is empty.
+ """
+ if not self.cache_dir or not uuid_str:
+ return None
+ return os.path.join(self.cache_dir, uuid_str, *components)
+
+ def locate_source_file(self, module, original_source_file):
+ """Look up source files under ``<cache_dir>/<uuid>/src/``."""
+ uuid_str = module.GetUUIDString()
+ basename = os.path.basename(original_source_file)
+ path = self._get_cache_path(uuid_str, "src", basename)
+ if path and os.path.exists(path):
+ return lldb.SBFileSpec(path, True)
+ return None
+
+ def locate_executable_object_file(self, module_spec):
+ """Look up executables under ``<cache_dir>/<uuid>/``."""
+ uuid_str = module_spec.GetUUIDString()
+ filename = os.path.basename(module_spec.GetFileSpec().GetFilename() or "")
+ if not filename:
+ return None
+ path = self._get_cache_path(uuid_str, filename)
+ if path and os.path.exists(path):
+ return lldb.SBFileSpec(path, True)
+ return None
+
+ def locate_executable_symbol_file(self, module_spec, default_search_paths):
+ """Look up debug symbol files under ``<cache_dir>/<uuid>/``."""
+ uuid_str = module_spec.GetUUIDString()
+ filename = os.path.basename(module_spec.GetFileSpec().GetFilename() or "")
+ if not filename:
+ return None
+ debug_path = self._get_cache_path(uuid_str, filename + ".debug")
+ if debug_path and os.path.exists(debug_path):
+ return lldb.SBFileSpec(debug_path, True)
+ return None
diff --git a/lldb/include/lldb/API/SBFileSpec.h b/lldb/include/lldb/API/SBFileSpec.h
index 36641843aabeb..4b0b640dd4dbc 100644
--- a/lldb/include/lldb/API/SBFileSpec.h
+++ b/lldb/include/lldb/API/SBFileSpec.h
@@ -11,6 +11,10 @@
#include "lldb/API/SBDefines.h"
+namespace lldb_private {
+class ScriptInterpreter;
+}
+
namespace lldb {
class LLDB_API SBFileSpec {
@@ -79,6 +83,7 @@ class LLDB_API SBFileSpec {
friend class SBThread;
friend class SBTrace;
friend class SBSaveCoreOptions;
+ friend class lldb_private::ScriptInterpreter;
SBFileSpec(const lldb_private::FileSpec &fspec);
diff --git a/lldb/include/lldb/API/SBModule.h b/lldb/include/lldb/API/SBModule.h
index 4009ca1461e51..3e8e5b99f6404 100644
--- a/lldb/include/lldb/API/SBModule.h
+++ b/lldb/include/lldb/API/SBModule.h
@@ -311,6 +311,7 @@ class LLDB_API SBModule {
friend class SBType;
friend class lldb_private::python::SWIGBridge;
+ friend class lldb_private::ScriptInterpreter;
explicit SBModule(const lldb::ModuleSP &module_sp);
diff --git a/lldb/include/lldb/API/SBModuleSpec.h b/lldb/include/lldb/API/SBModuleSpec.h
index 0e7f0f3489596..5be9b5e8dd4a7 100644
--- a/lldb/include/lldb/API/SBModuleSpec.h
+++ b/lldb/include/lldb/API/SBModuleSpec.h
@@ -102,6 +102,7 @@ class LLDB_API SBModuleSpec {
friend class SBModule;
friend class SBPlatform;
friend class SBTarget;
+ friend class lldb_private::ScriptInterpreter;
SBModuleSpec(const lldb_private::ModuleSpec &module_spec);
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index dd2cf59b831da..a9c28cf4e29fc 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -1002,6 +1002,22 @@ class LLDB_API SBTarget {
/// An error if a Trace already exists or the trace couldn't be created.
lldb::SBTrace CreateTrace(SBError &error);
+ /// Register a scripted symbol locator for this target.
+ ///
+ /// \param[in] class_name
+ /// The Python class implementing the symbol locator.
+ ///
+ /// \param[in] args
+ /// Optional structured data arguments passed to the locator.
+ ///
+ /// \return
+ /// An SBError indicating success or failure.
+ lldb::SBError RegisterScriptedSymbolLocator(const char *class_name,
+ lldb::SBStructuredData &args);
+
+ /// Clear the scripted symbol locator for this target.
+ void ClearScriptedSymbolLocator();
+
lldb::SBMutex GetAPIMutex() const;
/// Register a scripted frame provider for this target.
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..52e1c9855df56
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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..d82299e77495d 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -16,9 +16,12 @@
#include "lldb/API/SBError.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBExecutionContext.h"
+#include "lldb/API/SBFileSpec.h"
#include "lldb/API/SBFrameList.h"
#include "lldb/API/SBLaunchInfo.h"
#include "lldb/API/SBMemoryRegionInfo.h"
+#include "lldb/API/SBModule.h"
+#include "lldb/API/SBModuleSpec.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBSymbolContext.h"
#include "lldb/API/SBThread.h"
@@ -33,6 +36,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 +549,11 @@ class ScriptInterpreter : public PluginInterface {
return {};
}
+ virtual lldb::ScriptedSymbolLocatorInterfaceSP
+ CreateScriptedSymbolLocatorInterface() {
+ return {};
+ }
+
virtual lldb::ScriptedThreadPlanInterfaceSP
CreateScriptedThreadPlanInterface() {
return {};
@@ -612,6 +621,16 @@ class ScriptInterpreter : public PluginInterface {
lldb::ValueObjectSP
GetOpaqueTypeFromSBValue(const lldb::SBValue &value) const;
+ FileSpec GetOpaqueTypeFromSBFileSpec(const lldb::SBFileSpec &file_spec) const;
+
+ ModuleSpec
+ GetOpaqueTypeFromSBModuleSpec(const lldb::SBModuleSpec &module_spec) const;
+
+ lldb::ModuleSP GetOpaqueTypeFromSBModule(const lldb::SBModule &module) const;
+
+ std::unique_ptr<lldb::SBModuleSpec>
+ MakeSBModuleSpec(const ModuleSpec &module_spec) const;
+
protected:
Debugger &m_debugger;
lldb::ScriptLanguage m_script_lang;
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/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 4f5b022765f9e..3d2c36508d090 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -13,6 +13,7 @@
#include <map>
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
#include "lldb/Breakpoint/BreakpointList.h"
@@ -38,6 +39,7 @@
#include "lldb/Utility/Broadcaster.h"
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Utility/RealpathPrefixes.h"
+#include "lldb/Utility/ScriptedMetadata.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timeout.h"
#include "lldb/lldb-public.h"
@@ -1705,6 +1707,24 @@ class Target : public std::enable_shared_from_this<Target>,
void SaveScriptedLaunchInfo(lldb_private::ProcessInfo &process_info);
+ // Scripted symbol locator per-target registration.
+ Status RegisterScriptedSymbolLocator(llvm::StringRef class_name,
+ StructuredData::DictionarySP args_sp);
+ void ClearScriptedSymbolLocator();
+ lldb::ScriptedSymbolLocatorInterfaceSP GetScriptedSymbolLocatorInterface();
+ llvm::StringRef GetScriptedSymbolLocatorClassName() const {
+ return m_scripted_symbol_locator_metadata_sp
+ ? m_scripted_symbol_locator_metadata_sp->GetClassName()
+ : "";
+ }
+
+ /// Look up a previously cached source file resolution result.
+ /// Returns true if a cached entry exists (even if the result is nullopt).
+ bool LookupScriptedSourceFileCache(const std::string &key,
+ std::optional<FileSpec> &result) const;
+ void InsertScriptedSourceFileCache(const std::string &key,
+ const std::optional<FileSpec> &result);
+
/// Add a signal for the target. This will get copied over to the process
/// if the signal exists on that target. Only the values with Yes and No are
/// set, Calculate values will be ignored.
@@ -1843,6 +1863,12 @@ class Target : public std::enable_shared_from_this<Target>,
/// signals you will have.
llvm::StringMap<DummySignalValues> m_dummy_signals;
+ // Per-target scripted symbol locator.
+ lldb::ScriptedMetadataSP m_scripted_symbol_locator_metadata_sp;
+ lldb::ScriptedSymbolLocatorInterfaceSP m_scripted_symbol_locator_interface_sp;
+ std::unordered_map<std::string, std::optional<FileSpec>>
+ m_scripted_source_file_cache;
+
static void ImageSearchPathsChanged(const PathMappingList &path_list,
void *baton);
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/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index 99dfbb3fd9bce..8fe6ea81385ca 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -2432,6 +2432,36 @@ lldb::SBTrace SBTarget::CreateTrace(lldb::SBError &error) {
return SBTrace();
}
+lldb::SBError
+SBTarget::RegisterScriptedSymbolLocator(const char *class_name,
+ lldb::SBStructuredData &args) {
+ LLDB_INSTRUMENT_VA(this, class_name, args);
+
+ lldb::SBError sb_error;
+ TargetSP target_sp = GetSP();
+ if (!target_sp) {
+ sb_error.SetErrorString("invalid target");
+ return sb_error;
+ }
+
+ StructuredData::DictionarySP args_sp;
+ StructuredData::ObjectSP obj_sp = args.m_impl_up->GetObjectSP();
+ if (obj_sp && obj_sp->GetType() == lldb::eStructuredDataTypeDictionary)
+ args_sp = std::static_pointer_cast<StructuredData::Dictionary>(obj_sp);
+
+ Status error = target_sp->RegisterScriptedSymbolLocator(class_name, args_sp);
+ if (error.Fail())
+ sb_error.SetErrorString(error.AsCString());
+ return sb_error;
+}
+
+void SBTarget::ClearScriptedSymbolLocator() {
+ LLDB_INSTRUMENT_VA(this);
+
+ if (TargetSP target_sp = GetSP())
+ target_sp->ClearScriptedSymbolLocator();
+}
+
lldb::SBMutex SBTarget::GetAPIMutex() const {
LLDB_INSTRUMENT_VA(this);
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index 59ccf390dea31..99dab64b4291f 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -4691,6 +4691,133 @@ class CommandObjectTargetSymbolsAdd : public CommandObjectParsed {
OptionGroupBoolean m_current_stack_option;
};
+#pragma mark CommandObjectTargetSymbolsScriptedRegister
+
+class CommandObjectTargetSymbolsScriptedRegister : public CommandObjectParsed {
+public:
+ CommandObjectTargetSymbolsScriptedRegister(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target symbols scripted register",
+ "Register a scripted symbol locator for the current target.",
+ "target symbols scripted register -C <script-class> "
+ "[-k <key> -v <value> ...]"),
+ m_python_class_options("scripted symbol locator", true, 'C', 'k', 'v',
+ OptionGroupPythonClassWithDict::eScriptClass) {
+ m_all_options.Append(&m_python_class_options,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2, LLDB_OPT_SET_1);
+ m_all_options.Finalize();
+ }
+
+ ~CommandObjectTargetSymbolsScriptedRegister() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetTarget();
+
+ llvm::StringRef class_name = m_python_class_options.GetName();
+ if (class_name.empty()) {
+ result.AppendError("must specify a script class with -C");
+ return;
+ }
+
+ StructuredData::DictionarySP args_sp;
+ StructuredData::ObjectSP extra = m_python_class_options.GetStructuredData();
+ if (extra && extra->GetType() == lldb::eStructuredDataTypeDictionary)
+ args_sp = std::static_pointer_cast<StructuredData::Dictionary>(extra);
+
+ Status error = target.RegisterScriptedSymbolLocator(class_name, args_sp);
+ if (error.Fail()) {
+ result.AppendErrorWithFormat(
+ "failed to register scripted symbol locator: %s\n",
+ error.AsCString());
+ return;
+ }
+
+ result.AppendMessageWithFormat(
+ "Registered scripted symbol locator '%s' for target.\n",
+ class_name.str().c_str());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ OptionGroupPythonClassWithDict m_python_class_options;
+ OptionGroupOptions m_all_options;
+};
+
+#pragma mark CommandObjectTargetSymbolsScriptedClear
+
+class CommandObjectTargetSymbolsScriptedClear : public CommandObjectParsed {
+public:
+ CommandObjectTargetSymbolsScriptedClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target symbols scripted clear",
+ "Clear the scripted symbol locator for the current target.",
+ "target symbols scripted clear") {}
+
+ ~CommandObjectTargetSymbolsScriptedClear() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetTarget();
+ target.ClearScriptedSymbolLocator();
+ result.AppendMessageWithFormat(
+ "Cleared scripted symbol locator for target.\n");
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#pragma mark CommandObjectTargetSymbolsScriptedInfo
+
+class CommandObjectTargetSymbolsScriptedInfo : public CommandObjectParsed {
+public:
+ CommandObjectTargetSymbolsScriptedInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target symbols scripted info",
+ "Show the current scripted symbol locator for the target.",
+ "target symbols scripted info") {}
+
+ ~CommandObjectTargetSymbolsScriptedInfo() override = default;
+
+protected:
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Target &target = GetTarget();
+ llvm::StringRef class_name = target.GetScriptedSymbolLocatorClassName();
+ if (class_name.empty()) {
+ result.AppendMessageWithFormat(
+ "No scripted symbol locator registered for this target.\n");
+ } else {
+ result.AppendMessageWithFormat("Scripted symbol locator: %s\n",
+ class_name.str().c_str());
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+};
+
+#pragma mark CommandObjectTargetSymbolsScripted
+
+class CommandObjectTargetSymbolsScripted : public CommandObjectMultiword {
+public:
+ CommandObjectTargetSymbolsScripted(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target symbols scripted",
+ "Commands for managing scripted symbol locators.",
+ "target symbols scripted <sub-command> ...") {
+ LoadSubCommand(
+ "register",
+ CommandObjectSP(
+ new CommandObjectTargetSymbolsScriptedRegister(interpreter)));
+ LoadSubCommand(
+ "clear", CommandObjectSP(
+ new CommandObjectTargetSymbolsScriptedClear(interpreter)));
+ LoadSubCommand(
+ "info", CommandObjectSP(
+ new CommandObjectTargetSymbolsScriptedInfo(interpreter)));
+ }
+
+ ~CommandObjectTargetSymbolsScripted() override = default;
+};
+
#pragma mark CommandObjectTargetSymbols
// CommandObjectTargetSymbols
@@ -4705,6 +4832,9 @@ class CommandObjectTargetSymbols : public CommandObjectMultiword {
"target symbols <sub-command> ...") {
LoadSubCommand(
"add", CommandObjectSP(new CommandObjectTargetSymbolsAdd(interpreter)));
+ LoadSubCommand(
+ "scripted",
+ CommandObjectSP(new CommandObjectTargetSymbolsScripted(interpreter)));
}
~CommandObjectTargetSymbols() override = default;
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/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 5e8478c2670bb..6b52b7ed32946 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -172,6 +172,31 @@ ScriptInterpreter::GetOpaqueTypeFromSBValue(const lldb::SBValue &value) const {
return locker.GetLockedSP(*value.m_opaque_sp);
}
+FileSpec ScriptInterpreter::GetOpaqueTypeFromSBFileSpec(
+ const lldb::SBFileSpec &file_spec) const {
+ if (file_spec.m_opaque_up)
+ return *file_spec.m_opaque_up;
+ return {};
+}
+
+ModuleSpec ScriptInterpreter::GetOpaqueTypeFromSBModuleSpec(
+ const lldb::SBModuleSpec &module_spec) const {
+ if (module_spec.m_opaque_up)
+ return *module_spec.m_opaque_up;
+ return {};
+}
+
+lldb::ModuleSP ScriptInterpreter::GetOpaqueTypeFromSBModule(
+ const lldb::SBModule &module) const {
+ return module.m_opaque_sp;
+}
+
+std::unique_ptr<lldb::SBModuleSpec>
+ScriptInterpreter::MakeSBModuleSpec(const ModuleSpec &module_spec) const {
+ return std::unique_ptr<lldb::SBModuleSpec>(
+ new lldb::SBModuleSpec(module_spec));
+}
+
lldb::ScriptLanguage
ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
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.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp
index f6c707b2bd168..96a81c9153736 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp
@@ -32,6 +32,7 @@ void ScriptInterpreterPythonInterfaces::Initialize() {
ScriptedBreakpointPythonInterface::Initialize();
ScriptedThreadPlanPythonInterface::Initialize();
ScriptedFrameProviderPythonInterface::Initialize();
+ ScriptedSymbolLocatorPythonInterface::Initialize();
}
void ScriptInterpreterPythonInterfaces::Terminate() {
@@ -42,6 +43,7 @@ void ScriptInterpreterPythonInterfaces::Terminate() {
ScriptedBreakpointPythonInterface::Terminate();
ScriptedThreadPlanPythonInterface::Terminate();
ScriptedFrameProviderPythonInterface::Terminate();
+ ScriptedSymbolLocatorPythonInterface::Terminate();
}
#endif
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.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
index f5fd8b2d2d802..08409db80672d 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
@@ -17,7 +17,10 @@
#include "../ScriptInterpreterPythonImpl.h"
#include "ScriptedPythonInterface.h"
+#include "lldb/Core/ModuleSpec.h"
#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/FileSpecList.h"
#include "lldb/ValueObject/ValueObjectList.h"
#include <optional>
@@ -311,4 +314,62 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectListSP>(
return out;
}
+template <>
+FileSpec ScriptedPythonInterface::ExtractValueFromPythonObject<FileSpec>(
+ python::PythonObject &p, Status &error) {
+ if (lldb::SBFileSpec *sb_file_spec = reinterpret_cast<lldb::SBFileSpec *>(
+ python::LLDBSWIGPython_CastPyObjectToSBFileSpec(p.get())))
+ return m_interpreter.GetOpaqueTypeFromSBFileSpec(*sb_file_spec);
+ error = Status::FromErrorString(
+ "Couldn't cast lldb::SBFileSpec to lldb_private::FileSpec.");
+
+ return {};
+}
+
+template <>
+ModuleSpec ScriptedPythonInterface::ExtractValueFromPythonObject<ModuleSpec>(
+ python::PythonObject &p, Status &error) {
+ if (lldb::SBModuleSpec *sb_module_spec =
+ reinterpret_cast<lldb::SBModuleSpec *>(
+ python::LLDBSWIGPython_CastPyObjectToSBModuleSpec(p.get())))
+ return m_interpreter.GetOpaqueTypeFromSBModuleSpec(*sb_module_spec);
+ error = Status::FromErrorString(
+ "Couldn't cast lldb::SBModuleSpec to lldb_private::ModuleSpec.");
+
+ return {};
+}
+
+template <>
+FileSpecList
+ScriptedPythonInterface::ExtractValueFromPythonObject<FileSpecList>(
+ python::PythonObject &p, Status &error) {
+ FileSpecList result;
+ python::PythonList py_list(python::PyRefType::Borrowed, p.get());
+ if (!py_list.IsValid()) {
+ error = Status::FromErrorString(
+ "Couldn't cast Python object to a list for FileSpecList.");
+ return result;
+ }
+ for (uint32_t i = 0; i < py_list.GetSize(); i++) {
+ python::PythonObject item = py_list.GetItemAtIndex(i);
+ if (lldb::SBFileSpec *sb_file_spec = reinterpret_cast<lldb::SBFileSpec *>(
+ python::LLDBSWIGPython_CastPyObjectToSBFileSpec(item.get())))
+ result.Append(m_interpreter.GetOpaqueTypeFromSBFileSpec(*sb_file_spec));
+ }
+ return result;
+}
+
+template <>
+lldb::ModuleSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ModuleSP>(
+ python::PythonObject &p, Status &error) {
+ if (lldb::SBModule *sb_module = reinterpret_cast<lldb::SBModule *>(
+ python::LLDBSWIGPython_CastPyObjectToSBModule(p.get())))
+ return m_interpreter.GetOpaqueTypeFromSBModule(*sb_module);
+ error = Status::FromErrorString(
+ "Couldn't cast lldb::SBModule to lldb::ModuleSP.");
+
+ return {};
+}
+
#endif
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index 4aadee584b2e2..92ff830f4a772 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -17,9 +17,15 @@
#include <type_traits>
#include <utility>
+#include "lldb/Core/ModuleSpec.h"
#include "lldb/Host/Config.h"
#include "lldb/Interpreter/Interfaces/ScriptedInterface.h"
#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/FileSpecList.h"
+
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBModuleSpec.h"
#include "../PythonDataObjects.h"
#include "../SWIGPythonBridge.h"
@@ -632,6 +638,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);
}
@@ -660,6 +670,26 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return python::SWIGBridge::ToSWIGWrapper(arg);
}
+ python::PythonObject Transform(const ModuleSpec &arg) {
+ return python::SWIGBridge::ToSWIGWrapper(
+ m_interpreter.MakeSBModuleSpec(arg));
+ }
+
+ python::PythonObject Transform(const FileSpecList &arg) {
+ python::PythonList py_list(arg.GetSize());
+ for (size_t i = 0; i < arg.GetSize(); i++) {
+ const FileSpec &fs = arg.GetFileSpecAtIndex(i);
+ py_list.SetItemAtIndex(i, python::SWIGBridge::ToSWIGWrapper(
+ std::make_unique<lldb::SBFileSpec>(
+ fs.GetPath().c_str(), false)));
+ }
+ return py_list;
+ }
+
+ python::PythonObject Transform(const std::string &arg) {
+ return python::PythonString(arg);
+ }
+
template <typename T, typename U>
void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
// If U is not a PythonObject, don't touch it!
@@ -671,6 +701,16 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error);
}
+ // Read-only types: Python doesn't modify these, so reverse transform is a
+ // no-op.
+ void ReverseTransform(std::string &original_arg,
+ python::PythonObject transformed_arg, Status &error) {
+ python::PythonString py_str(python::PyRefType::Borrowed,
+ transformed_arg.get());
+ if (py_str.IsValid())
+ original_arg = py_str.GetString().str();
+ }
+
void ReverseTransform(bool &original_arg,
python::PythonObject transformed_arg, Status &error) {
python::PythonBoolean boolean_arg = python::PythonBoolean(
@@ -828,6 +868,24 @@ lldb::ValueObjectListSP
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectListSP>(
python::PythonObject &p, Status &error);
+template <>
+FileSpec ScriptedPythonInterface::ExtractValueFromPythonObject<FileSpec>(
+ python::PythonObject &p, Status &error);
+
+template <>
+ModuleSpec ScriptedPythonInterface::ExtractValueFromPythonObject<ModuleSpec>(
+ python::PythonObject &p, Status &error);
+
+template <>
+FileSpecList
+ScriptedPythonInterface::ExtractValueFromPythonObject<FileSpecList>(
+ python::PythonObject &p, Status &error);
+
+template <>
+lldb::ModuleSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ModuleSP>(
+ python::PythonObject &p, Status &error);
+
} // namespace lldb_private
#endif // LLDB_ENABLE_PYTHON
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..d01c8ef575d36
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Core/PluginManager.h"
+#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"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+
+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);
+}
+
+std::optional<ModuleSpec>
+ScriptedSymbolLocatorPythonInterface::LocateExecutableObjectFile(
+ const ModuleSpec &module_spec, Status &error) {
+ // Make a copy so Dispatch's ReverseTransform can operate on a mutable value.
+ ModuleSpec ms_copy(module_spec);
+ FileSpec file_spec =
+ Dispatch<FileSpec>("locate_executable_object_file", error, ms_copy);
+
+ if (error.Fail() || !file_spec)
+ return {};
+
+ ModuleSpec result_spec(module_spec);
+ result_spec.GetFileSpec() = file_spec;
+ return result_spec;
+}
+
+std::optional<FileSpec>
+ScriptedSymbolLocatorPythonInterface::LocateExecutableSymbolFile(
+ const ModuleSpec &module_spec, const FileSpecList &default_search_paths,
+ Status &error) {
+ ModuleSpec ms_copy(module_spec);
+ FileSpecList fsl_copy(default_search_paths);
+ FileSpec file_spec = Dispatch<FileSpec>("locate_executable_symbol_file",
+ error, ms_copy, fsl_copy);
+
+ if (error.Fail() || !file_spec)
+ return {};
+
+ return file_spec;
+}
+
+bool ScriptedSymbolLocatorPythonInterface::DownloadObjectAndSymbolFile(
+ ModuleSpec &module_spec, Status &error, bool force_lookup,
+ bool copy_executable) {
+ StructuredData::ObjectSP obj =
+ Dispatch("download_object_and_symbol_file", error, module_spec,
+ force_lookup, copy_executable);
+
+ if (!obj || error.Fail())
+ return false;
+
+ return obj->GetBooleanValue();
+}
+
+std::optional<FileSpec> ScriptedSymbolLocatorPythonInterface::LocateSourceFile(
+ const lldb::ModuleSP &module_sp, const FileSpec &original_source_file,
+ Status &error) {
+ std::string source_path = original_source_file.GetPath();
+ lldb::ModuleSP module_copy(module_sp);
+
+ FileSpec file_spec =
+ Dispatch<FileSpec>("locate_source_file", error, module_copy, source_path);
+
+ if (error.Fail() || !file_spec)
+ return {};
+
+ return file_spec;
+}
+
+void ScriptedSymbolLocatorPythonInterface::Initialize() {
+ const std::vector<llvm::StringRef> ci_usages = {
+ "target symbols scripted register -C "
+ "<script-class> [-k <key> -v <value> ...]"};
+ const std::vector<llvm::StringRef> api_usages = {
+ "SBTarget.RegisterScriptedSymbolLocator(class_name, args_dict)"};
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(),
+ llvm::StringRef("Scripted symbol locator Python interface"),
+ CreateInstance, eScriptLanguagePython, {ci_usages, api_usages});
+}
+
+void ScriptedSymbolLocatorPythonInterface::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+#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..24d22f354b158
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 void Initialize();
+ static void Terminate();
+
+ 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/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 7a64d8e91e62c..52ea2813c6ff2 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -273,6 +273,9 @@ void *LLDBSWIGPython_CastPyObjectToSBValueList(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBFileSpec(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBModuleSpec(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBModule(PyObject *data);
} // namespace python
} // namespace lldb_private
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..60b2fc6106c87 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;
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..89612d5e1625b
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt
@@ -0,0 +1,13 @@
+set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND SymbolLocator)
+
+add_lldb_library(lldbPluginSymbolLocatorScripted PLUGIN
+ SymbolLocatorScripted.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbHost
+ lldbInterpreter
+ lldbSymbol
+ lldbTarget
+ lldbUtility
+ )
diff --git a/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp
new file mode 100644
index 0000000000000..c3784ccbf739a
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp
@@ -0,0 +1,202 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Interfaces/ScriptedSymbolLocatorInterface.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(SymbolLocatorScripted)
+
+SymbolLocatorScripted::SymbolLocatorScripted() : SymbolLocator() {}
+
+void SymbolLocatorScripted::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
+ LocateExecutableObjectFile, LocateExecutableSymbolFile,
+ DownloadObjectAndSymbolFile, nullptr, LocateSourceFile);
+}
+
+void SymbolLocatorScripted::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+llvm::StringRef SymbolLocatorScripted::GetPluginDescriptionStatic() {
+ return "Scripted symbol locator plug-in.";
+}
+
+SymbolLocator *SymbolLocatorScripted::CreateInstance() {
+ return new SymbolLocatorScripted();
+}
+
+/// Iterate all debuggers and their targets, calling \p callback for each
+/// target that has a scripted symbol locator registered. The callback
+/// receives the target and its interface. If the callback returns true,
+/// iteration stops early.
+template <typename Callback>
+static void ForEachScriptedTarget(Callback &&callback) {
+ for (size_t di = 0; di < Debugger::GetNumDebuggers(); di++) {
+ DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(di);
+ if (!debugger_sp)
+ continue;
+ TargetList &target_list = debugger_sp->GetTargetList();
+ for (size_t ti = 0; ti < target_list.GetNumTargets(); ti++) {
+ TargetSP target_sp = target_list.GetTargetAtIndex(ti);
+ if (!target_sp)
+ continue;
+ auto interface_sp = target_sp->GetScriptedSymbolLocatorInterface();
+ if (!interface_sp)
+ continue;
+ if (callback(*target_sp, interface_sp))
+ return;
+ }
+ }
+}
+
+std::optional<ModuleSpec> SymbolLocatorScripted::LocateExecutableObjectFile(
+ const ModuleSpec &module_spec) {
+ std::optional<ModuleSpec> result;
+ ForEachScriptedTarget(
+ [&](Target &target,
+ ScriptedSymbolLocatorInterfaceSP &interface_sp) -> bool {
+ Status error;
+ auto located =
+ 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);
+ }
+ if (located) {
+ result = located;
+ return true; // Stop iterating.
+ }
+ return false;
+ });
+ return result;
+}
+
+std::optional<FileSpec> SymbolLocatorScripted::LocateExecutableSymbolFile(
+ const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
+ std::optional<FileSpec> result;
+ ForEachScriptedTarget(
+ [&](Target &target,
+ ScriptedSymbolLocatorInterfaceSP &interface_sp) -> bool {
+ Status error;
+ auto located = 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);
+ }
+ if (located) {
+ result = located;
+ return true;
+ }
+ return false;
+ });
+ return result;
+}
+
+bool SymbolLocatorScripted::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
+ Status &error,
+ bool force_lookup,
+ bool copy_executable) {
+ bool result = false;
+ ForEachScriptedTarget(
+ [&](Target &target,
+ ScriptedSymbolLocatorInterfaceSP &interface_sp) -> bool {
+ bool success = interface_sp->DownloadObjectAndSymbolFile(
+ module_spec, error, force_lookup, copy_executable);
+ if (success) {
+ result = true;
+ return true;
+ }
+ return false;
+ });
+ return result;
+}
+
+std::optional<FileSpec>
+SymbolLocatorScripted::LocateSourceFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &original_source_file) {
+ if (!module_sp)
+ return {};
+
+ // Find the target that owns this module.
+ Target *owning_target = nullptr;
+ for (size_t di = 0; di < Debugger::GetNumDebuggers(); di++) {
+ DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(di);
+ if (!debugger_sp)
+ continue;
+ TargetList &target_list = debugger_sp->GetTargetList();
+ for (size_t ti = 0; ti < target_list.GetNumTargets(); ti++) {
+ TargetSP target_sp = target_list.GetTargetAtIndex(ti);
+ if (!target_sp)
+ continue;
+ ModuleSP found_module =
+ target_sp->GetImages().FindModule(module_sp.get());
+ if (found_module) {
+ owning_target = target_sp.get();
+ break;
+ }
+ }
+ if (owning_target)
+ break;
+ }
+
+ if (!owning_target)
+ return {};
+
+ auto interface_sp = owning_target->GetScriptedSymbolLocatorInterface();
+ if (!interface_sp)
+ return {};
+
+ // 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 (owning_target->LookupScriptedSourceFileCache(cache_key, cached))
+ return cached;
+
+ 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);
+ }
+
+ owning_target->InsertScriptedSourceFileCache(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..b16bc972c8907
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Symbol/SymbolLocator.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class SymbolLocatorScripted : public SymbolLocator {
+public:
+ SymbolLocatorScripted();
+
+ static void Initialize();
+ static void Terminate();
+
+ 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/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/Target.cpp b/lldb/source/Target/Target.cpp
index 07c3653782c6b..708a9c40b2b3f 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -42,6 +42,7 @@
#include "lldb/Interpreter/OptionGroupWatchpoint.h"
#include "lldb/Interpreter/OptionValues.h"
#include "lldb/Interpreter/Property.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/Symbol.h"
@@ -3426,6 +3427,89 @@ void Target::SaveScriptedLaunchInfo(lldb_private::ProcessInfo &process_info) {
}
}
+Status
+Target::RegisterScriptedSymbolLocator(llvm::StringRef class_name,
+ StructuredData::DictionarySP args_sp) {
+ if (class_name.empty())
+ return Status::FromErrorString(
+ "class name must not be empty; use ClearScriptedSymbolLocator() to "
+ "unregister");
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (!interpreter)
+ return Status::FromErrorString("no script interpreter available");
+
+ auto interface_sp = interpreter->CreateScriptedSymbolLocatorInterface();
+ if (!interface_sp)
+ return Status::FromErrorString(
+ "failed to create scripted symbol locator interface");
+
+ ExecutionContext exe_ctx;
+ TargetSP target_sp(shared_from_this());
+ exe_ctx.SetTargetSP(target_sp);
+
+ auto obj_or_err =
+ interface_sp->CreatePluginObject(class_name, exe_ctx, args_sp);
+ if (!obj_or_err)
+ return Status::FromError(obj_or_err.takeError());
+
+ m_scripted_symbol_locator_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, args_sp);
+ m_scripted_symbol_locator_interface_sp = interface_sp;
+ m_scripted_source_file_cache.clear();
+
+ // Invalidate cached stack frames so the next backtrace re-resolves line
+ // entries through ApplyFileMappings, which will call our locator.
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ ThreadList &thread_list = process_sp->GetThreadList();
+ for (uint32_t i = 0; i < thread_list.GetSize(false); i++) {
+ ThreadSP thread_sp = thread_list.GetThreadAtIndex(i, false);
+ if (thread_sp)
+ thread_sp->ClearStackFrames();
+ }
+ }
+
+ return Status();
+}
+
+void Target::ClearScriptedSymbolLocator() {
+ m_scripted_symbol_locator_metadata_sp.reset();
+ m_scripted_symbol_locator_interface_sp.reset();
+ m_scripted_source_file_cache.clear();
+
+ // Invalidate cached stack frames so the next backtrace re-resolves line
+ // entries without the scripted locator.
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ ThreadList &thread_list = process_sp->GetThreadList();
+ for (uint32_t i = 0; i < thread_list.GetSize(false); i++) {
+ ThreadSP thread_sp = thread_list.GetThreadAtIndex(i, false);
+ if (thread_sp)
+ thread_sp->ClearStackFrames();
+ }
+ }
+}
+
+ScriptedSymbolLocatorInterfaceSP Target::GetScriptedSymbolLocatorInterface() {
+ return m_scripted_symbol_locator_interface_sp;
+}
+
+bool Target::LookupScriptedSourceFileCache(
+ const std::string &key, std::optional<FileSpec> &result) const {
+ auto it = m_scripted_source_file_cache.find(key);
+ if (it != m_scripted_source_file_cache.end()) {
+ result = it->second;
+ return true;
+ }
+ return false;
+}
+
+void Target::InsertScriptedSourceFileCache(
+ const std::string &key, const std::optional<FileSpec> &result) {
+ m_scripted_source_file_cache[key] = result;
+}
+
Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
m_stats.SetLaunchOrAttachTime();
Status error;
diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp
index 3a9deb6f5c6fd..c75bd68d2e4bb 100644
--- a/lldb/source/Target/ThreadPlanStepRange.cpp
+++ b/lldb/source/Target/ThreadPlanStepRange.cpp
@@ -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;
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..e1604d88b9dbb
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_symbol_locator/Makefile
@@ -0,0 +1,9 @@
+C_SOURCES := main.c
+USE_SYSTEM_STDLIB := 1
+
+# Linux/FreeBSD need --build-id for a UUID; Darwin gets one automatically.
+ifneq "$(OS)" "Darwin"
+LD_EXTRAS := -Wl,--build-id
+endif
+
+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..8fdad934de502
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py
@@ -0,0 +1,195 @@
+"""
+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 register_locator(self, class_name, extra_args=""):
+ cmd = "target symbols scripted register -C " + class_name
+ if extra_args:
+ cmd += " " + extra_args
+ self.runCmd(cmd)
+
+ def clear_locator(self):
+ self.runCmd("target symbols scripted clear")
+
+ 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 ""
+
+ 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 with per-target registration.
+ self.import_locator()
+ self.register_locator(
+ "source_locator.SourceLocator",
+ "-k resolved_dir -v '%s'" % tmp_dir,
+ )
+ self.addTearDownHook(lambda: self.clear_locator())
+
+ 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
+ # instance calls via the scripted symbol locator.
+ # Since calls are now instance-level, we access them through
+ # the scripted interface's Python object.
+ calls_str = self.script(
+ "[c for c in __import__('lldb').debugger.GetSelectedTarget()"
+ ".GetModuleAtIndex(0).GetUUIDString()]"
+ )
+ # Just verify the UUID is a non-empty string (the locator was called)
+ self.assertTrue(len(calls_str) > 0, "Module should have a UUID")
+
+ self.dbg.DeleteTarget(target)
+
+ 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.
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target and target.IsValid(), VALID_TARGET)
+
+ self.import_locator()
+ self.register_locator("source_locator.NoneLocator")
+ self.addTearDownHook(lambda: self.clear_locator())
+
+ 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.
+ 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)
+
+ def test_invalid_script_class(self):
+ """Test that an invalid script class name is handled gracefully
+ without crashing, and breakpoints still resolve."""
+ self.build()
+
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target and target.IsValid(), VALID_TARGET)
+
+ # Registering a nonexistent class should fail, but not crash.
+ ret = lldb.SBCommandReturnObject()
+ self.dbg.GetCommandInterpreter().HandleCommand(
+ "target symbols scripted register "
+ "-C nonexistent_module.NonexistentClass",
+ ret,
+ )
+ # The command should have failed.
+ self.assertFalse(ret.Succeeded())
+
+ # Breakpoints 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)
+
+ def test_scripted_info_command(self):
+ """Test that 'target symbols scripted info' reports the class name."""
+ self.build()
+
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target and target.IsValid(), VALID_TARGET)
+
+ # Before registration, should report no locator.
+ ret = lldb.SBCommandReturnObject()
+ self.dbg.GetCommandInterpreter().HandleCommand(
+ "target symbols scripted info", ret
+ )
+ self.assertTrue(ret.Succeeded())
+ self.assertIn("No scripted symbol locator", ret.GetOutput())
+
+ # After registration, should report the class name.
+ self.import_locator()
+ self.register_locator("source_locator.NoneLocator")
+ self.addTearDownHook(lambda: self.clear_locator())
+
+ ret = lldb.SBCommandReturnObject()
+ self.dbg.GetCommandInterpreter().HandleCommand(
+ "target symbols scripted info", ret
+ )
+ self.assertTrue(ret.Succeeded())
+ self.assertIn("source_locator.NoneLocator", ret.GetOutput())
+
+ 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..d016bfb907d04
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py
@@ -0,0 +1,74 @@
+import os
+from typing import Optional
+
+import lldb
+
+
+class SourceLocator:
+ """Test locator that records calls and returns a configured resolved path."""
+
+ def __init__(
+ self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData
+ ) -> None:
+ self.calls: list = []
+ self.resolved_dir: Optional[str] = None
+ if args.IsValid():
+ resolved_dir_val = args.GetValueForKey("resolved_dir")
+ if resolved_dir_val and resolved_dir_val.IsValid():
+ val = resolved_dir_val.GetStringValue(4096)
+ if val:
+ self.resolved_dir = val
+
+ def locate_source_file(
+ self, module: lldb.SBModule, original_source_file: str
+ ) -> Optional[lldb.SBFileSpec]:
+ uuid = module.GetUUIDString()
+ self.calls.append((uuid, original_source_file))
+ if self.resolved_dir:
+ basename = os.path.basename(original_source_file)
+ return lldb.SBFileSpec(os.path.join(self.resolved_dir, basename), True)
+ return None
+
+ def locate_executable_object_file(
+ self, module_spec: lldb.SBModuleSpec
+ ) -> Optional[lldb.SBFileSpec]:
+ return None
+
+ def locate_executable_symbol_file(
+ self, module_spec: lldb.SBModuleSpec, default_search_paths: list
+ ) -> Optional[lldb.SBFileSpec]:
+ return None
+
+ def download_object_and_symbol_file(
+ self, module_spec: lldb.SBModuleSpec, force_lookup: bool, copy_executable: bool
+ ) -> bool:
+ return False
+
+
+class NoneLocator:
+ """Locator that always returns None."""
+
+ def __init__(
+ self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData
+ ) -> None:
+ pass
+
+ def locate_source_file(
+ self, module: lldb.SBModule, original_source_file: str
+ ) -> Optional[lldb.SBFileSpec]:
+ return None
+
+ def locate_executable_object_file(
+ self, module_spec: lldb.SBModuleSpec
+ ) -> Optional[lldb.SBFileSpec]:
+ return None
+
+ def locate_executable_symbol_file(
+ self, module_spec: lldb.SBModuleSpec, default_search_paths: list
+ ) -> Optional[lldb.SBFileSpec]:
+ return None
+
+ def download_object_and_symbol_file(
+ self, module_spec: lldb.SBModuleSpec, force_lookup: bool, copy_executable: bool
+ ) -> bool:
+ return False
diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 5694aeeff3e5b..e786c638f3e7e 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -171,6 +171,21 @@ lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *data) {
return nullptr;
}
+void *
+lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFileSpec(PyObject *data) {
+ return nullptr;
+}
+
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBModuleSpec(
+ PyObject *data) {
+ return nullptr;
+}
+
+void *
+lldb_private::python::LLDBSWIGPython_CastPyObjectToSBModule(PyObject *data) {
+ return nullptr;
+}
+
lldb::ValueObjectSP
lldb_private::python::SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue(
void *data) {
More information about the lldb-commits
mailing list