[Lldb-commits] [lldb] [lldb] Support CommandInterpreter print callbacks (PR #125006)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Fri Jan 31 09:37:37 PST 2025
https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/125006
>From d9c0b0e1e6c18b6710c5b15843e23a96ca0a9dc8 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Tue, 28 Jan 2025 17:08:52 -0800
Subject: [PATCH 1/4] [lldb] Support CommandInterpreter print callbacks
Xcode uses a pseudoterminal for the debugger console.
- The upside of this apporach is that it means that it can rely on
LLDB's IOHandlers for multiline and script input.
- The downside of this approach is that the command output is printed
to the PTY and you don't get a SBCommandReturnObject. Adrian added
support for inline diagnostics in #110901 and we'd like to access
those from the IDE.
This patch adds support for registering a callback in the command
interpreter that gives access to the (SB)CommandReturnObject right
before it will be printed. The callback implementation can choose
whether it likes to handle printing the result or defer to lldb. If the
callback indicated it handled the result, the command interpreter will
skip printing the result.
We considered a few other alternatives to solve this problem:
- The most obvious one is using `HandleCommand`, which returns a
`SBCommandReturnObject`. The problem with this approach is the
multiline input mentioned above. We would need a way to tell the IDE
that it should expect multiline input, which isn't known until LLDB
starts handling the command.
- To address the multiline issue,we considered exposing (some of the)
IOHandler machinery through the SB API. To solve this particular
issue, that would require reimplementing a ton of logic that already
exists today in the CommandInterpeter. Furthermore that seems like
overkill compared to the proposed solution.
rdar://141254310
---
lldb/bindings/python/python-swigsafecast.swig | 4 ++
lldb/bindings/python/python-typemaps.swig | 19 ++++++
lldb/bindings/python/python-wrapper.swig | 24 ++++++-
lldb/include/lldb/API/SBCommandInterpreter.h | 8 ++-
lldb/include/lldb/API/SBCommandReturnObject.h | 2 +
lldb/include/lldb/API/SBDefines.h | 3 +
.../lldb/Interpreter/CommandInterpreter.h | 13 ++++
lldb/include/lldb/lldb-enumerations.h | 9 +++
lldb/source/API/SBCommandInterpreter.cpp | 40 ++++++++++-
.../source/Interpreter/CommandInterpreter.cpp | 60 +++++++++++------
.../Python/SWIGPythonBridge.h | 13 ++--
.../python_api/interpreter_callback/Makefile | 3 +
.../TestCommandInterepterPrintCallback.py | 66 +++++++++++++++++++
.../python_api/interpreter_callback/main.c | 6 ++
14 files changed, 238 insertions(+), 32 deletions(-)
create mode 100644 lldb/test/API/python_api/interpreter_callback/Makefile
create mode 100644 lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py
create mode 100644 lldb/test/API/python_api/interpreter_callback/main.c
diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig
index 429baad158ca5d..4721dfdc17e6a0 100644
--- a/lldb/bindings/python/python-swigsafecast.swig
+++ b/lldb/bindings/python/python-swigsafecast.swig
@@ -9,6 +9,10 @@ PythonObject SWIGBridge::ToSWIGWrapper(std::unique_ptr<lldb::SBValue> value_sb)
return ToSWIGHelper(value_sb.release(), SWIGTYPE_p_lldb__SBValue);
}
+PythonObject SWIGBridge::ToSWIGWrapper(std::unique_ptr<lldb::SBCommandReturnObject> result_up) {
+ return ToSWIGHelper(result_up.release(), SWIGTYPE_p_lldb__SBCommandReturnObject);
+}
+
PythonObject SWIGBridge::ToSWIGWrapper(lldb::ValueObjectSP value_sp) {
return ToSWIGWrapper(std::unique_ptr<lldb::SBValue>(new lldb::SBValue(value_sp)));
}
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index f8c33e15c03e66..88b6cd9ef6b6e7 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -476,6 +476,25 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
$1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
}
+// For lldb::SBCommandPrintCallback
+%typemap(in) (lldb::SBCommandPrintCallback callback, void *baton) {
+ if (!($input == Py_None ||
+ PyCallable_Check(reinterpret_cast<PyObject *>($input)))) {
+ PyErr_SetString(PyExc_TypeError, "Need a callable object or None!");
+ SWIG_fail;
+ }
+
+ // Don't lose the callback reference.
+ Py_INCREF($input);
+ $1 = LLDBSwigPythonCallPythonCommandPrintCallback;
+ $2 = $input;
+}
+
+%typemap(typecheck) (lldb::SBCommandPrintCallback callback, void *baton) {
+ $1 = $input == Py_None;
+ $1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
+}
+
%typemap(in) (lldb::CommandOverrideCallback callback, void *baton) {
if (!($input == Py_None ||
PyCallable_Check(reinterpret_cast<PyObject *>($input)))) {
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index b72a462d04643b..fcabf60008b17d 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -727,7 +727,7 @@ lldb_private::python::SWIGBridge::LLDBSwigPythonHandleOptionArgumentCompletionFo
dict_sp->AddBooleanItem("no-completion", true);
return dict_sp;
}
-
+
// Convert the return dictionary to a DictionarySP.
StructuredData::ObjectSP result_obj_sp = result.CreateStructuredObject();
@@ -753,7 +753,7 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
auto pfunc = self.ResolveName<PythonCallable>("__call__");
if (!pfunc.IsAllocated()) {
- cmd_retobj.AppendError("Could not find '__call__' method in implementation class");
+ cmd_retobj.AppendError("Could not find '__call__' method in implementation class");
return false;
}
@@ -1012,6 +1012,26 @@ static void LLDBSwigPythonCallPythonLogOutputCallback(const char *str,
}
}
+// For DebuggerTerminateCallback functions
+static CommandReturnObjectCallbackResult LLDBSwigPythonCallPythonCommandPrintCallback(SBCommandReturnObject& result, void *callback_baton) {
+ SWIG_Python_Thread_Block swig_thread_block;
+
+ PyErr_Cleaner py_err_cleaner(true);
+
+ PythonObject result_arg = SWIGBridge::ToSWIGWrapper(
+ std::make_unique<SBCommandReturnObject>(result));
+ PythonCallable callable =
+ Retain<PythonCallable>(reinterpret_cast<PyObject *>(callback_baton));
+
+ if (!callable.IsValid())
+ return eCommandReturnObjectPrintCallbackSkipped;
+
+ PythonObject callback_result = callable(result_arg);
+
+ long long ret_val = unwrapOrSetPythonException(As<long long>(callback_result));
+ return (CommandReturnObjectCallbackResult)ret_val;
+}
+
// For DebuggerTerminateCallback functions
static void LLDBSwigPythonCallPythonSBDebuggerTerminateCallback(lldb::user_id_t debugger_id,
void *baton) {
diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index b7e39b78586168..dd475ddf6c1702 100644
--- a/lldb/include/lldb/API/SBCommandInterpreter.h
+++ b/lldb/include/lldb/API/SBCommandInterpreter.h
@@ -247,13 +247,13 @@ class SBCommandInterpreter {
lldb::SBStringList &matches,
lldb::SBStringList &descriptions);
- /// Returns whether an interrupt flag was raised either by the SBDebugger -
+ /// Returns whether an interrupt flag was raised either by the SBDebugger -
/// when the function is not running on the RunCommandInterpreter thread, or
/// by SBCommandInterpreter::InterruptCommand if it is. If your code is doing
- /// interruptible work, check this API periodically, and interrupt if it
+ /// interruptible work, check this API periodically, and interrupt if it
/// returns true.
bool WasInterrupted() const;
-
+
/// Interrupts the command currently executing in the RunCommandInterpreter
/// thread.
///
@@ -331,6 +331,8 @@ class SBCommandInterpreter {
/// this list. Otherwise this list is empty.
SBStructuredData GetTranscript();
+ void SetPrintCallback(lldb::SBCommandPrintCallback callback, void *baton);
+
protected:
friend class lldb_private::CommandPluginInterfaceImplementation;
diff --git a/lldb/include/lldb/API/SBCommandReturnObject.h b/lldb/include/lldb/API/SBCommandReturnObject.h
index e8e20a3f3016b8..377e4714f6d26b 100644
--- a/lldb/include/lldb/API/SBCommandReturnObject.h
+++ b/lldb/include/lldb/API/SBCommandReturnObject.h
@@ -17,6 +17,7 @@
namespace lldb_private {
class CommandPluginInterfaceImplementation;
+class CommandPrintCallbackBaton;
class SBCommandReturnObjectImpl;
namespace python {
class SWIGBridge;
@@ -138,6 +139,7 @@ class LLDB_API SBCommandReturnObject {
friend class lldb_private::CommandPluginInterfaceImplementation;
friend class lldb_private::python::SWIGBridge;
+ friend class lldb_private::CommandPrintCallbackBaton;
SBCommandReturnObject(lldb_private::CommandReturnObject &ref);
diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h
index 31e8c9279f8b8b..b7b5cc06546f86 100644
--- a/lldb/include/lldb/API/SBDefines.h
+++ b/lldb/include/lldb/API/SBDefines.h
@@ -144,6 +144,9 @@ typedef bool (*SBBreakpointHitCallback)(void *baton, lldb::SBProcess &process,
typedef void (*SBDebuggerDestroyCallback)(lldb::user_id_t debugger_id,
void *baton);
+typedef CommandReturnObjectCallbackResult (*SBCommandPrintCallback)(
+ lldb::SBCommandReturnObject &result, void *baton);
+
typedef lldb::SBError (*SBPlatformLocateModuleCallback)(
void *baton, const lldb::SBModuleSpec &module_spec,
lldb::SBFileSpec &module_file_spec, lldb::SBFileSpec &symbol_file_spec);
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 910c1d84303354..b0a2d7ed3ace05 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -16,6 +16,7 @@
#include "lldb/Interpreter/CommandObject.h"
#include "lldb/Interpreter/ScriptInterpreter.h"
#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Baton.h"
#include "lldb/Utility/Broadcaster.h"
#include "lldb/Utility/CompletionRequest.h"
#include "lldb/Utility/Event.h"
@@ -253,6 +254,9 @@ class CommandInterpreter : public Broadcaster,
eCommandTypesAllThem = 0xFFFF //< all commands
};
+ typedef lldb::CommandReturnObjectCallbackResult (
+ *CommandReturnObjectCallback)(CommandReturnObject &, void *);
+
// The CommandAlias and CommandInterpreter both have a hand in
// substituting for alias commands. They work by writing special tokens
// in the template form of the Alias command, and then detecting them when the
@@ -664,6 +668,9 @@ class CommandInterpreter : public Broadcaster,
++m_command_usages[cmd_obj.GetCommandName()];
}
+ void SetPrintCallback(CommandReturnObjectCallback callback,
+ lldb::BatonSP baton_sp);
+
llvm::json::Value GetStatistics();
const StructuredData::Array &GetTranscript() const;
@@ -774,6 +781,12 @@ class CommandInterpreter : public Broadcaster,
std::vector<uint32_t> m_command_source_flags;
CommandInterpreterRunResult m_result;
+ /// An optional callback to handle printing the CommandReturnObject.
+ /// @{
+ CommandReturnObjectCallback m_print_callback = nullptr;
+ lldb::BatonSP m_print_callback_baton_sp;
+ /// @}
+
// The exit code the user has requested when calling the 'quit' command.
// No value means the user hasn't set a custom exit code so far.
std::optional<int> m_quit_exit_code;
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 50d2233509de6f..fecf9cbb765f71 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -1368,6 +1368,15 @@ enum Severity {
eSeverityInfo, // Equivalent to Remark used in clang.
};
+/// Callback return value, indicating whether it handled printing the
+/// CommandReturnObject or deferred doing so to the CommandInterpreter.
+enum CommandReturnObjectCallbackResult {
+ /// The callback deferred printing the command return object.
+ eCommandReturnObjectPrintCallbackSkipped = 0,
+ /// The callback handled printing the command return object.
+ eCommandReturnObjectPrintCallbackHandled = 1,
+};
+
} // namespace lldb
#endif // LLDB_LLDB_ENUMERATIONS_H
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index 7a35473283684c..08bac1afc64e87 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -151,7 +151,7 @@ bool SBCommandInterpreter::WasInterrupted() const {
bool SBCommandInterpreter::InterruptCommand() {
LLDB_INSTRUMENT_VA(this);
-
+
return (IsValid() ? m_opaque_ptr->InterruptCommand() : false);
}
@@ -743,3 +743,41 @@ void SBCommand::SetFlags(uint32_t flags) {
if (IsValid())
m_opaque_sp->GetFlags().Set(flags);
}
+
+namespace lldb_private {
+struct CommandCallbackData {
+ SBCommandPrintCallback callback;
+ void *callback_baton;
+};
+
+class CommandPrintCallbackBaton
+ : public lldb_private::TypedBaton<CommandCallbackData> {
+public:
+ CommandPrintCallbackBaton(SBCommandPrintCallback callback, void *baton)
+ : TypedBaton(std::make_unique<CommandCallbackData>()) {
+ getItem()->callback = callback;
+ getItem()->callback_baton = baton;
+ }
+
+ static lldb::CommandReturnObjectCallbackResult
+ PrivateCallback(lldb_private::CommandReturnObject &result, void *baton) {
+ if (baton) {
+ CommandCallbackData *data = (CommandCallbackData *)baton;
+ SBCommandReturnObject sb_result(result);
+ return data->callback(sb_result, data->callback_baton);
+ }
+ return eCommandReturnObjectPrintCallbackSkipped;
+ }
+};
+} // namespace lldb_private
+
+void SBCommandInterpreter::SetPrintCallback(
+ lldb::SBCommandPrintCallback callback, void *baton) {
+ LLDB_INSTRUMENT_VA(this, callback, baton);
+
+ BatonSP baton_sp =
+ std::make_shared<CommandPrintCallbackBaton>(callback, baton);
+ if (m_opaque_ptr)
+ return m_opaque_ptr->SetPrintCallback(
+ &CommandPrintCallbackBaton::PrivateCallback, baton_sp);
+}
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 04226af1a1eb8e..2128eb25366b54 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -3186,30 +3186,44 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
if ((result.Succeeded() &&
io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) ||
io_handler.GetFlags().Test(eHandleCommandFlagPrintErrors)) {
- // Display any inline diagnostics first.
- const bool inline_diagnostics = !result.GetImmediateErrorStream() &&
- GetDebugger().GetShowInlineDiagnostics();
- if (inline_diagnostics) {
- unsigned prompt_len = m_debugger.GetPrompt().size();
- if (auto indent = result.GetDiagnosticIndent()) {
- std::string diags =
- result.GetInlineDiagnosticString(prompt_len + *indent);
- PrintCommandOutput(io_handler, diags, true);
+ auto DefaultPrintCallback = [&](const CommandReturnObject &result) {
+ // Display any inline diagnostics first.
+ const bool inline_diagnostics = !result.GetImmediateErrorStream() &&
+ GetDebugger().GetShowInlineDiagnostics();
+ if (inline_diagnostics) {
+ unsigned prompt_len = m_debugger.GetPrompt().size();
+ if (auto indent = result.GetDiagnosticIndent()) {
+ std::string diags =
+ result.GetInlineDiagnosticString(prompt_len + *indent);
+ PrintCommandOutput(io_handler, diags, true);
+ }
}
- }
- // Display any STDOUT/STDERR _prior_ to emitting the command result text.
- GetProcessOutput();
+ // Display any STDOUT/STDERR _prior_ to emitting the command result text.
+ GetProcessOutput();
- if (!result.GetImmediateOutputStream()) {
- llvm::StringRef output = result.GetOutputString();
- PrintCommandOutput(io_handler, output, true);
- }
+ if (!result.GetImmediateOutputStream()) {
+ llvm::StringRef output = result.GetOutputString();
+ PrintCommandOutput(io_handler, output, true);
+ }
- // Now emit the command error text from the command we just executed.
- if (!result.GetImmediateErrorStream()) {
- std::string error = result.GetErrorString(!inline_diagnostics);
- PrintCommandOutput(io_handler, error, false);
+ // Now emit the command error text from the command we just executed.
+ if (!result.GetImmediateErrorStream()) {
+ std::string error = result.GetErrorString(!inline_diagnostics);
+ PrintCommandOutput(io_handler, error, false);
+ }
+ };
+
+ if (m_print_callback) {
+ void *baton = m_print_callback_baton_sp
+ ? m_print_callback_baton_sp->data()
+ : nullptr;
+ lldb::CommandReturnObjectCallbackResult callback_result =
+ m_print_callback(result, baton);
+ if (callback_result == eCommandReturnObjectPrintCallbackSkipped)
+ DefaultPrintCallback(result);
+ } else {
+ DefaultPrintCallback(result);
}
}
@@ -3660,3 +3674,9 @@ llvm::json::Value CommandInterpreter::GetStatistics() {
const StructuredData::Array &CommandInterpreter::GetTranscript() const {
return m_transcript;
}
+
+void CommandInterpreter::SetPrintCallback(CommandReturnObjectCallback callback,
+ lldb::BatonSP baton_sp) {
+ m_print_callback = callback;
+ m_print_callback_baton_sp = baton_sp;
+}
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 0f0e4a563e8b2b..a2252d164ab83d 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -81,6 +81,8 @@ template <typename T> class ScopedPythonObject : PythonObject {
class SWIGBridge {
public:
static PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBValue> value_sb);
+ static PythonObject
+ ToSWIGWrapper(std::unique_ptr<lldb::SBCommandReturnObject> result_up);
static PythonObject ToSWIGWrapper(lldb::ValueObjectSP value_sp);
static PythonObject ToSWIGWrapper(lldb::TargetSP target_sp);
static PythonObject ToSWIGWrapper(lldb::ProcessSP process_sp);
@@ -190,12 +192,11 @@ class SWIGBridge {
lldb::DebuggerSP debugger, const char *args,
lldb_private::CommandReturnObject &cmd_retobj,
lldb::ExecutionContextRefSP exe_ctx_ref_sp);
- static bool
- LLDBSwigPythonCallParsedCommandObject(PyObject *implementor,
- lldb::DebuggerSP debugger,
- StructuredDataImpl &args_impl,
- lldb_private::CommandReturnObject &cmd_retobj,
- lldb::ExecutionContextRefSP exe_ctx_ref_sp);
+ static bool LLDBSwigPythonCallParsedCommandObject(
+ PyObject *implementor, lldb::DebuggerSP debugger,
+ StructuredDataImpl &args_impl,
+ lldb_private::CommandReturnObject &cmd_retobj,
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp);
static std::optional<std::string>
LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor,
diff --git a/lldb/test/API/python_api/interpreter_callback/Makefile b/lldb/test/API/python_api/interpreter_callback/Makefile
new file mode 100644
index 00000000000000..10495940055b63
--- /dev/null
+++ b/lldb/test/API/python_api/interpreter_callback/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py b/lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py
new file mode 100644
index 00000000000000..50ab5c821e675c
--- /dev/null
+++ b/lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py
@@ -0,0 +1,66 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class BreakpointAPITestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def run_command_interpreter_with_output_file(self, out_filename, input_str):
+ with open(out_filename, "w") as f:
+ self.dbg.SetOutputFileHandle(f, False)
+ self.dbg.SetInputString(input_str)
+ opts = lldb.SBCommandInterpreterRunOptions()
+ self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False)
+
+ def test_command_interpreter_print_callback(self):
+ """Make sure that if an SBBreakpoint gets deleted its IsValid returns false."""
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, VALID_TARGET)
+
+ lldbutil.run_to_source_breakpoint(
+ self, "// Break here", lldb.SBFileSpec("main.c")
+ )
+
+ out_filename = self.getBuildArtifact("output")
+ ci = self.dbg.GetCommandInterpreter()
+ called = False
+
+ # The string we'll be looking for in the command output.
+ needle = "Show a list of all debugger commands"
+
+ # Test registering a callback that handles the printing. Make sure the
+ # result is passed to the callback and that we don't print the result.
+ def handling_callback(return_object):
+ nonlocal called
+ called = True
+ self.assertIn(needle, return_object.GetOutput())
+ return lldb.eCommandReturnObjectPrintCallbackHandled
+
+ ci.SetPrintCallback(handling_callback)
+ self.assertFalse(called)
+ self.run_command_interpreter_with_output_file(out_filename, "help help\n")
+ with open(out_filename, "r") as f:
+ self.assertNotIn(needle, f.read())
+
+ # Test registering a callback that defers the printing to lldb. Make
+ # sure the result is passed to the callback and that the result is
+ # printed by lldb.
+ def non_handling_callback(return_object):
+ nonlocal called
+ called = True
+ self.assertIn(needle, return_object.GetOutput())
+ return lldb.eCommandReturnObjectPrintCallbackSkipped
+
+ called = False
+ ci.SetPrintCallback(non_handling_callback)
+ self.assertFalse(called)
+ self.run_command_interpreter_with_output_file(out_filename, "help help\n")
+ self.assertTrue(called)
+
+ with open(out_filename, "r") as f:
+ self.assertIn(needle, f.read())
diff --git a/lldb/test/API/python_api/interpreter_callback/main.c b/lldb/test/API/python_api/interpreter_callback/main.c
new file mode 100644
index 00000000000000..78d5c0714714aa
--- /dev/null
+++ b/lldb/test/API/python_api/interpreter_callback/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main() {
+ int i = 1;
+ return i; // Break here
+}
>From 3586cf4485cf747bcc673b1113c0707a5089498e Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Wed, 29 Jan 2025 23:10:15 -0800
Subject: [PATCH 2/4] Fix copy-paste mistakes
---
lldb/bindings/python/python-wrapper.swig | 2 +-
.../TestCommandInterepterPrintCallback.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index fcabf60008b17d..57c7ac387145ef 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -1012,7 +1012,7 @@ static void LLDBSwigPythonCallPythonLogOutputCallback(const char *str,
}
}
-// For DebuggerTerminateCallback functions
+// For CommandPrintCallback functions
static CommandReturnObjectCallbackResult LLDBSwigPythonCallPythonCommandPrintCallback(SBCommandReturnObject& result, void *callback_baton) {
SWIG_Python_Thread_Block swig_thread_block;
diff --git a/lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py b/lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py
index 50ab5c821e675c..d11fd88aa80448 100644
--- a/lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py
+++ b/lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py
@@ -4,7 +4,7 @@
from lldbsuite.test import lldbutil
-class BreakpointAPITestCase(TestBase):
+class CommandInterepterPrintCallbackTest(TestBase):
NO_DEBUG_INFO_TESTCASE = True
def run_command_interpreter_with_output_file(self, out_filename, input_str):
@@ -15,7 +15,7 @@ def run_command_interpreter_with_output_file(self, out_filename, input_str):
self.dbg.RunCommandInterpreter(True, False, opts, 0, False, False)
def test_command_interpreter_print_callback(self):
- """Make sure that if an SBBreakpoint gets deleted its IsValid returns false."""
+ """Test the command interpreter print callback."""
self.build()
exe = self.getBuildArtifact("a.out")
>From e6d04c1652c502f33569582c5903d4d554ff356e Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Thu, 30 Jan 2025 08:54:47 -0800
Subject: [PATCH 3/4] Use std::function and remove the boilerplate in the SB
API
---
.../lldb/Interpreter/CommandInterpreter.h | 13 +++---
lldb/source/API/SBCommandInterpreter.cpp | 44 ++++---------------
.../source/Interpreter/CommandInterpreter.cpp | 11 ++---
3 files changed, 17 insertions(+), 51 deletions(-)
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index b0a2d7ed3ace05..d5425788fe1c98 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -254,8 +254,9 @@ class CommandInterpreter : public Broadcaster,
eCommandTypesAllThem = 0xFFFF //< all commands
};
- typedef lldb::CommandReturnObjectCallbackResult (
- *CommandReturnObjectCallback)(CommandReturnObject &, void *);
+ using CommandReturnObjectCallback =
+ std::function<lldb::CommandReturnObjectCallbackResult(
+ CommandReturnObject &)>;
// The CommandAlias and CommandInterpreter both have a hand in
// substituting for alias commands. They work by writing special tokens
@@ -668,8 +669,7 @@ class CommandInterpreter : public Broadcaster,
++m_command_usages[cmd_obj.GetCommandName()];
}
- void SetPrintCallback(CommandReturnObjectCallback callback,
- lldb::BatonSP baton_sp);
+ void SetPrintCallback(CommandReturnObjectCallback callback);
llvm::json::Value GetStatistics();
const StructuredData::Array &GetTranscript() const;
@@ -782,10 +782,7 @@ class CommandInterpreter : public Broadcaster,
CommandInterpreterRunResult m_result;
/// An optional callback to handle printing the CommandReturnObject.
- /// @{
- CommandReturnObjectCallback m_print_callback = nullptr;
- lldb::BatonSP m_print_callback_baton_sp;
- /// @}
+ CommandReturnObjectCallback m_print_callback;
// The exit code the user has requested when calling the 'quit' command.
// No value means the user hasn't set a custom exit code so far.
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index 08bac1afc64e87..a6d7977e221d94 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -98,8 +98,8 @@ SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs)
SBCommandInterpreter::~SBCommandInterpreter() = default;
-const SBCommandInterpreter &SBCommandInterpreter::
-operator=(const SBCommandInterpreter &rhs) {
+const SBCommandInterpreter &
+SBCommandInterpreter::operator=(const SBCommandInterpreter &rhs) {
LLDB_INSTRUMENT_VA(this, rhs);
m_opaque_ptr = rhs.m_opaque_ptr;
@@ -222,8 +222,7 @@ void SBCommandInterpreter::HandleCommandsFromFile(
if (override_context.get())
m_opaque_ptr->HandleCommandsFromFile(tmp_spec,
override_context.get()->Lock(true),
- options.ref(),
- result.ref());
+ options.ref(), result.ref());
else
m_opaque_ptr->HandleCommandsFromFile(tmp_spec, options.ref(), result.ref());
@@ -649,7 +648,8 @@ SBCommand::operator bool() const {
const char *SBCommand::GetName() {
LLDB_INSTRUMENT_VA(this);
- return (IsValid() ? ConstString(m_opaque_sp->GetCommandName()).AsCString() : nullptr);
+ return (IsValid() ? ConstString(m_opaque_sp->GetCommandName()).AsCString()
+ : nullptr);
}
const char *SBCommand::GetHelp() {
@@ -744,40 +744,14 @@ void SBCommand::SetFlags(uint32_t flags) {
m_opaque_sp->GetFlags().Set(flags);
}
-namespace lldb_private {
-struct CommandCallbackData {
- SBCommandPrintCallback callback;
- void *callback_baton;
-};
-
-class CommandPrintCallbackBaton
- : public lldb_private::TypedBaton<CommandCallbackData> {
-public:
- CommandPrintCallbackBaton(SBCommandPrintCallback callback, void *baton)
- : TypedBaton(std::make_unique<CommandCallbackData>()) {
- getItem()->callback = callback;
- getItem()->callback_baton = baton;
- }
-
- static lldb::CommandReturnObjectCallbackResult
- PrivateCallback(lldb_private::CommandReturnObject &result, void *baton) {
- if (baton) {
- CommandCallbackData *data = (CommandCallbackData *)baton;
- SBCommandReturnObject sb_result(result);
- return data->callback(sb_result, data->callback_baton);
- }
- return eCommandReturnObjectPrintCallbackSkipped;
- }
-};
-} // namespace lldb_private
-
void SBCommandInterpreter::SetPrintCallback(
lldb::SBCommandPrintCallback callback, void *baton) {
LLDB_INSTRUMENT_VA(this, callback, baton);
- BatonSP baton_sp =
- std::make_shared<CommandPrintCallbackBaton>(callback, baton);
if (m_opaque_ptr)
return m_opaque_ptr->SetPrintCallback(
- &CommandPrintCallbackBaton::PrivateCallback, baton_sp);
+ [callback, baton](lldb_private::CommandReturnObject &result) {
+ SBCommandReturnObject sb_result(result);
+ return callback(sb_result, baton);
+ });
}
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 2128eb25366b54..5b136614022c21 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -3215,11 +3215,7 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
};
if (m_print_callback) {
- void *baton = m_print_callback_baton_sp
- ? m_print_callback_baton_sp->data()
- : nullptr;
- lldb::CommandReturnObjectCallbackResult callback_result =
- m_print_callback(result, baton);
+ const auto callback_result = m_print_callback(result);
if (callback_result == eCommandReturnObjectPrintCallbackSkipped)
DefaultPrintCallback(result);
} else {
@@ -3675,8 +3671,7 @@ const StructuredData::Array &CommandInterpreter::GetTranscript() const {
return m_transcript;
}
-void CommandInterpreter::SetPrintCallback(CommandReturnObjectCallback callback,
- lldb::BatonSP baton_sp) {
+void CommandInterpreter::SetPrintCallback(
+ CommandReturnObjectCallback callback) {
m_print_callback = callback;
- m_print_callback_baton_sp = baton_sp;
}
>From 113870c7ef9e26cf15b011889780bfee3f776284 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 31 Jan 2025 09:36:28 -0800
Subject: [PATCH 4/4] Address Pavel's code review feedback
---
lldb/include/lldb/API/SBCommandReturnObject.h | 2 --
lldb/source/API/SBCommandInterpreter.cpp | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/lldb/include/lldb/API/SBCommandReturnObject.h b/lldb/include/lldb/API/SBCommandReturnObject.h
index 377e4714f6d26b..e8e20a3f3016b8 100644
--- a/lldb/include/lldb/API/SBCommandReturnObject.h
+++ b/lldb/include/lldb/API/SBCommandReturnObject.h
@@ -17,7 +17,6 @@
namespace lldb_private {
class CommandPluginInterfaceImplementation;
-class CommandPrintCallbackBaton;
class SBCommandReturnObjectImpl;
namespace python {
class SWIGBridge;
@@ -139,7 +138,6 @@ class LLDB_API SBCommandReturnObject {
friend class lldb_private::CommandPluginInterfaceImplementation;
friend class lldb_private::python::SWIGBridge;
- friend class lldb_private::CommandPrintCallbackBaton;
SBCommandReturnObject(lldb_private::CommandReturnObject &ref);
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index a6d7977e221d94..d153e2acdf853f 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -749,7 +749,7 @@ void SBCommandInterpreter::SetPrintCallback(
LLDB_INSTRUMENT_VA(this, callback, baton);
if (m_opaque_ptr)
- return m_opaque_ptr->SetPrintCallback(
+ m_opaque_ptr->SetPrintCallback(
[callback, baton](lldb_private::CommandReturnObject &result) {
SBCommandReturnObject sb_result(result);
return callback(sb_result, baton);
More information about the lldb-commits
mailing list