[Lldb-commits] [lldb] [lldb/Interpreter] Surface Python exceptions from scripted extensions (PR #198153)
Med Ismail Bennani via lldb-commits
lldb-commits at lists.llvm.org
Sun May 17 09:39:54 PDT 2026
https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/198153
>From d81d7248a35166813ba87b9d6ce62c9e7d6976ed Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Sun, 17 May 2026 09:39:36 -0700
Subject: [PATCH] [lldb/Interpreter] Surface Python exceptions from scripted
extensions
When calling scripted affordance methods, Python exceptions were silently
consumed by llvm::consumeError(), making it very hard to debug scripted
extensions: users would see a generic "Failed to create script object."
or just a null result, with no clue about what raised in their script.
This patch propagates the actual Python error through the existing
error-reporting channel of each call site:
- The Dispatch and CallStaticMethod templates now extract the full Python
backtrace via PythonException::ReadBacktrace and populate the Status
out-parameter with a message of the form "Python exception in
<script-class> method '<method>':\n<traceback>".
- ErrorWithMessage no longer overwrites a Status that already has detailed
content, so the traceback survives the helper's existing logging.
- ScriptedInterface now holds the ScriptedMetadata (set by each concrete
CreatePluginObject) so the error message can name the script class.
- For entry points that have no return-channel for errors
(ScriptedProcess::CreateInstance, ScriptedThreadPlan::DidPush,
BreakpointResolverScripted, OperatingSystemPython's constructor) the
detailed error is broadcast via Debugger::ReportError so users see
it instead of a silent failure.
- For entry points that already return llvm::Expected<T> or Status
(ScriptedThread::Create, ScriptedFrameProvider::CreateInstance,
StopHookScripted::SetScriptCallback) the existing return type now
carries the detailed error, replacing the previous consumeError +
generic-message pattern.
- ScriptedProcess::DoAttach / DoReadMemory / DoWriteMemory wrap their
affordance Status with the operation that failed.
A new test directory at lldb/test/API/functionalities/scripted_extensions/
listens for eBroadcastBitError diagnostics and asserts that each
malformed scripted extension produces a user-visible error.
Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
.../Interfaces/ScriptedBreakpointInterface.h | 2 +-
.../ScriptedFrameProviderInterface.h | 2 +-
.../Interfaces/ScriptedInterface.h | 43 +++-
.../Interfaces/ScriptedStopHookInterface.h | 2 +-
.../Interfaces/ScriptedThreadPlanInterface.h | 2 +-
.../lldb/Interpreter/ScriptInterpreter.h | 4 +
lldb/include/lldb/Utility/ScriptedMetadata.h | 2 +-
.../Breakpoint/BreakpointResolverScripted.cpp | 8 +-
.../Python/OperatingSystemPython.cpp | 8 +-
.../Process/scripted/ScriptedProcess.cpp | 66 +++++-
.../Process/scripted/ScriptedProcess.h | 14 +-
.../Process/scripted/ScriptedThread.cpp | 7 +-
.../OperatingSystemPythonInterface.cpp | 31 ++-
.../ScriptedBreakpointPythonInterface.cpp | 33 ++-
.../ScriptedFrameProviderPythonInterface.cpp | 30 ++-
.../ScriptedFramePythonInterface.cpp | 60 +++++-
.../ScriptedPlatformPythonInterface.cpp | 16 +-
.../ScriptedProcessPythonInterface.cpp | 83 ++++++--
.../Interfaces/ScriptedPythonInterface.h | 128 +++++++++++-
.../ScriptedStopHookPythonInterface.cpp | 13 +-
.../ScriptedThreadPlanPythonInterface.cpp | 33 ++-
.../ScriptedThreadPythonInterface.cpp | 59 +++++-
lldb/source/Target/ScriptedThreadPlan.cpp | 5 +
.../scripted_extensions/Makefile | 3 +
.../TestScriptedExtensionsDiagnostics.py | 124 +++++++++++
.../scripted_extensions/main.c | 1 +
.../malformed_scripted_extensions.py | 195 ++++++++++++++++++
27 files changed, 897 insertions(+), 77 deletions(-)
create mode 100644 lldb/test/API/functionalities/scripted_extensions/Makefile
create mode 100644 lldb/test/API/functionalities/scripted_extensions/TestScriptedExtensionsDiagnostics.py
create mode 100644 lldb/test/API/functionalities/scripted_extensions/main.c
create mode 100644 lldb/test/API/functionalities/scripted_extensions/malformed_scripted_extensions.py
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h
index a169432d2759a..1cfc6728258b9 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h
@@ -15,7 +15,7 @@
#include "lldb/lldb-private.h"
namespace lldb_private {
-class ScriptedBreakpointInterface : public ScriptedInterface {
+class ScriptedBreakpointInterface : virtual public ScriptedInterface {
public:
virtual llvm::Expected<StructuredData::GenericSP>
CreatePluginObject(llvm::StringRef class_name, lldb::BreakpointSP break_sp,
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
index b04af0c817b6e..2f149b6e85857 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h
@@ -14,7 +14,7 @@
#include "ScriptedInterface.h"
namespace lldb_private {
-class ScriptedFrameProviderInterface : public ScriptedInterface {
+class ScriptedFrameProviderInterface : virtual public ScriptedInterface {
public:
virtual bool AppliesToThread(llvm::StringRef class_name,
lldb::ThreadSP thread_sp) {
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedInterface.h
index 8ace90927b582..4f5686833932a 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedInterface.h
@@ -14,11 +14,13 @@
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
+#include "lldb/Utility/ScriptedMetadata.h"
#include "lldb/Utility/UnimplementedError.h"
#include "lldb/lldb-private.h"
#include "llvm/Support/Compiler.h"
+#include <functional>
#include <string>
namespace lldb_private {
@@ -31,6 +33,23 @@ class ScriptedInterface {
return m_object_instance_sp;
}
+ /// Set error callback to surface Python exceptions directly to users.
+ ///
+ /// This allows command handlers to receive Python exception details
+ /// immediately rather than relying on diagnostic broadcasts.
+ ///
+ /// \param callback Function to call with Status containing exception details.
+ virtual void SetErrorCallback(std::function<void(const Status &)> callback) {}
+
+ /// Clear the error callback.
+ virtual void ClearErrorCallback() {}
+
+ llvm::StringRef GetScriptClassName() const {
+ if (m_scripted_metadata_sp)
+ return m_scripted_metadata_sp->GetClassName();
+ return {};
+ }
+
struct AbstractMethodRequirement {
llvm::StringLiteral name;
size_t min_arg_count = 0;
@@ -56,18 +75,20 @@ class ScriptedInterface {
static Ret ErrorWithMessage(llvm::StringRef caller_name,
llvm::StringRef error_msg, Status &error,
LLDBLog log_category = LLDBLog::Process) {
+ // Log the error for debugging (includes function signature for context).
LLDB_LOGF(GetLog(log_category), "%s ERROR = %s", caller_name.data(),
error_msg.data());
- std::string full_error_message =
- llvm::Twine(caller_name + llvm::Twine(" ERROR = ") +
- llvm::Twine(error_msg))
- .str();
- if (const char *detailed_error = error.AsCString())
- full_error_message +=
- llvm::Twine(llvm::Twine(" (") + llvm::Twine(detailed_error) +
- llvm::Twine(")"))
- .str();
- error = Status(std::move(full_error_message));
+
+ // For user-facing messages, just pass through the Status if it already
+ // has detailed information (like Python tracebacks); otherwise set it.
+ const char *existing_error = error.AsCString();
+ if (!error.Fail() || !existing_error || existing_error[0] == '\0') {
+ // Status is empty, populate it with the simple error message.
+ error = Status::FromErrorString(error_msg.data());
+ }
+ // If Status already has content, leave it as-is (it has the Python
+ // traceback).
+
return {};
}
@@ -96,6 +117,8 @@ class ScriptedInterface {
protected:
StructuredData::GenericSP m_object_instance_sp;
+ lldb::ScriptedMetadataSP m_scripted_metadata_sp = nullptr;
};
} // namespace lldb_private
+
#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDINTERFACE_H
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedStopHookInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedStopHookInterface.h
index a829e62351fe8..73262bac909d1 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedStopHookInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedStopHookInterface.h
@@ -14,7 +14,7 @@
#include "ScriptedInterface.h"
namespace lldb_private {
-class ScriptedStopHookInterface : public ScriptedInterface {
+class ScriptedStopHookInterface : virtual public ScriptedInterface {
public:
virtual llvm::Expected<StructuredData::GenericSP>
CreatePluginObject(llvm::StringRef class_name, lldb::TargetSP target_sp,
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h
index ee634d15f2a9e..c773d6ef12832 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h
@@ -14,7 +14,7 @@
#include "ScriptedInterface.h"
namespace lldb_private {
-class ScriptedThreadPlanInterface : public ScriptedInterface {
+class ScriptedThreadPlanInterface : virtual public ScriptedInterface {
public:
virtual llvm::Expected<StructuredData::GenericSP>
CreatePluginObject(llvm::StringRef class_name,
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 58af82fb48390..2cc3890d86916 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -663,6 +663,10 @@ class ScriptInterpreter : public PluginInterface {
lldb::TargetSP GetOpaqueTypeFromSBTarget(const lldb::SBTarget &target) const;
+ /// Get the debugger associated with this script interpreter.
+ Debugger &GetDebugger() { return m_debugger; }
+ const Debugger &GetDebugger() const { return m_debugger; }
+
protected:
Debugger &m_debugger;
lldb::ScriptLanguage m_script_lang;
diff --git a/lldb/include/lldb/Utility/ScriptedMetadata.h b/lldb/include/lldb/Utility/ScriptedMetadata.h
index c6b660830e158..0d9b3909249ee 100644
--- a/lldb/include/lldb/Utility/ScriptedMetadata.h
+++ b/lldb/include/lldb/Utility/ScriptedMetadata.h
@@ -19,7 +19,7 @@ class ScriptedMetadata {
public:
ScriptedMetadata(llvm::StringRef class_name,
StructuredData::DictionarySP dict_sp)
- : m_class_name(class_name.data()), m_args_sp(dict_sp) {}
+ : m_class_name(class_name.str()), m_args_sp(dict_sp) {}
ScriptedMetadata(const ProcessInfo &process_info) {
lldb::ScriptedMetadataSP metadata_sp = process_info.GetScriptedMetadata();
diff --git a/lldb/source/Breakpoint/BreakpointResolverScripted.cpp b/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
index 84d918029faf8..e23bd978c5a4e 100644
--- a/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
+++ b/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
@@ -20,6 +20,7 @@
#include "lldb/Target/Target.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
+#include "llvm/Support/FormatVariadic.h"
using namespace lldb;
using namespace lldb_private;
@@ -88,7 +89,12 @@ void BreakpointResolverScripted::CreateImplementationIfNeeded(
m_interface_sp->CreatePluginObject(m_class_name, breakpoint_sp, m_args);
if (!obj_or_err) {
m_interface_sp.reset();
- m_error = Status::FromError(obj_or_err.takeError());
+ std::string msg = llvm::toString(obj_or_err.takeError());
+ Debugger::ReportError(
+ llvm::formatv("Failed to create BreakpointResolverScripted: {0}", msg)
+ .str(),
+ target.GetDebugger().GetID());
+ m_error = Status(msg);
return;
}
StructuredData::ObjectSP object_sp = *obj_or_err;
diff --git a/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp b/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp
index 96b2b9d9ee088..aedea7f886571 100644
--- a/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp
+++ b/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp
@@ -33,6 +33,7 @@
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
+#include "llvm/Support/FormatVariadic.h"
#include <memory>
@@ -120,7 +121,12 @@ OperatingSystemPython::OperatingSystemPython(lldb_private::Process *process,
os_plugin_class_name, exe_ctx, nullptr);
if (!obj_or_err) {
- llvm::consumeError(obj_or_err.takeError());
+ std::string msg = llvm::toString(obj_or_err.takeError());
+ if (process)
+ Debugger::ReportError(
+ llvm::formatv("Failed to create OperatingSystemPython: {0}", msg)
+ .str(),
+ process->GetTarget().GetDebugger().GetID());
return;
}
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
index c254a6841b707..5470936d6051d 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -27,6 +27,10 @@
#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"
+#include "llvm/Support/Error.h"
+
+#include <string>
+
using namespace lldb;
using namespace lldb_private;
@@ -57,11 +61,27 @@ lldb::ProcessSP ScriptedProcess::CreateInstance(lldb::TargetSP target_sp,
ScriptedMetadata scripted_metadata(target_sp->GetProcessLaunchInfo());
+ // CreateInstance is invoked for every process plugin during process
+ // creation; if the launch info doesn't request a scripted process, bail
+ // out silently rather than treating the missing class name as an error.
+ if (!scripted_metadata)
+ return nullptr;
+
Status error;
auto process_sp = std::shared_ptr<ScriptedProcess>(
new ScriptedProcess(target_sp, listener_sp, scripted_metadata, error));
if (error.Fail() || !process_sp || !process_sp->m_interface_up) {
+ // CreateInstance returns nullptr on failure with no Status output
+ // parameter, so we must report the error via the diagnostic system for
+ // users to see it.
+ if (error.Fail()) {
+ Debugger::ReportError(
+ llvm::formatv("Failed to create ScriptedProcess: {0}",
+ error.AsCString())
+ .str(),
+ target_sp->GetDebugger().GetID());
+ }
LLDB_LOGF(GetLog(LLDBLog::Process), "%s", error.AsCString());
return nullptr;
}
@@ -113,8 +133,10 @@ ScriptedProcess::ScriptedProcess(lldb::TargetSP target_sp,
m_scripted_metadata.GetArgsSP());
if (!obj_or_err) {
- llvm::consumeError(obj_or_err.takeError());
- error = Status::FromErrorString("Failed to create script object.");
+ // Extract the detailed error message including the Python backtrace.
+ std::string error_msg = llvm::toString(obj_or_err.takeError());
+ error = Status::FromErrorStringWithFormatv(
+ "Failed to create script object: {0}", error_msg);
return;
}
@@ -181,8 +203,10 @@ void ScriptedProcess::DidResume() {
Status ScriptedProcess::DoResume(RunDirection direction) {
LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s resuming process", __FUNCTION__);
- if (direction == RunDirection::eRunForward)
- return GetInterface().Resume();
+ if (direction == RunDirection::eRunForward) {
+ Status error = GetInterface().Resume();
+ return error;
+ }
// FIXME: Pipe reverse continue through Scripted Processes
return Status::FromErrorStringWithFormatv(
"{0} does not support reverse execution of processes", GetPluginName());
@@ -190,10 +214,13 @@ Status ScriptedProcess::DoResume(RunDirection direction) {
Status ScriptedProcess::DoAttach(const ProcessAttachInfo &attach_info) {
Status error = GetInterface().Attach(attach_info);
+ if (error.Fail()) {
+ error = Status::FromErrorStringWithFormatv(
+ "Failed to attach to scripted process: {0}", error.AsCString());
+ return error;
+ }
SetPrivateState(eStateRunning);
SetPrivateState(eStateStopped);
- if (error.Fail())
- return error;
// NOTE: We need to set the PID before finishing to attach otherwise we will
// hit an assert when calling the attach completion handler.
DidLaunch();
@@ -225,8 +252,14 @@ size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
lldb::DataExtractorSP data_extractor_sp =
GetInterface().ReadMemoryAtAddress(addr, size, error);
- if (!data_extractor_sp || !data_extractor_sp->HasData() || error.Fail())
+ if (!data_extractor_sp || !data_extractor_sp->HasData() || error.Fail()) {
+ if (error.Fail()) {
+ error = Status::FromErrorStringWithFormatv(
+ "Failed to read memory from scripted process at 0x{0:x}: {1}", addr,
+ error.AsCString());
+ }
return 0;
+ }
offset_t bytes_copied = data_extractor_sp->CopyByteOrderedData(
0, data_extractor_sp->GetByteSize(), buf, size, GetByteOrder());
@@ -252,9 +285,15 @@ size_t ScriptedProcess::DoWriteMemory(lldb::addr_t vm_addr, const void *buf,
lldb::offset_t bytes_written =
GetInterface().WriteMemoryAtAddress(vm_addr, data_extractor_sp, error);
- if (!bytes_written || bytes_written == LLDB_INVALID_OFFSET)
+ if (!bytes_written || bytes_written == LLDB_INVALID_OFFSET) {
+ if (error.Fail()) {
+ error = Status::FromErrorStringWithFormatv(
+ "Failed to write memory to scripted process at 0x{0:x}: {1}", vm_addr,
+ error.AsCString());
+ }
return ScriptedInterface::ErrorWithMessage<size_t>(
LLVM_PRETTY_FUNCTION, "Failed to copy write buffer to memory.", error);
+ }
// FIXME: We should use the diagnostic system to report a warning if the
// `bytes_written` is different from `size`.
@@ -566,3 +605,14 @@ void *ScriptedProcess::GetImplementation() {
return object_instance_sp->GetAsGeneric()->GetValue();
return nullptr;
}
+
+void ScriptedProcess::SetScriptedInterfaceErrorCallback(
+ std::function<void(const Status &)> callback) {
+ if (m_interface_up)
+ m_interface_up->SetErrorCallback(std::move(callback));
+}
+
+void ScriptedProcess::ClearScriptedInterfaceErrorCallback() {
+ if (m_interface_up)
+ m_interface_up->ClearErrorCallback();
+}
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
index 8371180734217..9ce4a3f8d3cc9 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
@@ -17,8 +17,6 @@
#include "ScriptedThread.h"
-#include <mutex>
-
namespace lldb_private {
class ScriptedProcess : public Process {
public:
@@ -93,6 +91,18 @@ class ScriptedProcess : public Process {
void *GetImplementation() override;
+ /// Set error callback to surface Python exceptions directly to users.
+ ///
+ /// This allows command handlers to receive Python exception details
+ /// immediately rather than relying on diagnostic broadcasts.
+ ///
+ /// \param callback Function to call with Status containing exception details.
+ void SetScriptedInterfaceErrorCallback(
+ std::function<void(const Status &)> callback);
+
+ /// Clear the error callback.
+ void ClearScriptedInterfaceErrorCallback();
+
void ForceScriptedState(lldb::StateType state) override {
// If we're about to stop, we should fetch the loaded dynamic libraries
// dictionary before emitting the private stop event to avoid having the
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index 77c418f5a11df..3f6a09f2cbf8a 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -61,11 +61,8 @@ ScriptedThread::Create(ScriptedProcess &process,
thread_class_name, exe_ctx, process.m_scripted_metadata.GetArgsSP(),
script_object);
- if (!obj_or_err) {
- llvm::consumeError(obj_or_err.takeError());
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "Failed to create script object.");
- }
+ if (!obj_or_err)
+ return obj_or_err.takeError();
StructuredData::GenericSP owned_script_object_sp = *obj_or_err;
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/OperatingSystemPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/OperatingSystemPythonInterface.cpp
index a48a9bb8fa292..8d45a590c65a4 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/OperatingSystemPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/OperatingSystemPythonInterface.cpp
@@ -30,8 +30,12 @@ llvm::Expected<StructuredData::GenericSP>
OperatingSystemPythonInterface::CreatePluginObject(
llvm::StringRef class_name, ExecutionContext &exe_ctx,
StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) {
- return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr,
- exe_ctx.GetProcessSP());
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, nullptr, exe_ctx.GetProcessSP());
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, nullptr);
+ return obj_or_err;
}
StructuredData::DictionarySP
@@ -41,6 +45,11 @@ OperatingSystemPythonInterface::CreateThread(lldb::tid_t tid,
StructuredData::DictionarySP dict = Dispatch<StructuredData::DictionarySP>(
"create_thread", error, tid, context);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "CreateThread Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};
@@ -53,6 +62,11 @@ StructuredData::ArraySP OperatingSystemPythonInterface::GetThreadInfo() {
StructuredData::ArraySP arr =
Dispatch<StructuredData::ArraySP>("get_thread_info", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetThreadInfo Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr,
error))
return {};
@@ -69,6 +83,12 @@ OperatingSystemPythonInterface::GetRegisterContextForTID(lldb::tid_t tid) {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_register_data", error, tid);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetRegisterContextForTID Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -79,6 +99,13 @@ OperatingSystemPythonInterface::GetRegisterContextForTID(lldb::tid_t tid) {
std::optional<bool> OperatingSystemPythonInterface::DoesPluginReportAllThreads() {
Status error;
StructuredData::ObjectSP obj = Dispatch("does_plugin_report_all_threads", error);
+
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "DoesPluginReportAllThreads Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp
index 76e7a8e5338c8..c2bdbb8963273 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp
@@ -32,8 +32,12 @@ llvm::Expected<StructuredData::GenericSP>
ScriptedBreakpointPythonInterface::CreatePluginObject(
llvm::StringRef class_name, lldb::BreakpointSP break_sp,
const StructuredDataImpl &args_sp) {
- return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr,
- break_sp, args_sp);
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, nullptr, break_sp, args_sp);
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, nullptr);
+ return obj_or_err;
}
bool ScriptedBreakpointPythonInterface::OverridesResolver(
@@ -71,6 +75,11 @@ bool ScriptedBreakpointPythonInterface::ResolverCallback(
StructuredData::ObjectSP obj = Dispatch("__callback__", error, sym_ctx);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "ResolverCallback Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
Log *log = GetLog(LLDBLog::Script);
@@ -84,6 +93,11 @@ lldb::SearchDepth ScriptedBreakpointPythonInterface::GetDepth() {
Status error;
StructuredData::ObjectSP obj = Dispatch("__get_depth__", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetDepth Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
return lldb::eSearchDepthModule;
@@ -100,6 +114,11 @@ std::optional<std::string> ScriptedBreakpointPythonInterface::GetShortHelp() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_short_help", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetShortHelp Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
return {};
@@ -114,8 +133,11 @@ lldb::BreakpointLocationSP ScriptedBreakpointPythonInterface::WasHit(
lldb::BreakpointLocationSP loc_sp = Dispatch<lldb::BreakpointLocationSP>(
"was_hit", py_error, frame_sp, bp_loc_sp);
- if (py_error.Fail())
+ if (py_error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "WasHit Python exception: {0}",
+ py_error.AsCString());
return bp_loc_sp;
+ }
return loc_sp;
}
@@ -127,6 +149,11 @@ ScriptedBreakpointPythonInterface::GetLocationDescription(
StructuredData::ObjectSP obj =
Dispatch("get_location_description", error, bp_loc_sp, level);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetLocationDescription Python exception: {0}", error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
index 8c95592dfc5f0..bd44a1b739649 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp
@@ -35,6 +35,12 @@ bool ScriptedFrameProviderPythonInterface::AppliesToThread(
Status error;
StructuredData::ObjectSP obj =
CallStaticMethod(class_name, "applies_to_thread", error, thread_sp);
+
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "AppliesToThread Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return fail_value;
@@ -50,8 +56,12 @@ ScriptedFrameProviderPythonInterface::CreatePluginObject(
return llvm::createStringError("invalid frame list");
StructuredDataImpl sd_impl(args_sp);
- return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr,
- input_frames, sd_impl);
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, nullptr, input_frames, sd_impl);
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, args_sp);
+ return obj_or_err;
}
std::string ScriptedFrameProviderPythonInterface::GetDescription(
@@ -59,6 +69,12 @@ std::string ScriptedFrameProviderPythonInterface::GetDescription(
Status error;
StructuredData::ObjectSP obj =
CallStaticMethod(class_name, "get_description", error);
+
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetDescription Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -72,6 +88,11 @@ ScriptedFrameProviderPythonInterface::GetPriority(llvm::StringRef class_name) {
StructuredData::ObjectSP obj =
CallStaticMethod(class_name, "get_priority", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetPriority Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return std::nullopt;
@@ -89,6 +110,11 @@ ScriptedFrameProviderPythonInterface::GetFrameAtIndex(uint32_t index) {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_frame_at_index", error, index);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetFrameAtIndex Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
index eaee6a51992c6..62b541f328267 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
@@ -34,14 +34,23 @@ ScriptedFramePythonInterface::CreatePluginObject(
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);
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, script_obj, exe_ctx_ref_sp, sd_impl);
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, args_sp);
+ return obj_or_err;
}
lldb::user_id_t ScriptedFramePythonInterface::GetID() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_id", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetID Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return LLDB_INVALID_FRAME_ID;
@@ -53,6 +62,11 @@ lldb::addr_t ScriptedFramePythonInterface::GetPC() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_pc", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetPC Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return LLDB_INVALID_ADDRESS;
@@ -65,6 +79,8 @@ std::optional<SymbolContext> ScriptedFramePythonInterface::GetSymbolContext() {
auto sym_ctx = Dispatch<SymbolContext>("get_symbol_context", error);
if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetSymbolContext Python exception: {0}",
+ error.AsCString());
return ErrorWithMessage<SymbolContext>(LLVM_PRETTY_FUNCTION,
error.AsCString(), error);
}
@@ -76,6 +92,11 @@ std::optional<std::string> ScriptedFramePythonInterface::GetFunctionName() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_function_name", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetFunctionName Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -88,6 +109,11 @@ ScriptedFramePythonInterface::GetDisplayFunctionName() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_display_function_name", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetDisplayFunctionName Python exception: {0}", error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -99,6 +125,11 @@ bool ScriptedFramePythonInterface::IsInlined() {
Status error;
StructuredData::ObjectSP obj = Dispatch("is_inlined", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "IsInlined Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return false;
@@ -110,6 +141,11 @@ bool ScriptedFramePythonInterface::IsArtificial() {
Status error;
StructuredData::ObjectSP obj = Dispatch("is_artificial", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "IsArtificial Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return false;
@@ -121,6 +157,11 @@ bool ScriptedFramePythonInterface::IsHidden() {
Status error;
StructuredData::ObjectSP obj = Dispatch("is_hidden", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "IsHidden Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return false;
@@ -133,6 +174,11 @@ StructuredData::DictionarySP ScriptedFramePythonInterface::GetRegisterInfo() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_register_info", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetRegisterInfo Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};
@@ -144,6 +190,11 @@ std::optional<std::string> ScriptedFramePythonInterface::GetRegisterContext() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_register_context", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetRegisterContext Python exception: {0}", error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -156,6 +207,8 @@ lldb::ValueObjectListSP ScriptedFramePythonInterface::GetVariables() {
auto vals = Dispatch<lldb::ValueObjectListSP>("get_variables", error);
if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetVariables Python exception: {0}",
+ error.AsCString());
return ErrorWithMessage<lldb::ValueObjectListSP>(LLVM_PRETTY_FUNCTION,
error.AsCString(), error);
}
@@ -172,6 +225,9 @@ ScriptedFramePythonInterface::GetValueObjectForVariableExpression(
status);
if (dispatch_error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetValueObjectForVariableExpression Python exception: {0}",
+ dispatch_error.AsCString());
return ErrorWithMessage<lldb::ValueObjectSP>(
LLVM_PRETTY_FUNCTION, dispatch_error.AsCString(), dispatch_error);
}
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp
index 76ae3d32033a9..c6662fac534d9 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp
@@ -34,8 +34,12 @@ ScriptedPlatformPythonInterface::CreatePluginObject(
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);
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, script_obj, exe_ctx_ref_sp, sd_impl);
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, args_sp);
+ return obj_or_err;
}
StructuredData::DictionarySP ScriptedPlatformPythonInterface::ListProcesses() {
@@ -44,6 +48,10 @@ StructuredData::DictionarySP ScriptedPlatformPythonInterface::ListProcesses() {
Dispatch<StructuredData::DictionarySP>("list_processes", error);
if (!dict_sp || !dict_sp->IsValid() || error.Fail()) {
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "ListProcesses Python exception: {0}",
+ error.AsCString());
+ }
return ScriptedInterface::ErrorWithMessage<StructuredData::DictionarySP>(
LLVM_PRETTY_FUNCTION,
llvm::Twine("Null or invalid object (" +
@@ -62,6 +70,10 @@ ScriptedPlatformPythonInterface::GetProcessInfo(lldb::pid_t pid) {
Dispatch<StructuredData::DictionarySP>("get_process_info", error, pid);
if (!dict_sp || !dict_sp->IsValid() || error.Fail()) {
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetProcessInfo Python exception: {0}",
+ error.AsCString());
+ }
return ScriptedInterface::ErrorWithMessage<StructuredData::DictionarySP>(
LLVM_PRETTY_FUNCTION,
llvm::Twine("Null or invalid object (" +
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp
index e57426cc90ba2..33dd2dab5e8a7 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp
@@ -37,8 +37,12 @@ ScriptedProcessPythonInterface::CreatePluginObject(
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);
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, script_obj, exe_ctx_ref_sp, sd_impl);
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, args_sp);
+ return obj_or_err;
}
StructuredData::DictionarySP ScriptedProcessPythonInterface::GetCapabilities() {
@@ -46,6 +50,11 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetCapabilities() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_capabilities", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetCapabilities Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};
@@ -88,6 +97,11 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetThreadsInfo() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_threads_info", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetThreadsInfo Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};
@@ -97,48 +111,53 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetThreadsInfo() {
bool ScriptedProcessPythonInterface::CreateBreakpoint(lldb::addr_t addr,
Status &error) {
- Status py_error;
StructuredData::ObjectSP obj =
- Dispatch("create_breakpoint", py_error, addr, error);
+ Dispatch("create_breakpoint", error, addr, error);
- // If there was an error on the python call, surface it to the user.
- if (py_error.Fail())
- error = std::move(py_error);
+ // Error is already set by Dispatch if a Python exception occurred.
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "CreateBreakpoint failed: {0}",
+ error.AsCString());
+ return false;
+ }
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
- return {};
+ return false;
return obj->GetBooleanValue();
}
lldb::DataExtractorSP ScriptedProcessPythonInterface::ReadMemoryAtAddress(
lldb::addr_t address, size_t size, Status &error) {
- Status py_error;
lldb::DataExtractorSP data_sp = Dispatch<lldb::DataExtractorSP>(
- "read_memory_at_address", py_error, address, size, error);
+ "read_memory_at_address", error, address, size, error);
- // If there was an error on the python call, surface it to the user.
- if (py_error.Fail())
- error = std::move(py_error);
+ // Error is already set by Dispatch if a Python exception occurred.
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "ReadMemoryAtAddress failed: {0}",
+ error.AsCString());
+ }
return data_sp;
}
lldb::offset_t ScriptedProcessPythonInterface::WriteMemoryAtAddress(
lldb::addr_t addr, lldb::DataExtractorSP data_sp, Status &error) {
- Status py_error;
StructuredData::ObjectSP obj =
- Dispatch("write_memory_at_address", py_error, addr, data_sp, error);
+ Dispatch("write_memory_at_address", error, addr, data_sp, error);
+
+ // Error is already set by Dispatch if a Python exception occurred.
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "WriteMemoryAtAddress failed: {0}",
+ error.AsCString());
+ return LLDB_INVALID_OFFSET;
+ }
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return LLDB_INVALID_OFFSET;
- // If there was an error on the python call, surface it to the user.
- if (py_error.Fail())
- error = std::move(py_error);
-
return obj->GetUnsignedIntegerValue(LLDB_INVALID_OFFSET);
}
@@ -147,6 +166,11 @@ StructuredData::ArraySP ScriptedProcessPythonInterface::GetLoadedImages() {
StructuredData::ArraySP array =
Dispatch<StructuredData::ArraySP>("get_loaded_images", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetLoadedImages Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, array,
error))
return {};
@@ -158,6 +182,11 @@ lldb::pid_t ScriptedProcessPythonInterface::GetProcessID() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_process_id", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetProcessID Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return LLDB_INVALID_PROCESS_ID;
@@ -169,6 +198,11 @@ bool ScriptedProcessPythonInterface::IsAlive() {
Status error;
StructuredData::ObjectSP obj = Dispatch("is_alive", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "IsAlive Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -181,6 +215,12 @@ ScriptedProcessPythonInterface::GetScriptedThreadPluginName() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_scripted_thread_plugin", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetScriptedThreadPluginName Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -198,6 +238,11 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetMetadata() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_process_metadata", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetMetadata Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index 637ca1b2ab1f9..fbaec7249ccc6 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -9,12 +9,14 @@
#ifndef LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
#define LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDPYTHONINTERFACE_H
+#include <functional>
#include <optional>
#include <sstream>
#include <tuple>
#include <type_traits>
#include <utility>
+#include "lldb/Core/Debugger.h"
#include "lldb/Interpreter/Interfaces/ScriptedInterface.h"
#include "lldb/Utility/DataBufferHeap.h"
@@ -29,6 +31,22 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
ScriptedPythonInterface(ScriptInterpreterPythonImpl &interpreter);
~ScriptedPythonInterface() override = default;
+ /// Set callback to surface Python exceptions to CommandReturnObject.
+ ///
+ /// When set, this callback will be invoked whenever a Python exception occurs
+ /// in scripting affordance methods, allowing errors to be surfaced directly
+ /// to the user via CommandReturnObject::AppendError().
+ ///
+ /// If no callback is registered, errors will be reported via
+ /// Debugger::ReportError() instead.
+ ///
+ /// \param callback Function to call with Status containing exception details.
+ using ErrorCallback = std::function<void(const Status &)>;
+ void SetErrorCallback(ErrorCallback callback) override {
+ m_error_callback = std::move(callback);
+ }
+ void ClearErrorCallback() override { m_error_callback = nullptr; }
+
enum class AbstractMethodCheckerCases {
eNotImplemented,
eNotAllocated,
@@ -130,7 +148,15 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
llvm::Expected<PythonObject> callable_or_err =
class_dict.GetItem(method_name);
if (!callable_or_err) {
- llvm::consumeError(callable_or_err.takeError());
+ Log *log = GetLog(LLDBLog::Script);
+ if (log) {
+ std::string error_msg =
+ ExtractPythonError(callable_or_err.takeError());
+ LLDB_LOGF(log, "Failed to get method '%s': %s", method_name.data(),
+ error_msg.c_str());
+ } else {
+ llvm::consumeError(callable_or_err.takeError());
+ }
SET_CASE_AND_CONTINUE(method_name,
AbstractMethodCheckerCases::eNotAllocated)
}
@@ -145,7 +171,15 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
auto arg_info_or_err = callable.GetArgInfo();
if (!arg_info_or_err) {
- llvm::consumeError(arg_info_or_err.takeError());
+ Log *log = GetLog(LLDBLog::Script);
+ if (log) {
+ std::string error_msg =
+ ExtractPythonError(arg_info_or_err.takeError());
+ LLDB_LOGF(log, "Failed to get arg info for method '%s': %s",
+ method_name.data(), error_msg.c_str());
+ } else {
+ llvm::consumeError(arg_info_or_err.takeError());
+ }
SET_CASE_AND_CONTINUE(method_name,
AbstractMethodCheckerCases::eUnknownArgumentCount)
}
@@ -256,14 +290,18 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
std::apply(
[&init, &expected_return_object](auto &&...args) {
- llvm::consumeError(expected_return_object.takeError());
+ // Consume placeholder error (expected initial state).
+ if (!expected_return_object)
+ llvm::consumeError(expected_return_object.takeError());
expected_return_object = init(args...);
},
std::tuple_cat(transformed_args, std::make_tuple(dict)));
} else {
std::apply(
[&init, &expected_return_object](auto &&...args) {
- llvm::consumeError(expected_return_object.takeError());
+ // Consume placeholder error (expected initial state).
+ if (!expected_return_object)
+ llvm::consumeError(expected_return_object.takeError());
expected_return_object = init(args...);
},
transformed_args);
@@ -465,13 +503,39 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
llvm::createStringError("not initialized");
std::apply(
[&method, &expected_return_object](auto &&...args) {
- llvm::consumeError(expected_return_object.takeError());
+ // Consume placeholder error (expected initial state).
+ if (!expected_return_object)
+ 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));
+ // Extract Python backtrace and log it.
+ std::string detailed_error = ExtractPythonError(std::move(e));
+
+ Log *log = GetLog(LLDBLog::Script);
+ if (log) {
+ LLDB_LOGF(log, "%s: Python exception in static method %s:\n%s",
+ caller_signature.c_str(), method_name.data(),
+ detailed_error.c_str());
+ }
+
+ // Create Status with full context including the interface type.
+ // TODO: Stringify `args` and include them in the message so users
+ // can see what was passed to the failing call (e.g.
+ // `read_memory_at_address(0x500000000, 4)`). Requires a SFINAE
+ // helper that falls back to a placeholder for types without a
+ // format_provider / operator<<.
+ error = Status::FromErrorStringWithFormatv(
+ "Python exception in {0} method '{1}':\n{2}", GetScriptClassName(),
+ method_name, detailed_error);
+
+ // Surface error to user: use callback if available.
+ if (m_error_callback) {
+ m_error_callback(error);
+ }
+
return ErrorWithMessage<T>(
caller_signature, "python static method could not be called", error);
}
@@ -490,6 +554,23 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
}
protected:
+ /// Extract detailed error message including Python backtrace if available.
+ ///
+ /// This helper processes llvm::Error objects that may contain PythonException
+ /// instances, extracting full Python backtraces when available.
+ ///
+ /// \param error The llvm::Error to extract information from.
+ /// \return A string containing the error message, including full Python
+ /// backtrace if the error was a PythonException.
+ static std::string ExtractPythonError(llvm::Error error) {
+ std::string error_msg;
+ llvm::handleAllErrors(
+ std::move(error),
+ [&](python::PythonException &E) { error_msg = E.ReadBacktrace(); },
+ [&](const llvm::ErrorInfoBase &E) { error_msg = E.message(); });
+ return error_msg;
+ }
+
template <typename T = StructuredData::ObjectSP>
T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) {
return p.CreateStructuredObject();
@@ -528,14 +609,40 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
llvm::createStringError("not initialized");
std::apply(
[&implementor, &method_name, &expected_return_object](auto &&...args) {
- llvm::consumeError(expected_return_object.takeError());
+ // Consume placeholder error (expected initial state).
+ if (!expected_return_object)
+ llvm::consumeError(expected_return_object.takeError());
expected_return_object =
implementor.CallMethod(method_name.data(), args...);
},
transformed_args);
if (llvm::Error e = expected_return_object.takeError()) {
- error = Status::FromError(std::move(e));
+ // Extract Python backtrace and log it.
+ std::string detailed_error = ExtractPythonError(std::move(e));
+
+ Log *log = GetLog(LLDBLog::Script);
+ if (log) {
+ LLDB_LOGF(log, "%s: Python exception in %s:\n%s",
+ caller_signature.c_str(), method_name.data(),
+ detailed_error.c_str());
+ }
+
+ // Create Status with full context including the interface type.
+ // TODO: Stringify `args` and include them in the message so users
+ // can see what was passed to the failing call (e.g.
+ // `read_memory_at_address(0x500000000, 4)`). Requires a SFINAE
+ // helper that falls back to a placeholder for types without a
+ // format_provider / operator<<.
+ error = Status::FromErrorStringWithFormatv(
+ "Python exception in {0} method '{1}':\n{2}", GetScriptClassName(),
+ method_name, detailed_error);
+
+ // Surface error to user: use callback if available.
+ if (m_error_callback) {
+ m_error_callback(error);
+ }
+
return ErrorWithMessage<T>(caller_signature,
"python method could not be called", error);
}
@@ -730,6 +837,11 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
// The lifetime is managed by the ScriptInterpreter
ScriptInterpreterPythonImpl &m_interpreter;
+
+ // Optional callback for surfacing errors to users (e.g.,
+ // CommandReturnObject). If not set, errors are reported via
+ // Debugger::ReportError().
+ ErrorCallback m_error_callback;
};
template <>
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStopHookPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStopHookPythonInterface.cpp
index d497fc8a15721..5171ab5213713 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStopHookPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedStopHookPythonInterface.cpp
@@ -29,8 +29,12 @@ llvm::Expected<StructuredData::GenericSP>
ScriptedStopHookPythonInterface::CreatePluginObject(llvm::StringRef class_name,
lldb::TargetSP target_sp,
const StructuredDataImpl &args_sp) {
- return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr,
- target_sp, args_sp);
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, nullptr, target_sp, args_sp);
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, nullptr);
+ return obj_or_err;
}
llvm::Expected<bool>
@@ -41,6 +45,11 @@ ScriptedStopHookPythonInterface::HandleStop(ExecutionContext &exe_ctx,
Status error;
StructuredData::ObjectSP obj = Dispatch("handle_stop", error, exe_ctx_ref_sp, output_sp);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "HandleStop Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
if (!obj)
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.cpp
index 95789bfdc39aa..8df055eae6285 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.cpp
@@ -28,8 +28,12 @@ llvm::Expected<StructuredData::GenericSP>
ScriptedThreadPlanPythonInterface::CreatePluginObject(
const llvm::StringRef class_name, lldb::ThreadPlanSP thread_plan_sp,
const StructuredDataImpl &args_sp) {
- return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr,
- thread_plan_sp, args_sp);
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, nullptr, thread_plan_sp, args_sp);
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, nullptr);
+ return obj_or_err;
}
llvm::Expected<bool>
@@ -37,6 +41,11 @@ ScriptedThreadPlanPythonInterface::ExplainsStop(Event *event) {
Status error;
StructuredData::ObjectSP obj = Dispatch("explains_stop", error, event);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "ExplainsStop Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
if (!obj)
@@ -52,6 +61,11 @@ ScriptedThreadPlanPythonInterface::ShouldStop(Event *event) {
Status error;
StructuredData::ObjectSP obj = Dispatch("should_stop", error, event);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "ShouldStop Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
if (!obj)
@@ -66,6 +80,11 @@ llvm::Expected<bool> ScriptedThreadPlanPythonInterface::IsStale() {
Status error;
StructuredData::ObjectSP obj = Dispatch("is_stale", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "IsStale Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
if (!obj)
@@ -80,6 +99,11 @@ lldb::StateType ScriptedThreadPlanPythonInterface::GetRunState() {
Status error;
StructuredData::ObjectSP obj = Dispatch("should_step", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetRunState Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return lldb::eStateStepping;
@@ -93,8 +117,11 @@ ScriptedThreadPlanPythonInterface::GetStopDescription(lldb::StreamSP &stream) {
Status error;
Dispatch("stop_description", error, stream);
- if (error.Fail())
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetStopDescription Python exception: {0}", error.AsCString());
return error.ToError();
+ }
return llvm::Error::success();
}
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
index c34c6ee74fbb5..5a384a2e07374 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
@@ -34,14 +34,23 @@ ScriptedThreadPythonInterface::CreatePluginObject(
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);
+ auto obj_or_err = ScriptedPythonInterface::CreatePluginObject(
+ class_name, script_obj, exe_ctx_ref_sp, sd_impl);
+ if (obj_or_err)
+ m_scripted_metadata_sp =
+ std::make_shared<ScriptedMetadata>(class_name, args_sp);
+ return obj_or_err;
}
lldb::tid_t ScriptedThreadPythonInterface::GetThreadID() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_thread_id", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetThreadID Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return LLDB_INVALID_THREAD_ID;
@@ -53,6 +62,11 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetName() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_name", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetName Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -64,6 +78,11 @@ lldb::StateType ScriptedThreadPythonInterface::GetState() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_state", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetState Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return eStateInvalid;
@@ -75,6 +94,11 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetQueue() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_queue", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetQueue Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -87,6 +111,11 @@ StructuredData::DictionarySP ScriptedThreadPythonInterface::GetStopReason() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_stop_reason", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetStopReason Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};
@@ -99,6 +128,11 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetStackFrames() {
StructuredData::ArraySP arr =
Dispatch<StructuredData::ArraySP>("get_stackframes", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetStackFrames Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr,
error))
return {};
@@ -111,6 +145,11 @@ StructuredData::DictionarySP ScriptedThreadPythonInterface::GetRegisterInfo() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_register_info", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetRegisterInfo Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};
@@ -122,6 +161,11 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetRegisterContext() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_register_context", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetRegisterContext Python exception: {0}", error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
@@ -134,6 +178,11 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetExtendedInfo() {
StructuredData::ArraySP arr =
Dispatch<StructuredData::ArraySP>("get_extended_info", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script), "GetExtendedInfo Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr,
error))
return {};
@@ -146,6 +195,12 @@ ScriptedThreadPythonInterface::GetScriptedFramePluginName() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_scripted_frame_plugin", error);
+ if (error.Fail()) {
+ LLDB_LOG(GetLog(LLDBLog::Script),
+ "GetScriptedFramePluginName Python exception: {0}",
+ error.AsCString());
+ }
+
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};
diff --git a/lldb/source/Target/ScriptedThreadPlan.cpp b/lldb/source/Target/ScriptedThreadPlan.cpp
index 7b59231f14d21..ddaf8dacf800b 100644
--- a/lldb/source/Target/ScriptedThreadPlan.cpp
+++ b/lldb/source/Target/ScriptedThreadPlan.cpp
@@ -20,6 +20,7 @@
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/State.h"
+#include "llvm/Support/FormatVariadic.h"
using namespace lldb;
using namespace lldb_private;
@@ -83,6 +84,10 @@ void ScriptedThreadPlan::DidPush() {
m_class_name, this->shared_from_this(), m_args_data);
if (!obj_or_err) {
m_error_str = llvm::toString(obj_or_err.takeError());
+ Debugger::ReportError(
+ llvm::formatv("Failed to create ScriptedThreadPlan: {0}", m_error_str)
+ .str(),
+ m_process.GetTarget().GetDebugger().GetID());
SetPlanComplete(false);
} else
m_implementation_sp = *obj_or_err;
diff --git a/lldb/test/API/functionalities/scripted_extensions/Makefile b/lldb/test/API/functionalities/scripted_extensions/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_extensions/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/scripted_extensions/TestScriptedExtensionsDiagnostics.py b/lldb/test/API/functionalities/scripted_extensions/TestScriptedExtensionsDiagnostics.py
new file mode 100644
index 0000000000000..5ffe7c316546f
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_extensions/TestScriptedExtensionsDiagnostics.py
@@ -0,0 +1,124 @@
+"""
+Verify that exceptions raised inside scripted extension affordance methods
+(or missing required abstract methods) are surfaced to the user.
+
+For entry points with no return-channel for errors
+(`ScriptedProcess::CreateInstance`, `OperatingSystemPython` ctor,
+`ScriptedThreadPlan::DidPush`, `BreakpointResolverScripted` ctor) the
+diagnostic is broadcast via `Debugger::ReportError` and asserted on a
+listener.
+
+Entry points that already return an `llvm::Expected` / `Status`
+(`ScriptedThread::Create`, `ScriptedFrameProvider::CreateInstance`,
+`StopHookScripted::SetScriptCallback`) propagate the detailed error
+through their return type; tests for those are tracked as follow-up.
+"""
+
+import os
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import expectedFailureAll
+from lldbsuite.test.lldbtest import TestBase
+
+
+class TestScriptedExtensionsDiagnostics(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.broadcaster = self.dbg.GetBroadcaster()
+ self.listener = lldbutil.start_listening_from(
+ self.broadcaster,
+ lldb.SBDebugger.eBroadcastBitWarning | lldb.SBDebugger.eBroadcastBitError,
+ )
+ script_path = os.path.join(
+ self.getSourceDir(), "malformed_scripted_extensions.py"
+ )
+ self.runCmd("command script import " + script_path)
+
+ def assert_diagnostic(self, expected_substring):
+ event = lldbutil.fetch_next_event(self, self.listener, self.broadcaster)
+ data = lldb.SBDebugger.GetDiagnosticFromEvent(event)
+ self.assertTrue(data.IsValid(), "event has diagnostic data")
+ message = data.GetValueForKey("message").GetStringValue(4096)
+ self.assertIn(expected_substring, message)
+
+ def create_target(self):
+ self.build()
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target, "valid target")
+ return target
+
+ # ------------------------------------------------------------------
+ # ScriptedProcess - reports via ScriptedProcess::CreateInstance
+ # ------------------------------------------------------------------
+
+ def test_scripted_process_missing_methods(self):
+ """A ScriptedProcess missing abstract methods should emit a
+ diagnostic naming the missing method."""
+ target = self.create_target()
+ launch_info = lldb.SBLaunchInfo(None)
+ launch_info.SetProcessPluginName("ScriptedProcess")
+ launch_info.SetScriptedProcessClassName(
+ "malformed_scripted_extensions.MissingMethodsScriptedProcess"
+ )
+ error = lldb.SBError()
+ target.Launch(launch_info, error)
+ self.assertTrue(error.Fail(), "launch should fail")
+ self.assert_diagnostic("read_memory_at_address")
+
+ # ------------------------------------------------------------------
+ # BreakpointResolverScripted - reports via
+ # BreakpointResolverScripted::CreateImplementationIfNeeded.
+ # `m_error` is set but never surfaced to the user, so ReportError is
+ # the only user-visible channel.
+ # ------------------------------------------------------------------
+
+ # TODO: malformed_scripted_extensions needs a class whose __init__
+ # raises (the current ExceptionScriptedBreakpointResolver only raises
+ # from __callback__, which goes through Dispatch/Status). Tracked as
+ # follow-up.
+ @expectedFailureAll(bugnumber="needs an init-raising malformed class")
+ def test_scripted_breakpoint_resolver_init_failure(self):
+ target = self.create_target()
+ target.BreakpointCreateFromScript(
+ "malformed_scripted_extensions.ExceptionScriptedBreakpointResolver",
+ lldb.SBStructuredData(),
+ lldb.SBFileSpecList(),
+ lldb.SBFileSpecList(),
+ )
+ self.assert_diagnostic("ExceptionScriptedBreakpointResolver")
+
+ # ------------------------------------------------------------------
+ # ScriptedThreadPlan - reports via ScriptedThreadPlan::DidPush. The
+ # plan stores the error in m_error_str but never surfaces it; the
+ # diagnostic is the user-visible channel.
+ # ------------------------------------------------------------------
+
+ @expectedFailureAll(bugnumber="needs an init-raising malformed class")
+ def test_scripted_thread_plan_init_failure(self):
+ target = self.create_target()
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "valid process")
+ thread = process.GetSelectedThread()
+ self.assertTrue(thread, "valid thread")
+ thread.StepUsingScriptedThreadPlan(
+ "malformed_scripted_extensions.ExceptionScriptedThreadPlan"
+ )
+ self.assert_diagnostic("ExceptionScriptedThreadPlan")
+
+ # ------------------------------------------------------------------
+ # The remaining entry points need a live inferior process to trigger
+ # their creation paths, or have no plugin implementation yet.
+ # ------------------------------------------------------------------
+
+ @expectedFailureAll(
+ bugnumber="OperatingSystemPython needs a live process to load the OS plugin"
+ )
+ def test_operating_system_missing_methods(self):
+ self.assert_diagnostic("get_thread_info")
+
+ @expectedFailureAll(bugnumber="ScriptedPlatform has no plugin implementation yet")
+ def test_scripted_platform_missing_methods(self):
+ self.assert_diagnostic("list_processes")
diff --git a/lldb/test/API/functionalities/scripted_extensions/main.c b/lldb/test/API/functionalities/scripted_extensions/main.c
new file mode 100644
index 0000000000000..237c8ce181774
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_extensions/main.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/lldb/test/API/functionalities/scripted_extensions/malformed_scripted_extensions.py b/lldb/test/API/functionalities/scripted_extensions/malformed_scripted_extensions.py
new file mode 100644
index 0000000000000..e9ff0fd17a57c
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_extensions/malformed_scripted_extensions.py
@@ -0,0 +1,195 @@
+"""
+Intentionally malformed scripted extensions used by
+TestScriptedExtensionsDiagnostics.
+
+Each class either omits a required abstract method or raises a Python
+exception from one of its affordance methods. The corresponding test asserts
+that LLDB surfaces those errors to the user instead of silently swallowing
+them.
+"""
+
+# ---------------------------------------------------------------------------
+# Scripted Process
+# ---------------------------------------------------------------------------
+
+
+class MissingMethodsScriptedProcess:
+ """Missing required abstract method `read_memory_at_address`."""
+
+ def __init__(self, exe_ctx, args):
+ self.exe_ctx = exe_ctx
+ self.args = args
+
+ def get_scripted_thread_plugin(self):
+ return None
+
+ def is_alive(self):
+ return True
+
+
+class ExceptionScriptedProcess:
+ """All abstract methods present, but `launch` raises."""
+
+ def __init__(self, exe_ctx, args):
+ self.exe_ctx = exe_ctx
+ self.args = args
+
+ def get_scripted_thread_plugin(self):
+ return None
+
+ def is_alive(self):
+ return True
+
+ def read_memory_at_address(self, addr, size, error):
+ return None
+
+ def launch(self):
+ raise RuntimeError("intentional exception from launch()")
+
+
+# ---------------------------------------------------------------------------
+# Scripted Thread
+# ---------------------------------------------------------------------------
+
+
+class ExceptionScriptedThread:
+ def __init__(self, process, args):
+ self.process = process
+ self.args = args
+
+ def get_thread_id(self):
+ raise ValueError("intentional exception from get_thread_id()")
+
+ def get_register_context(self):
+ return ""
+
+ def get_name(self):
+ return "ExceptionScriptedThread"
+
+ def get_state(self):
+ return 0
+
+
+# ---------------------------------------------------------------------------
+# Scripted Platform
+# ---------------------------------------------------------------------------
+
+
+class MissingMethodsScriptedPlatform:
+ """Missing required abstract method `list_processes`."""
+
+ def __init__(self, exe_ctx, args):
+ self.exe_ctx = exe_ctx
+ self.args = args
+
+
+class ExceptionScriptedPlatform:
+ def __init__(self, exe_ctx, args):
+ self.exe_ctx = exe_ctx
+ self.args = args
+
+ def list_processes(self):
+ raise RuntimeError("intentional exception from list_processes()")
+
+ def get_process_info(self, pid):
+ return None
+
+ def launch_process(self, launch_info):
+ return None
+
+ def kill_process(self, pid):
+ return None
+
+
+# ---------------------------------------------------------------------------
+# Scripted Frame Provider
+# ---------------------------------------------------------------------------
+
+
+class ExceptionScriptedFrameProvider:
+ def __init__(self, frames, args):
+ self.frames = frames
+ self.args = args
+
+ def get_num_frames(self):
+ raise RuntimeError("intentional exception from get_num_frames()")
+
+ def get_frame_at_index(self, idx):
+ return None
+
+
+# ---------------------------------------------------------------------------
+# Scripted Thread Plan
+# ---------------------------------------------------------------------------
+
+
+class ExceptionScriptedThreadPlan:
+ def __init__(self, thread_plan, args):
+ self.thread_plan = thread_plan
+ self.args = args
+
+ def explains_stop(self, event):
+ raise RuntimeError("intentional exception from explains_stop()")
+
+ def should_stop(self, event):
+ return True
+
+ def is_stale(self):
+ return False
+
+
+# ---------------------------------------------------------------------------
+# Scripted Breakpoint Resolver
+# ---------------------------------------------------------------------------
+
+
+class ExceptionScriptedBreakpointResolver:
+ def __init__(self, bkpt, args):
+ self.bkpt = bkpt
+ self.args = args
+
+ def __callback__(self, sym_ctx):
+ raise RuntimeError("intentional exception from __callback__()")
+
+ def get_short_help(self):
+ return "Exception breakpoint resolver"
+
+
+# ---------------------------------------------------------------------------
+# Scripted Stop Hook
+# ---------------------------------------------------------------------------
+
+
+class ExceptionScriptedStopHook:
+ def __init__(self, target, args):
+ self.target = target
+ self.args = args
+
+ def handle_stop(self, exe_ctx, stream):
+ raise RuntimeError("intentional exception from handle_stop()")
+
+
+# ---------------------------------------------------------------------------
+# Operating System
+# ---------------------------------------------------------------------------
+
+
+class MissingMethodsOperatingSystem:
+ """Missing required abstract method `get_thread_info`."""
+
+ def __init__(self, process):
+ self.process = process
+
+
+class ExceptionOperatingSystem:
+ def __init__(self, process):
+ self.process = process
+
+ def get_thread_info(self):
+ raise RuntimeError("intentional exception from get_thread_info()")
+
+ def get_register_info(self):
+ return {}
+
+ def get_register_data(self, tid):
+ return b""
More information about the lldb-commits
mailing list