1"""
2Watch a large unaligned memory region that
3lldb will need multiple hardware watchpoints
4to cover.
5"""
6
7
8import lldb
9from lldbsuite.test.decorators import *
10from lldbsuite.test.lldbtest import *
11from lldbsuite.test import lldbutil
12
13
14class UnalignedLargeWatchpointTestCase(TestBase):
15    def continue_and_report_stop_reason(self, process, iter_str):
16        if self.TraceOn():
17            self.runCmd("script print('continue')")
18        process.Continue()
19        self.assertIn(
20            process.GetState(), [lldb.eStateStopped, lldb.eStateExited], iter_str
21        )
22        thread = process.GetSelectedThread()
23        return thread.GetStopReason()
24
25    NO_DEBUG_INFO_TESTCASE = True
26
27    # The Windows process plugins haven't been updated to break
28    # watchpoints into WatchpointResources yet.
29    @skipIfWindows
30
31    # Test on 64-bit targets where we probably have
32    # four watchpoint registers that can watch doublewords (8-byte).
33    @skipIf(archs=no_match(["arm64", "arm64e", "aarch64", "x86_64"]))
34    def test_unaligned_large_watchpoint(self):
35        """Test watching an unaligned region of memory that requires multiple watchpoints."""
36        self.build()
37        self.main_source_file = lldb.SBFileSpec("main.c")
38        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
39            self, "break here", self.main_source_file
40        )
41        self.runCmd("break set -p done")
42        self.runCmd("break set -p exiting")
43
44        frame = thread.GetFrameAtIndex(0)
45
46        array_addr = frame.GetValueForVariablePath("array").GetValueAsUnsigned()
47
48        # Don't assume that the heap allocated array is aligned
49        # to a 1024 byte boundary to begin with, force alignment.
50        # wa_addr = (array_addr + 1024) & ~(1024 - 1)
51        wa_addr = array_addr
52
53        # Now make the start address unaligned.
54        wa_addr = wa_addr + 7
55
56        err = lldb.SBError()
57        wp_opts = lldb.SBWatchpointOptions()
58        wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
59        wp = target.WatchpointCreateByAddress(wa_addr, 22, wp_opts, err)
60        self.assertTrue(wp.IsValid())
61        self.assertSuccess(err)
62        if self.TraceOn():
63            self.runCmd("watch list -v")
64
65        c_count = 0
66        reason = self.continue_and_report_stop_reason(process, "continue #%d" % c_count)
67        while reason == lldb.eStopReasonWatchpoint:
68            c_count = c_count + 1
69            reason = self.continue_and_report_stop_reason(
70                process, "continue #%d" % c_count
71            )
72            self.assertLessEqual(c_count, 22)
73
74        self.assertEqual(c_count, 22)
75        self.expect("watchpoint list -v", substrs=["hit_count = 22"])
76        self.assertEqual(wp.GetHitCount(), 22)
77
78        target.DeleteWatchpoint(wp.GetID())
79
80        # Now try watching a 16 byte variable
81        # (not unaligned, but a good check to do anyway)
82        frame = thread.GetFrameAtIndex(0)
83        err = lldb.SBError()
84        wp = frame.locals["variable"][0].Watch(True, False, True, err)
85        self.assertSuccess(err)
86        if self.TraceOn():
87            self.runCmd("frame select 0")
88            self.runCmd("watchpoint list")
89
90        c_count = 0
91        reason = self.continue_and_report_stop_reason(process, "continue #%d" % c_count)
92        while reason == lldb.eStopReasonWatchpoint:
93            c_count = c_count + 1
94            reason = self.continue_and_report_stop_reason(
95                process, "continue #%d" % c_count
96            )
97            self.assertLessEqual(c_count, 4)
98
99        if self.TraceOn():
100            self.runCmd("frame select 0")
101
102        self.assertEqual(c_count, 4)
103        self.expect("watchpoint list -v", substrs=["hit_count = 4"])
104        self.assertEqual(wp.GetHitCount(), 4)
105