| """ |
| Test that we handle breakpoints on consecutive instructions correctly. |
| """ |
| |
| import lldb |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test import lldbutil |
| |
| |
| class ConsecutiveBreakpointsTestCase(TestBase): |
| def prepare_test(self): |
| self.build() |
| |
| ( |
| self.target, |
| self.process, |
| self.thread, |
| bkpt, |
| ) = lldbutil.run_to_source_breakpoint( |
| self, "Set breakpoint here", lldb.SBFileSpec("main.cpp") |
| ) |
| |
| # Set breakpoint to the next instruction |
| frame = self.thread.GetFrameAtIndex(0) |
| |
| address = frame.GetPCAddress() |
| instructions = self.target.ReadInstructions(address, 2) |
| self.assertEqual(len(instructions), 2) |
| self.bkpt_address = instructions[1].GetAddress() |
| self.breakpoint2 = self.target.BreakpointCreateByAddress( |
| self.bkpt_address.GetLoadAddress(self.target) |
| ) |
| self.assertTrue( |
| self.breakpoint2 and self.breakpoint2.GetNumLocations() == 1, |
| VALID_BREAKPOINT, |
| ) |
| |
| def finish_test(self): |
| # Run the process until termination |
| self.process.Continue() |
| self.assertState(self.process.GetState(), lldb.eStateExited) |
| |
| @no_debug_info_test |
| def test_continue(self): |
| """Test that continue stops at the second breakpoint.""" |
| self.prepare_test() |
| |
| self.process.Continue() |
| self.assertState(self.process.GetState(), lldb.eStateStopped) |
| # We should be stopped at the second breakpoint |
| self.thread = lldbutil.get_one_thread_stopped_at_breakpoint( |
| self.process, self.breakpoint2 |
| ) |
| self.assertIsNotNone( |
| self.thread, "Expected one thread to be stopped at breakpoint 2" |
| ) |
| |
| self.finish_test() |
| |
| @no_debug_info_test |
| def test_single_step(self): |
| """Test that single step stops at the second breakpoint.""" |
| self.prepare_test() |
| |
| # Instruction step to the next instruction |
| # We haven't executed breakpoint2 yet, we're sitting at it now. |
| step_over = False |
| self.thread.StepInstruction(step_over) |
| |
| step_over = False |
| self.thread.StepInstruction(step_over) |
| |
| # We've now hit the breakpoint and this StepInstruction has |
| # been interrupted, it is still sitting on the thread plan stack. |
| |
| self.assertState(self.process.GetState(), lldb.eStateStopped) |
| self.assertEqual( |
| self.thread.GetFrameAtIndex(0).GetPCAddress().GetLoadAddress(self.target), |
| self.bkpt_address.GetLoadAddress(self.target), |
| ) |
| |
| # One more instruction to complete the Step that was interrupted |
| # earlier. |
| self.thread.StepInstruction(step_over) |
| strm = lldb.SBStream() |
| self.thread.GetDescription(strm) |
| self.assertIn("instruction step into", strm.GetData()) |
| self.assertIsNotNone(self.thread, "Expected to see that step-in had completed") |
| |
| self.finish_test() |
| |
| @no_debug_info_test |
| def test_single_step_thread_specific(self): |
| """Test that single step stops, even though the second breakpoint is not valid.""" |
| self.prepare_test() |
| |
| # Choose a thread other than the current one. A non-existing thread is |
| # fine. |
| thread_index = self.process.GetNumThreads() + 1 |
| self.assertFalse(self.process.GetThreadAtIndex(thread_index).IsValid()) |
| self.breakpoint2.SetThreadIndex(thread_index) |
| |
| step_over = False |
| self.thread.StepInstruction(step_over) |
| |
| self.assertState(self.process.GetState(), lldb.eStateStopped) |
| self.assertEqual( |
| self.thread.GetFrameAtIndex(0).GetPCAddress().GetLoadAddress(self.target), |
| self.bkpt_address.GetLoadAddress(self.target), |
| ) |
| self.assertEqual( |
| self.thread.GetStopReason(), |
| lldb.eStopReasonPlanComplete, |
| "Stop reason should be 'plan complete'", |
| ) |
| |
| # Hit our second breakpoint |
| self.process.Continue() |
| |
| self.finish_test() |