1""" 2Test that thread plan listing, and deleting works. 3""" 4 5 6 7import lldb 8from lldbsuite.test.decorators import * 9import lldbsuite.test.lldbutil as lldbutil 10from lldbsuite.test.lldbtest import * 11 12 13class TestThreadPlanCommands(TestBase): 14 15 mydir = TestBase.compute_mydir(__file__) 16 17 NO_DEBUG_INFO_TESTCASE = True 18 19 @skipIfWindows 20 @expectedFailureAll(oslist=["linux"], archs=["aarch64"]) 21 def test_thread_plan_actions(self): 22 self.build() 23 self.main_source_file = lldb.SBFileSpec("main.c") 24 self.thread_plan_test() 25 26 def check_list_output(self, command, active_plans = [], completed_plans = [], discarded_plans = []): 27 # Check the "thread plan list" output against a list of active & completed and discarded plans. 28 # If all three check arrays are empty, that means the command is expected to fail. 29 30 interp = self.dbg.GetCommandInterpreter() 31 result = lldb.SBCommandReturnObject() 32 33 num_active = len(active_plans) 34 num_completed = len(completed_plans) 35 num_discarded = len(discarded_plans) 36 37 interp.HandleCommand(command, result) 38 print("Command: %s"%(command)) 39 print(result.GetOutput()) 40 41 if num_active == 0 and num_completed == 0 and num_discarded == 0: 42 self.assertFalse(result.Succeeded(), "command: '%s' succeeded when it should have failed: '%s'"% 43 (command, result.GetError())) 44 return 45 46 self.assertTrue(result.Succeeded(), "command: '%s' failed: '%s'"%(command, result.GetError())) 47 result_arr = result.GetOutput().splitlines() 48 num_results = len(result_arr) 49 50 # Now iterate through the results array and pick out the results. 51 result_idx = 0 52 self.assertIn("thread #", result_arr[result_idx], "Found thread header") ; result_idx += 1 53 self.assertIn("Active plan stack", result_arr[result_idx], "Found active header") ; result_idx += 1 54 self.assertIn("Element 0: Base thread plan", result_arr[result_idx], "Found base plan") ; result_idx += 1 55 56 for text in active_plans: 57 self.assertIn(text, result_arr[result_idx], "Didn't find active plan: %s"%(text)) ; result_idx += 1 58 59 60 if len(completed_plans) > 0: 61 # First consume any remaining active plans: 62 while not "Completed plan stack:" in result_arr[result_idx]: 63 result_idx += 1 64 if result_idx == num_results: 65 self.fail("There should have been completed plans, but I never saw the completed stack header") 66 # We are at the Completed header, skip it: 67 result_idx += 1 68 for text in completed_plans: 69 self.assertIn(text, result_arr[result_idx], "Didn't find completed plan: %s"%(text)) ; result_idx += 1 70 71 if len(discarded_plans) > 0: 72 # First consume any remaining completed plans: 73 while not "Discarded plan stack:" in result_arr[result_idx]: 74 result_idx += 1 75 if result_idx == num_results: 76 self.fail("There should have been discarded plans, but I never saw the discarded stack header") 77 78 # We are at the Discarded header, skip it: 79 result_idx += 1 80 for text in discarded_plans: 81 self.assertIn(text, result_arr[result_idx], "Didn't find discarded plan: %s"%(text)) ; result_idx += 1 82 83 84 def thread_plan_test(self): 85 (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, 86 "Set a breakpoint here", self.main_source_file) 87 88 # We need to have an internal plan so we can test listing one. 89 # The most consistent way to do that is to use a scripted thread plan 90 # that uses a sub-plan. Source that in now. 91 source_path = os.path.join(self.getSourceDir(), "wrap_step_over.py") 92 self.runCmd("command script import '%s'"%(source_path)) 93 94 # Now set a breakpoint that we will hit by running our scripted step. 95 call_me_bkpt = target.BreakpointCreateBySourceRegex("Set another here", self.main_source_file) 96 self.assertTrue(call_me_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully") 97 thread.StepUsingScriptedThreadPlan("wrap_step_over.WrapStepOver") 98 threads = lldbutil.get_threads_stopped_at_breakpoint(process, call_me_bkpt) 99 self.assertEqual(len(threads), 1, "Hit my breakpoint while stepping over") 100 101 current_id = threads[0].GetIndexID() 102 current_tid = threads[0].GetThreadID() 103 # Run thread plan list without the -i flag: 104 command = "thread plan list %d"%(current_id) 105 self.check_list_output (command, ["wrap_step_over.WrapStepOver"], []) 106 107 # Run thread plan list with the -i flag: 108 command = "thread plan list -i %d"%(current_id) 109 self.check_list_output(command, ["WrapStepOver", "Stepping over line main.c"]) 110 111 # Run thread plan list providing TID, output should be the same: 112 command = "thread plan list -t %d"%(current_tid) 113 self.check_list_output(command, ["wrap_step_over.WrapStepOver"]) 114 115 # Provide both index & tid, and make sure we only print once: 116 command = "thread plan list -t %d %d"%(current_tid, current_id) 117 self.check_list_output(command, ["wrap_step_over.WrapStepOver"]) 118 119 # Try a fake TID, and make sure that fails: 120 fake_tid = 0 121 for i in range(100, 10000, 100): 122 fake_tid = current_tid + i 123 thread = process.GetThreadByID(fake_tid) 124 if not thread: 125 break 126 127 command = "thread plan list -t %d"%(fake_tid) 128 self.check_list_output(command) 129 130 # Now continue, and make sure we printed the completed plan: 131 process.Continue() 132 threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete) 133 self.assertEqual(len(threads), 1, "One thread completed a step") 134 135 # Run thread plan list - there aren't any private plans at this point: 136 command = "thread plan list %d"%(current_id) 137 self.check_list_output(command, [], ["wrap_step_over.WrapStepOver"]) 138 139 # Set another breakpoint that we can run to, to try deleting thread plans. 140 second_step_bkpt = target.BreakpointCreateBySourceRegex("Run here to step over again", 141 self.main_source_file) 142 self.assertTrue(second_step_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully") 143 final_bkpt = target.BreakpointCreateBySourceRegex("Make sure we get here on last continue", 144 self.main_source_file) 145 self.assertTrue(final_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully") 146 147 threads = lldbutil.continue_to_breakpoint(process, second_step_bkpt) 148 self.assertEqual(len(threads), 1, "Hit the second step breakpoint") 149 150 threads[0].StepOver() 151 threads = lldbutil.get_threads_stopped_at_breakpoint(process, call_me_bkpt) 152 153 result = lldb.SBCommandReturnObject() 154 interp = self.dbg.GetCommandInterpreter() 155 interp.HandleCommand("thread plan discard 1", result) 156 self.assertTrue(result.Succeeded(), "Deleted the step over plan: %s"%(result.GetOutput())) 157 158 # Make sure the plan gets listed in the discarded plans: 159 command = "thread plan list %d"%(current_id) 160 self.check_list_output(command, [], [], ["Stepping over line main.c:"]) 161 162 process.Continue() 163 threads = lldbutil.get_threads_stopped_at_breakpoint(process, final_bkpt) 164 self.assertEqual(len(threads), 1, "Ran to final breakpoint") 165 threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete) 166 self.assertEqual(len(threads), 0, "Did NOT complete the step over plan") 167 168