| """ | |
| Test that updating a breakpoint condition correctly invalidates cached state. | |
| This test verifies that when a breakpoint condition is changed, the new condition | |
| is properly evaluated. Previously, due to a bug in StopCondition::SetText where | |
| the hash was computed from a moved-from string, updating conditions could fail | |
| to invalidate cached condition state at breakpoint locations. | |
| """ | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| class UpdateBreakpointConditionTestCase(TestBase): | |
| def setUp(self): | |
| TestBase.setUp(self) | |
| self.source = "main.c" | |
| @add_test_categories(["pyapi"]) | |
| # Flaky on Windows on Arm buildbot - https://github.com/llvm/llvm-project/issues/206141. | |
| @skipIf(oslist=["windows"], archs=["aarch64"]) | |
| def test_update_condition_python_api(self): | |
| """Test that updating a breakpoint condition works correctly using Python API.""" | |
| self.build() | |
| target, process, thread, breakpoint = lldbutil.run_to_source_breakpoint( | |
| self, "Set breakpoint here", lldb.SBFileSpec(self.source) | |
| ) | |
| # Set initial condition: x == y. | |
| breakpoint.SetCondition("x == y") | |
| self.assertEqual(breakpoint.GetCondition(), "x == y") | |
| # Need to continue since we're already stopped, but the condition wasn't set initially. | |
| # First hit should be at foo(5, 5) where x == y. | |
| process.Continue() | |
| thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) | |
| self.assertTrue(thread.IsValid(), "Should stop at first x == y condition") | |
| frame = thread.GetFrameAtIndex(0) | |
| x_val = frame.FindVariable("x") | |
| y_val = frame.FindVariable("y") | |
| self.assertEqual(x_val.GetValueAsSigned(), 5, "x should be 5") | |
| self.assertEqual(y_val.GetValueAsSigned(), 5, "y should be 5") | |
| self.assertEqual(breakpoint.GetHitCount(), 2) | |
| # Continue to second hit with x == y. | |
| process.Continue() | |
| thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) | |
| self.assertTrue(thread.IsValid(), "Should stop at second x == y condition") | |
| frame = thread.GetFrameAtIndex(0) | |
| x_val = frame.FindVariable("x") | |
| y_val = frame.FindVariable("y") | |
| self.assertEqual(x_val.GetValueAsSigned(), 6, "x should be 6") | |
| self.assertEqual(y_val.GetValueAsSigned(), 6, "y should be 6") | |
| self.assertEqual(breakpoint.GetHitCount(), 3) | |
| # Now update the condition to x > y. | |
| # This tests the fix for the bug where the hash wasn't updated correctly. | |
| breakpoint.SetCondition("x > y") | |
| self.assertEqual(breakpoint.GetCondition(), "x > y") | |
| # Continue - should now hit at foo(3, 1) where x > y (3 > 1). | |
| # Without the fix, it would incorrectly hit at foo(7, 7) due to stale condition hash. | |
| process.Continue() | |
| thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) | |
| self.assertTrue(thread.IsValid(), "Should stop at x > y condition") | |
| frame = thread.GetFrameAtIndex(0) | |
| x_val = frame.FindVariable("x") | |
| y_val = frame.FindVariable("y") | |
| self.assertEqual(x_val.GetValueAsSigned(), 3, "x should be 3") | |
| self.assertEqual(y_val.GetValueAsSigned(), 1, "y should be 1") | |
| self.assertTrue( | |
| x_val.GetValueAsSigned() > y_val.GetValueAsSigned(), | |
| "Condition x > y should be true", | |
| ) | |
| self.assertEqual(breakpoint.GetHitCount(), 4) | |
| # Flaky on Windows on Arm buildbot - https://github.com/llvm/llvm-project/issues/206141. | |
| @skipIf(oslist=["windows"], archs=["aarch64"]) | |
| def test_update_condition_command(self): | |
| """Test that updating a breakpoint condition works correctly using breakpoint modify.""" | |
| self.build() | |
| target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( | |
| self, "Set breakpoint here", lldb.SBFileSpec(self.source) | |
| ) | |
| # Set initial condition: x == y. | |
| self.runCmd("breakpoint modify -c 'x == y' 1") | |
| self.expect( | |
| "breakpoint list", | |
| substrs=["Condition: x == y"], | |
| ) | |
| # Continue to first hit at foo(5, 5). | |
| self.runCmd("continue") | |
| self.expect("process status", PROCESS_STOPPED, patterns=["Process .* stopped"]) | |
| self.expect( | |
| "frame variable x y", | |
| substrs=["x = 5", "y = 5"], | |
| ) | |
| # Continue to second hit. | |
| self.runCmd("continue") | |
| self.expect("process status", PROCESS_STOPPED, patterns=["Process .* stopped"]) | |
| self.expect( | |
| "frame variable x y", | |
| substrs=["x = 6", "y = 6"], | |
| ) | |
| # Update condition to x > y. | |
| self.runCmd("breakpoint modify -c 'x > y' 1") | |
| self.expect( | |
| "breakpoint list", | |
| substrs=["Condition: x > y"], | |
| ) | |
| # Continue - should hit at foo(3, 1) where x > y. | |
| self.runCmd("continue") | |
| self.expect("process status", PROCESS_STOPPED, patterns=["Process .* stopped"]) | |
| self.expect( | |
| "frame variable x y", | |
| substrs=["x = 3", "y = 1"], | |
| ) | |
| # Verify x > y is actually true. | |
| self.expect("expr x > y", substrs=["true"]) |