summaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
diff options
context:
space:
mode:
authorMed Ismail Bennani <ismail@bennani.ma>2025-11-11 12:18:45 -0800
committerGitHub <noreply@github.com>2025-11-11 20:18:45 +0000
commit1e467e44851a9da96c16c0dcd16725f996e6abf7 (patch)
treeabdc4638385ff1f196cf85ba8d8f093f982942e2 /lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
parent75ef0be0c3b6b0313d541b2af673ee4bb091572b (diff)
[lldb] Introduce ScriptedFrameProvider for real threads (#161870)
This patch extends ScriptedFrame to work with real (non-scripted) threads, enabling frame providers to synthesize frames for native processes. Previously, ScriptedFrame only worked within ScriptedProcess/ScriptedThread contexts. This patch decouples ScriptedFrame from ScriptedThread, allowing users to augment or replace stack frames in real debugging sessions for use cases like custom calling conventions, reconstructing corrupted frames from core files, or adding diagnostic frames. Key changes: - ScriptedFrame::Create() now accepts ThreadSP instead of requiring ScriptedThread, extracting architecture from the target triple rather than ScriptedProcess.arch - Added SBTarget::RegisterScriptedFrameProvider() and ClearScriptedFrameProvider() APIs, with Target storing a SyntheticFrameProviderDescriptor template for new threads - Added "target frame-provider register/clear" commands for CLI access - Thread class gains LoadScriptedFrameProvider(), ClearScriptedFrameProvider(), and GetFrameProvider() methods for per-thread frame provider management - New SyntheticStackFrameList overrides FetchFramesUpTo() to lazily provide frames from either the frame provider or the real stack This enables practical use of the SyntheticFrameProvider infrastructure in real debugging workflows. rdar://161834688 Signed-off-by: Med Ismail Bennani <ismail@bennani.ma> Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h')
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h121
1 files changed, 116 insertions, 5 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index af88a69e34a1..c460f58b4e72 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -330,6 +330,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) {
@@ -346,7 +452,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,
@@ -358,7 +464,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{};
@@ -379,20 +485,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 {};
@@ -599,6 +705,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);