1"""
2Test lldb watchpoint that uses 'watchpoint set -w write -s size' to watch a pointed location with size.
3"""
4
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11
12class WatchLocationUsingWatchpointSetTestCase(TestBase):
13    NO_DEBUG_INFO_TESTCASE = True
14
15    # on arm64 targets, lldb has incorrect hit-count / ignore-counts
16    # for watchpoints when they are hit with multiple threads at
17    # the same time.  Tracked as llvm.org/pr49433
18    # or rdar://93863107 inside Apple.
19    def affected_by_radar_93863107(self):
20        return (
21            self.getArchitecture() in ["arm64", "arm64e"]
22        ) and self.platformIsDarwin()
23
24    def setUp(self):
25        # Call super's setUp().
26        TestBase.setUp(self)
27        # Our simple source filename.
28        self.source = "main.cpp"
29        # Find the line number to break inside main().
30        self.line = line_number(self.source, "// Set break point at this line.")
31        # This is for verifying that watch location works.
32        self.violating_func = "do_bad_thing_with_location"
33        # Build dictionary to have unique executable names for each test
34        # method.
35
36    @skipIf(oslist=["linux"], archs=["aarch64", "arm"], bugnumber="llvm.org/pr26031")
37    @skipIfWindows  # This test is flaky on Windows
38    def test_watchlocation_using_watchpoint_set(self):
39        """Test watching a location with 'watchpoint set expression -w write -s size' option."""
40        self.build()
41        self.setTearDownCleanup()
42
43        exe = self.getBuildArtifact("a.out")
44        target = self.dbg.CreateTarget(exe)
45
46        # Add a breakpoint to set a watchpoint when stopped on the breakpoint.
47        lldbutil.run_break_set_by_file_and_line(
48            self, None, self.line, num_expected_locations=1
49        )
50
51        # Run the program.
52        self.runCmd("run", RUN_SUCCEEDED)
53
54        # We should be stopped again due to the breakpoint.
55        # The stop reason of the thread should be breakpoint.
56        self.expect(
57            "thread list",
58            STOPPED_DUE_TO_BREAKPOINT,
59            substrs=["stopped", "stop reason = breakpoint"],
60        )
61
62        # Now let's set a write-type watchpoint pointed to by 'g_char_ptr' and
63        # with offset as 7.
64        # The main.cpp, by design, misbehaves by not following the agreed upon
65        # protocol of only accessing the allowable index range of [0, 6].
66        self.expect(
67            "watchpoint set expression -w write -s 1 -- g_char_ptr + 7",
68            WATCHPOINT_CREATED,
69            substrs=["Watchpoint created", "size = 1", "type = w"],
70        )
71        self.runCmd("expr unsigned val = g_char_ptr[7]; val")
72        self.expect(self.res.GetOutput().splitlines()[0], exe=False, endstr=" = 0")
73
74        # Use the '-v' option to do verbose listing of the watchpoint.
75        # The hit count should be 0 initially.
76        self.expect("watchpoint list -v", substrs=["hit_count = 0"])
77
78        # Check the underlying SBWatchpoint.
79        watchpoint = target.GetWatchpointAtIndex(0)
80        self.assertEqual(watchpoint.GetWatchSize(), 1)
81        self.assertEqual(watchpoint.GetHitCount(), 0)
82        self.assertEqual(watchpoint.GetWatchSpec(), "g_char_ptr + 7")
83
84        self.runCmd("process continue")
85
86        # We should be stopped again due to the watchpoint (write type), but
87        # only once.  The stop reason of the thread should be watchpoint.
88        self.expect(
89            "thread list",
90            STOPPED_DUE_TO_WATCHPOINT,
91            substrs=[
92                "stopped",
93                self.violating_func,
94                "stop reason = watchpoint",
95            ],
96        )
97
98        # Switch to the thread stopped due to watchpoint and issue some
99        # commands.
100        self.switch_to_thread_with_stop_reason(lldb.eStopReasonWatchpoint)
101        self.runCmd("thread backtrace")
102        self.runCmd("expr unsigned val = g_char_ptr[7]; val")
103        self.expect(self.res.GetOutput().splitlines()[0], exe=False, endstr=" = 99")
104
105        # Use the '-v' option to do verbose listing of the watchpoint.
106        # The hit count should now be the same as the number of threads that
107        # stopped on a watchpoint.
108        threads = lldbutil.get_stopped_threads(
109            self.process(), lldb.eStopReasonWatchpoint
110        )
111
112        if not self.affected_by_radar_93863107():
113            self.expect("watchpoint list -v", substrs=["hit_count = %d" % len(threads)])
114
115        self.runCmd("thread backtrace all")
116