[Lldb-commits] [lldb] c2be702 - Allow scripted thread plans to modify the thread stop description when
Jim Ingham via lldb-commits
lldb-commits at lists.llvm.org
Wed May 3 10:52:21 PDT 2023
Author: Jim Ingham
Date: 2023-05-03T10:52:12-07:00
New Revision: c2be702104284cb3facf31124494b9a400296179
URL: https://github.com/llvm/llvm-project/commit/c2be702104284cb3facf31124494b9a400296179
DIFF: https://github.com/llvm/llvm-project/commit/c2be702104284cb3facf31124494b9a400296179.diff
LOG: Allow scripted thread plans to modify the thread stop description when
they are completed.
Added:
Modified:
lldb/bindings/python/python-wrapper.swig
lldb/examples/python/scripted_step.py
lldb/include/lldb/Interpreter/ScriptInterpreter.h
lldb/include/lldb/Target/ThreadPlanPython.h
lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
lldb/source/Target/ThreadPlanPython.cpp
lldb/test/API/functionalities/step_scripted/Steps.py
lldb/test/API/functionalities/step_scripted/TestStepScripted.py
lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
Removed:
################################################################################
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index d339f592cec4c..bb452658a8380 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -367,6 +367,38 @@ bool lldb_private::LLDBSWIGPythonCallThreadPlan(
return false;
}
+bool lldb_private::LLDBSWIGPythonCallThreadPlan(
+ void *implementor, const char *method_name, lldb_private::Stream *stream,
+ bool &got_error) {
+ got_error = false;
+
+ PyErr_Cleaner py_err_cleaner(false);
+ PythonObject self(PyRefType::Borrowed, static_cast<PyObject *>(implementor));
+ auto pfunc = self.ResolveName<PythonCallable>(method_name);
+
+ if (!pfunc.IsAllocated())
+ return false;
+
+ auto *sb_stream = new lldb::SBStream();
+ PythonObject sb_stream_arg =
+ ToSWIGWrapper(std::unique_ptr<lldb::SBStream>(sb_stream));
+
+ PythonObject result;
+ result = pfunc(sb_stream_arg);
+
+ if (PyErr_Occurred()) {
+ printf("Error occured for call to %s.\n",
+ method_name);
+ PyErr_Print();
+ got_error = true;
+ return false;
+ }
+ if (stream)
+ stream->PutCString(sb_stream->GetData());
+ return true;
+
+}
+
PythonObject lldb_private::LLDBSwigPythonCreateScriptedBreakpointResolver(
const char *python_class_name, const char *session_dictionary_name,
const StructuredDataImpl &args_impl,
diff --git a/lldb/examples/python/scripted_step.py b/lldb/examples/python/scripted_step.py
index 011e24d70cb26..6e53072e9735c 100644
--- a/lldb/examples/python/scripted_step.py
+++ b/lldb/examples/python/scripted_step.py
@@ -77,6 +77,10 @@
# is stale. In this case, if the step_out plan that the FinishPrintAndContinue
# plan is driving is stale, so is ours, and it is time to do our printing.
#
+# 4) If you implement the "stop_description(SBStream stream)" method in your
+# python class, then that will show up as the "plan completed" reason when
+# your thread plan is complete.
+#
# Both examples show stepping through an address range for 20 bytes from the
# current PC. The first one does it by single stepping and checking a condition.
# It doesn't, however handle the case where you step into another frame while
@@ -122,6 +126,8 @@ def should_stop(self, event):
def should_step(self):
return True
+ def stop_description(self, stream):
+ stream.Print("Simple step completed")
class StepWithPlan:
@@ -146,6 +152,9 @@ def should_stop(self, event):
def should_step(self):
return False
+ def stop_description(self, stream):
+ self.step_thread_plan.GetDescription(stream, lldb.eDescriptionLevelBrief)
+
# Here's another example which does "step over" through the current function,
# and when it stops at each line, it checks some condition (in this example the
# value of a variable) and stops if that condition is true.
@@ -205,6 +214,9 @@ def should_stop(self, event):
def should_step(self):
return True
+ def stop_description(self, stream):
+ stream.Print(f"Stepped until a == 20")
+
# Here's an example that steps out of the current frame, gathers some information
# and then continues. The information in this case is rax. Currently the thread
# plans are not a safe place to call lldb command-line commands, so the information
@@ -242,3 +254,6 @@ def do_print(self):
print("RAX on exit: ", rax_value.GetValue())
else:
print("Couldn't get rax value:", rax_value.GetError().GetCString())
+
+ def stop_description(self, stream):
+ self.step_out_thread_plan.GetDescription(stream, lldb.eDescriptionLevelBrief)
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index aeb822e148c77..0095905fd320c 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -314,6 +314,14 @@ class ScriptInterpreter : public PluginInterface {
return lldb::eStateStepping;
}
+ virtual bool
+ ScriptedThreadPlanGetStopDescription(StructuredData::ObjectSP implementor_sp,
+ lldb_private::Stream *stream,
+ bool &script_error) {
+ script_error = true;
+ return false;
+ }
+
virtual StructuredData::GenericSP
CreateScriptedBreakpointResolver(const char *class_name,
const StructuredDataImpl &args_data,
diff --git a/lldb/include/lldb/Target/ThreadPlanPython.h b/lldb/include/lldb/Target/ThreadPlanPython.h
index f148f88d4c46d..64854d66b8f25 100644
--- a/lldb/include/lldb/Target/ThreadPlanPython.h
+++ b/lldb/include/lldb/Target/ThreadPlanPython.h
@@ -51,6 +51,9 @@ class ThreadPlanPython : public ThreadPlan {
void DidPush() override;
bool IsPlanStale() override;
+
+ bool DoWillResume(lldb::StateType resume_state, bool current_plan) override;
+
protected:
bool DoPlanExplainsStop(Event *event_ptr) override;
@@ -64,6 +67,7 @@ class ThreadPlanPython : public ThreadPlan {
StructuredDataImpl m_args_data;
std::string m_error_str;
StructuredData::ObjectSP m_implementation_sp;
+ StreamString m_stop_description; // Cache the stop description here
bool m_did_push;
bool m_stop_others;
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 7e33c07213e0b..78b1375aedad9 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -148,6 +148,11 @@ python::PythonObject LLDBSwigPythonCreateScriptedThreadPlan(
bool LLDBSWIGPythonCallThreadPlan(void *implementor, const char *method_name,
lldb_private::Event *event_sp,
bool &got_error);
+
+bool LLDBSWIGPythonCallThreadPlan(void *implementor,
+ const char *method_name,
+ lldb_private::Stream *stream,
+ bool &got_error);
python::PythonObject LLDBSwigPythonCreateScriptedBreakpointResolver(
const char *python_class_name, const char *session_dictionary_name,
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 3af3c87dab338..a98ac58e488bc 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1743,7 +1743,7 @@ bool ScriptInterpreterPythonImpl::ScriptedThreadPlanIsStale(
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
is_stale = LLDBSWIGPythonCallThreadPlan(generic->GetValue(), "is_stale",
- nullptr, script_error);
+ (Event *) nullptr, script_error);
if (script_error)
return true;
}
@@ -1760,7 +1760,7 @@ lldb::StateType ScriptInterpreterPythonImpl::ScriptedThreadPlanGetRunState(
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
should_step = LLDBSWIGPythonCallThreadPlan(
- generic->GetValue(), "should_step", nullptr, script_error);
+ generic->GetValue(), "should_step", (Event *) nullptr, script_error);
if (script_error)
should_step = true;
}
@@ -1769,6 +1769,24 @@ lldb::StateType ScriptInterpreterPythonImpl::ScriptedThreadPlanGetRunState(
return lldb::eStateRunning;
}
+bool
+ScriptInterpreterPythonImpl::ScriptedThreadPlanGetStopDescription(
+ StructuredData::ObjectSP implementor_sp, lldb_private::Stream *stream,
+ bool &script_error) {
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (!generic) {
+ script_error = true;
+ return false;
+ }
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ return LLDBSWIGPythonCallThreadPlan(generic->GetValue(), "stop_description",
+ stream, script_error);
+}
+
+
StructuredData::GenericSP
ScriptInterpreterPythonImpl::CreateScriptedBreakpointResolver(
const char *class_name, const StructuredDataImpl &args_data,
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index 21fdf12505f86..b810bdbd9a0c7 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -97,6 +97,11 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
ScriptedThreadPlanGetRunState(StructuredData::ObjectSP implementor_sp,
bool &script_error) override;
+ bool
+ ScriptedThreadPlanGetStopDescription(StructuredData::ObjectSP implementor_sp,
+ lldb_private::Stream *s,
+ bool &script_error) override;
+
StructuredData::GenericSP
CreateScriptedBreakpointResolver(const char *class_name,
const StructuredDataImpl &args_data,
diff --git a/lldb/source/Target/ThreadPlanPython.cpp b/lldb/source/Target/ThreadPlanPython.cpp
index bc2a7a02e99c6..d6de6b3c3cf04 100644
--- a/lldb/source/Target/ThreadPlanPython.cpp
+++ b/lldb/source/Target/ThreadPlanPython.cpp
@@ -136,8 +136,11 @@ bool ThreadPlanPython::MischiefManaged() {
// I don't really need mischief_managed, since it's simpler to just call
// SetPlanComplete in should_stop.
mischief_managed = IsPlanComplete();
- if (mischief_managed)
+ if (mischief_managed) {
+ // We need to cache the stop reason here we'll need it in GetDescription.
+ GetDescription(&m_stop_description, eDescriptionLevelBrief);
m_implementation_sp.reset();
+ }
}
return mischief_managed;
}
@@ -158,15 +161,40 @@ lldb::StateType ThreadPlanPython::GetPlanRunState() {
return run_state;
}
-// The ones below are not currently exported to Python.
void ThreadPlanPython::GetDescription(Stream *s, lldb::DescriptionLevel level) {
- s->Printf("Python thread plan implemented by class %s.",
+ Log *log = GetLog(LLDBLog::Thread);
+ LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
+ m_class_name.c_str());
+ if (m_implementation_sp) {
+ ScriptInterpreter *script_interp = GetScriptInterpreter();
+ if (script_interp) {
+ bool script_error;
+ bool added_desc = script_interp->ScriptedThreadPlanGetStopDescription(
+ m_implementation_sp, s, script_error);
+ if (script_error || !added_desc)
+ s->Printf("Python thread plan implemented by class %s.",
m_class_name.c_str());
+ }
+ return;
+ }
+ // It's an error not to have a description, so if we get here, we should
+ // add something.
+ if (m_stop_description.Empty())
+ s->Printf("Python thread plan implemented by class %s.",
+ m_class_name.c_str());
+ s->PutCString(m_stop_description.GetData());
}
+// The ones below are not currently exported to Python.
bool ThreadPlanPython::WillStop() {
Log *log = GetLog(LLDBLog::Thread);
LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
m_class_name.c_str());
return true;
}
+
+bool ThreadPlanPython::DoWillResume(lldb::StateType resume_state,
+ bool current_plan) {
+ m_stop_description.Clear();
+ return true;
+}
diff --git a/lldb/test/API/functionalities/step_scripted/Steps.py b/lldb/test/API/functionalities/step_scripted/Steps.py
index d41d01d43095e..6b49de772f8a0 100644
--- a/lldb/test/API/functionalities/step_scripted/Steps.py
+++ b/lldb/test/API/functionalities/step_scripted/Steps.py
@@ -19,6 +19,11 @@ def should_stop(self, event):
def should_step(self):
return False
+ def stop_description(self, stream):
+ if self.child_thread_plan.IsPlanComplete():
+ return self.child_thread_plan.GetDescription(stream)
+ return True
+
def queue_child_thread_plan(self):
return None
@@ -39,19 +44,18 @@ def queue_child_thread_plan(self):
# This plan does a step-over until a variable changes value.
class StepUntil(StepWithChild):
def __init__(self, thread_plan, args_data, dict):
+ self.thread_plan = thread_plan
self.frame = thread_plan.GetThread().frames[0]
self.target = thread_plan.GetThread().GetProcess().GetTarget()
- func_entry = args_data.GetValueForKey("variable_name")
+ var_entry = args_data.GetValueForKey("variable_name")
- if not func_entry.IsValid():
+ if not var_entry.IsValid():
print("Did not get a valid entry for variable_name")
- func_name = func_entry.GetStringValue(100)
+ self.var_name = var_entry.GetStringValue(100)
- self.value = self.frame.FindVariable(func_name)
+ self.value = self.frame.FindVariable(self.var_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)
@@ -70,17 +74,23 @@ def should_stop(self, event):
# If we've stepped out of this frame, stop.
if not self.frame.IsValid():
+ self.thread_plan.SetPlanComplete(True)
return True
if not self.value.IsValid():
+ self.thread_plan.SetPlanComplete(True)
return True
if not self.value.GetValueDidChange():
self.child_thread_plan = self.queue_child_thread_plan()
return False
else:
+ self.thread_plan.SetPlanComplete(True)
return True
+ def stop_description(self, stream):
+ stream.Print(f"Stepped until {self.var_name} changed.")
+
# This plan does nothing, but sets stop_mode to the
# value of GetStopOthers for this plan.
class StepReportsStopOthers():
@@ -92,7 +102,6 @@ def __init__(self, thread_plan, args_data, dict):
def should_stop(self, event):
self.thread_plan.SetPlanComplete(True)
- print("Called in should_stop")
StepReportsStopOthers.stop_mode_dict[self.key] = self.thread_plan.GetStopOthers()
return True
diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py
index b02c5ebfd90ca..50482782a3665 100644
--- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py
+++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py
@@ -41,7 +41,8 @@ def step_out_with_scripted_plan(self, name):
frame = thread.GetFrameAtIndex(0)
self.assertEqual("main", frame.GetFunctionName())
-
+ stop_desc = thread.GetStopDescription(1000)
+ self.assertIn("Stepping out from", stop_desc, "Got right description")
def test_misspelled_plan_name(self):
"""Test that we get a useful error if we misspell the plan class name"""
@@ -106,6 +107,10 @@ def do_test_checking_variable(self, use_cli):
# And foo should have changed:
self.assertTrue(foo_val.GetValueDidChange(), "Foo changed")
+ # And we should have a reasonable stop description:
+ desc = thread.GetStopDescription(1000)
+ self.assertIn("Stepped until foo changed", desc, "Got right stop description")
+
def test_stop_others_from_command(self):
"""Test that the stop-others flag is set correctly by the command line.
Also test that the run-all-threads property overrides this."""
@@ -119,10 +124,12 @@ def run_step(self, stop_others_value, run_mode, token):
cmd = "thread step-scripted -C Steps.StepReportsStopOthers -k token -v %s"%(token)
if run_mode != None:
cmd = cmd + " --run-mode %s"%(run_mode)
- print(cmd)
+ if self.TraceOn():
+ print(cmd)
interp.HandleCommand(cmd, result)
self.assertTrue(result.Succeeded(), "Step scripted failed: %s."%(result.GetError()))
- print(Steps.StepReportsStopOthers.stop_mode_dict)
+ if self.TraceOn():
+ print(Steps.StepReportsStopOthers.stop_mode_dict)
value = Steps.StepReportsStopOthers.stop_mode_dict[token]
self.assertEqual(value, stop_others_value, "Stop others has the correct value.")
diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 4b562192579ec..2883b39639e1a 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -107,6 +107,14 @@ bool lldb_private::LLDBSWIGPythonCallThreadPlan(void *implementor,
return false;
}
+bool
+lldb_private::LLDBSWIGPythonCallThreadPlan(void *implementor,
+ const char *method_name,
+ Stream *event_sp,
+ bool &got_error) {
+ return false;
+}
+
python::PythonObject
lldb_private::LLDBSwigPythonCreateScriptedBreakpointResolver(
const char *python_class_name, const char *session_dictionary_name,
More information about the lldb-commits
mailing list