Index: lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h +++ lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h @@ -208,6 +208,7 @@ virtual StructuredData::ObjectSP CreateScriptedThreadPlan(const char *class_name, + std::string &error_str, lldb::ThreadPlanSP thread_plan_sp) { return StructuredData::ObjectSP(); } Index: lldb/trunk/include/lldb/Target/ThreadPlanPython.h =================================================================== --- lldb/trunk/include/lldb/Target/ThreadPlanPython.h +++ lldb/trunk/include/lldb/Target/ThreadPlanPython.h @@ -55,6 +55,7 @@ private: std::string m_class_name; + std::string m_error_str; StructuredData::ObjectSP m_implementation_sp; bool m_did_push; Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/step_scripted/TestStepScripted.py @@ -26,10 +26,13 @@ def setUp(self): TestBase.setUp(self) + self.main_source_file = lldb.SBFileSpec("main.c") self.runCmd("command script import Steps.py") def step_out_with_scripted_plan(self, name): - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here", self.main_source_file) + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + "Set a breakpoint here", + self.main_source_file) frame = thread.GetFrameAtIndex(0) self.assertEqual("foo", frame.GetFunctionName()) @@ -39,3 +42,21 @@ frame = thread.GetFrameAtIndex(0) self.assertEqual("main", frame.GetFunctionName()) + + def test_misspelled_plan_name(self): + """Test that we get a useful error if we misspell the plan class name""" + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + "Set a breakpoint here", + self.main_source_file) + stop_id = process.GetStopID() + # Pass a non-existent class for the plan class: + err = thread.StepUsingScriptedThreadPlan("NoSuchModule.NoSuchPlan") + + # Make sure we got a good error: + self.assertTrue(err.Fail(), "We got a failure state") + msg = err.GetCString() + self.assertTrue("NoSuchModule.NoSuchPlan" in msg, "Mentioned missing class") + + # Make sure we didn't let the process run: + self.assertEqual(stop_id, process.GetStopID(), "Process didn't run") Index: lldb/trunk/scripts/Python/python-wrapper.swig =================================================================== --- lldb/trunk/scripts/Python/python-wrapper.swig +++ lldb/trunk/scripts/Python/python-wrapper.swig @@ -250,6 +250,7 @@ ( const char *python_class_name, const char *session_dictionary_name, + std::string &error_string, const lldb::ThreadPlanSP& thread_plan_sp ) { @@ -267,8 +268,11 @@ auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); auto pfunc = PythonObject::ResolveNameWithDictionary(python_class_name, dict); - if (!pfunc.IsAllocated()) + if (!pfunc.IsAllocated()) { + error_string.append("could not find script class: "); + error_string.append(python_class_name); return nullptr; + } PythonObject tp_arg(PyRefType::Owned, SBTypeToSWIGWrapper(tp_value)); Index: lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp =================================================================== --- lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -97,6 +97,7 @@ extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan( const char *python_class_name, const char *session_dictionary_name, + std::string &error_string, const lldb::ThreadPlanSP &thread_plan_sp); extern "C" bool LLDBSWIGPythonCallThreadPlan(void *implementor, @@ -1844,12 +1845,13 @@ } StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( - const char *class_name, lldb::ThreadPlanSP thread_plan_sp) { + const char *class_name, std::string &error_str, + lldb::ThreadPlanSP thread_plan_sp) { if (class_name == nullptr || class_name[0] == '\0') return StructuredData::ObjectSP(); if (!thread_plan_sp.get()) - return StructuredData::ObjectSP(); + return {}; Debugger &debugger = thread_plan_sp->GetTarget().GetDebugger(); ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter(); @@ -1857,17 +1859,18 @@ static_cast(script_interpreter); if (!script_interpreter) - return StructuredData::ObjectSP(); + return {}; void *ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - ret_val = LLDBSwigPythonCreateScriptedThreadPlan( class_name, python_interpreter->m_dictionary_name.c_str(), - thread_plan_sp); + error_str, thread_plan_sp); + if (!ret_val) + return {}; } return StructuredData::ObjectSP(new StructuredPythonObject(ret_val)); Index: lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h =================================================================== --- lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -78,6 +78,7 @@ StructuredData::ObjectSP CreateScriptedThreadPlan(const char *class_name, + std::string &error_str, lldb::ThreadPlanSP thread_plan) override; bool ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp, Index: lldb/trunk/source/Target/ThreadPlanPython.cpp =================================================================== --- lldb/trunk/source/Target/ThreadPlanPython.cpp +++ lldb/trunk/source/Target/ThreadPlanPython.cpp @@ -45,7 +45,9 @@ if (!m_implementation_sp) { if (error) - error->Printf("Python thread plan does not have an implementation"); + error->Printf("Error constructing Python ThreadPlan: %s", + m_error_str.empty() ? "" + : m_error_str.c_str()); return false; } @@ -63,7 +65,7 @@ .GetScriptInterpreter(); if (script_interp) { m_implementation_sp = script_interp->CreateScriptedThreadPlan( - m_class_name.c_str(), this->shared_from_this()); + m_class_name.c_str(), m_error_str, this->shared_from_this()); } } } Index: lldb/trunk/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp =================================================================== --- lldb/trunk/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ lldb/trunk/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -95,6 +95,7 @@ extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan( const char *python_class_name, const char *session_dictionary_name, + std::string &error_string, const lldb::ThreadPlanSP &thread_plan_sp) { return nullptr; }