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 def test_thread_plan_actions(self): 21 self.build() 22 self.main_source_file = lldb.SBFileSpec("main.c") 23 self.thread_plan_test() 24 25 def check_list_output(self, command, active_plans = [], completed_plans = [], discarded_plans = []): 26 # Check the "thread plan list" output against a list of active & completed and discarded plans. 27 # If all three check arrays are empty, that means the command is expected to fail. 28 29 interp = self.dbg.GetCommandInterpreter() 30 result = lldb.SBCommandReturnObject() 31 32 num_active = len(active_plans) 33 num_completed = len(completed_plans) 34 num_discarded = len(discarded_plans) 35 36 interp.HandleCommand(command, result) 37 print("Command: %s"%(command)) 38 print(result.GetOutput()) 39 40 if num_active == 0 and num_completed == 0 and num_discarded == 0: 41 self.assertFalse(result.Succeeded(), "command: '%s' succeeded when it should have failed: '%s'"% 42 (command, result.GetError())) 43 return 44 45 self.assertTrue(result.Succeeded(), "command: '%s' failed: '%s'"%(command, result.GetError())) 46 result_arr = result.GetOutput().splitlines() 47 num_results = len(result_arr) 48 49 # Match the expected number of elements. 50 # Adjust the count for the number of header lines we aren't matching: 51 fudge = 0 52 53 if num_completed == 0 and num_discarded == 0: 54 # The fudge is 3: Thread header, Active Plan header and base plan 55 fudge = 3 56 elif num_completed == 0 or num_discarded == 0: 57 # The fudge is 4: The above plus either the Completed or Discarded Plan header: 58 fudge = 4 59 else: 60 # The fudge is 5 since we have both headers: 61 fudge = 5 62 63 self.assertEqual(num_results, num_active + num_completed + num_discarded + fudge, 64 "Too many elements in match arrays for: \n%s\n"%result.GetOutput()) 65 66 # Now iterate through the results array and pick out the results. 67 result_idx = 0 68 self.assertIn("thread #", result_arr[result_idx], "Found thread header") ; result_idx += 1 69 self.assertIn("Active plan stack", result_arr[result_idx], "Found active header") ; result_idx += 1 70 self.assertIn("Element 0: Base thread plan", result_arr[result_idx], "Found base plan") ; result_idx += 1 71 72 for text in active_plans: 73 self.assertFalse("Completed plan stack" in result_arr[result_idx], "Found Completed header too early.") 74 self.assertIn(text, result_arr[result_idx], "Didn't find active plan: %s"%(text)) ; result_idx += 1 75 76 if len(completed_plans) > 0: 77 self.assertIn("Completed plan stack:", result_arr[result_idx], "Found completed plan stack header") ; result_idx += 1 78 for text in completed_plans: 79 self.assertIn(text, result_arr[result_idx], "Didn't find completed plan: %s"%(text)) ; result_idx += 1 80 81 if len(discarded_plans) > 0: 82 self.assertIn("Discarded plan stack:", result_arr[result_idx], "Found discarded plan stack header") ; result_idx += 1 83 for text in discarded_plans: 84 self.assertIn(text, result_arr[result_idx], "Didn't find completed plan: %s"%(text)) ; result_idx += 1 85 86 87 def thread_plan_test(self): 88 (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, 89 "Set a breakpoint here", self.main_source_file) 90 91 # Now set a breakpoint in call_me and step over. We should have 92 # two public thread plans 93 call_me_bkpt = target.BreakpointCreateBySourceRegex("Set another here", self.main_source_file) 94 self.assertTrue(call_me_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully") 95 thread.StepOver() 96 threads = lldbutil.get_threads_stopped_at_breakpoint(process, call_me_bkpt) 97 self.assertEqual(len(threads), 1, "Hit my breakpoint while stepping over") 98 99 current_id = threads[0].GetIndexID() 100 current_tid = threads[0].GetThreadID() 101 # Run thread plan list without the -i flag: 102 command = "thread plan list %d"%(current_id) 103 self.check_list_output (command, ["Stepping over line main.c"], []) 104 105 # Run thread plan list with the -i flag: 106 command = "thread plan list -i %d"%(current_id) 107 self.check_list_output(command, ["Stepping over line main.c", "Stepping out from"]) 108 109 # Run thread plan list providing TID, output should be the same: 110 command = "thread plan list -t %d"%(current_tid) 111 self.check_list_output(command, ["Stepping over line main.c"]) 112 113 # Provide both index & tid, and make sure we only print once: 114 command = "thread plan list -t %d %d"%(current_tid, current_id) 115 self.check_list_output(command, ["Stepping over line main.c"]) 116 117 # Try a fake TID, and make sure that fails: 118 fake_tid = 0 119 for i in range(100, 10000, 100): 120 fake_tid = current_tid + i 121 thread = process.GetThreadByID(fake_tid) 122 if not thread: 123 break 124 125 command = "thread plan list -t %d"%(fake_tid) 126 self.check_list_output(command) 127 128 # Now continue, and make sure we printed the completed plan: 129 process.Continue() 130 threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete) 131 self.assertEqual(len(threads), 1, "One thread completed a step") 132 133 # Run thread plan list - there aren't any private plans at this point: 134 command = "thread plan list %d"%(current_id) 135 self.check_list_output(command, [], ["Stepping over line main.c"]) 136 137 # Set another breakpoint that we can run to, to try deleting thread plans. 138 second_step_bkpt = target.BreakpointCreateBySourceRegex("Run here to step over again", 139 self.main_source_file) 140 self.assertTrue(second_step_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully") 141 final_bkpt = target.BreakpointCreateBySourceRegex("Make sure we get here on last continue", 142 self.main_source_file) 143 self.assertTrue(final_bkpt.GetNumLocations() > 0, "Set the breakpoint successfully") 144 145 threads = lldbutil.continue_to_breakpoint(process, second_step_bkpt) 146 self.assertEqual(len(threads), 1, "Hit the second step breakpoint") 147 148 threads[0].StepOver() 149 threads = lldbutil.get_threads_stopped_at_breakpoint(process, call_me_bkpt) 150 151 result = lldb.SBCommandReturnObject() 152 interp = self.dbg.GetCommandInterpreter() 153 interp.HandleCommand("thread plan discard 1", result) 154 self.assertTrue(result.Succeeded(), "Deleted the step over plan: %s"%(result.GetOutput())) 155 156 # Make sure the plan gets listed in the discarded plans: 157 command = "thread plan list %d"%(current_id) 158 self.check_list_output(command, [], [], ["Stepping over line main.c:"]) 159 160 process.Continue() 161 threads = lldbutil.get_threads_stopped_at_breakpoint(process, final_bkpt) 162 self.assertEqual(len(threads), 1, "Ran to final breakpoint") 163 threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete) 164 self.assertEqual(len(threads), 0, "Did NOT complete the step over plan") 165 166