Files
clang-p2996/lldb/test/API/functionalities/thread_plan/TestThreadPlanCommands.py
Jim Ingham 3270748127 The thread plan list test is failing at least on Ubuntu Linux.
Mark it expected fail for now.

The test output shows that the "internal" thread listing isn't showing the
step out plan that we use to step back out of a function we're stepping into.
The internal plan listing code has nothing platform specific in it, so that
isn't the problem.

I am pretty sure the difference is that on MacOS we step into the function and then need to
step back out again so we push the internal plan the test is checking for.  But on Linux we
are able to step past the function without stepping into it.

So nothing is actually going wrong here, I just need to find a better test case where I
can ensure we are going to have to push a private plan.  It's probably better to test this
using a custom thread plan, then I can control the state of the plan stack better.

That's for Monday...
2020-04-03 20:06:11 -07:00

167 lines
7.7 KiB
Python

"""
Test that thread plan listing, and deleting works.
"""
import lldb
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *
class TestThreadPlanCommands(TestBase):
mydir = TestBase.compute_mydir(__file__)
NO_DEBUG_INFO_TESTCASE = True
@skipIfWindows
@expectedFailureAll(oslist=["Linux"])
def test_thread_plan_actions(self):
self.build()
self.main_source_file = lldb.SBFileSpec("main.c")
self.thread_plan_test()
def check_list_output(self, command, active_plans = [], completed_plans = [], discarded_plans = []):
# Check the "thread plan list" output against a list of active & completed and discarded plans.
# If all three check arrays are empty, that means the command is expected to fail.
interp = self.dbg.GetCommandInterpreter()
result = lldb.SBCommandReturnObject()
num_active = len(active_plans)
num_completed = len(completed_plans)
num_discarded = len(discarded_plans)
interp.HandleCommand(command, result)
print("Command: %s"%(command))
print(result.GetOutput())
if num_active == 0 and num_completed == 0 and num_discarded == 0:
self.assertFalse(result.Succeeded(), "command: '%s' succeeded when it should have failed: '%s'"%
(command, result.GetError()))
return
self.assertTrue(result.Succeeded(), "command: '%s' failed: '%s'"%(command, result.GetError()))
result_arr = result.GetOutput().splitlines()
num_results = len(result_arr)
# Match the expected number of elements.
# Adjust the count for the number of header lines we aren't matching:
fudge = 0
if num_completed == 0 and num_discarded == 0:
# The fudge is 3: Thread header, Active Plan header and base plan
fudge = 3
elif num_completed == 0 or num_discarded == 0:
# The fudge is 4: The above plus either the Completed or Discarded Plan header:
fudge = 4
else:
# The fudge is 5 since we have both headers:
fudge = 5
self.assertEqual(num_results, num_active + num_completed + num_discarded + fudge,
"Too many elements in match arrays for: \n%s\n"%result.GetOutput())
# Now iterate through the results array and pick out the results.
result_idx = 0
self.assertIn("thread #", result_arr[result_idx], "Found thread header") ; result_idx += 1
self.assertIn("Active plan stack", result_arr[result_idx], "Found active header") ; result_idx += 1
self.assertIn("Element 0: Base thread plan", result_arr[result_idx], "Found base plan") ; result_idx += 1
for text in active_plans:
self.assertFalse("Completed plan stack" in result_arr[result_idx], "Found Completed header too early.")
self.assertIn(text, result_arr[result_idx], "Didn't find active plan: %s"%(text)) ; result_idx += 1
if len(completed_plans) > 0:
self.assertIn("Completed plan stack:", result_arr[result_idx], "Found completed plan stack header") ; result_idx += 1
for text in completed_plans:
self.assertIn(text, result_arr[result_idx], "Didn't find completed plan: %s"%(text)) ; result_idx += 1
if len(discarded_plans) > 0:
self.assertIn("Discarded plan stack:", result_arr[result_idx], "Found discarded plan stack header") ; result_idx += 1
for text in discarded_plans:
self.assertIn(text, result_arr[result_idx], "Didn't find completed plan: %s"%(text)) ; result_idx += 1
def thread_plan_test(self):
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
"Set a breakpoint here", self.main_source_file)
# Now set a breakpoint in call_me and step over. We should have
# two public thread plans
call_me_bkpt = target.BreakpointCreateBySourceRegex("Set another here", self.main_source_file)
self.assertTrue(call_me_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully")
thread.StepOver()
threads = lldbutil.get_threads_stopped_at_breakpoint(process, call_me_bkpt)
self.assertEqual(len(threads), 1, "Hit my breakpoint while stepping over")
current_id = threads[0].GetIndexID()
current_tid = threads[0].GetThreadID()
# Run thread plan list without the -i flag:
command = "thread plan list %d"%(current_id)
self.check_list_output (command, ["Stepping over line main.c"], [])
# Run thread plan list with the -i flag:
command = "thread plan list -i %d"%(current_id)
self.check_list_output(command, ["Stepping over line main.c", "Stepping out from"])
# Run thread plan list providing TID, output should be the same:
command = "thread plan list -t %d"%(current_tid)
self.check_list_output(command, ["Stepping over line main.c"])
# Provide both index & tid, and make sure we only print once:
command = "thread plan list -t %d %d"%(current_tid, current_id)
self.check_list_output(command, ["Stepping over line main.c"])
# Try a fake TID, and make sure that fails:
fake_tid = 0
for i in range(100, 10000, 100):
fake_tid = current_tid + i
thread = process.GetThreadByID(fake_tid)
if not thread:
break
command = "thread plan list -t %d"%(fake_tid)
self.check_list_output(command)
# Now continue, and make sure we printed the completed plan:
process.Continue()
threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete)
self.assertEqual(len(threads), 1, "One thread completed a step")
# Run thread plan list - there aren't any private plans at this point:
command = "thread plan list %d"%(current_id)
self.check_list_output(command, [], ["Stepping over line main.c"])
# Set another breakpoint that we can run to, to try deleting thread plans.
second_step_bkpt = target.BreakpointCreateBySourceRegex("Run here to step over again",
self.main_source_file)
self.assertTrue(second_step_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully")
final_bkpt = target.BreakpointCreateBySourceRegex("Make sure we get here on last continue",
self.main_source_file)
self.assertTrue(final_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully")
threads = lldbutil.continue_to_breakpoint(process, second_step_bkpt)
self.assertEqual(len(threads), 1, "Hit the second step breakpoint")
threads[0].StepOver()
threads = lldbutil.get_threads_stopped_at_breakpoint(process, call_me_bkpt)
result = lldb.SBCommandReturnObject()
interp = self.dbg.GetCommandInterpreter()
interp.HandleCommand("thread plan discard 1", result)
self.assertTrue(result.Succeeded(), "Deleted the step over plan: %s"%(result.GetOutput()))
# Make sure the plan gets listed in the discarded plans:
command = "thread plan list %d"%(current_id)
self.check_list_output(command, [], [], ["Stepping over line main.c:"])
process.Continue()
threads = lldbutil.get_threads_stopped_at_breakpoint(process, final_bkpt)
self.assertEqual(len(threads), 1, "Ran to final breakpoint")
threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete)
self.assertEqual(len(threads), 0, "Did NOT complete the step over plan")