diff options
26 files changed, 467 insertions, 137 deletions
diff --git a/lldb/include/lldb/API/SBBreakpoint.h b/lldb/include/lldb/API/SBBreakpoint.h index 75c0e69a482..a5ce91d9508 100644 --- a/lldb/include/lldb/API/SBBreakpoint.h +++ b/lldb/include/lldb/API/SBBreakpoint.h @@ -94,6 +94,9 @@ public: void SetScriptCallbackFunction(const char *callback_function_name); + SBError SetScriptCallbackFunction(const char *callback_function_name, + SBStructuredData &extra_args); + void SetCommandLineCommands(SBStringList &commands); bool GetCommandLineCommands(SBStringList &commands); diff --git a/lldb/include/lldb/API/SBBreakpointLocation.h b/lldb/include/lldb/API/SBBreakpointLocation.h index 085bed9399b..a9e2ef1dd1b 100644 --- a/lldb/include/lldb/API/SBBreakpointLocation.h +++ b/lldb/include/lldb/API/SBBreakpointLocation.h @@ -55,11 +55,14 @@ public: void SetScriptCallbackFunction(const char *callback_function_name); + SBError SetScriptCallbackFunction(const char *callback_function_name, + lldb::SBStructuredData &extra_args); + SBError SetScriptCallbackBody(const char *script_body_text); - void SetCommandLineCommands(SBStringList &commands); + void SetCommandLineCommands(lldb::SBStringList &commands); - bool GetCommandLineCommands(SBStringList &commands); + bool GetCommandLineCommands(lldb::SBStringList &commands); void SetThreadID(lldb::tid_t sb_thread_id); diff --git a/lldb/include/lldb/API/SBBreakpointName.h b/lldb/include/lldb/API/SBBreakpointName.h index 018238bcd07..3a5f1acf3e4 100644 --- a/lldb/include/lldb/API/SBBreakpointName.h +++ b/lldb/include/lldb/API/SBBreakpointName.h @@ -85,9 +85,12 @@ public: void SetScriptCallbackFunction(const char *callback_function_name); - void SetCommandLineCommands(SBStringList &commands); + SBError SetScriptCallbackFunction(const char *callback_function_name, + SBStructuredData &extra_args); - bool GetCommandLineCommands(SBStringList &commands); + void SetCommandLineCommands(lldb::SBStringList &commands); + + bool GetCommandLineCommands(lldb::SBStringList &commands); SBError SetScriptCallbackBody(const char *script_body_text); diff --git a/lldb/include/lldb/API/SBStructuredData.h b/lldb/include/lldb/API/SBStructuredData.h index a090272e45a..785e91047fd 100644 --- a/lldb/include/lldb/API/SBStructuredData.h +++ b/lldb/include/lldb/API/SBStructuredData.h @@ -93,6 +93,9 @@ protected: friend class SBTarget; friend class SBThread; friend class SBThreadPlan; + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBBreakpointName; StructuredDataImplUP m_impl_up; }; diff --git a/lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h b/lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h index 6aec7eb0f0b..2229c1aa08a 100644 --- a/lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h +++ b/lldb/include/lldb/Interpreter/OptionGroupPythonClassWithDict.h @@ -24,13 +24,10 @@ namespace lldb_private { class OptionGroupPythonClassWithDict : public OptionGroup { public: OptionGroupPythonClassWithDict(const char *class_use, - int class_option = 'C', - int key_option = 'k', - int value_option = 'v', - const char *class_long_option = "python-class", - const char *key_long_option = "python-class-key", - const char *value_long_option = "python-class-value", - bool required = false); + bool is_class = true, + int class_option = 'C', + int key_option = 'k', + int value_option = 'v'); ~OptionGroupPythonClassWithDict() override; @@ -48,16 +45,17 @@ public: const StructuredData::DictionarySP GetStructuredData() { return m_dict_sp; } - const std::string &GetClassName() { - return m_class_name; + const std::string &GetName() { + return m_name; } protected: - std::string m_class_name; + std::string m_name; std::string m_current_key; StructuredData::DictionarySP m_dict_sp; std::string m_class_usage_text, m_key_usage_text, m_value_usage_text; - OptionDefinition m_option_definition[3]; + bool m_is_class; + OptionDefinition m_option_definition[4]; }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 23fadf02e59..2213274f1db 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -120,8 +120,10 @@ public: return error; } - virtual Status GenerateBreakpointCommandCallbackData(StringList &input, - std::string &output) { + virtual Status GenerateBreakpointCommandCallbackData( + StringList &input, + std::string &output, + bool has_extra_args) { Status error; error.SetErrorString("not implemented"); return error; @@ -311,14 +313,17 @@ public: return error; } - void SetBreakpointCommandCallbackFunction( + Status SetBreakpointCommandCallbackFunction( std::vector<BreakpointOptions *> &bp_options_vec, - const char *function_name); + const char *function_name, + StructuredData::ObjectSP extra_args_sp); - /// Set a one-liner as the callback for the breakpoint. - virtual void - SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options, - const char *function_name) {} + /// Set a script function as the callback for the breakpoint. + virtual Status + SetBreakpointCommandCallbackFunction( + BreakpointOptions *bp_options, + const char *function_name, + StructuredData::ObjectSP extra_args_sp) {} /// Set a one-liner as the callback for the watchpoint. virtual void SetWatchpointCommandCallback(WatchpointOptions *wp_options, @@ -463,6 +468,12 @@ public: const char *GetScriptInterpreterPtyName(); int GetMasterFileDescriptor(); + + virtual llvm::Expected<size_t> + GetNumFixedArgumentsForCallable(const llvm::StringRef &callable_name) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), "Unimplemented function"); + } static std::string LanguageToString(lldb::ScriptLanguage language); diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py index b75a6db6e80..af8ece16564 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py @@ -18,7 +18,7 @@ class BreakpointCommandTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") - def test_breakpoint_command_sequence(self): + def not_test_breakpoint_command_sequence(self): """Test a sequence of breakpoint command add, list, and delete.""" self.build() self.breakpoint_command_sequence() @@ -107,6 +107,10 @@ class BreakpointCommandTestCase(TestBase): "breakpoint command add -s command -o 'frame variable --show-types --scope' 1 4") self.runCmd( "breakpoint command add -s python -o 'import side_effect; side_effect.one_liner = \"one liner was here\"' 2") + + import side_effect + self.runCmd("command script import --allow-reload ./bktptcmd.py") + self.runCmd( "breakpoint command add --python-function bktptcmd.function 3") @@ -151,8 +155,6 @@ class BreakpointCommandTestCase(TestBase): self.runCmd("breakpoint delete 4") - self.runCmd("command script import --allow-reload ./bktptcmd.py") - # Next lets try some other breakpoint kinds. First break with a regular expression # and then specify only one file. The first time we should get two locations, # the second time only one: diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py index 962728a324e..ccb61d79c40 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/TestBreakpointCommandsFromPython.py @@ -19,10 +19,15 @@ class PythonBreakpointCommandSettingTestCase(TestBase): @add_test_categories(['pyapi']) def test_step_out_python(self): - """Test stepping out using avoid-no-debug with dsyms.""" + """Test stepping out using a python breakpoint command.""" self.build() self.do_set_python_command_from_python() + def test_bkpt_cmd_bad_arguments(self): + """Test what happens when pass structured data to a command:""" + self.build() + self.do_bad_args_to_python_command() + def setUp(self): TestBase.setUp(self) self.main_source = "main.c" @@ -43,6 +48,18 @@ class PythonBreakpointCommandSettingTestCase(TestBase): "Set break point at this line.", self.main_source_spec) self.assertTrue(func_bkpt, VALID_BREAKPOINT) + fancy_bkpt = self.target.BreakpointCreateBySourceRegex( + "Set break point at this line.", self.main_source_spec) + self.assertTrue(fancy_bkpt, VALID_BREAKPOINT) + + fancier_bkpt = self.target.BreakpointCreateBySourceRegex( + "Set break point at this line.", self.main_source_spec) + self.assertTrue(fancier_bkpt, VALID_BREAKPOINT) + + not_so_fancy_bkpt = self.target.BreakpointCreateBySourceRegex( + "Set break point at this line.", self.main_source_spec) + self.assertTrue(not_so_fancy_bkpt, VALID_BREAKPOINT) + # Also test that setting a source regex breakpoint with an empty file # spec list sets it on all files: no_files_bkpt = self.target.BreakpointCreateBySourceRegex( @@ -75,14 +92,37 @@ class PythonBreakpointCommandSettingTestCase(TestBase): "Failed to set the script callback body: %s." % (error.GetCString())) - self.dbg.HandleCommand( - "command script import --allow-reload ./bktptcmd.py") + self.expect("command script import --allow-reload ./bktptcmd.py") + func_bkpt.SetScriptCallbackFunction("bktptcmd.function") + extra_args = lldb.SBStructuredData() + stream = lldb.SBStream() + stream.Print('{"side_effect" : "I am fancy"}') + extra_args.SetFromJSON(stream) + error = fancy_bkpt.SetScriptCallbackFunction("bktptcmd.another_function", extra_args) + self.assertTrue(error.Success(), "Failed to add callback %s"%(error.GetCString())) + + stream.Clear() + stream.Print('{"side_effect" : "I am so much fancier"}') + extra_args.SetFromJSON(stream) + + # Fancier's callback is set up from the command line + id = fancier_bkpt.GetID() + self.expect("breakpoint command add -F bktptcmd.a_third_function -k side_effect -v 'I am fancier' %d"%(id)) + + # Not so fancy gets an empty extra_args: + empty_args = lldb.SBStructuredData() + error = not_so_fancy_bkpt.SetScriptCallbackFunction("bktptcmd.empty_extra_args", empty_args) + self.assertTrue(error.Success(), "Failed to add callback %s"%(error.GetCString())) + # Clear out canary variables side_effect.bktptcmd = None side_effect.callback = None - + side_effect.fancy = None + side_effect.fancier = None + side_effect.not_so_fancy = None + # Now launch the process, and do not stop at entry point. self.process = self.target.LaunchSimple( None, None, self.get_process_working_directory()) @@ -97,3 +137,38 @@ class PythonBreakpointCommandSettingTestCase(TestBase): self.assertEquals("callback was here", side_effect.callback) self.assertEquals("function was here", side_effect.bktptcmd) + self.assertEquals("I am fancy", side_effect.fancy) + self.assertEquals("I am fancier", side_effect.fancier) + self.assertEquals("Not so fancy", side_effect.not_so_fancy) + + def do_bad_args_to_python_command(self): + exe = self.getBuildArtifact("a.out") + error = lldb.SBError() + + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + + self.expect("command script import --allow-reload ./bktptcmd.py") + + bkpt = self.target.BreakpointCreateBySourceRegex( + "Set break point at this line.", self.main_source_spec) + self.assertTrue(bkpt, VALID_BREAKPOINT) + + # Pass a breakpoint command function that doesn't take extra_args, + # but pass it extra args: + + extra_args = lldb.SBStructuredData() + stream = lldb.SBStream() + stream.Print('{"side_effect" : "I am fancy"}') + extra_args.SetFromJSON(stream) + + error = bkpt.SetScriptCallbackFunction("bktptcmd.function", extra_args) + self.assertTrue(error.Fail(), "Can't pass extra args if the function doesn't take them") + + error = bkpt.SetScriptCallbackFunction("bktptcmd.useless_function", extra_args) + self.assertTrue(error.Fail(), "Can't pass extra args if the function has wrong number of args.") + + error = bkpt.SetScriptCallbackFunction("bktptcmd.nosuch_function", extra_args) + self.assertTrue(error.Fail(), "Can't pass extra args if the function doesn't exist.") + diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py index ac0f753ccd8..e839de57fb7 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_command/bktptcmd.py @@ -1,5 +1,23 @@ from __future__ import print_function import side_effect +def useless_function(first, second): + print("I have the wrong number of arguments.") + def function(frame, bp_loc, dict): side_effect.bktptcmd = "function was here" + +def another_function(frame, bp_loc, extra_args, dict): + se_value = extra_args.GetValueForKey("side_effect") + se_string = se_value.GetStringValue(100) + side_effect.fancy = se_string + +def a_third_function(frame, bp_loc, extra_args, dict): + se_value = extra_args.GetValueForKey("side_effect") + se_string = se_value.GetStringValue(100) + side_effect.fancier = se_string + +def empty_extra_args(frame, bp_loc, extra_args, dict): + if extra_args.IsValid(): + side_effect.not_so_fancy = "Extra args should not be valid" + side_effect.not_so_fancy = "Not so fancy" diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/scripts/Python/python-wrapper.swig index 8bb74647741..b7af3422193 100644 --- a/lldb/scripts/Python/python-wrapper.swig +++ b/lldb/scripts/Python/python-wrapper.swig @@ -45,7 +45,8 @@ LLDBSwigPythonBreakpointCallbackFunction const char *python_function_name, const char *session_dictionary_name, const lldb::StackFrameSP& frame_sp, - const lldb::BreakpointLocationSP& bp_loc_sp + const lldb::BreakpointLocationSP& bp_loc_sp, + lldb_private::StructuredDataImpl *args_impl ) { lldb::SBFrame sb_frame (frame_sp); @@ -62,7 +63,19 @@ LLDBSwigPythonBreakpointCallbackFunction PythonObject frame_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_frame)); PythonObject bp_loc_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_bp_loc)); - PythonObject result = pfunc(frame_arg, bp_loc_arg, dict); + + PythonObject result; + // If the called function doesn't take extra_args, drop them here: + auto arg_info = pfunc.GetNumArguments(); + if (arg_info.count == 3) + result = pfunc(frame_arg, bp_loc_arg, dict); + else if (arg_info.count == 4) { + lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl); + PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value)); + result = pfunc(frame_arg, bp_loc_arg, args_arg, dict); + } else { + return stop_at_breakpoint; + } if (result.get() == Py_False) stop_at_breakpoint = false; diff --git a/lldb/scripts/interface/SBBreakpoint.i b/lldb/scripts/interface/SBBreakpoint.i index 6df0b958085..a33aeea40b7 100644 --- a/lldb/scripts/interface/SBBreakpoint.i +++ b/lldb/scripts/interface/SBBreakpoint.i @@ -180,6 +180,14 @@ public: SetScriptCallbackFunction (const char *callback_function_name); %feature("docstring", " + Set the name of the script function to be called when the breakpoint is hit. + To use this variant, the function should take (frame, bp_loc, extra_args, dict) and + when the breakpoint is hit the extra_args will be passed to the callback function.") SetScriptCallbackFunction; + SBError + SetScriptCallbackFunction (const char *callback_function_name, + SBStructuredData &extra_args); + + %feature("docstring", " Provide the body for the script function to be called when the breakpoint is hit. The body will be wrapped in a function, which be passed two arguments: 'frame' - which holds the bottom-most SBFrame of the thread that hit the breakpoint diff --git a/lldb/scripts/interface/SBBreakpointLocation.i b/lldb/scripts/interface/SBBreakpointLocation.i index 90a23234862..44fd42b514f 100644 --- a/lldb/scripts/interface/SBBreakpointLocation.i +++ b/lldb/scripts/interface/SBBreakpointLocation.i @@ -73,11 +73,20 @@ public: void SetAutoContinue(bool auto_continue); %feature("docstring", " - Set the callback to the given Python function name.") SetScriptCallbackFunction; + Set the callback to the given Python function name. + The function takes three arguments (frame, bp_loc, dict).") SetScriptCallbackFunction; void SetScriptCallbackFunction (const char *callback_function_name); %feature("docstring", " + Set the name of the script function to be called when the breakpoint is hit. + To use this variant, the function should take (frame, bp_loc, extra_args, dict) and + when the breakpoint is hit the extra_args will be passed to the callback function.") SetScriptCallbackFunction; + SBError + SetScriptCallbackFunction (const char *callback_function_name, + SBStructuredData &extra_args); + + %feature("docstring", " Provide the body for the script function to be called when the breakpoint location is hit. The body will be wrapped in a function, which be passed two arguments: 'frame' - which holds the bottom-most SBFrame of the thread that hit the breakpoint diff --git a/lldb/scripts/interface/SBBreakpointName.i b/lldb/scripts/interface/SBBreakpointName.i index 42dd4236338..2a06d0a2105 100644 --- a/lldb/scripts/interface/SBBreakpointName.i +++ b/lldb/scripts/interface/SBBreakpointName.i @@ -84,6 +84,10 @@ public: void SetScriptCallbackFunction(const char *callback_function_name); + SBError + SetScriptCallbackFunction (const char *callback_function_name, + SBStructuredData &extra_args); + void SetCommandLineCommands(SBStringList &commands); bool GetCommandLineCommands(SBStringList &commands); diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp index 45eaea6b618..8159b851d58 100644 --- a/lldb/source/API/SBBreakpoint.cpp +++ b/lldb/source/API/SBBreakpoint.cpp @@ -14,6 +14,7 @@ #include "lldb/API/SBProcess.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" +#include "lldb/API/SBStructuredData.h" #include "lldb/API/SBThread.h" #include "lldb/Breakpoint/Breakpoint.h" @@ -25,6 +26,7 @@ #include "lldb/Core/Address.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/Process.h" @@ -590,22 +592,38 @@ void SBBreakpoint ::SetCallback(SBBreakpointHitCallback callback, void *baton) { } void SBBreakpoint::SetScriptCallbackFunction( - const char *callback_function_name) { - LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallbackFunction, - (const char *), callback_function_name); - + const char *callback_function_name) { +LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallbackFunction, + (const char *), callback_function_name); + SBStructuredData empty_args; + SetScriptCallbackFunction(callback_function_name, empty_args); +} + +SBError SBBreakpoint::SetScriptCallbackFunction( + const char *callback_function_name, + SBStructuredData &extra_args) { + LLDB_RECORD_METHOD(SBError, SBBreakpoint, SetScriptCallbackFunction, + (const char *, SBStructuredData &), callback_function_name, extra_args); + SBError sb_error; BreakpointSP bkpt_sp = GetSP(); if (bkpt_sp) { + Status error; std::lock_guard<std::recursive_mutex> guard( bkpt_sp->GetTarget().GetAPIMutex()); BreakpointOptions *bp_options = bkpt_sp->GetOptions(); - bkpt_sp->GetTarget() + error = bkpt_sp->GetTarget() .GetDebugger() .GetScriptInterpreter() ->SetBreakpointCommandCallbackFunction(bp_options, - callback_function_name); - } + callback_function_name, + extra_args.m_impl_up + ->GetObjectSP()); + sb_error.SetError(error); + } else + sb_error.SetErrorString("invalid breakpoint"); + + return LLDB_RECORD_RESULT(sb_error); } SBError SBBreakpoint::SetScriptCallbackBody(const char *callback_body_text) { @@ -992,6 +1010,8 @@ void RegisterMethods<SBBreakpoint>(Registry &R) { (lldb::SBAddress &)); LLDB_REGISTER_METHOD(void, SBBreakpoint, SetScriptCallbackFunction, (const char *)); + LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpoint, SetScriptCallbackFunction, + (const char *, SBStructuredData &)); LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpoint, SetScriptCallbackBody, (const char *)); LLDB_REGISTER_METHOD(bool, SBBreakpoint, AddName, (const char *)); diff --git a/lldb/source/API/SBBreakpointLocation.cpp b/lldb/source/API/SBBreakpointLocation.cpp index 640545f55ef..2b62a69a21e 100644 --- a/lldb/source/API/SBBreakpointLocation.cpp +++ b/lldb/source/API/SBBreakpointLocation.cpp @@ -12,12 +12,14 @@ #include "lldb/API/SBDebugger.h" #include "lldb/API/SBDefines.h" #include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" #include "lldb/API/SBStringList.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/Target.h" @@ -207,23 +209,38 @@ bool SBBreakpointLocation::GetAutoContinue() { } void SBBreakpointLocation::SetScriptCallbackFunction( - const char *callback_function_name) { - LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction, - (const char *), callback_function_name); + const char *callback_function_name) { +LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction, + (const char *), callback_function_name); +} +SBError SBBreakpointLocation::SetScriptCallbackFunction( + const char *callback_function_name, + SBStructuredData &extra_args) { + LLDB_RECORD_METHOD(SBError, SBBreakpointLocation, SetScriptCallbackFunction, + (const char *, SBStructuredData &), + callback_function_name, extra_args); + SBError sb_error; BreakpointLocationSP loc_sp = GetSP(); if (loc_sp) { + Status error; std::lock_guard<std::recursive_mutex> guard( loc_sp->GetTarget().GetAPIMutex()); BreakpointOptions *bp_options = loc_sp->GetLocationOptions(); - loc_sp->GetBreakpoint() + error = loc_sp->GetBreakpoint() .GetTarget() .GetDebugger() .GetScriptInterpreter() ->SetBreakpointCommandCallbackFunction(bp_options, - callback_function_name); - } + callback_function_name, + extra_args.m_impl_up + ->GetObjectSP()); + sb_error.SetError(error); + } else + sb_error.SetErrorString("invalid breakpoint"); + + return LLDB_RECORD_RESULT(sb_error); } SBError @@ -482,6 +499,8 @@ void RegisterMethods<SBBreakpointLocation>(Registry &R) { LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, GetAutoContinue, ()); LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction, (const char *)); + LLDB_REGISTER_METHOD(SBError, SBBreakpointLocation, SetScriptCallbackFunction, + (const char *, SBStructuredData &)); LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointLocation, SetScriptCallbackBody, (const char *)); LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetCommandLineCommands, diff --git a/lldb/source/API/SBBreakpointName.cpp b/lldb/source/API/SBBreakpointName.cpp index 1c794fca8ca..5bd7732ebb6 100644 --- a/lldb/source/API/SBBreakpointName.cpp +++ b/lldb/source/API/SBBreakpointName.cpp @@ -12,11 +12,13 @@ #include "lldb/API/SBError.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" +#include "lldb/API/SBStructuredData.h" #include "lldb/API/SBTarget.h" #include "lldb/Breakpoint/BreakpointName.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/Target.h" @@ -565,24 +567,41 @@ void SBBreakpointName::SetCallback(SBBreakpointHitCallback callback, } void SBBreakpointName::SetScriptCallbackFunction( - const char *callback_function_name) { - LLDB_RECORD_METHOD(void, SBBreakpointName, SetScriptCallbackFunction, - (const char *), callback_function_name); - + const char *callback_function_name) { +LLDB_RECORD_METHOD(void, SBBreakpointName, SetScriptCallbackFunction, + (const char *), callback_function_name); + SBStructuredData empty_args; + SetScriptCallbackFunction(callback_function_name, empty_args); +} + +SBError SBBreakpointName::SetScriptCallbackFunction( + const char *callback_function_name, + SBStructuredData &extra_args) { + LLDB_RECORD_METHOD(SBError, SBBreakpointName, SetScriptCallbackFunction, + (const char *, SBStructuredData &), + callback_function_name, extra_args); + SBError sb_error; BreakpointName *bp_name = GetBreakpointName(); - if (!bp_name) - return; + if (!bp_name) { + sb_error.SetErrorString("unrecognized breakpoint name"); + return LLDB_RECORD_RESULT(sb_error); + } std::lock_guard<std::recursive_mutex> guard( m_impl_up->GetTarget()->GetAPIMutex()); BreakpointOptions &bp_options = bp_name->GetOptions(); - m_impl_up->GetTarget() + Status error; + error = m_impl_up->GetTarget() ->GetDebugger() .GetScriptInterpreter() ->SetBreakpointCommandCallbackFunction(&bp_options, - callback_function_name); + callback_function_name, + extra_args.m_impl_up + ->GetObjectSP()); + sb_error.SetError(error); UpdateName(*bp_name); + return LLDB_RECORD_RESULT(sb_error); } SBError @@ -728,6 +747,8 @@ void RegisterMethods<SBBreakpointName>(Registry &R) { (lldb::SBStream &)); LLDB_REGISTER_METHOD(void, SBBreakpointName, SetScriptCallbackFunction, (const char *)); + LLDB_REGISTER_METHOD(SBError, SBBreakpointName, SetScriptCallbackFunction, + (const char *, SBStructuredData &)); LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointName, SetScriptCallbackBody, (const char *)); LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointName, GetAllowList, ()); diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index ad699975b50..042fc06a2bf 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -242,10 +242,10 @@ public: interpreter, "breakpoint set", "Sets a breakpoint or set of breakpoints in the executable.", "breakpoint set <cmd-options>"), - m_bp_opts(), m_python_class_options("scripted breakpoint", 'P'), + m_bp_opts(), m_python_class_options("scripted breakpoint", true, 'P'), m_options() { // We're picking up all the normal options, commands and disable. - m_all_options.Append(&m_python_class_options, LLDB_OPT_SET_1, + m_all_options.Append(&m_python_class_options, LLDB_OPT_SET_1|LLDB_OPT_SET_2, LLDB_OPT_SET_11); m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4, @@ -543,7 +543,7 @@ protected: BreakpointSetType break_type = eSetTypeInvalid; - if (!m_python_class_options.GetClassName().empty()) + if (!m_python_class_options.GetName().empty()) break_type = eSetTypeScripted; else if (m_options.m_line_num != 0) break_type = eSetTypeFileAndLine; @@ -699,7 +699,7 @@ protected: Status error; bp_sp = target.CreateScriptedBreakpoint( - m_python_class_options.GetClassName().c_str(), &(m_options.m_modules), + m_python_class_options.GetName().c_str(), &(m_options.m_modules), &(m_options.m_filenames), false, m_options.m_hardware, m_python_class_options.GetStructuredData(), &error); if (error.Fail()) { diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp index a6bcd1d8dc3..ab853bc743a 100644 --- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -17,6 +17,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/State.h" @@ -66,7 +67,7 @@ public: nullptr), IOHandlerDelegateMultiline("DONE", IOHandlerDelegate::Completion::LLDBCommand), - m_options() { + m_options(), m_func_options("breakpoint command", false, 'F') { SetHelpLong( R"( General information about entering breakpoint commands @@ -201,6 +202,11 @@ LLDB to stop." "Final Note: A warning that no breakpoint command was generated when there \ are no syntax errors may indicate that a function was declared but never called."); + m_all_options.Append(&m_options); + m_all_options.Append(&m_func_options, LLDB_OPT_SET_2 | LLDB_OPT_SET_3, + LLDB_OPT_SET_2); + m_all_options.Finalize(); + CommandArgumentEntry arg; CommandArgumentData bp_id_arg; @@ -218,7 +224,7 @@ are no syntax errors may indicate that a function was declared but never called. ~CommandObjectBreakpointCommandAdd() override = default; - Options *GetOptions() override { return &m_options; } + Options *GetOptions() override { return &m_all_options; } void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); @@ -269,19 +275,20 @@ are no syntax errors may indicate that a function was declared but never called. } } - class CommandOptions : public Options { + class CommandOptions : public OptionGroup { public: CommandOptions() - : Options(), m_use_commands(false), m_use_script_language(false), + : OptionGroup(), m_use_commands(false), m_use_script_language(false), m_script_language(eScriptLanguageNone), m_use_one_liner(false), - m_one_liner(), m_function_name() {} + m_one_liner() {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; - const int short_option = m_getopt_table[option_idx].val; + const int short_option + = g_breakpoint_command_add_options[option_idx].short_option; switch (short_option) { case 'o': @@ -313,12 +320,6 @@ are no syntax errors may indicate that a function was declared but never called. option_arg.str().c_str()); } break; - case 'F': - m_use_one_liner = false; - m_use_script_language = true; - m_function_name.assign(option_arg); - break; - case 'D': m_use_dummy = true; break; @@ -337,7 +338,6 @@ are no syntax errors may indicate that a function was declared but never called. m_use_one_liner = false; m_stop_on_error = true; m_one_liner.clear(); - m_function_name.clear(); m_use_dummy = false; } @@ -355,7 +355,6 @@ are no syntax errors may indicate that a function was declared but never called. bool m_use_one_liner; std::string m_one_liner; bool m_stop_on_error; - std::string m_function_name; bool m_use_dummy; }; @@ -372,12 +371,9 @@ protected: return false; } - if (!m_options.m_use_script_language && - !m_options.m_function_name.empty()) { - result.AppendError("need to enable scripting to have a function run as a " - "breakpoint command"); - result.SetStatus(eReturnStatusFailed); - return false; + if (!m_func_options.GetName().empty()) { + m_options.m_use_one_liner = false; + m_options.m_use_script_language = true; } BreakpointIDList valid_bp_ids; @@ -421,9 +417,12 @@ protected: if (m_options.m_use_one_liner) { script_interp->SetBreakpointCommandCallback( m_bp_options_vec, m_options.m_one_liner.c_str()); - } else if (!m_options.m_function_name.empty()) { - script_interp->SetBreakpointCommandCallbackFunction( - m_bp_options_vec, m_options.m_function_name.c_str()); + } else if (!m_func_options.GetName().empty()) { + Status error = script_interp->SetBreakpointCommandCallbackFunction( + m_bp_options_vec, m_func_options.GetName().c_str(), + m_func_options.GetStructuredData()); + if (!error.Success()) + result.SetError(error); } else { script_interp->CollectDataForBreakpointCommandCallback( m_bp_options_vec, result); @@ -443,6 +442,9 @@ protected: private: CommandOptions m_options; + OptionGroupPythonClassWithDict m_func_options; + OptionGroupOptions m_all_options; + std::vector<BreakpointOptions *> m_bp_options_vec; // This stores the // breakpoint options that // we are currently diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 8c527455390..d498418601d 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -546,7 +546,9 @@ public: m_arguments.push_back(arg); if (step_type == eStepTypeScripted) { - m_all_options.Append(&m_class_options, LLDB_OPT_SET_1, LLDB_OPT_SET_1); + m_all_options.Append(&m_class_options, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2, + LLDB_OPT_SET_1); } m_all_options.Append(&m_options); m_all_options.Finalize(); @@ -596,15 +598,15 @@ protected: } if (m_step_type == eStepTypeScripted) { - if (m_class_options.GetClassName().empty()) { + if (m_class_options.GetName().empty()) { result.AppendErrorWithFormat("empty class name for scripted step."); result.SetStatus(eReturnStatusFailed); return false; } else if (!GetDebugger().GetScriptInterpreter()->CheckObjectExists( - m_class_options.GetClassName().c_str())) { + m_class_options.GetName().c_str())) { result.AppendErrorWithFormat( "class for scripted step: \"%s\" does not exist.", - m_class_options.GetClassName().c_str()); + m_class_options.GetName().c_str()); result.SetStatus(eReturnStatusFailed); return false; } @@ -720,7 +722,7 @@ protected: m_options.m_step_out_avoid_no_debug); } else if (m_step_type == eStepTypeScripted) { new_plan_sp = thread->QueueThreadPlanForStepScripted( - abort_other_plans, m_class_options.GetClassName().c_str(), + abort_other_plans, m_class_options.GetName().c_str(), m_class_options.GetStructuredData(), bool_stop_other_threads, new_plan_status); } else { diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 87f5506c305..ce9c3fa184a 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -268,10 +268,6 @@ let Command = "breakpoint command add" in { EnumArg<"None", "ScriptOptionEnum()">, Desc<"Specify the language for the commands - if none is specified, the " "lldb command interpreter will be used.">; - def breakpoint_add_python_function : Option<"python-function", "F">, - Group<2>, Arg<"PythonFunction">, - Desc<"Give the name of a Python function to run as command for this " - "breakpoint. Be sure to give a module name if appropriate.">; def breakpoint_add_dummy_breakpoints : Option<"dummy-breakpoints", "D">, Desc<"Sets Dummy breakpoints - i.e. breakpoints set before a file is " "provided, which prime new targets.">; @@ -907,9 +903,6 @@ let Command = "thread step scope" in { def thread_step_scope_step_in_target : Option<"step-in-target", "t">, Group<1>, Arg<"FunctionName">, Desc<"The name of the directly called " "function step in should stop at when stepping into.">; - def thread_step_scope_python_class : Option<"python-class", "C">, Group<2>, - Arg<"PythonClass">, Desc<"The name of the class that will manage this step " - "- only supported for Scripted Step.">; } let Command = "thread until" in { diff --git a/lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp b/lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp index 9a893ec5362..20a7ed1f76c 100644 --- a/lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp +++ b/lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp @@ -15,30 +15,29 @@ using namespace lldb_private; OptionGroupPythonClassWithDict::OptionGroupPythonClassWithDict (const char *class_use, + bool is_class, int class_option, int key_option, - int value_option, - const char *class_long_option, - const char *key_long_option, - const char *value_long_option, - bool required) { - m_key_usage_text.assign("The key for a key/value pair passed to the class" - " that implements a "); + int value_option) : OptionGroup(), m_is_class(is_class) { + m_key_usage_text.assign("The key for a key/value pair passed to the " + "implementation of a "); m_key_usage_text.append(class_use); m_key_usage_text.append(". Pairs can be specified more than once."); - m_value_usage_text.assign("The value for a previous key in the pair passed to" - " the class that implements a "); + m_value_usage_text.assign("The value for the previous key in the pair passed " + "to the implementation of a "); m_value_usage_text.append(class_use); m_value_usage_text.append(". Pairs can be specified more than once."); - m_class_usage_text.assign("The name of the class that will manage a "); + m_class_usage_text.assign("The name of the "); + m_class_usage_text.append(m_is_class ? "class" : "function"); + m_class_usage_text.append(" that will manage a "); m_class_usage_text.append(class_use); m_class_usage_text.append("."); m_option_definition[0].usage_mask = LLDB_OPT_SET_1; - m_option_definition[0].required = required; - m_option_definition[0].long_option = class_long_option; + m_option_definition[0].required = true; + m_option_definition[0].long_option = "script-class"; m_option_definition[0].short_option = class_option; m_option_definition[0].validator = nullptr; m_option_definition[0].option_has_arg = OptionParser::eRequiredArgument; @@ -47,9 +46,9 @@ OptionGroupPythonClassWithDict::OptionGroupPythonClassWithDict m_option_definition[0].argument_type = eArgTypePythonClass; m_option_definition[0].usage_text = m_class_usage_text.data(); - m_option_definition[1].usage_mask = LLDB_OPT_SET_1; - m_option_definition[1].required = required; - m_option_definition[1].long_option = key_long_option; + m_option_definition[1].usage_mask = LLDB_OPT_SET_2; + m_option_definition[1].required = false; + m_option_definition[1].long_option = "structured-data-key"; m_option_definition[1].short_option = key_option; m_option_definition[1].validator = nullptr; m_option_definition[1].option_has_arg = OptionParser::eRequiredArgument; @@ -58,9 +57,9 @@ OptionGroupPythonClassWithDict::OptionGroupPythonClassWithDict m_option_definition[1].argument_type = eArgTypeNone; m_option_definition[1].usage_text = m_key_usage_text.data(); - m_option_definition[2].usage_mask = LLDB_OPT_SET_1; - m_option_definition[2].required = required; - m_option_definition[2].long_option = value_long_option; + m_option_definition[2].usage_mask = LLDB_OPT_SET_2; + m_option_definition[2].required = false; + m_option_definition[2].long_option = "structured-data-value"; m_option_definition[2].short_option = value_option; m_option_definition[2].validator = nullptr; m_option_definition[2].option_has_arg = OptionParser::eRequiredArgument; @@ -68,6 +67,18 @@ OptionGroupPythonClassWithDict::OptionGroupPythonClassWithDict m_option_definition[2].completion_type = 0; m_option_definition[2].argument_type = eArgTypeNone; m_option_definition[2].usage_text = m_value_usage_text.data(); + + m_option_definition[3].usage_mask = LLDB_OPT_SET_3; + m_option_definition[3].required = true; + m_option_definition[3].long_option = "python-function"; + m_option_definition[3].short_option = class_option; + m_option_definition[3].validator = nullptr; + m_option_definition[3].option_has_arg = OptionParser::eRequiredArgument; + m_option_definition[3].enum_values = {}; + m_option_definition[3].completion_type = 0; + m_option_definition[3].argument_type = eArgTypePythonFunction; + m_option_definition[3].usage_text = m_class_usage_text.data(); + } OptionGroupPythonClassWithDict::~OptionGroupPythonClassWithDict() {} @@ -78,10 +89,13 @@ Status OptionGroupPythonClassWithDict::SetOptionValue( ExecutionContext *execution_context) { Status error; switch (option_idx) { - case 0: { - m_class_name.assign(option_arg); + case 0: + case 3: { + m_name.assign(option_arg); } break; case 1: { + if (!m_dict_sp) + m_dict_sp = std::make_shared<StructuredData::Dictionary>(); if (m_current_key.empty()) m_current_key.assign(option_arg); else @@ -90,6 +104,8 @@ Status OptionGroupPythonClassWithDict::SetOptionValue( } break; case 2: { + if (!m_dict_sp) + m_dict_sp = std::make_shared<StructuredData::Dictionary>(); if (!m_current_key.empty()) { m_dict_sp->AddStringItem(m_current_key, option_arg); m_current_key.clear(); @@ -107,7 +123,10 @@ Status OptionGroupPythonClassWithDict::SetOptionValue( void OptionGroupPythonClassWithDict::OptionParsingStarting( ExecutionContext *execution_context) { m_current_key.erase(); - m_dict_sp = std::make_shared<StructuredData::Dictionary>(); + // Leave the dictionary shared pointer unset. That way you can tell that + // the user didn't pass any -k -v pairs. We want to be able to warn if these + // were passed when the function they passed won't use them. + m_dict_sp.reset(); } Status OptionGroupPythonClassWithDict::OptionParsingFinished( diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index d04baec76e6..fd47aed6dcf 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -81,12 +81,18 @@ Status ScriptInterpreter::SetBreakpointCommandCallback( return return_error; } -void ScriptInterpreter::SetBreakpointCommandCallbackFunction( +Status ScriptInterpreter::SetBreakpointCommandCallbackFunction( std::vector<BreakpointOptions *> &bp_options_vec, - const char *function_name) { + const char *function_name, + StructuredData::ObjectSP extra_args_sp) { + Status error; for (BreakpointOptions *bp_options : bp_options_vec) { - SetBreakpointCommandCallbackFunction(bp_options, function_name); + error = SetBreakpointCommandCallbackFunction(bp_options, function_name, + extra_args_sp); + if (!error.Success()) + return error; } + return error; } std::unique_ptr<ScriptInterpreterLocker> diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 3eee5218414..0c48ac498ec 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -75,7 +75,8 @@ extern "C" void init_lldb(void); extern "C" bool LLDBSwigPythonBreakpointCallbackFunction( const char *python_function_name, const char *session_dictionary_name, const lldb::StackFrameSP &sb_frame, - const lldb::BreakpointLocationSP &sb_bp_loc); + const lldb::BreakpointLocationSP &sb_bp_loc, + StructuredDataImpl *args_impl); extern "C" bool LLDBSwigPythonWatchpointCallbackFunction( const char *python_function_name, const char *session_dictionary_name, @@ -552,8 +553,10 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler, break; data_up->user_source.SplitIntoLines(data); + StructuredData::ObjectSP empty_args_sp; if (GenerateBreakpointCommandCallbackData(data_up->user_source, - data_up->script_source) + data_up->script_source, + false) .Success()) { auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>( std::move(data_up)); @@ -779,6 +782,32 @@ PythonDictionary &ScriptInterpreterPythonImpl::GetSysModuleDictionary() { return m_sys_module_dict; } +llvm::Expected<size_t> +ScriptInterpreterPythonImpl::GetNumFixedArgumentsForCallable( + const llvm::StringRef &callable_name) +{ + if (callable_name.empty()) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "called with empty callable name."); + } + Locker py_lock(this, Locker::AcquireLock | + Locker::InitSession | + Locker::NoSTDIN); + auto dict = PythonModule::MainModule() + .ResolveName<PythonDictionary>(m_dictionary_name); + auto pfunc + = PythonObject::ResolveNameWithDictionary<PythonCallable>(callable_name, + dict); + if (!pfunc.IsAllocated()) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "can't find callable: %s", callable_name.str().c_str()); + } + PythonCallable::ArgInfo arg_info = pfunc.GetNumArguments(); + return arg_info.count; +} + static std::string GenerateUniqueName(const char *base_name_wanted, uint32_t &functions_counter, const void *name_token = nullptr) { @@ -1196,14 +1225,46 @@ void ScriptInterpreterPythonImpl::CollectDataForWatchpointCommandCallback( " ", *this, true, wp_options); } -void ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction( - BreakpointOptions *bp_options, const char *function_name) { +Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction( + BreakpointOptions *bp_options, const char *function_name, + StructuredData::ObjectSP extra_args_sp) { + Status error; // For now just cons up a oneliner that calls the provided function. std::string oneliner("return "); oneliner += function_name; - oneliner += "(frame, bp_loc, internal_dict)"; - m_debugger.GetScriptInterpreter()->SetBreakpointCommandCallback( - bp_options, oneliner.c_str()); + + llvm::Expected<size_t> maybe_args + = GetNumFixedArgumentsForCallable(function_name); + if (!maybe_args) { + error.SetErrorStringWithFormat("could not get num args: %s", + llvm::toString(maybe_args.takeError()).c_str()); + return error; + } + size_t num_args = *maybe_args; + + bool uses_extra_args = false; + if (num_args == 4) { + uses_extra_args = true; + oneliner += "(frame, bp_loc, extra_args, internal_dict)"; + } + else if (num_args == 3) { + if (extra_args_sp) { + error.SetErrorString("cannot pass extra_args to a three argument callback" + ); + return error; + } + uses_extra_args = false; + oneliner += "(frame, bp_loc, internal_dict)"; + } else { + error.SetErrorStringWithFormat("expected 3 or 4 argument " + "function, %s has %d", + function_name, num_args); + return error; + } + + SetBreakpointCommandCallback(bp_options, oneliner.c_str(), extra_args_sp, + uses_extra_args); + return error; } Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( @@ -1211,7 +1272,8 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( std::unique_ptr<BreakpointOptions::CommandData> &cmd_data_up) { Status error; error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source, - cmd_data_up->script_source); + cmd_data_up->script_source, + false); if (error.Fail()) { return error; } @@ -1222,11 +1284,17 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( return error; } -// Set a Python one-liner as the callback for the breakpoint. Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( BreakpointOptions *bp_options, const char *command_body_text) { - auto data_up = std::make_unique<CommandDataPython>(); + return SetBreakpointCommandCallback(bp_options, command_body_text, {},false); +} +// Set a Python one-liner as the callback for the breakpoint. +Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( + BreakpointOptions *bp_options, const char *command_body_text, + StructuredData::ObjectSP extra_args_sp, + bool uses_extra_args) { + auto data_up = std::make_unique<CommandDataPython>(extra_args_sp); // Split the command_body_text into lines, and pass that to // GenerateBreakpointCommandCallbackData. That will wrap the body in an // auto-generated function, and return the function name in script_source. @@ -1234,7 +1302,8 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( data_up->user_source.SplitIntoLines(command_body_text); Status error = GenerateBreakpointCommandCallbackData(data_up->user_source, - data_up->script_source); + data_up->script_source, + uses_extra_args); if (error.Success()) { auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up)); @@ -2063,7 +2132,8 @@ bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass( } Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData( - StringList &user_input, std::string &output) { + StringList &user_input, std::string &output, + bool has_extra_args) { static uint32_t num_created_functions = 0; user_input.RemoveBlankLines(); StreamString sstr; @@ -2075,8 +2145,12 @@ Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData( std::string auto_generated_function_name(GenerateUniqueName( "lldb_autogen_python_bp_callback_func_", num_created_functions)); - sstr.Printf("def %s (frame, bp_loc, internal_dict):", - auto_generated_function_name.c_str()); + if (has_extra_args) + sstr.Printf("def %s (frame, bp_loc, extra_args, internal_dict):", + auto_generated_function_name.c_str()); + else + sstr.Printf("def %s (frame, bp_loc, internal_dict):", + auto_generated_function_name.c_str()); error = GenerateFunction(sstr.GetData(), user_input); if (!error.Success()) @@ -2196,7 +2270,8 @@ bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction( ret_val = LLDBSwigPythonBreakpointCallbackFunction( python_function_name, python_interpreter->m_dictionary_name.c_str(), stop_frame_sp, - bp_loc_sp); + bp_loc_sp, + bp_option_data->m_extra_args_up.get()); } return ret_val; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h index 33ae308041b..9ae4a036138 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h @@ -17,6 +17,7 @@ #include "lldb/Breakpoint/BreakpointOptions.h" #include "lldb/Core/IOHandler.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/lldb-private.h" @@ -34,6 +35,13 @@ public: CommandDataPython() : BreakpointOptions::CommandData() { interpreter = lldb::eScriptLanguagePython; } + CommandDataPython(StructuredData::ObjectSP extra_args_sp) : + BreakpointOptions::CommandData(), + m_extra_args_up(new StructuredDataImpl()) { + interpreter = lldb::eScriptLanguagePython; + m_extra_args_up->SetObjectSP(extra_args_sp); + } + lldb::StructuredDataImplUP m_extra_args_up; }; ScriptInterpreterPython(Debugger &debugger) diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 929567e579d..e480bc3af99 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -179,8 +179,10 @@ public: Status GenerateFunction(const char *signature, const StringList &input) override; - Status GenerateBreakpointCommandCallbackData(StringList &input, - std::string &output) override; + Status GenerateBreakpointCommandCallbackData( + StringList &input, + std::string &output, + bool has_extra_args) override; bool GenerateWatchpointCommandCallbackData(StringList &input, std::string &output) override; @@ -244,14 +246,22 @@ public: Status SetBreakpointCommandCallback(BreakpointOptions *bp_options, const char *callback_body) override; - void SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options, - const char *function_name) override; + Status SetBreakpointCommandCallbackFunction( + BreakpointOptions *bp_options, + const char *function_name, + StructuredData::ObjectSP extra_args_sp) override; /// This one is for deserialization: Status SetBreakpointCommandCallback( BreakpointOptions *bp_options, std::unique_ptr<BreakpointOptions::CommandData> &data_up) override; + Status SetBreakpointCommandCallback( + BreakpointOptions *bp_options, + const char *command_body_text, + StructuredData::ObjectSP extra_args_sp, + bool uses_extra_args); + /// Set a one-liner as the callback for the watchpoint. void SetWatchpointCommandCallback(WatchpointOptions *wp_options, const char *oneliner) override; @@ -368,6 +378,10 @@ public: python::PythonDictionary &GetSessionDictionary(); python::PythonDictionary &GetSysModuleDictionary(); + + llvm::Expected<size_t> + GetNumFixedArgumentsForCallable(const llvm::StringRef &callable_name) + override; bool GetEmbeddedInterpreterModuleObjects(); diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index 9c0b90b28eb..66b5ff84b04 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -62,7 +62,8 @@ extern "C" void init_lldb(void) {} extern "C" bool LLDBSwigPythonBreakpointCallbackFunction( const char *python_function_name, const char *session_dictionary_name, const lldb::StackFrameSP &sb_frame, - const lldb::BreakpointLocationSP &sb_bp_loc) { + const lldb::BreakpointLocationSP &sb_bp_loc, + StructuredDataImpl *args_impl) { return false; } |