aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces
diff options
context:
space:
mode:
authorMed Ismail Bennani <ismail@bennani.ma>2025-12-02 10:59:40 -0800
committerGitHub <noreply@github.com>2025-12-02 18:59:40 +0000
commitc50802cbee3f6f25059422ba0edcc455e395a207 (patch)
treed0161c129df974f9a6f7b6bdb9a01e54bfd3f576 /lldb/source/Plugins/ScriptInterpreter/Python/Interfaces
parent879dddf2b4ede2e6474964f9e5b63545d271c733 (diff)
downloadllvm-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')
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp2
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp58
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h23
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp13
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h121
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);