diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig index 0127ac6bfa468..bf139e906704b 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 value_sb) return ToSWIGHelper(value_sb.release(), SWIGTYPE_p_lldb__SBValue); } +PythonObject SWIGBridge::ToSWIGWrapper(std::unique_ptr result_up) { + return ToSWIGHelper(result_up.release(), SWIGTYPE_p_lldb__SBCommandReturnObject); +} + PythonObject SWIGBridge::ToSWIGWrapper(lldb::ValueObjectSP value_sp) { return ToSWIGWrapper(std::unique_ptr(new lldb::SBValue(value_sp))); } diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig index f8c33e15c03e6..88b6cd9ef6b6e 100644 --- a/lldb/bindings/python/python-typemaps.swig +++ b/lldb/bindings/python/python-typemaps.swig @@ -476,6 +476,25 @@ template <> bool SetNumberFromPyObject(double &number, PyObject *obj) { $1 = $1 || PyCallable_Check(reinterpret_cast($input)); } +// For lldb::SBCommandPrintCallback +%typemap(in) (lldb::SBCommandPrintCallback callback, void *baton) { + if (!($input == Py_None || + PyCallable_Check(reinterpret_cast($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($input)); +} + %typemap(in) (lldb::CommandOverrideCallback callback, void *baton) { if (!($input == Py_None || PyCallable_Check(reinterpret_cast($input)))) { diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index 810673aaec5d1..db31b37b7c900 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -765,7 +765,7 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject( auto pfunc = self.ResolveName("__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; } @@ -1024,6 +1024,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(result)); + PythonCallable callable = + Retain(reinterpret_cast(callback_baton)); + + if (!callable.IsValid()) + return eCommandReturnObjectPrintCallbackSkipped; + + PythonObject callback_result = callable(result_arg); + + long long ret_val = unwrapOrSetPythonException(As(callback_result)); + return (CommandReturnObjectCallbackResult)ret_val; +} + // For DebuggerTerminateCallback functions static void LLDBSwigPythonCallPythonSBDebuggerTerminateCallback(lldb::user_id_t debugger_id, void *baton) { diff --git a/lldb/bindings/python/static-binding/LLDBWrapPython.cpp b/lldb/bindings/python/static-binding/LLDBWrapPython.cpp index 6bf72350d2546..ae6553cc2ad1f 100644 --- a/lldb/bindings/python/static-binding/LLDBWrapPython.cpp +++ b/lldb/bindings/python/static-binding/LLDBWrapPython.cpp @@ -5652,7 +5652,7 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject( auto pfunc = self.ResolveName("__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; } @@ -5911,6 +5911,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(result)); + PythonCallable callable = + Retain(reinterpret_cast(callback_baton)); + + if (!callable.IsValid()) + return eCommandReturnObjectPrintCallbackSkipped; + + PythonObject callback_result = callable(result_arg); + + long long ret_val = unwrapOrSetPythonException(As(callback_result)); + return (CommandReturnObjectCallbackResult)ret_val; +} + // For DebuggerTerminateCallback functions static void LLDBSwigPythonCallPythonSBDebuggerTerminateCallback(lldb::user_id_t debugger_id, void *baton) { @@ -17699,6 +17719,46 @@ SWIGINTERN PyObject *_wrap_SBCommandInterpreter_GetTranscript(PyObject *self, Py } +SWIGINTERN PyObject *_wrap_SBCommandInterpreter_SetPrintCallback(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + lldb::SBCommandInterpreter *arg1 = (lldb::SBCommandInterpreter *) 0 ; + lldb::SBCommandPrintCallback arg2 = (lldb::SBCommandPrintCallback) 0 ; + void *arg3 = (void *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SBCommandInterpreter_SetPrintCallback", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBCommandInterpreter, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBCommandInterpreter_SetPrintCallback" "', argument " "1"" of type '" "lldb::SBCommandInterpreter *""'"); + } + arg1 = reinterpret_cast< lldb::SBCommandInterpreter * >(argp1); + { + if (!(swig_obj[1] == Py_None || + PyCallable_Check(reinterpret_cast(swig_obj[1])))) { + PyErr_SetString(PyExc_TypeError, "Need a callable object or None!"); + SWIG_fail; + } + + // Don't lose the callback reference. + Py_INCREF(swig_obj[1]); + arg2 = LLDBSwigPythonCallPythonCommandPrintCallback; + arg3 = swig_obj[1]; + } + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + (arg1)->SetPrintCallback(arg2,arg3); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + SWIGINTERN PyObject *SBCommandInterpreter_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *obj = NULL; if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; @@ -18665,6 +18725,34 @@ SWIGINTERN PyObject *_wrap_SBCommandReturnObject_IsValid(PyObject *self, PyObjec } +SWIGINTERN PyObject *_wrap_SBCommandReturnObject_GetCommand(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + lldb::SBCommandReturnObject *arg1 = (lldb::SBCommandReturnObject *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + char *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBCommandReturnObject, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBCommandReturnObject_GetCommand" "', argument " "1"" of type '" "lldb::SBCommandReturnObject *""'"); + } + arg1 = reinterpret_cast< lldb::SBCommandReturnObject * >(argp1); + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + result = (char *)(arg1)->GetCommand(); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + SWIGINTERN PyObject *_wrap_SBCommandReturnObject_GetOutput__SWIG_0(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) { PyObject *resultobj = 0; lldb::SBCommandReturnObject *arg1 = (lldb::SBCommandReturnObject *) 0 ; @@ -29195,6 +29283,33 @@ SWIGINTERN PyObject *_wrap_SBDebugger_GetSyntheticForType(PyObject *self, PyObje } +SWIGINTERN PyObject *_wrap_SBDebugger_ResetStatistics(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + lldb::SBDebugger *arg1 = (lldb::SBDebugger *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBDebugger, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBDebugger_ResetStatistics" "', argument " "1"" of type '" "lldb::SBDebugger *""'"); + } + arg1 = reinterpret_cast< lldb::SBDebugger * >(argp1); + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + (arg1)->ResetStatistics(); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + SWIGINTERN PyObject *_wrap_SBDebugger_RunCommandInterpreter(PyObject *self, PyObject *args) { PyObject *resultobj = 0; lldb::SBDebugger *arg1 = (lldb::SBDebugger *) 0 ; @@ -65557,6 +65672,33 @@ SWIGINTERN PyObject *_wrap_SBTarget_GetStatistics(PyObject *self, PyObject *args } +SWIGINTERN PyObject *_wrap_SBTarget_ResetStatistics(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + lldb::SBTarget *arg1 = (lldb::SBTarget *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBTarget, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBTarget_ResetStatistics" "', argument " "1"" of type '" "lldb::SBTarget *""'"); + } + arg1 = reinterpret_cast< lldb::SBTarget * >(argp1); + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + (arg1)->ResetStatistics(); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + SWIGINTERN PyObject *_wrap_SBTarget_GetPlatform(PyObject *self, PyObject *args) { PyObject *resultobj = 0; lldb::SBTarget *arg1 = (lldb::SBTarget *) 0 ; @@ -96323,6 +96465,7 @@ static PyMethodDef SwigMethods[] = { { "SBCommandInterpreter_ResolveCommand", _wrap_SBCommandInterpreter_ResolveCommand, METH_VARARGS, "SBCommandInterpreter_ResolveCommand(SBCommandInterpreter self, char const * command_line, SBCommandReturnObject result)"}, { "SBCommandInterpreter_GetStatistics", _wrap_SBCommandInterpreter_GetStatistics, METH_O, "SBCommandInterpreter_GetStatistics(SBCommandInterpreter self) -> SBStructuredData"}, { "SBCommandInterpreter_GetTranscript", _wrap_SBCommandInterpreter_GetTranscript, METH_O, "SBCommandInterpreter_GetTranscript(SBCommandInterpreter self) -> SBStructuredData"}, + { "SBCommandInterpreter_SetPrintCallback", _wrap_SBCommandInterpreter_SetPrintCallback, METH_VARARGS, "SBCommandInterpreter_SetPrintCallback(SBCommandInterpreter self, lldb::SBCommandPrintCallback callback)"}, { "SBCommandInterpreter_swigregister", SBCommandInterpreter_swigregister, METH_O, NULL}, { "SBCommandInterpreter_swiginit", SBCommandInterpreter_swiginit, METH_VARARGS, NULL}, { "new_SBCommandInterpreterRunOptions", _wrap_new_SBCommandInterpreterRunOptions, METH_VARARGS, "\n" @@ -96361,6 +96504,7 @@ static PyMethodDef SwigMethods[] = { { "delete_SBCommandReturnObject", _wrap_delete_SBCommandReturnObject, METH_O, "delete_SBCommandReturnObject(SBCommandReturnObject self)"}, { "SBCommandReturnObject___nonzero__", _wrap_SBCommandReturnObject___nonzero__, METH_O, "SBCommandReturnObject___nonzero__(SBCommandReturnObject self) -> bool"}, { "SBCommandReturnObject_IsValid", _wrap_SBCommandReturnObject_IsValid, METH_O, "SBCommandReturnObject_IsValid(SBCommandReturnObject self) -> bool"}, + { "SBCommandReturnObject_GetCommand", _wrap_SBCommandReturnObject_GetCommand, METH_O, "SBCommandReturnObject_GetCommand(SBCommandReturnObject self) -> char const *"}, { "SBCommandReturnObject_GetErrorData", _wrap_SBCommandReturnObject_GetErrorData, METH_O, "SBCommandReturnObject_GetErrorData(SBCommandReturnObject self) -> SBStructuredData"}, { "SBCommandReturnObject_PutOutput", _wrap_SBCommandReturnObject_PutOutput, METH_VARARGS, "\n" "SBCommandReturnObject_PutOutput(SBCommandReturnObject self, SBFile file) -> size_t\n" @@ -96712,6 +96856,7 @@ static PyMethodDef SwigMethods[] = { { "SBDebugger_GetSummaryForType", _wrap_SBDebugger_GetSummaryForType, METH_VARARGS, "SBDebugger_GetSummaryForType(SBDebugger self, SBTypeNameSpecifier arg2) -> SBTypeSummary"}, { "SBDebugger_GetFilterForType", _wrap_SBDebugger_GetFilterForType, METH_VARARGS, "SBDebugger_GetFilterForType(SBDebugger self, SBTypeNameSpecifier arg2) -> SBTypeFilter"}, { "SBDebugger_GetSyntheticForType", _wrap_SBDebugger_GetSyntheticForType, METH_VARARGS, "SBDebugger_GetSyntheticForType(SBDebugger self, SBTypeNameSpecifier arg2) -> SBTypeSynthetic"}, + { "SBDebugger_ResetStatistics", _wrap_SBDebugger_ResetStatistics, METH_O, "SBDebugger_ResetStatistics(SBDebugger self)"}, { "SBDebugger_RunCommandInterpreter", _wrap_SBDebugger_RunCommandInterpreter, METH_VARARGS, "\n" "SBDebugger_RunCommandInterpreter(SBDebugger self, bool auto_handle_events, bool spawn_thread, SBCommandInterpreterRunOptions options, int & num_errors, bool & quit_requested, bool & stopped_for_crash)\n" "Launch a command interpreter session. Commands are read from standard input or\n" @@ -98526,6 +98671,7 @@ static PyMethodDef SwigMethods[] = { "SBTarget_GetStatistics(SBTarget self) -> SBStructuredData\n" "SBTarget_GetStatistics(SBTarget self, SBStatisticsOptions options) -> SBStructuredData\n" ""}, + { "SBTarget_ResetStatistics", _wrap_SBTarget_ResetStatistics, METH_O, "SBTarget_ResetStatistics(SBTarget self)"}, { "SBTarget_GetPlatform", _wrap_SBTarget_GetPlatform, METH_O, "\n" "SBTarget_GetPlatform(SBTarget self) -> SBPlatform\n" "\n" @@ -102876,6 +103022,7 @@ SWIG_init(void) { SWIG_Python_SetConstant(d, "eSectionTypeDWARFDebugTuIndex",SWIG_From_int(static_cast< int >(lldb::eSectionTypeDWARFDebugTuIndex))); SWIG_Python_SetConstant(d, "eSectionTypeCTF",SWIG_From_int(static_cast< int >(lldb::eSectionTypeCTF))); SWIG_Python_SetConstant(d, "eSectionTypeLLDBTypeSummaries",SWIG_From_int(static_cast< int >(lldb::eSectionTypeLLDBTypeSummaries))); + SWIG_Python_SetConstant(d, "eSectionTypeLLDBFormatters",SWIG_From_int(static_cast< int >(lldb::eSectionTypeLLDBFormatters))); SWIG_Python_SetConstant(d, "eSectionTypeSwiftModules",SWIG_From_int(static_cast< int >(lldb::eSectionTypeSwiftModules))); SWIG_Python_SetConstant(d, "eEmulateInstructionOptionNone",SWIG_From_int(static_cast< int >(lldb::eEmulateInstructionOptionNone))); SWIG_Python_SetConstant(d, "eEmulateInstructionOptionAutoAdvancePC",SWIG_From_int(static_cast< int >(lldb::eEmulateInstructionOptionAutoAdvancePC))); @@ -103180,6 +103327,8 @@ SWIG_init(void) { SWIG_Python_SetConstant(d, "eSeverityError",SWIG_From_int(static_cast< int >(lldb::eSeverityError))); SWIG_Python_SetConstant(d, "eSeverityWarning",SWIG_From_int(static_cast< int >(lldb::eSeverityWarning))); SWIG_Python_SetConstant(d, "eSeverityInfo",SWIG_From_int(static_cast< int >(lldb::eSeverityInfo))); + SWIG_Python_SetConstant(d, "eCommandReturnObjectPrintCallbackSkipped",SWIG_From_int(static_cast< int >(lldb::eCommandReturnObjectPrintCallbackSkipped))); + SWIG_Python_SetConstant(d, "eCommandReturnObjectPrintCallbackHandled",SWIG_From_int(static_cast< int >(lldb::eCommandReturnObjectPrintCallbackHandled))); SWIG_Python_SetConstant(d, "SBCommandInterpreter_eBroadcastBitThreadShouldExit",SWIG_From_int(static_cast< int >(lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit))); SWIG_Python_SetConstant(d, "SBCommandInterpreter_eBroadcastBitResetPrompt",SWIG_From_int(static_cast< int >(lldb::SBCommandInterpreter::eBroadcastBitResetPrompt))); SWIG_Python_SetConstant(d, "SBCommandInterpreter_eBroadcastBitQuitCommandReceived",SWIG_From_int(static_cast< int >(lldb::SBCommandInterpreter::eBroadcastBitQuitCommandReceived))); diff --git a/lldb/bindings/python/static-binding/lldb.py b/lldb/bindings/python/static-binding/lldb.py index 795a2ae848607..e81585c031a85 100644 --- a/lldb/bindings/python/static-binding/lldb.py +++ b/lldb/bindings/python/static-binding/lldb.py @@ -1209,6 +1209,8 @@ def lldb_iter(obj, getsize, getelem): eSectionTypeLLDBTypeSummaries = _lldb.eSectionTypeLLDBTypeSummaries +eSectionTypeLLDBFormatters = _lldb.eSectionTypeLLDBFormatters + eSectionTypeSwiftModules = _lldb.eSectionTypeSwiftModules eEmulateInstructionOptionNone = _lldb.eEmulateInstructionOptionNone @@ -1817,6 +1819,10 @@ def lldb_iter(obj, getsize, getelem): eSeverityInfo = _lldb.eSeverityInfo +eCommandReturnObjectPrintCallbackSkipped = _lldb.eCommandReturnObjectPrintCallbackSkipped + +eCommandReturnObjectPrintCallbackHandled = _lldb.eCommandReturnObjectPrintCallbackHandled + class SBAddress(object): r""" A section + offset based address class. @@ -3609,6 +3615,10 @@ def GetTranscript(self): r"""GetTranscript(SBCommandInterpreter self) -> SBStructuredData""" return _lldb.SBCommandInterpreter_GetTranscript(self) + def SetPrintCallback(self, callback): + r"""SetPrintCallback(SBCommandInterpreter self, lldb::SBCommandPrintCallback callback)""" + return _lldb.SBCommandInterpreter_SetPrintCallback(self, callback) + # Register SBCommandInterpreter in _lldb: _lldb.SBCommandInterpreter_swigregister(SBCommandInterpreter) class SBCommandInterpreterRunOptions(object): @@ -3761,6 +3771,10 @@ def IsValid(self): r"""IsValid(SBCommandReturnObject self) -> bool""" return _lldb.SBCommandReturnObject_IsValid(self) + def GetCommand(self): + r"""GetCommand(SBCommandReturnObject self) -> char const *""" + return _lldb.SBCommandReturnObject_GetCommand(self) + def GetErrorData(self): r"""GetErrorData(SBCommandReturnObject self) -> SBStructuredData""" return _lldb.SBCommandReturnObject_GetErrorData(self) @@ -5187,6 +5201,10 @@ def GetSyntheticForType(self, arg2): r"""GetSyntheticForType(SBDebugger self, SBTypeNameSpecifier arg2) -> SBTypeSynthetic""" return _lldb.SBDebugger_GetSyntheticForType(self, arg2) + def ResetStatistics(self): + r"""ResetStatistics(SBDebugger self)""" + return _lldb.SBDebugger_ResetStatistics(self) + def RunCommandInterpreter(self, auto_handle_events, spawn_thread, options, num_errors, quit_requested, stopped_for_crash): r""" RunCommandInterpreter(SBDebugger self, bool auto_handle_events, bool spawn_thread, SBCommandInterpreterRunOptions options, int & num_errors, bool & quit_requested, bool & stopped_for_crash) @@ -11243,6 +11261,10 @@ def GetStatistics(self, *args): """ return _lldb.SBTarget_GetStatistics(self, *args) + def ResetStatistics(self): + r"""ResetStatistics(SBTarget self)""" + return _lldb.SBTarget_ResetStatistics(self) + def GetPlatform(self): r""" GetPlatform(SBTarget self) -> SBPlatform diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h index b7e39b7858616..dd475ddf6c170 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 e8e20a3f3016b..9a63c1f96aa70 100644 --- a/lldb/include/lldb/API/SBCommandReturnObject.h +++ b/lldb/include/lldb/API/SBCommandReturnObject.h @@ -42,6 +42,10 @@ class LLDB_API SBCommandReturnObject { bool IsValid() const; + /// Get the command as the user typed it. Empty string if commands were run on + /// behalf of lldb. + const char *GetCommand(); + const char *GetOutput(); const char *GetError(); diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h index 2c2afc55b4b82..b3c335c920a26 100644 --- a/lldb/include/lldb/API/SBDefines.h +++ b/lldb/include/lldb/API/SBDefines.h @@ -144,6 +144,9 @@ typedef bool (*SBBreakpointHitCallback)(void *baton, SBProcess &process, typedef void (*SBDebuggerDestroyCallback)(lldb::user_id_t debugger_id, void *baton); +typedef CommandReturnObjectCallbackResult (*SBCommandPrintCallback)( + lldb::SBCommandReturnObject &result, void *baton); + typedef SBError (*SBPlatformLocateModuleCallback)( void *baton, const SBModuleSpec &module_spec, SBFileSpec &module_file_spec, SBFileSpec &symbol_file_spec); diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h index 2bafc30cc8e23..e8a1240905257 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" @@ -254,7 +255,11 @@ class CommandInterpreter : public Broadcaster, eCommandTypesAllThem = 0xFFFF //< all commands }; - // The CommandAlias and CommandInterpreter both have a hand in + using CommandReturnObjectCallback = + std::function; + + // 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 // command is executed. These are the special tokens: @@ -596,7 +601,7 @@ class CommandInterpreter : public Broadcaster, void SetEchoCommentCommands(bool enable); bool GetRepeatPreviousCommand() const; - + bool GetRequireCommandOverwrite() const; const CommandObject::CommandMap &GetUserCommands() const { @@ -666,6 +671,8 @@ class CommandInterpreter : public Broadcaster, ++m_command_usages[cmd_obj.GetCommandName()]; } + void SetPrintCallback(CommandReturnObjectCallback callback); + llvm::json::Value GetStatistics(); const StructuredData::Array &GetTranscript() const; @@ -776,6 +783,9 @@ class CommandInterpreter : public Broadcaster, std::vector 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 m_quit_exit_code; diff --git a/lldb/include/lldb/Interpreter/CommandReturnObject.h b/lldb/include/lldb/Interpreter/CommandReturnObject.h index a491a6c1535b1..470650d273505 100644 --- a/lldb/include/lldb/Interpreter/CommandReturnObject.h +++ b/lldb/include/lldb/Interpreter/CommandReturnObject.h @@ -31,10 +31,16 @@ class CommandReturnObject { ~CommandReturnObject() = default; + /// Get the command as the user typed it. Empty string if commands were run on + /// behalf of lldb. + const std::string &GetCommand() const { return m_command; } + + void SetCommand(std::string command) { m_command = std::move(command); } + /// Format any inline diagnostics with an indentation of \c indent. - std::string GetInlineDiagnosticString(unsigned indent); + std::string GetInlineDiagnosticString(unsigned indent) const; - llvm::StringRef GetOutputString() { + llvm::StringRef GetOutputString() const { lldb::StreamSP stream_sp(m_out_stream.GetStreamAtIndex(eStreamStringIndex)); if (stream_sp) return std::static_pointer_cast(stream_sp)->GetString(); @@ -46,7 +52,7 @@ class CommandReturnObject { /// If \c with_diagnostics is true, all diagnostics are also /// rendered into the string. Otherwise the expectation is that they /// are fetched with \ref GetInlineDiagnosticString(). - std::string GetErrorString(bool with_diagnostics = true); + std::string GetErrorString(bool with_diagnostics = true) const; StructuredData::ObjectSP GetErrorData(); Stream &GetOutputStream() { @@ -95,11 +101,11 @@ class CommandReturnObject { m_err_stream.SetStreamAtIndex(eImmediateStreamIndex, stream_sp); } - lldb::StreamSP GetImmediateOutputStream() { + lldb::StreamSP GetImmediateOutputStream() const { return m_out_stream.GetStreamAtIndex(eImmediateStreamIndex); } - lldb::StreamSP GetImmediateErrorStream() { + lldb::StreamSP GetImmediateErrorStream() const { return m_err_stream.GetStreamAtIndex(eImmediateStreamIndex); } @@ -172,6 +178,8 @@ class CommandReturnObject { private: enum { eStreamStringIndex = 0, eImmediateStreamIndex = 1 }; + std::string m_command; + StreamTee m_out_stream; StreamTee m_err_stream; std::vector m_diagnostics; diff --git a/lldb/include/lldb/Interpreter/Options.h b/lldb/include/lldb/Interpreter/Options.h index 9a6a17c2793fa..864bda6f24c8c 100644 --- a/lldb/include/lldb/Interpreter/Options.h +++ b/lldb/include/lldb/Interpreter/Options.h @@ -76,12 +76,12 @@ class Options { // This gets passed the short option as an integer... void OptionSeen(int short_option); - bool VerifyOptions(CommandReturnObject &result); + llvm::Error VerifyOptions(); // Verify that the options given are in the options table and can be used // together, but there may be some required options that are missing (used to // verify options that get folded into command aliases). - bool VerifyPartialOptions(CommandReturnObject &result); + llvm::Error VerifyPartialOptions(); void OutputFormattedUsageText(Stream &strm, const OptionDefinition &option_def, diff --git a/lldb/include/lldb/Utility/StreamTee.h b/lldb/include/lldb/Utility/StreamTee.h index 5695586171f35..571548e2e23fe 100644 --- a/lldb/include/lldb/Utility/StreamTee.h +++ b/lldb/include/lldb/Utility/StreamTee.h @@ -85,7 +85,7 @@ class StreamTee : public Stream { return result; } - lldb::StreamSP GetStreamAtIndex(uint32_t idx) { + lldb::StreamSP GetStreamAtIndex(uint32_t idx) const { lldb::StreamSP stream_sp; std::lock_guard guard(m_streams_mutex); if (idx < m_streams.size()) diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index b54fb12cb7512..882640eccc3d2 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -1395,6 +1395,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 7a35473283684..d153e2acdf853 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; @@ -151,7 +151,7 @@ bool SBCommandInterpreter::WasInterrupted() const { bool SBCommandInterpreter::InterruptCommand() { LLDB_INSTRUMENT_VA(this); - + return (IsValid() ? m_opaque_ptr->InterruptCommand() : false); } @@ -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() { @@ -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) + m_opaque_ptr->SetPrintCallback( + [callback, baton](lldb_private::CommandReturnObject &result) { + SBCommandReturnObject sb_result(result); + return callback(sb_result, baton); + }); +} diff --git a/lldb/source/API/SBCommandReturnObject.cpp b/lldb/source/API/SBCommandReturnObject.cpp index 9df8aa48b9936..6f54581e64ef4 100644 --- a/lldb/source/API/SBCommandReturnObject.cpp +++ b/lldb/source/API/SBCommandReturnObject.cpp @@ -84,6 +84,13 @@ SBCommandReturnObject::operator bool() const { return true; } +const char *SBCommandReturnObject::GetCommand() { + LLDB_INSTRUMENT_VA(this); + + ConstString output(ref().GetCommand()); + return output.AsCString(/*value_if_empty*/ ""); +} + const char *SBCommandReturnObject::GetOutput() { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/source/Interpreter/CommandAlias.cpp b/lldb/source/Interpreter/CommandAlias.cpp index c5971b52f837f..b45fcca358a5e 100644 --- a/lldb/source/Interpreter/CommandAlias.cpp +++ b/lldb/source/Interpreter/CommandAlias.cpp @@ -10,6 +10,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormatAdapters.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" @@ -20,20 +21,17 @@ using namespace lldb; using namespace lldb_private; -static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp, - llvm::StringRef options_args, - OptionArgVectorSP &option_arg_vector_sp) { - bool success = true; +static llvm::Error +ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp, + llvm::StringRef options_args, + OptionArgVectorSP &option_arg_vector_sp) { OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); if (options_args.size() < 1) - return true; + return llvm::Error::success(); Args args(options_args); std::string options_string(options_args); - // TODO: Find a way to propagate errors in this CommandReturnObject up the - // stack. - CommandReturnObject result(false); // Check to see if the command being aliased can take any command options. Options *options = cmd_obj_sp->GetOptions(); if (options) { @@ -45,34 +43,30 @@ static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp, llvm::Expected args_or = options->ParseAlias(args, option_arg_vector, options_string); - if (!args_or) { - result.AppendError(toString(args_or.takeError())); - result.AppendError("Unable to create requested alias.\n"); - return false; - } + if (!args_or) + return llvm::createStringError( + llvm::formatv("unable to create alias: {0}", + llvm::fmt_consume(args_or.takeError()))); args = std::move(*args_or); - options->VerifyPartialOptions(result); - if (!result.Succeeded() && - result.GetStatus() != lldb::eReturnStatusStarted) { - result.AppendError("Unable to create requested alias.\n"); - return false; - } + if (llvm::Error error = options->VerifyPartialOptions()) + return error; } if (!options_string.empty()) { - if (cmd_obj_sp->WantsRawCommandString()) - option_arg_vector->emplace_back(CommandInterpreter::g_argument, - -1, options_string); - else { + if (cmd_obj_sp->WantsRawCommandString()) { + option_arg_vector->emplace_back(CommandInterpreter::g_argument, -1, + options_string); + } else { for (auto &entry : args.entries()) { if (!entry.ref().empty()) - option_arg_vector->emplace_back(std::string(CommandInterpreter::g_argument), -1, - std::string(entry.ref())); + option_arg_vector->emplace_back( + std::string(CommandInterpreter::g_argument), -1, + std::string(entry.ref())); } } } - return success; + return llvm::Error::success(); } CommandAlias::CommandAlias(CommandInterpreter &interpreter, @@ -85,10 +79,15 @@ CommandAlias::CommandAlias(CommandInterpreter &interpreter, m_option_args_sp(new OptionArgVector), m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false), m_did_set_help_long(false) { - if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) { + if (llvm::Error error = + ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) { + // FIXME: Find a way to percolate this error up. + LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), + "ProcessAliasOptionsArgs failed: {0}"); + } else { m_underlying_command_sp = cmd_sp; for (int i = 0; - auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i); + auto *cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i); i++) { m_arguments.push_back(*cmd_entry); } diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 70642d6a582f4..d7fe97077cb34 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -286,8 +286,6 @@ bool CommandInterpreter::GetRequireCommandOverwrite() const { void CommandInterpreter::Initialize() { LLDB_SCOPED_TIMER(); - CommandReturnObject result(m_debugger.GetUseColor()); - LoadCommandDictionary(); // An alias arguments vector to reuse - reset it before use... @@ -1905,12 +1903,13 @@ bool CommandInterpreter::HandleCommand(const char *command_line, std::string real_original_command_string(command_string); Log *log = GetLog(LLDBLog::Commands); - llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")", - command_line); - LLDB_LOGF(log, "Processing command: %s", command_line); LLDB_SCOPED_TIMERF("Processing command: %s.", command_line); + // Set the command in the CommandReturnObject here so that it's there even if + // the command is interrupted. + result.SetCommand(command_line); + if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted initiating command")) { result.AppendError("... Interrupted"); return false; @@ -2659,7 +2658,8 @@ void CommandInterpreter::HandleCommands(const StringList &commands, (uint64_t)idx, cmd, error_msg); m_debugger.SetAsyncExecution(old_async_execution); return; - } else if (options.GetPrintResults()) { + } + if (options.GetPrintResults()) { result.AppendMessageWithFormatv("Command #{0} '{1}' failed with {2}", (uint64_t)idx + 1, cmd, error_msg); } @@ -3198,30 +3198,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); } } @@ -3668,3 +3678,8 @@ llvm::json::Value CommandInterpreter::GetStatistics() { const StructuredData::Array &CommandInterpreter::GetTranscript() const { return m_transcript; } + +void CommandInterpreter::SetPrintCallback( + CommandReturnObjectCallback callback) { + m_print_callback = callback; +} diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 6b044a28eb37e..7008253e32bca 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -120,17 +120,24 @@ bool CommandObject::ParseOptions(Args &args, CommandReturnObject &result) { if (args_or) { args = std::move(*args_or); error = options->NotifyOptionParsingFinished(&exe_ctx); - } else + } else { error = Status::FromError(args_or.takeError()); + } - if (error.Success()) { - if (options->VerifyOptions(result)) - return true; - } else { + if (error.Fail()) { result.SetError(error.takeError()); + result.SetStatus(eReturnStatusFailed); + return false; } - result.SetStatus(eReturnStatusFailed); - return false; + + if (llvm::Error error = options->VerifyOptions()) { + result.SetError(std::move(error)); + result.SetStatus(eReturnStatusFailed); + return false; + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; } return true; } @@ -278,7 +285,6 @@ void CommandObject::HandleCompletion(CompletionRequest &request) { } else { // Can we do anything generic with the options? Options *cur_options = GetOptions(); - CommandReturnObject result(m_interpreter.GetDebugger().GetUseColor()); OptionElementVector opt_element_vector; if (cur_options != nullptr) { diff --git a/lldb/source/Interpreter/CommandReturnObject.cpp b/lldb/source/Interpreter/CommandReturnObject.cpp index 7c5c80e9b0925..3cc718511606b 100644 --- a/lldb/source/Interpreter/CommandReturnObject.cpp +++ b/lldb/source/Interpreter/CommandReturnObject.cpp @@ -123,7 +123,8 @@ void CommandReturnObject::SetError(llvm::Error error) { } } -std::string CommandReturnObject::GetInlineDiagnosticString(unsigned indent) { +std::string +CommandReturnObject::GetInlineDiagnosticString(unsigned indent) const { StreamString diag_stream(m_colors); RenderDiagnosticDetails(diag_stream, indent, true, m_diagnostics); // Duplex the diagnostics to the secondary stream (but not inlined). @@ -133,7 +134,7 @@ std::string CommandReturnObject::GetInlineDiagnosticString(unsigned indent) { return diag_stream.GetString().str(); } -std::string CommandReturnObject::GetErrorString(bool with_diagnostics) { +std::string CommandReturnObject::GetErrorString(bool with_diagnostics) const { StreamString stream(m_colors); if (with_diagnostics) RenderDiagnosticDetails(stream, std::nullopt, false, m_diagnostics); diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp index 046910ab15361..e1020d6937eb6 100644 --- a/lldb/source/Interpreter/Options.cpp +++ b/lldb/source/Interpreter/Options.cpp @@ -138,46 +138,6 @@ void Options::OptionsSetUnion(const OptionSet &set_a, const OptionSet &set_b, } } -bool Options::VerifyOptions(CommandReturnObject &result) { - bool options_are_valid = false; - - int num_levels = GetRequiredOptions().size(); - if (num_levels) { - for (int i = 0; i < num_levels && !options_are_valid; ++i) { - // This is the correct set of options if: 1). m_seen_options contains - // all of m_required_options[i] (i.e. all the required options at this - // level are a subset of m_seen_options); AND 2). { m_seen_options - - // m_required_options[i] is a subset of m_options_options[i] (i.e. all - // the rest of m_seen_options are in the set of optional options at this - // level. - - // Check to see if all of m_required_options[i] are a subset of - // m_seen_options - if (IsASubset(GetRequiredOptions()[i], m_seen_options)) { - // Construct the set difference: remaining_options = {m_seen_options} - - // {m_required_options[i]} - OptionSet remaining_options; - OptionsSetDiff(m_seen_options, GetRequiredOptions()[i], - remaining_options); - // Check to see if remaining_options is a subset of - // m_optional_options[i] - if (IsASubset(remaining_options, GetOptionalOptions()[i])) - options_are_valid = true; - } - } - } else { - options_are_valid = true; - } - - if (options_are_valid) { - result.SetStatus(eReturnStatusSuccessFinishNoResult); - } else { - result.AppendError("invalid combination of options for the given command"); - } - - return options_are_valid; -} - // This is called in the Options constructor, though we could call it lazily if // that ends up being a performance problem. @@ -593,13 +553,50 @@ void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd, strm.SetIndentLevel(save_indent_level); } +llvm::Error Options::VerifyOptions() { + bool options_are_valid = false; + + int num_levels = GetRequiredOptions().size(); + if (num_levels) { + for (int i = 0; i < num_levels && !options_are_valid; ++i) { + // This is the correct set of options if: 1). m_seen_options contains + // all of m_required_options[i] (i.e. all the required options at this + // level are a subset of m_seen_options); AND 2). { m_seen_options - + // m_required_options[i] is a subset of m_options_options[i] (i.e. all + // the rest of m_seen_options are in the set of optional options at this + // level. + + // Check to see if all of m_required_options[i] are a subset of + // m_seen_options + if (IsASubset(GetRequiredOptions()[i], m_seen_options)) { + // Construct the set difference: remaining_options = {m_seen_options} - + // {m_required_options[i]} + OptionSet remaining_options; + OptionsSetDiff(m_seen_options, GetRequiredOptions()[i], + remaining_options); + // Check to see if remaining_options is a subset of + // m_optional_options[i] + if (IsASubset(remaining_options, GetOptionalOptions()[i])) + options_are_valid = true; + } + } + } else { + options_are_valid = true; + } + + if (!options_are_valid) + return llvm::createStringError( + "invalid combination of options for the given command"); + + return llvm::Error::success(); +} + // This function is called when we have been given a potentially incomplete set // of options, such as when an alias has been defined (more options might be // added at at the time the alias is invoked). We need to verify that the // options in the set m_seen_options are all part of a set that may be used // together, but m_seen_options may be missing some of the "required" options. - -bool Options::VerifyPartialOptions(CommandReturnObject &result) { +llvm::Error Options::VerifyPartialOptions() { bool options_are_valid = false; int num_levels = GetRequiredOptions().size(); @@ -616,7 +613,11 @@ bool Options::VerifyPartialOptions(CommandReturnObject &result) { } } - return options_are_valid; + if (!options_are_valid) + return llvm::createStringError( + "invalid combination of options for the given command"); + + return llvm::Error::success(); } bool Options::HandleOptionCompletion(CompletionRequest &request, diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index 97a3837fd7aa6..27d70fb8f0c5f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -81,6 +81,8 @@ template class ScopedPythonObject : PythonObject { class SWIGBridge { public: static PythonObject ToSWIGWrapper(std::unique_ptr value_sb); + static PythonObject + ToSWIGWrapper(std::unique_ptr result_up); static PythonObject ToSWIGWrapper(lldb::ValueObjectSP value_sp); static PythonObject ToSWIGWrapper(lldb::TargetSP target_sp); static PythonObject ToSWIGWrapper(lldb::ProcessSP process_sp); @@ -199,12 +201,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 LLDBSwigPythonGetRepeatCommandForScriptedCommand(PyObject *implementor, diff --git a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp index 4ca8bd2f9085d..82f18c5fe37a2 100644 --- a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp +++ b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp @@ -975,8 +975,6 @@ EnableOptionsSP ParseAutoEnableOptions(Status &error, Debugger &debugger) { EnableOptionsSP options_sp(new EnableOptions()); options_sp->NotifyOptionParsingStarting(&exe_ctx); - CommandReturnObject result(debugger.GetUseColor()); - // Parse the arguments. auto options_property_sp = debugger.GetPropertyValue(nullptr, @@ -1013,8 +1011,13 @@ EnableOptionsSP ParseAutoEnableOptions(Status &error, Debugger &debugger) { return EnableOptionsSP(); } - if (!options_sp->VerifyOptions(result)) + if (llvm::Error error = options_sp->VerifyOptions()) { + LLDB_LOG_ERROR( + log, std::move(error), + "Parsing plugin.structured-data.darwin-log.auto-enable-options value " + "failed: {0}"); return EnableOptionsSP(); + } // We successfully parsed and validated the options. return options_sp; diff --git a/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py b/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py index a8cbffbb7ba4a..cc767edaaa619 100644 --- a/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py +++ b/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py @@ -45,10 +45,12 @@ def test_command_abbreviations_and_aliases(self): # Make sure an unabbreviated command is not mangled. command_interpreter.ResolveCommand( - "breakpoint set --name main --line 123", result + "breakpoint set --name main --ignore-count 123", result ) self.assertTrue(result.Succeeded()) - self.assertEqual("breakpoint set --name main --line 123", result.GetOutput()) + self.assertEqual( + "breakpoint set --name main --ignore-count 123", result.GetOutput() + ) # Create some aliases. self.runCmd("com a alias com al") @@ -72,10 +74,10 @@ def test_command_abbreviations_and_aliases(self): "process launch -s -o /dev/tty0 -e /dev/tty0", result.GetOutput() ) - self.runCmd("alias xyzzy breakpoint set -n %1 -l %2") + self.runCmd("alias xyzzy breakpoint set -n %1 -i %2") command_interpreter.ResolveCommand("xyzzy main 123", result) self.assertTrue(result.Succeeded()) - self.assertEqual("breakpoint set -n main -l 123", result.GetOutput().strip()) + self.assertEqual("breakpoint set -n main -i 123", result.GetOutput().strip()) # And again, without enough parameters. command_interpreter.ResolveCommand("xyzzy main", result) @@ -92,6 +94,12 @@ def test_command_abbreviations_and_aliases(self): self.assertTrue(result.Succeeded()) self.assertEqual("scripting run 1+1", result.GetOutput()) + # Name and line are incompatible options. + command_interpreter.HandleCommand( + "alias zzyx breakpoint set -n %1 -l %2", result + ) + self.assertFalse(result.Succeeded()) + # Prompt changing stuff should be tested, but this doesn't seem like the # right test to do it in. It has nothing to do with aliases or abbreviations. # self.runCmd("com sou ./change_prompt.lldb") diff --git a/lldb/test/API/python_api/commandreturnobject/TestSBCommandReturnObject.py b/lldb/test/API/python_api/commandreturnobject/TestSBCommandReturnObject.py new file mode 100644 index 0000000000000..b0d0b7a8dfe4e --- /dev/null +++ b/lldb/test/API/python_api/commandreturnobject/TestSBCommandReturnObject.py @@ -0,0 +1,17 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class SBCommandReturnObjectTest(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test(self): + res = lldb.SBCommandReturnObject() + self.assertEqual(res.GetCommand(), "") + + ci = self.dbg.GetCommandInterpreter() + ci.HandleCommand("help help", res) + self.assertTrue(res.Succeeded()) + self.assertEqual(res.GetCommand(), "help help") 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 0000000000000..10495940055b6 --- /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 0000000000000..c99c636bda3d6 --- /dev/null +++ b/lldb/test/API/python_api/interpreter_callback/TestCommandInterepterPrintCallback.py @@ -0,0 +1,68 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class CommandInterepterPrintCallbackTest(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): + """Test the command interpreter print callback.""" + 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.assertEqual("help help", return_object.GetCommand()) + 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.assertEqual("he help", return_object.GetCommand()) + 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, "he 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 0000000000000..78d5c0714714a --- /dev/null +++ b/lldb/test/API/python_api/interpreter_callback/main.c @@ -0,0 +1,6 @@ +#include + +int main() { + int i = 1; + return i; // Break here +}