jingham created this revision. jingham added reviewers: labath, JDevlieghere, aprantl. Herald added a project: LLDB. Herald added a subscriber: lldb-commits. jingham added a parent revision: D68363: Segregate the Python class + key/value dictionary into a separate OptionGroup.
It is trivial to write a ScriptedThreadPlan to do "step until the variable 'foo' changes value". It is equally easy to write one that does "step until the variable 'bar' changes value". But you would have to write a different Python class for each of these tasks, since there's no way to pass parameters into the ThreadPlan on creation. This patch adds that ability. You can say: (lldb) thread step-scripted -C Module.ThreadPlan -k variable_name -v foo lldb will package the key and value into an SBStructuredData dictionary, and pass that to the __init__ method of the ThreadPlan class. Then the ThreadPlan can fetch the value for that key from the dictionary and watch that variable. I also added an SB API that allows you to pass any SBStructuredData to SBThread.StepUsingScriptedThreadPlan, if you need to pass in something more complex than a simple key/value dictionary. This revision depends on: https://reviews.llvm.org/D68363 Not sure who to put on to review this, most of the folks who worked actively on the PythonObjects aren't really active anymore... I added folks who touched some of the relevant files recently... Repository: rLLDB LLDB https://reviews.llvm.org/D68366 Files: lldb/include/lldb/API/SBStructuredData.h lldb/include/lldb/API/SBThread.h lldb/include/lldb/API/SBThreadPlan.h lldb/include/lldb/Interpreter/ScriptInterpreter.h lldb/include/lldb/Target/Thread.h lldb/include/lldb/Target/ThreadPlanPython.h lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/Steps.py lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py lldb/scripts/Python/python-wrapper.swig lldb/scripts/interface/SBThread.i lldb/scripts/interface/SBThreadPlan.i lldb/source/API/SBThread.cpp lldb/source/API/SBThreadPlan.cpp lldb/source/Commands/CommandObjectThread.cpp lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h lldb/source/Target/Thread.cpp lldb/source/Target/ThreadPlanPython.cpp lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
Index: lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp =================================================================== --- lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -95,6 +95,7 @@ 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; Index: lldb/source/Target/ThreadPlanPython.cpp =================================================================== --- lldb/source/Target/ThreadPlanPython.cpp +++ lldb/source/Target/ThreadPlanPython.cpp @@ -25,10 +25,11 @@ // 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 @@ .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()); } } } Index: lldb/source/Target/Thread.cpp =================================================================== --- lldb/source/Target/Thread.cpp +++ 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 @@ } 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; Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -78,6 +78,7 @@ StructuredData::ObjectSP CreateScriptedThreadPlan(const char *class_name, + StructuredDataImpl *args_data, std::string &error_str, lldb::ThreadPlanSP thread_plan) override; Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -97,6 +97,7 @@ 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::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 @@ 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 {}; } Index: lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -438,6 +438,10 @@ 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()(); Index: lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -887,6 +887,23 @@ 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<PythonCallable>(); + if (__init_callable__.IsValid()) + return __init_callable__.GetNumArguments(); + } + return result; +} + PythonCallable::ArgInfo PythonCallable::GetNumArguments() const { ArgInfo result = {0, false, false, false}; if (!IsValid()) Index: lldb/source/Commands/CommandObjectThread.cpp =================================================================== --- lldb/source/Commands/CommandObjectThread.cpp +++ 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,129 @@ #define LLDB_OPTIONS_thread_step_scope #include "CommandOptions.inc" -class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed { +class ThreadStepScopeOptionGroup : public OptionGroup { 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; + ThreadStepScopeOptionGroup() : OptionGroup() { + // Keep default values of all options in one place: OptionParsingStarting + // () + OptionParsingStarting(nullptr); + } - case 'C': - m_class_name.clear(); - m_class_name.assign(option_arg); - break; + ~ThreadStepScopeOptionGroup() override = default; - case 'm': { - auto enum_values = GetDefinitions()[option_idx].enum_values; - m_run_mode = (lldb::RunMode)OptionArgParser::ToOptionEnum( - option_arg, enum_values, eOnlyDuringStepping, error); - } break; + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_thread_step_scope_options); + } - 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; + 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 'r': - m_avoid_regexp.clear(); - m_avoid_regexp.assign(option_arg); - break; + case 'c': + if (option_arg.getAsInteger(0, m_step_count)) + error.SetErrorStringWithFormat("invalid step count '%s'", + option_arg.str().c_str()); + break; - case 't': - m_step_in_target.clear(); - m_step_in_target.assign(option_arg); + 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; - - 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; + 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_class_name.clear(); - m_step_count = 1; - m_end_line = LLDB_INVALID_LINE_NUMBER; - m_end_line_is_block_end = false; - } + m_step_in_target.assign(option_arg); + break; - llvm::ArrayRef<OptionDefinition> GetDefinitions() override { - return llvm::makeArrayRef(g_thread_step_scope_options); + default: + llvm_unreachable("Unimplemented option"); } + return error; + } - // 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; - }; + 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; + } + + // 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; +}; + +class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed { +public: CommandObjectThreadStepWithTypeAndScope(CommandInterpreter &interpreter, const char *name, const char *help, @@ -533,7 +536,8 @@ 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 +551,19 @@ // 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 +603,15 @@ } 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 +727,8 @@ 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_options.m_class_name.c_str(), + m_class_options.GetStructuredData(), bool_stop_other_threads, new_plan_status); } else { result.AppendError("step type is not supported"); @@ -783,7 +796,9 @@ protected: StepType m_step_type; StepScope m_step_scope; - CommandOptions m_options; + ThreadStepScopeOptionGroup m_options; + OptionGroupPythonClassWithDict m_class_options; + OptionGroupOptions m_all_options; }; // CommandObjectThreadContinue Index: lldb/source/API/SBThreadPlan.cpp =================================================================== --- lldb/source/API/SBThreadPlan.cpp +++ 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 @@ Thread *thread = sb_thread.get(); if (thread) - m_opaque_sp = std::make_shared<ThreadPlanPython>(*thread, class_name); + m_opaque_sp = std::make_shared<ThreadPlanPython>(*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<ThreadPlanPython>(*thread, class_name, + args_data.m_impl_up.get()); } // Assignment operator @@ -368,9 +383,35 @@ 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()); @@ -433,6 +474,10 @@ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan, QueueThreadPlanForStepScripted, (const char *, lldb::SBError &)); + LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan, + QueueThreadPlanForStepScripted, + (const char *, lldb::SBStructuredData &, + lldb::SBError &)); } } Index: lldb/source/API/SBThread.cpp =================================================================== --- lldb/source/API/SBThread.cpp +++ 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, + 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 @@ 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()); Index: lldb/scripts/interface/SBThreadPlan.i =================================================================== --- lldb/scripts/interface/SBThreadPlan.i +++ lldb/scripts/interface/SBThreadPlan.i @@ -109,6 +109,14 @@ 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; Index: lldb/scripts/interface/SBThread.i =================================================================== --- lldb/scripts/interface/SBThread.i +++ lldb/scripts/interface/SBThread.i @@ -253,6 +253,11 @@ 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); Index: lldb/scripts/Python/python-wrapper.swig =================================================================== --- lldb/scripts/Python/python-wrapper.swig +++ lldb/scripts/Python/python-wrapper.swig @@ -250,6 +250,7 @@ ( 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 @@ 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. Index: lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py =================================================================== --- lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py +++ lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py @@ -63,7 +63,14 @@ 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""" + 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""" + 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,14 +82,28 @@ 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") # We should still be in foo: self.assertEqual("foo", frame.GetFunctionName()) - + lldbutil.print_stacktrace(thread) + print("Foo value: %d."%(foo_val.GetValueAsUnsigned())) # And foo should have changed: self.assertTrue(foo_val.GetValueDidChange(), "Foo changed") Index: lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/Steps.py =================================================================== --- lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/Steps.py +++ lldb/packages/Python/lldbsuite/test/functionalities/step_scripted/Steps.py @@ -38,18 +38,29 @@ # 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) Index: lldb/include/lldb/Target/ThreadPlanPython.h =================================================================== --- lldb/include/lldb/Target/ThreadPlanPython.h +++ lldb/include/lldb/Target/ThreadPlanPython.h @@ -12,6 +12,8 @@ #include <string> +#include "lldb/lldb-forward.h" + #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" @@ -29,7 +31,8 @@ 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 @@ 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; Index: lldb/include/lldb/Target/Thread.h =================================================================== --- lldb/include/lldb/Target/Thread.h +++ lldb/include/lldb/Target/Thread.h @@ -899,6 +899,7 @@ 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: Index: lldb/include/lldb/Interpreter/ScriptInterpreter.h =================================================================== --- lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -208,6 +208,7 @@ virtual StructuredData::ObjectSP CreateScriptedThreadPlan(const char *class_name, + StructuredDataImpl *args_data, std::string &error_str, lldb::ThreadPlanSP thread_plan_sp) { return StructuredData::ObjectSP(); Index: lldb/include/lldb/API/SBThreadPlan.h =================================================================== --- lldb/include/lldb/API/SBThreadPlan.h +++ lldb/include/lldb/API/SBThreadPlan.h @@ -28,6 +28,9 @@ 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 @@ 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; Index: lldb/include/lldb/API/SBThread.h =================================================================== --- lldb/include/lldb/API/SBThread.h +++ lldb/include/lldb/API/SBThread.h @@ -122,6 +122,10 @@ 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); Index: lldb/include/lldb/API/SBStructuredData.h =================================================================== --- lldb/include/lldb/API/SBStructuredData.h +++ lldb/include/lldb/API/SBStructuredData.h @@ -91,6 +91,8 @@ friend class SBTraceOptions; friend class SBDebugger; friend class SBTarget; + friend class SBThread; + friend class SBThreadPlan; StructuredDataImplUP m_impl_up; };
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits