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 <[email protected]> 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 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
