Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lldb/bindings/python/python-swigsafecast.swig
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
}
Expand Down
19 changes: 19 additions & 0 deletions lldb/bindings/python/python-typemaps.swig
Original file line number Diff line number Diff line change
Expand Up @@ -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)))) {
Expand Down
24 changes: 22 additions & 2 deletions lldb/bindings/python/python-wrapper.swig
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
}

Expand Down Expand Up @@ -1012,6 +1012,26 @@ static void LLDBSwigPythonCallPythonLogOutputCallback(const char *str,
}
}

// For CommandPrintCallback 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) {
Expand Down
8 changes: 5 additions & 3 deletions lldb/include/lldb/API/SBCommandInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/API/SBCommandReturnObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

namespace lldb_private {
class CommandPluginInterfaceImplementation;
class CommandPrintCallbackBaton;
class SBCommandReturnObjectImpl;
namespace python {
class SWIGBridge;
Expand Down Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions lldb/include/lldb/API/SBDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions lldb/include/lldb/Interpreter/CommandInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -253,6 +254,10 @@ class CommandInterpreter : public Broadcaster,
eCommandTypesAllThem = 0xFFFF //< all commands
};

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
// in the template form of the Alias command, and then detecting them when the
Expand Down Expand Up @@ -664,6 +669,8 @@ class CommandInterpreter : public Broadcaster,
++m_command_usages[cmd_obj.GetCommandName()];
}

void SetPrintCallback(CommandReturnObjectCallback callback);

llvm::json::Value GetStatistics();
const StructuredData::Array &GetTranscript() const;

Expand Down Expand Up @@ -774,6 +781,9 @@ 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;

// 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;
Expand Down
9 changes: 9 additions & 0 deletions lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
24 changes: 18 additions & 6 deletions lldb/source/API/SBCommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -151,7 +151,7 @@ bool SBCommandInterpreter::WasInterrupted() const {

bool SBCommandInterpreter::InterruptCommand() {
LLDB_INSTRUMENT_VA(this);

return (IsValid() ? m_opaque_ptr->InterruptCommand() : false);
}

Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -743,3 +743,15 @@ void SBCommand::SetFlags(uint32_t flags) {
if (IsValid())
m_opaque_sp->GetFlags().Set(flags);
}

void SBCommandInterpreter::SetPrintCallback(
lldb::SBCommandPrintCallback callback, void *baton) {
LLDB_INSTRUMENT_VA(this, callback, baton);

if (m_opaque_ptr)
return m_opaque_ptr->SetPrintCallback(
[callback, baton](lldb_private::CommandReturnObject &result) {
SBCommandReturnObject sb_result(result);
return callback(sb_result, baton);
});
}
55 changes: 35 additions & 20 deletions lldb/source/Interpreter/CommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3186,30 +3186,40 @@ 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) {
const auto callback_result = m_print_callback(result);
if (callback_result == eCommandReturnObjectPrintCallbackSkipped)
DefaultPrintCallback(result);
} else {
DefaultPrintCallback(result);
}
}

Expand Down Expand Up @@ -3660,3 +3670,8 @@ llvm::json::Value CommandInterpreter::GetStatistics() {
const StructuredData::Array &CommandInterpreter::GetTranscript() const {
return m_transcript;
}

void CommandInterpreter::SetPrintCallback(
CommandReturnObjectCallback callback) {
m_print_callback = callback;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions lldb/test/API/python_api/interpreter_callback/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
C_SOURCES := main.c

include Makefile.rules
Loading