StopCondition::SetText was computing the hash from the moved-from text parameter instead of the stored m_text member. After std::move(text), the source parameter becomes empty, causing the hash to always be computed from an empty string. This caused breakpoint condition updates to fail silently. When a user modified a condition (e.g., from "x == y" to "x > y"), the hash remained unchanged. Breakpoint locations use this hash to detect when conditions need re-evaluation, so with a stale hash they would continue using cached state for the old condition, triggering at incorrect locations. The patch fixes this issue by computing the hash from m_text after the move operation, ensuring it reflects the actual stored condition text. It also adds API test that updates a breakpoint condition and verifies the new condition is properly evaluated. rdar://170191229 Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
127 lines
4.9 KiB
Python
127 lines
4.9 KiB
Python
"""
|
|
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"])
|
|
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)
|
|
|
|
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"])
|