diff --git a/lldb/include/lldb/API/SBStructuredData.h b/lldb/include/lldb/API/SBStructuredData.h index 5b10133500ef..a090272e45ac 100644 --- a/lldb/include/lldb/API/SBStructuredData.h +++ b/lldb/include/lldb/API/SBStructuredData.h @@ -91,6 +91,8 @@ protected: friend class SBTraceOptions; friend class SBDebugger; friend class SBTarget; + friend class SBThread; + friend class SBThreadPlan; StructuredDataImplUP m_impl_up; }; diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h index da8726a2a9b2..b5f1794f9fe0 100644 --- a/lldb/include/lldb/API/SBThread.h +++ b/lldb/include/lldb/API/SBThread.h @@ -122,6 +122,10 @@ public: SBError StepUsingScriptedThreadPlan(const char *script_class_name, bool resume_immediately); + SBError StepUsingScriptedThreadPlan(const char *script_class_name, + lldb::SBStructuredData &args_data, + bool resume_immediately); + SBError JumpToLine(lldb::SBFileSpec &file_spec, uint32_t line); void RunToAddress(lldb::addr_t addr); diff --git a/lldb/include/lldb/API/SBThreadPlan.h b/lldb/include/lldb/API/SBThreadPlan.h index 007e9e2de21a..dbe417e63749 100644 --- a/lldb/include/lldb/API/SBThreadPlan.h +++ b/lldb/include/lldb/API/SBThreadPlan.h @@ -28,6 +28,9 @@ public: SBThreadPlan(lldb::SBThread &thread, const char *class_name); + SBThreadPlan(lldb::SBThread &thread, const char *class_name, + lldb::SBStructuredData &args_data); + ~SBThreadPlan(); explicit operator bool() const; @@ -100,6 +103,9 @@ public: SBThreadPlan QueueThreadPlanForStepScripted(const char *script_class_name); SBThreadPlan QueueThreadPlanForStepScripted(const char *script_class_name, SBError &error); + SBThreadPlan QueueThreadPlanForStepScripted(const char *script_class_name, + lldb::SBStructuredData &args_data, + SBError &error); private: friend class SBBreakpoint; diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 9c68ffb07172..3dd75b558508 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -208,6 +208,7 @@ public: virtual StructuredData::ObjectSP CreateScriptedThreadPlan(const char *class_name, + StructuredDataImpl *args_data, std::string &error_str, lldb::ThreadPlanSP thread_plan_sp) { return StructuredData::ObjectSP(); diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index 4d6aaab4e9c7..7c5ff6093baf 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -899,6 +899,7 @@ public: virtual lldb::ThreadPlanSP QueueThreadPlanForStepScripted(bool abort_other_plans, const char *class_name, + StructuredData::ObjectSP extra_args_sp, bool stop_other_threads, Status &status); // Thread Plan accessors: diff --git a/lldb/include/lldb/Target/ThreadPlanPython.h b/lldb/include/lldb/Target/ThreadPlanPython.h index 58eb33e2c5b7..0ee559b12960 100644 --- a/lldb/include/lldb/Target/ThreadPlanPython.h +++ b/lldb/include/lldb/Target/ThreadPlanPython.h @@ -12,6 +12,8 @@ #include +#include "lldb/lldb-forward.h" + #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" @@ -29,7 +31,8 @@ namespace lldb_private { class ThreadPlanPython : public ThreadPlan { public: - ThreadPlanPython(Thread &thread, const char *class_name); + ThreadPlanPython(Thread &thread, const char *class_name, + StructuredDataImpl *args_data); ~ThreadPlanPython() override; void GetDescription(Stream *s, lldb::DescriptionLevel level) override; @@ -55,6 +58,10 @@ protected: private: std::string m_class_name; + StructuredDataImpl *m_args_data; // We own this, but the implementation + // has to manage the UP (since that is + // how it gets stored in the + // SBStructuredData). std::string m_error_str; StructuredData::ObjectSP m_implementation_sp; bool m_did_push; diff --git a/lldb/packages/Python/lldbsuite/test/commands/help/TestHelp.py b/lldb/packages/Python/lldbsuite/test/commands/help/TestHelp.py index 55bcbd02be71..5d52a00e2dec 100644 --- a/lldb/packages/Python/lldbsuite/test/commands/help/TestHelp.py +++ b/lldb/packages/Python/lldbsuite/test/commands/help/TestHelp.py @@ -188,7 +188,7 @@ class HelpCommandTestCase(TestBase): def test_help_next_shows_options(self): """Test that 'help next' shows all the options for thread step-over""" self.expect("help next", - substrs=['--python-class', '--run-mode'], matching=True) + substrs=['--step-out-avoids-no-debug', '--run-mode'], matching=True) @no_debug_info_test def test_help_provides_alternatives(self): diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/Steps.py b/lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/Steps.py index f93559af7365..4133cbbe6086 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/Steps.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/Steps.py @@ -38,18 +38,29 @@ class StepScripted(StepWithChild): # This plan does a step-over until a variable changes value. class StepUntil(StepWithChild): - def __init__(self, thread_plan, dict): + def __init__(self, thread_plan, args_data, dict): self.frame = thread_plan.GetThread().frames[0] self.target = thread_plan.GetThread().GetProcess().GetTarget() - self.value = self.frame.FindVariable("foo") + func_entry = args_data.GetValueForKey("variable_name") + + if not func_entry.IsValid(): + print("Did not get a valid entry for variable_name") + func_name = func_entry.GetStringValue(100) + + self.value = self.frame.FindVariable(func_name) + if self.value.GetError().Fail(): + print("Failed to get foo value: %s"%(self.value.GetError().GetCString())) + else: + print("'foo' value: %d"%(self.value.GetValueAsUnsigned())) + StepWithChild.__init__(self, thread_plan) + def queue_child_thread_plan(self): le = self.frame.GetLineEntry() start_addr = le.GetStartAddress() start = start_addr.GetLoadAddress(self.target) end = le.GetEndAddress().GetLoadAddress(self.target) - print("Stepping from 0x%x to 0x%x (0x%x)"%(start, end, end - start)) return self.thread_plan.QueueThreadPlanForStepOverRange(start_addr, end - start) diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py b/lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py index cadadde96778..eb1b58225806 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py @@ -63,7 +63,14 @@ class StepScriptedTestCase(TestBase): self.assertEqual(stop_id, process.GetStopID(), "Process didn't run") def test_checking_variable(self): - """Test that we can call SBValue API's from a scripted thread plan""" + """Test that we can call SBValue API's from a scripted thread plan - using SBAPI's to step""" + self.do_test_checking_variable(False) + + def test_checking_variable_cli(self): + """Test that we can call SBValue API's from a scripted thread plan - using cli to step""" + self.do_test_checking_variable(True) + + def do_test_checking_variable(self, use_cli): self.build() (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here", @@ -75,8 +82,21 @@ class StepScriptedTestCase(TestBase): self.assertTrue(foo_val.GetError().Success(), "Got the foo variable") self.assertEqual(foo_val.GetValueAsUnsigned(), 10, "foo starts at 10") - err = thread.StepUsingScriptedThreadPlan("Steps.StepUntil") - self.assertTrue(err.Success(), err.GetCString()) + if use_cli: + result = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand( + "thread step-scripted -C Steps.StepUntil -k variable_name -v foo", + result) + self.assertTrue(result.Succeeded()) + else: + args_data = lldb.SBStructuredData() + data = lldb.SBStream() + data.Print('{"variable_name" : "foo"}') + error = args_data.SetFromJSON(data) + self.assertTrue(error.Success(), "Made the args_data correctly") + + err = thread.StepUsingScriptedThreadPlan("Steps.StepUntil", args_data, True) + self.assertTrue(err.Success(), err.GetCString()) # We should not have exited: self.assertEqual(process.GetState(), lldb.eStateStopped, "We are stopped") diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/scripts/Python/python-wrapper.swig index 8a99daa90c83..b2984e6ddc22 100644 --- a/lldb/scripts/Python/python-wrapper.swig +++ b/lldb/scripts/Python/python-wrapper.swig @@ -250,6 +250,7 @@ LLDBSwigPythonCreateScriptedThreadPlan ( const char *python_class_name, const char *session_dictionary_name, + lldb_private::StructuredDataImpl *args_impl, std::string &error_string, const lldb::ThreadPlanSP& thread_plan_sp ) @@ -279,7 +280,23 @@ LLDBSwigPythonCreateScriptedThreadPlan if (!tp_arg.IsAllocated()) Py_RETURN_NONE; - PythonObject result = pfunc(tp_arg, dict); + PythonObject result = {}; + size_t init_num_args = pfunc.GetNumInitArguments().count; + if (init_num_args == 3) { + if (args_impl != nullptr) { + error_string.assign("args passed, but __init__ does not take an args dictionary"); + Py_RETURN_NONE; + } + result = pfunc(tp_arg, dict); + } else if (init_num_args = 4) { + lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl); + PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value)); + result = pfunc(tp_arg, args_arg, dict); + } else { + error_string.assign("wrong number of arguments in __init__, should be 1 or 2 (not including self & dict)"); + Py_RETURN_NONE; + } + // FIXME: At this point we should check that the class we found supports all the methods // that we need. diff --git a/lldb/scripts/interface/SBThread.i b/lldb/scripts/interface/SBThread.i index c759e4a5db92..934e4c3b6056 100644 --- a/lldb/scripts/interface/SBThread.i +++ b/lldb/scripts/interface/SBThread.i @@ -253,6 +253,11 @@ public: SBError StepUsingScriptedThreadPlan (const char *script_class_name, bool resume_immediately); + SBError + StepUsingScriptedThreadPlan(const char *script_class_name, + lldb::SBStructuredData &args_data, + bool resume_immediately); + SBError JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line); diff --git a/lldb/scripts/interface/SBThreadPlan.i b/lldb/scripts/interface/SBThreadPlan.i index edce0ee49c87..36131d529b7b 100644 --- a/lldb/scripts/interface/SBThreadPlan.i +++ b/lldb/scripts/interface/SBThreadPlan.i @@ -109,6 +109,14 @@ public: SBThreadPlan QueueThreadPlanForStepScripted(const char *script_class_name); + SBThreadPlan + QueueThreadPlanForStepScripted(const char *script_class_name, + SBError &error); + SBThreadPlan + QueueThreadPlanForStepScripted(const char *script_class_name, + SBStructuredData &args_data, + SBError &error); + protected: friend class SBBreakpoint; diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 85e9a6b47955..8d4930bf6edb 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -16,6 +16,7 @@ #include "lldb/API/SBFrame.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" #include "lldb/API/SBSymbolContext.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBThreadPlan.h" @@ -23,6 +24,7 @@ #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Core/ValueObject.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/CompileUnit.h" @@ -965,9 +967,24 @@ SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name) { } SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name, + bool resume_immediately) { + LLDB_RECORD_METHOD(lldb::SBError, SBThread, StepUsingScriptedThreadPlan, + (const char *, bool), script_class_name, + resume_immediately); + + lldb::SBStructuredData no_data; + return LLDB_RECORD_RESULT( + StepUsingScriptedThreadPlan(script_class_name, + no_data, + resume_immediately)); +} + +SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name, + SBStructuredData &args_data, bool resume_immediately) { LLDB_RECORD_METHOD(lldb::SBError, SBThread, StepUsingScriptedThreadPlan, - (const char *, bool), script_class_name, + (const char *, lldb::SBStructuredData &, bool), + script_class_name, args_data, resume_immediately); SBError error; @@ -982,8 +999,10 @@ SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name, Thread *thread = exe_ctx.GetThreadPtr(); Status new_plan_status; + StructuredData::ObjectSP obj_sp = args_data.m_impl_up->GetObjectSP(); + ThreadPlanSP new_plan_sp = thread->QueueThreadPlanForStepScripted( - false, script_class_name, false, new_plan_status); + false, script_class_name, obj_sp, false, new_plan_status); if (new_plan_status.Fail()) { error.SetErrorString(new_plan_status.AsCString()); @@ -1460,6 +1479,8 @@ void RegisterMethods(Registry &R) { (const char *)); LLDB_REGISTER_METHOD(lldb::SBError, SBThread, StepUsingScriptedThreadPlan, (const char *, bool)); + LLDB_REGISTER_METHOD(lldb::SBError, SBThread, StepUsingScriptedThreadPlan, + (const char *, SBStructuredData &, bool)); LLDB_REGISTER_METHOD(lldb::SBError, SBThread, JumpToLine, (lldb::SBFileSpec &, uint32_t)); LLDB_REGISTER_METHOD(lldb::SBError, SBThread, ReturnFromFrame, diff --git a/lldb/source/API/SBThreadPlan.cpp b/lldb/source/API/SBThreadPlan.cpp index 8f6802fe9cef..eed4d1bfb9c4 100644 --- a/lldb/source/API/SBThreadPlan.cpp +++ b/lldb/source/API/SBThreadPlan.cpp @@ -11,10 +11,12 @@ #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" #include "lldb/API/SBSymbolContext.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/Symbol/CompileUnit.h" #include "lldb/Symbol/SymbolContext.h" @@ -67,7 +69,20 @@ SBThreadPlan::SBThreadPlan(lldb::SBThread &sb_thread, const char *class_name) { Thread *thread = sb_thread.get(); if (thread) - m_opaque_sp = std::make_shared(*thread, class_name); + m_opaque_sp = std::make_shared(*thread, class_name, + nullptr); +} + +SBThreadPlan::SBThreadPlan(lldb::SBThread &sb_thread, const char *class_name, + lldb::SBStructuredData &args_data) { + LLDB_RECORD_CONSTRUCTOR(SBThreadPlan, (lldb::SBThread &, const char *, + SBStructuredData &), + sb_thread, class_name, args_data); + + Thread *thread = sb_thread.get(); + if (thread) + m_opaque_sp = std::make_shared(*thread, class_name, + args_data.m_impl_up.get()); } // Assignment operator @@ -368,9 +383,35 @@ SBThreadPlan::QueueThreadPlanForStepScripted(const char *script_class_name, if (m_opaque_sp) { Status plan_status; + StructuredData::ObjectSP empty_args; SBThreadPlan plan = SBThreadPlan(m_opaque_sp->GetThread().QueueThreadPlanForStepScripted( - false, script_class_name, false, plan_status)); + false, script_class_name, empty_args, false, plan_status)); + + if (plan_status.Fail()) + error.SetErrorString(plan_status.AsCString()); + + return LLDB_RECORD_RESULT(plan); + } else { + return LLDB_RECORD_RESULT(SBThreadPlan()); + } +} + +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepScripted(const char *script_class_name, + lldb::SBStructuredData &args_data, + SBError &error) { + LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan, + QueueThreadPlanForStepScripted, + (const char *, lldb::SBStructuredData &, lldb::SBError &), + script_class_name, args_data, error); + + if (m_opaque_sp) { + Status plan_status; + StructuredData::ObjectSP args_obj = args_data.m_impl_up->GetObjectSP(); + SBThreadPlan plan = + SBThreadPlan(m_opaque_sp->GetThread().QueueThreadPlanForStepScripted( + false, script_class_name, args_obj, false, plan_status)); if (plan_status.Fail()) error.SetErrorString(plan_status.AsCString()); @@ -390,6 +431,8 @@ void RegisterMethods(Registry &R) { LLDB_REGISTER_CONSTRUCTOR(SBThreadPlan, (const lldb::ThreadPlanSP &)); LLDB_REGISTER_CONSTRUCTOR(SBThreadPlan, (const lldb::SBThreadPlan &)); LLDB_REGISTER_CONSTRUCTOR(SBThreadPlan, (lldb::SBThread &, const char *)); + LLDB_REGISTER_CONSTRUCTOR(SBThreadPlan, (lldb::SBThread &, const char *, + lldb::SBStructuredData &)); LLDB_REGISTER_METHOD(const lldb::SBThreadPlan &, SBThreadPlan, operator=,(const lldb::SBThreadPlan &)); LLDB_REGISTER_METHOD_CONST(bool, SBThreadPlan, IsValid, ()); @@ -433,6 +476,10 @@ void RegisterMethods(Registry &R) { LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan, QueueThreadPlanForStepScripted, (const char *, lldb::SBError &)); + LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan, + QueueThreadPlanForStepScripted, + (const char *, lldb::SBStructuredData &, + lldb::SBError &)); } } diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 3fb69b87d16c..8c5274553902 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -16,6 +16,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" #include "lldb/Interpreter/Options.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" @@ -401,127 +402,122 @@ static constexpr OptionEnumValues TriRunningModes() { #define LLDB_OPTIONS_thread_step_scope #include "CommandOptions.inc" +class ThreadStepScopeOptionGroup : public OptionGroup { +public: + ThreadStepScopeOptionGroup() : OptionGroup() { + // Keep default values of all options in one place: OptionParsingStarting + // () + OptionParsingStarting(nullptr); + } + + ~ThreadStepScopeOptionGroup() override = default; + + llvm::ArrayRef GetDefinitions() override { + return llvm::makeArrayRef(g_thread_step_scope_options); + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option + = g_thread_step_scope_options[option_idx].short_option; + + switch (short_option) { + case 'a': { + bool success; + bool avoid_no_debug = + OptionArgParser::ToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat( + "invalid boolean value for option '%c'", short_option); + else { + m_step_in_avoid_no_debug = + avoid_no_debug ? eLazyBoolYes : eLazyBoolNo; + } + } break; + + case 'A': { + bool success; + bool avoid_no_debug = + OptionArgParser::ToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat( + "invalid boolean value for option '%c'", short_option); + else { + m_step_out_avoid_no_debug = + avoid_no_debug ? eLazyBoolYes : eLazyBoolNo; + } + } break; + + case 'c': + if (option_arg.getAsInteger(0, m_step_count)) + error.SetErrorStringWithFormat("invalid step count '%s'", + option_arg.str().c_str()); + break; + + case 'm': { + auto enum_values = GetDefinitions()[option_idx].enum_values; + m_run_mode = (lldb::RunMode)OptionArgParser::ToOptionEnum( + option_arg, enum_values, eOnlyDuringStepping, error); + } break; + + case 'e': + if (option_arg == "block") { + m_end_line_is_block_end = true; + break; + } + if (option_arg.getAsInteger(0, m_end_line)) + error.SetErrorStringWithFormat("invalid end line number '%s'", + option_arg.str().c_str()); + break; + + case 'r': + m_avoid_regexp.clear(); + m_avoid_regexp.assign(option_arg); + break; + + case 't': + m_step_in_target.clear(); + m_step_in_target.assign(option_arg); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_step_in_avoid_no_debug = eLazyBoolCalculate; + m_step_out_avoid_no_debug = eLazyBoolCalculate; + m_run_mode = eOnlyDuringStepping; + + // Check if we are in Non-Stop mode + TargetSP target_sp = + execution_context ? execution_context->GetTargetSP() : TargetSP(); + if (target_sp && target_sp->GetNonStopModeEnabled()) + m_run_mode = eOnlyThisThread; + + m_avoid_regexp.clear(); + m_step_in_target.clear(); + m_step_count = 1; + m_end_line = LLDB_INVALID_LINE_NUMBER; + m_end_line_is_block_end = false; + } + + // Instance variables to hold the values for command options. + LazyBool m_step_in_avoid_no_debug; + LazyBool m_step_out_avoid_no_debug; + RunMode m_run_mode; + std::string m_avoid_regexp; + std::string m_step_in_target; + uint32_t m_step_count; + uint32_t m_end_line; + bool m_end_line_is_block_end; +}; + class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed { public: - class CommandOptions : public Options { - public: - CommandOptions() : Options() { - // Keep default values of all options in one place: OptionParsingStarting - // () - OptionParsingStarting(nullptr); - } - - ~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; - - switch (short_option) { - case 'a': { - bool success; - bool avoid_no_debug = - OptionArgParser::ToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat( - "invalid boolean value for option '%c'", short_option); - else { - m_step_in_avoid_no_debug = - avoid_no_debug ? eLazyBoolYes : eLazyBoolNo; - } - } break; - - case 'A': { - bool success; - bool avoid_no_debug = - OptionArgParser::ToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat( - "invalid boolean value for option '%c'", short_option); - else { - m_step_out_avoid_no_debug = - avoid_no_debug ? eLazyBoolYes : eLazyBoolNo; - } - } break; - - case 'c': - if (option_arg.getAsInteger(0, m_step_count)) - error.SetErrorStringWithFormat("invalid step count '%s'", - option_arg.str().c_str()); - break; - - case 'C': - m_class_name.clear(); - m_class_name.assign(option_arg); - break; - - case 'm': { - auto enum_values = GetDefinitions()[option_idx].enum_values; - m_run_mode = (lldb::RunMode)OptionArgParser::ToOptionEnum( - option_arg, enum_values, eOnlyDuringStepping, error); - } break; - - case 'e': - if (option_arg == "block") { - m_end_line_is_block_end = true; - break; - } - if (option_arg.getAsInteger(0, m_end_line)) - error.SetErrorStringWithFormat("invalid end line number '%s'", - option_arg.str().c_str()); - break; - - case 'r': - m_avoid_regexp.clear(); - m_avoid_regexp.assign(option_arg); - break; - - case 't': - m_step_in_target.clear(); - m_step_in_target.assign(option_arg); - break; - - default: - llvm_unreachable("Unimplemented option"); - } - return error; - } - - void OptionParsingStarting(ExecutionContext *execution_context) override { - m_step_in_avoid_no_debug = eLazyBoolCalculate; - m_step_out_avoid_no_debug = eLazyBoolCalculate; - m_run_mode = eOnlyDuringStepping; - - // Check if we are in Non-Stop mode - TargetSP target_sp = - execution_context ? execution_context->GetTargetSP() : TargetSP(); - if (target_sp && target_sp->GetNonStopModeEnabled()) - m_run_mode = eOnlyThisThread; - - m_avoid_regexp.clear(); - m_step_in_target.clear(); - m_class_name.clear(); - m_step_count = 1; - m_end_line = LLDB_INVALID_LINE_NUMBER; - m_end_line_is_block_end = false; - } - - llvm::ArrayRef GetDefinitions() override { - return llvm::makeArrayRef(g_thread_step_scope_options); - } - - // Instance variables to hold the values for command options. - LazyBool m_step_in_avoid_no_debug; - LazyBool m_step_out_avoid_no_debug; - RunMode m_run_mode; - std::string m_avoid_regexp; - std::string m_step_in_target; - std::string m_class_name; - uint32_t m_step_count; - uint32_t m_end_line; - bool m_end_line_is_block_end; - }; CommandObjectThreadStepWithTypeAndScope(CommandInterpreter &interpreter, const char *name, const char *help, @@ -533,7 +529,8 @@ public: eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), - m_step_type(step_type), m_step_scope(step_scope), m_options() { + m_step_type(step_type), m_step_scope(step_scope), m_options(), + m_class_options("scripted step", 'C') { CommandArgumentEntry arg; CommandArgumentData thread_id_arg; @@ -547,11 +544,19 @@ public: // Push the data for the first argument into the m_arguments vector. 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_options); + m_all_options.Finalize(); } ~CommandObjectThreadStepWithTypeAndScope() override = default; - Options *GetOptions() override { return &m_options; } + Options *GetOptions() override { + return &m_all_options; + } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { @@ -591,15 +596,15 @@ protected: } if (m_step_type == eStepTypeScripted) { - if (m_options.m_class_name.empty()) { + if (m_class_options.GetClassName().empty()) { result.AppendErrorWithFormat("empty class name for scripted step."); result.SetStatus(eReturnStatusFailed); return false; } else if (!GetDebugger().GetScriptInterpreter()->CheckObjectExists( - m_options.m_class_name.c_str())) { + m_class_options.GetClassName().c_str())) { result.AppendErrorWithFormat( "class for scripted step: \"%s\" does not exist.", - m_options.m_class_name.c_str()); + m_class_options.GetClassName().c_str()); result.SetStatus(eReturnStatusFailed); return false; } @@ -715,7 +720,8 @@ protected: m_options.m_step_out_avoid_no_debug); } else if (m_step_type == eStepTypeScripted) { new_plan_sp = thread->QueueThreadPlanForStepScripted( - abort_other_plans, m_options.m_class_name.c_str(), + abort_other_plans, m_class_options.GetClassName().c_str(), + m_class_options.GetStructuredData(), bool_stop_other_threads, new_plan_status); } else { result.AppendError("step type is not supported"); @@ -783,7 +789,9 @@ protected: protected: StepType m_step_type; StepScope m_step_scope; - CommandOptions m_options; + ThreadStepScopeOptionGroup m_options; + OptionGroupPythonClassWithDict m_class_options; + OptionGroupOptions m_all_options; }; // CommandObjectThreadContinue @@ -2060,7 +2068,11 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread( "step-scripted", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-scripted", - "Step as instructed by the script class passed in the -C option.", + "Step as instructed by the script class passed in the -C option. " + "You can also specify a dictionary of key (-k) and value (-v) pairs " + "that will be used to populate an SBStructuredData Dictionary, which " + "will be passed to the constructor of the class implementing the " + "scripted step. See the Python Reference for more details.", nullptr, eStepTypeScripted, eStepScopeSource))); LoadSubCommand("plan", CommandObjectSP(new CommandObjectMultiwordThreadPlan( diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index fedd8f66a1ff..1862bbd92985 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -887,6 +887,23 @@ void PythonCallable::Reset(PyRefType type, PyObject *py_obj) { PythonObject::Reset(PyRefType::Borrowed, result.get()); } +PythonCallable::ArgInfo PythonCallable::GetNumInitArguments() const { + ArgInfo result = {0, false, false, false}; + if (!IsValid()) + return result; + PyObject *py_func_obj = m_py_obj; + if (!PyClass_Check(m_py_obj)) + return result; + + PythonObject __init__ = GetAttributeValue("__init__"); + if (__init__.IsValid() ) { + auto __init_callable__ = __init__.AsType(); + if (__init_callable__.IsValid()) + return __init_callable__.GetNumArguments(); + } + return result; +} + PythonCallable::ArgInfo PythonCallable::GetNumArguments() const { ArgInfo result = {0, false, false, false}; if (!IsValid()) diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h index 8fd2be1460ff..9e4a120f2ae9 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -438,6 +438,10 @@ public: void Reset(PyRefType type, PyObject *py_obj) override; ArgInfo GetNumArguments() const; + + // If the callable is a Py_Class, then find the number of arguments + // of the __init__ method. + ArgInfo GetNumInitArguments() const; PythonObject operator()(); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index dc13ed71c428..e5000bfd4ae5 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -97,6 +97,7 @@ LLDBSwigPythonCreateCommandObject(const char *python_class_name, extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan( const char *python_class_name, const char *session_dictionary_name, + StructuredDataImpl *args_data, std::string &error_string, const lldb::ThreadPlanSP &thread_plan_sp); @@ -1845,7 +1846,8 @@ StructuredData::DictionarySP ScriptInterpreterPythonImpl::OSPlugin_CreateThread( } StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( - const char *class_name, std::string &error_str, + const char *class_name, StructuredDataImpl *args_data, + std::string &error_str, lldb::ThreadPlanSP thread_plan_sp) { if (class_name == nullptr || class_name[0] == '\0') return StructuredData::ObjectSP(); @@ -1868,7 +1870,7 @@ StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = LLDBSwigPythonCreateScriptedThreadPlan( class_name, python_interpreter->m_dictionary_name.c_str(), - error_str, thread_plan_sp); + args_data, error_str, thread_plan_sp); if (!ret_val) return {}; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 9cf32059a503..1b3a1bf0ba40 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -78,6 +78,7 @@ public: StructuredData::ObjectSP CreateScriptedThreadPlan(const char *class_name, + StructuredDataImpl *args_data, std::string &error_str, lldb::ThreadPlanSP thread_plan) override; diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index ce4c4aa4bc0c..be6150630c35 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -13,6 +13,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Module.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Core/ValueObject.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" @@ -1482,9 +1483,18 @@ ThreadPlanSP Thread::QueueThreadPlanForStepUntil( } lldb::ThreadPlanSP Thread::QueueThreadPlanForStepScripted( - bool abort_other_plans, const char *class_name, bool stop_other_threads, + bool abort_other_plans, const char *class_name, + StructuredData::ObjectSP extra_args_sp, bool stop_other_threads, Status &status) { - ThreadPlanSP thread_plan_sp(new ThreadPlanPython(*this, class_name)); + + StructuredDataImpl *extra_args_impl = nullptr; + if (extra_args_sp) { + extra_args_impl = new StructuredDataImpl(); + extra_args_impl->SetObjectSP(extra_args_sp); + } + + ThreadPlanSP thread_plan_sp(new ThreadPlanPython(*this, class_name, + extra_args_impl)); status = QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; diff --git a/lldb/source/Target/ThreadPlanPython.cpp b/lldb/source/Target/ThreadPlanPython.cpp index ff57e0a25ff2..df432a0af3da 100644 --- a/lldb/source/Target/ThreadPlanPython.cpp +++ b/lldb/source/Target/ThreadPlanPython.cpp @@ -25,10 +25,11 @@ using namespace lldb_private; // ThreadPlanPython -ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name) +ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name, + StructuredDataImpl *args_data) : ThreadPlan(ThreadPlan::eKindPython, "Python based Thread Plan", thread, eVoteNoOpinion, eVoteNoOpinion), - m_class_name(class_name), m_did_push(false) { + m_class_name(class_name), m_args_data(args_data), m_did_push(false) { SetIsMasterPlan(true); SetOkayToDiscard(true); SetPrivate(false); @@ -65,7 +66,8 @@ void ThreadPlanPython::DidPush() { .GetScriptInterpreter(); if (script_interp) { m_implementation_sp = script_interp->CreateScriptedThreadPlan( - m_class_name.c_str(), m_error_str, this->shared_from_this()); + m_class_name.c_str(), m_args_data, m_error_str, + this->shared_from_this()); } } } diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index 3ccda8c6fed5..9c0b90b28eb5 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -95,6 +95,7 @@ LLDBSwigPythonCreateCommandObject(const char *python_class_name, extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan( const char *python_class_name, const char *session_dictionary_name, + StructuredDataImpl *args_data, std::string &error_string, const lldb::ThreadPlanSP &thread_plan_sp) { return nullptr;