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