diff options
| author | Med Ismail Bennani <ismail@bennani.ma> | 2025-12-02 10:59:40 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-02 18:59:40 +0000 |
| commit | c50802cbee3f6f25059422ba0edcc455e395a207 (patch) | |
| tree | d0161c129df974f9a6f7b6bdb9a01e54bfd3f576 /lldb/source/Plugins/ScriptInterpreter/Python/Interfaces | |
| parent | 879dddf2b4ede2e6474964f9e5b63545d271c733 (diff) | |
| download | llvm-c50802cbee3f6f25059422ba0edcc455e395a207.tar.gz llvm-c50802cbee3f6f25059422ba0edcc455e395a207.tar.bz2 llvm-c50802cbee3f6f25059422ba0edcc455e395a207.zip | |
Reland "[lldb] Introduce ScriptedFrameProvider for real threads (#161870)" (#170236)
This patch re-lands #161870 with fixes to the previous test failures.
rdar://161834688
Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/Interfaces')
5 files changed, 209 insertions, 8 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp index d43036d6fe54..f6c707b2bd16 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp @@ -31,6 +31,7 @@ void ScriptInterpreterPythonInterfaces::Initialize() { ScriptedStopHookPythonInterface::Initialize(); ScriptedBreakpointPythonInterface::Initialize(); ScriptedThreadPlanPythonInterface::Initialize(); + ScriptedFrameProviderPythonInterface::Initialize(); } void ScriptInterpreterPythonInterfaces::Terminate() { @@ -40,6 +41,7 @@ void ScriptInterpreterPythonInterfaces::Terminate() { ScriptedStopHookPythonInterface::Terminate(); ScriptedBreakpointPythonInterface::Terminate(); ScriptedThreadPlanPythonInterface::Terminate(); + ScriptedFrameProviderPythonInterface::Terminate(); } #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp index b866bf332b7b..3dde5036453f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "lldb/Core/PluginManager.h" #include "lldb/Host/Config.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" @@ -30,18 +31,45 @@ ScriptedFrameProviderPythonInterface::ScriptedFrameProviderPythonInterface( ScriptInterpreterPythonImpl &interpreter) : ScriptedFrameProviderInterface(), ScriptedPythonInterface(interpreter) {} +bool ScriptedFrameProviderPythonInterface::AppliesToThread( + llvm::StringRef class_name, lldb::ThreadSP thread_sp) { + // If there is any issue with this method, we will just assume it also applies + // to this thread which is the default behavior. + constexpr bool fail_value = true; + Status error; + StructuredData::ObjectSP obj = + CallStaticMethod(class_name, "applies_to_thread", error, thread_sp); + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return fail_value; + + return obj->GetBooleanValue(fail_value); +} + llvm::Expected<StructuredData::GenericSP> ScriptedFrameProviderPythonInterface::CreatePluginObject( const llvm::StringRef class_name, lldb::StackFrameListSP input_frames, StructuredData::DictionarySP args_sp) { if (!input_frames) - return llvm::createStringError("Invalid frame list"); + return llvm::createStringError("invalid frame list"); StructuredDataImpl sd_impl(args_sp); return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr, input_frames, sd_impl); } +std::string ScriptedFrameProviderPythonInterface::GetDescription( + llvm::StringRef class_name) { + Status error; + StructuredData::ObjectSP obj = + CallStaticMethod(class_name, "get_description", error); + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + StructuredData::ObjectSP ScriptedFrameProviderPythonInterface::GetFrameAtIndex(uint32_t index) { Status error; @@ -54,4 +82,32 @@ ScriptedFrameProviderPythonInterface::GetFrameAtIndex(uint32_t index) { return obj; } +bool ScriptedFrameProviderPythonInterface::CreateInstance( + lldb::ScriptLanguage language, ScriptedInterfaceUsages usages) { + if (language != eScriptLanguagePython) + return false; + + return true; +} + +void ScriptedFrameProviderPythonInterface::Initialize() { + const std::vector<llvm::StringRef> ci_usages = { + "target frame-provider register -C <script-name> [-k key -v value ...]", + "target frame-provider list", + "target frame-provider remove <provider-name>", + "target frame-provider clear"}; + const std::vector<llvm::StringRef> api_usages = { + "SBTarget.RegisterScriptedFrameProvider", + "SBTarget.RemoveScriptedFrameProvider", + "SBTarget.ClearScriptedFrameProvider"}; + PluginManager::RegisterPlugin( + GetPluginNameStatic(), + llvm::StringRef("Provide scripted stack frames for threads"), + CreateInstance, eScriptLanguagePython, {ci_usages, api_usages}); +} + +void ScriptedFrameProviderPythonInterface::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h index fd163984028d..97a5cc7c669e 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h @@ -14,17 +14,22 @@ #if LLDB_ENABLE_PYTHON #include "ScriptedPythonInterface.h" +#include "lldb/Core/PluginInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h" #include <optional> namespace lldb_private { class ScriptedFrameProviderPythonInterface : public ScriptedFrameProviderInterface, - public ScriptedPythonInterface { + public ScriptedPythonInterface, + public PluginInterface { public: ScriptedFrameProviderPythonInterface( ScriptInterpreterPythonImpl &interpreter); + bool AppliesToThread(llvm::StringRef class_name, + lldb::ThreadSP thread_sp) override; + llvm::Expected<StructuredData::GenericSP> CreatePluginObject(llvm::StringRef class_name, lldb::StackFrameListSP input_frames, @@ -33,10 +38,24 @@ public: llvm::SmallVector<AbstractMethodRequirement> GetAbstractMethodRequirements() const override { return llvm::SmallVector<AbstractMethodRequirement>( - {{"get_frame_at_index"}}); + {{"get_description"}, {"get_frame_at_index"}}); } + std::string GetDescription(llvm::StringRef class_name) override; + StructuredData::ObjectSP GetFrameAtIndex(uint32_t index) override; + + static void Initialize(); + static void Terminate(); + + static bool CreateInstance(lldb::ScriptLanguage language, + ScriptedInterfaceUsages usages); + + static llvm::StringRef GetPluginNameStatic() { + return "ScriptedFrameProviderPythonInterface"; + } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index af2e0b5df4d2..ba4473cf9ec4 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -94,6 +94,19 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>( } template <> +lldb::ThreadSP +ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ThreadSP>( + python::PythonObject &p, Status &error) { + if (lldb::SBThread *sb_thread = reinterpret_cast<lldb::SBThread *>( + python::LLDBSWIGPython_CastPyObjectToSBThread(p.get()))) + return m_interpreter.GetOpaqueTypeFromSBThread(*sb_thread); + error = Status::FromErrorString( + "Couldn't cast lldb::SBThread to lldb_private::Thread."); + + return nullptr; +} + +template <> SymbolContext ScriptedPythonInterface::ExtractValueFromPythonObject<SymbolContext>( python::PythonObject &p, Status &error) { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h index 23c56610124a..53a7ba65f64b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -387,6 +387,112 @@ public: return m_object_instance_sp; } + /// Call a static method on a Python class without creating an instance. + /// + /// This method resolves a Python class by name and calls a static method + /// on it, returning the result. This is useful for calling class-level + /// methods that don't require an instance. + /// + /// \param class_name The fully-qualified name of the Python class. + /// \param method_name The name of the static method to call. + /// \param error Output parameter to receive error information if the call + /// fails. + /// \param args Arguments to pass to the static method. + /// + /// \return The return value of the static method call, or an error value. + template <typename T = StructuredData::ObjectSP, typename... Args> + T CallStaticMethod(llvm::StringRef class_name, llvm::StringRef method_name, + Status &error, Args &&...args) { + using namespace python; + using Locker = ScriptInterpreterPythonImpl::Locker; + + std::string caller_signature = + llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") + + llvm::Twine(class_name) + llvm::Twine(".") + + llvm::Twine(method_name) + llvm::Twine(")")) + .str(); + + if (class_name.empty()) + return ErrorWithMessage<T>(caller_signature, "missing script class name", + error); + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + // Get the interpreter dictionary. + auto dict = + PythonModule::MainModule().ResolveName<python::PythonDictionary>( + m_interpreter.GetDictionaryName()); + if (!dict.IsAllocated()) + return ErrorWithMessage<T>( + caller_signature, + llvm::formatv("could not find interpreter dictionary: {0}", + m_interpreter.GetDictionaryName()) + .str(), + error); + + // Resolve the class. + auto class_obj = + PythonObject::ResolveNameWithDictionary<python::PythonCallable>( + class_name, dict); + if (!class_obj.IsAllocated()) + return ErrorWithMessage<T>( + caller_signature, + llvm::formatv("could not find script class: {0}", class_name).str(), + error); + + // Get the static method from the class. + if (!class_obj.HasAttribute(method_name)) + return ErrorWithMessage<T>( + caller_signature, + llvm::formatv("class {0} does not have method {1}", class_name, + method_name) + .str(), + error); + + PythonCallable method = + class_obj.GetAttributeValue(method_name).AsType<PythonCallable>(); + if (!method.IsAllocated()) + return ErrorWithMessage<T>(caller_signature, + llvm::formatv("method {0}.{1} is not callable", + class_name, method_name) + .str(), + error); + + // Transform the arguments. + std::tuple<Args...> original_args = std::forward_as_tuple(args...); + auto transformed_args = TransformArgs(original_args); + + // Call the static method. + llvm::Expected<PythonObject> expected_return_object = + llvm::make_error<llvm::StringError>("Not initialized.", + llvm::inconvertibleErrorCode()); + std::apply( + [&method, &expected_return_object](auto &&...args) { + llvm::consumeError(expected_return_object.takeError()); + expected_return_object = method(args...); + }, + transformed_args); + + if (llvm::Error e = expected_return_object.takeError()) { + error = Status::FromError(std::move(e)); + return ErrorWithMessage<T>( + caller_signature, "python static method could not be called", error); + } + + PythonObject py_return = std::move(expected_return_object.get()); + + // Re-assign reference and pointer arguments if needed. + if (sizeof...(Args) > 0) + if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) + return ErrorWithMessage<T>( + caller_signature, + "couldn't re-assign reference and pointer arguments", error); + + // Extract value from Python object (handles unallocated case). + return ExtractValueFromPythonObject<T>(py_return, error); + } + protected: template <typename T = StructuredData::ObjectSP> T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { @@ -403,7 +509,7 @@ protected: llvm::Twine(method_name) + llvm::Twine(")")) .str(); if (!m_object_instance_sp) - return ErrorWithMessage<T>(caller_signature, "Python object ill-formed", + return ErrorWithMessage<T>(caller_signature, "python object ill-formed", error); Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, @@ -415,7 +521,7 @@ protected: if (!implementor.IsAllocated()) return llvm::is_contained(GetAbstractMethods(), method_name) ? ErrorWithMessage<T>(caller_signature, - "Python implementor not allocated.", + "python implementor not allocated", error) : T{}; @@ -436,20 +542,20 @@ protected: if (llvm::Error e = expected_return_object.takeError()) { error = Status::FromError(std::move(e)); return ErrorWithMessage<T>(caller_signature, - "Python method could not be called.", error); + "python method could not be called", error); } PythonObject py_return = std::move(expected_return_object.get()); // Now that we called the python method with the transformed arguments, - // we need to interate again over both the original and transformed + // we need to iterate again over both the original and transformed // parameter pack, and transform back the parameter that were passed in // the original parameter pack as references or pointers. if (sizeof...(Args) > 0) if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) return ErrorWithMessage<T>( caller_signature, - "Couldn't re-assign reference and pointer arguments.", error); + "couldn't re-assign reference and pointer arguments", error); if (!py_return.IsAllocated()) return {}; @@ -656,6 +762,11 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>( python::PythonObject &p, Status &error); template <> +lldb::ThreadSP +ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ThreadSP>( + python::PythonObject &p, Status &error); + +template <> lldb::StackFrameSP ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>( python::PythonObject &p, Status &error); |
