1"""
2Watch 4 bytes which spawn two doubleword aligned regions.
3On a target that supports 8 byte watchpoints, this will
4need to be implemented with a hardware watchpoint on both
5doublewords.
6"""
7
8
9import lldb
10from lldbsuite.test.decorators import *
11from lldbsuite.test.lldbtest import *
12from lldbsuite.test import lldbutil
13
14
15class UnalignedWatchpointTestCase(TestBase):
16    def hit_watchpoint_and_continue(self, process, iter_str):
17        process.Continue()
18        self.assertEqual(process.GetState(), lldb.eStateStopped, iter_str)
19        thread = process.GetSelectedThread()
20        self.assertEqual(thread.GetStopReason(), lldb.eStopReasonWatchpoint, iter_str)
21        self.assertEqual(thread.GetStopReasonDataCount(), 1, iter_str)
22        wp_num = thread.GetStopReasonDataAtIndex(0)
23        self.assertEqual(wp_num, 1, iter_str)
24
25    NO_DEBUG_INFO_TESTCASE = True
26
27    # debugserver on AArch64 has this feature.
28    @skipIf(archs=no_match(["arm64", "arm64e", "aarch64"]))
29    @skipUnlessDarwin
30    # debugserver only started returning an exception address within
31    # a range lldb expects in https://reviews.llvm.org/D147820 2023-04-12.
32    # older debugservers will return the base address of the doubleword
33    # which lldb doesn't understand, and will stop executing without a
34    # proper stop reason.
35    @skipIfOutOfTreeDebugserver
36    def test_unaligned_watchpoint(self):
37        """Test a watchpoint that is handled by two hardware watchpoint registers."""
38        self.build()
39        self.main_source_file = lldb.SBFileSpec("main.c")
40        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
41            self, "break here", self.main_source_file
42        )
43
44        thread.StepOver()
45
46        frame = thread.GetFrameAtIndex(0)
47
48        a_bytebuf_6 = frame.GetValueForVariablePath("a.bytebuf[6]")
49        a_bytebuf_6_addr = a_bytebuf_6.GetAddress().GetLoadAddress(target)
50        err = lldb.SBError()
51        wp_opts = lldb.SBWatchpointOptions()
52        wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
53        wp = target.WatchpointCreateByAddress(a_bytebuf_6_addr, 4, wp_opts, err)
54        self.assertTrue(err.Success())
55        self.assertTrue(wp.IsEnabled())
56        self.assertEqual(wp.GetWatchSize(), 4)
57        self.assertGreater(
58            wp.GetWatchAddress() % 8, 4, "watched region spans two doublewords"
59        )
60
61        # We will hit our watchpoint 6 times during the execution
62        # of the inferior.  If the remote stub does not actually split
63        # the watched region into two doubleword watchpoints, we will
64        # exit before we get to 6 watchpoint hits.
65        for i in range(1, 7):
66            self.hit_watchpoint_and_continue(process, "wp hit number %s" % i)
67