aboutsummaryrefslogtreecommitdiff
path: root/lldb/test/API/functionalities/single-thread-step/TestSingleThreadStepTimeout.py
blob: 214a2fb6cb87b799138c3b24eb251b91e1447ef5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
"""
Test that single thread step over deadlock issue can be resolved 
after timeout.
"""

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class SingleThreadStepTimeoutTestCase(TestBase):
    NO_DEBUG_INFO_TESTCASE = True

    def setUp(self):
        TestBase.setUp(self)
        self.main_source = "main.cpp"
        self.build()

    def verify_hit_correct_line(self, pattern):
        target_line = line_number(self.main_source, pattern)
        self.assertNotEqual(target_line, 0, "Could not find source pattern " + pattern)
        cur_line = self.thread.frames[0].GetLineEntry().GetLine()
        self.assertEqual(
            cur_line,
            target_line,
            "Stepped to line %d instead of expected %d with pattern '%s'."
            % (cur_line, target_line, pattern),
        )

    def step_over_deadlock_helper(self):
        (target, _, self.thread, _) = lldbutil.run_to_source_breakpoint(
            self, "// Set breakpoint1 here", lldb.SBFileSpec(self.main_source)
        )

        signal_main_thread_value = target.FindFirstGlobalVariable("signal_main_thread")
        self.assertTrue(signal_main_thread_value.IsValid())

        # Change signal_main_thread global variable to 1 so that worker thread loop can
        # terminate and move forward to signal main thread
        signal_main_thread_value.SetValueFromCString("1")

        self.thread.StepOver(lldb.eOnlyThisThread)
        self.verify_hit_correct_line("// Finish step-over from breakpoint1")

    @skipIfWindows
    def test_step_over_deadlock_small_timeout_fast_stepping(self):
        """Test single thread step over deadlock on other threads can be resolved after timeout with small timeout and fast stepping."""
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 10"
        )
        self.dbg.HandleCommand("settings set target.use-fast-stepping true")
        self.step_over_deadlock_helper()

    @skipIfWindows
    def test_step_over_deadlock_small_timeout_slow_stepping(self):
        """Test single thread step over deadlock on other threads can be resolved after timeout with small timeout and slow stepping."""
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 10"
        )
        self.dbg.HandleCommand("settings set target.use-fast-stepping false")
        self.step_over_deadlock_helper()

    @skipIfWindows
    def test_step_over_deadlock_large_timeout_fast_stepping(self):
        """Test single thread step over deadlock on other threads can be resolved after timeout with large timeout and fast stepping."""
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 2000"
        )
        self.dbg.HandleCommand("settings set target.use-fast-stepping true")
        self.step_over_deadlock_helper()

    @skipIfWindows
    def test_step_over_deadlock_large_timeout_slow_stepping(self):
        """Test single thread step over deadlock on other threads can be resolved after timeout with large timeout and slow stepping."""
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 2000"
        )
        self.dbg.HandleCommand("settings set target.use-fast-stepping false")
        self.step_over_deadlock_helper()

    def step_over_multi_calls_helper(self):
        (target, _, self.thread, _) = lldbutil.run_to_source_breakpoint(
            self, "// Set breakpoint2 here", lldb.SBFileSpec(self.main_source)
        )
        self.thread.StepOver(lldb.eOnlyThisThread)
        self.verify_hit_correct_line("// Finish step-over from breakpoint2")

    @skipIfWindows
    def test_step_over_multi_calls_small_timeout_fast_stepping(self):
        """Test step over source line with multiple call instructions works fine with small timeout and fast stepping."""
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 10"
        )
        self.dbg.HandleCommand("settings set target.use-fast-stepping true")
        self.step_over_multi_calls_helper()

    @skipIfWindows
    def test_step_over_multi_calls_small_timeout_slow_stepping(self):
        """Test step over source line with multiple call instructions works fine with small timeout and slow stepping."""
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 10"
        )
        self.dbg.HandleCommand("settings set target.use-fast-stepping false")
        self.step_over_multi_calls_helper()

    @skipIfWindows
    def test_step_over_multi_calls_large_timeout_fast_stepping(self):
        """Test step over source line with multiple call instructions works fine with large timeout and fast stepping."""
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 2000"
        )
        self.dbg.HandleCommand("settings set target.use-fast-stepping true")
        self.step_over_multi_calls_helper()

    @skipIfWindows
    def test_step_over_multi_calls_large_timeout_slow_stepping(self):
        """Test step over source line with multiple call instructions works fine with large timeout and slow stepping."""
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 2000"
        )
        self.dbg.HandleCommand("settings set target.use-fast-stepping false")
        self.step_over_multi_calls_helper()

    @skipIfWindows
    def test_step_over_deadlock_with_inner_breakpoint_continue(self):
        """Test step over deadlock function with inner breakpoint will trigger the breakpoint
        and later continue will finish the stepping.
        """
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 2000"
        )
        (target, process, self.thread, _) = lldbutil.run_to_source_breakpoint(
            self, "// Set breakpoint1 here", lldb.SBFileSpec(self.main_source)
        )

        signal_main_thread_value = target.FindFirstGlobalVariable("signal_main_thread")
        self.assertTrue(signal_main_thread_value.IsValid())

        # Change signal_main_thread global variable to 1 so that worker thread loop can
        # terminate and move forward to signal main thread
        signal_main_thread_value.SetValueFromCString("1")

        # Set breakpoint on inner function call
        inner_breakpoint = target.BreakpointCreateByLocation(
            lldb.SBFileSpec(self.main_source),
            line_number("main.cpp", "// Set interrupt breakpoint here"),
            0,
            0,
            lldb.SBFileSpecList(),
            False,
        )

        # Step over will hit the inner breakpoint and stop
        self.thread.StepOver(lldb.eOnlyThisThread)
        self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonBreakpoint)
        thread1 = lldbutil.get_one_thread_stopped_at_breakpoint(
            process, inner_breakpoint
        )
        self.assertTrue(
            thread1.IsValid(),
            "We are indeed stopped at inner breakpoint inside deadlock_func",
        )

        # Continue the process should complete the step-over
        process.Continue()
        self.assertState(process.GetState(), lldb.eStateStopped)
        self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete)

        self.verify_hit_correct_line("// Finish step-over from breakpoint1")

    @skipIfWindows
    def test_step_over_deadlock_with_inner_breakpoint_step(self):
        """Test step over deadlock function with inner breakpoint will trigger the breakpoint
        and later step still works
        """
        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 2000"
        )
        (target, process, self.thread, _) = lldbutil.run_to_source_breakpoint(
            self, "// Set breakpoint1 here", lldb.SBFileSpec(self.main_source)
        )

        signal_main_thread_value = target.FindFirstGlobalVariable("signal_main_thread")
        self.assertTrue(signal_main_thread_value.IsValid())

        # Change signal_main_thread global variable to 1 so that worker thread loop can
        # terminate and move forward to signal main thread
        signal_main_thread_value.SetValueFromCString("1")

        # Set breakpoint on inner function call
        inner_breakpoint = target.BreakpointCreateByLocation(
            lldb.SBFileSpec(self.main_source),
            line_number("main.cpp", "// Set interrupt breakpoint here"),
            0,
            0,
            lldb.SBFileSpecList(),
            False,
        )

        # Step over will hit the inner breakpoint and stop
        self.thread.StepOver(lldb.eOnlyThisThread)
        self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonBreakpoint)
        thread1 = lldbutil.get_one_thread_stopped_at_breakpoint(
            process, inner_breakpoint
        )
        self.assertTrue(
            thread1.IsValid(),
            "We are indeed stopped at inner breakpoint inside deadlock_func",
        )

        # Step still works
        self.thread.StepOver(lldb.eOnlyThisThread)
        self.assertState(process.GetState(), lldb.eStateStopped)
        self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete)

        self.verify_hit_correct_line("// Finish step-over from inner breakpoint")

    @skipIfWindows
    def test_step_over_deadlock_with_user_async_interrupt(self):
        """Test step over deadlock function with large timeout then send async interrupt
        should report correct stop reason
        """

        self.dbg.HandleCommand(
            "settings set target.process.thread.single-thread-plan-timeout 2000000"
        )

        (target, process, self.thread, _) = lldbutil.run_to_source_breakpoint(
            self, "// Set breakpoint1 here", lldb.SBFileSpec(self.main_source)
        )

        signal_main_thread_value = target.FindFirstGlobalVariable("signal_main_thread")
        self.assertTrue(signal_main_thread_value.IsValid())

        # Change signal_main_thread global variable to 1 so that worker thread loop can
        # terminate and move forward to signal main thread
        signal_main_thread_value.SetValueFromCString("1")

        self.dbg.SetAsync(True)

        # This stepping should block due to large timeout and should be interrupted by the
        # async interrupt from the worker thread
        self.thread.StepOver(lldb.eOnlyThisThread)
        time.sleep(1)

        listener = self.dbg.GetListener()
        lldbutil.expect_state_changes(self, listener, process, [lldb.eStateRunning])
        self.dbg.SetAsync(False)

        process.SendAsyncInterrupt()

        lldbutil.expect_state_changes(self, listener, process, [lldb.eStateStopped])
        self.assertStopReason(self.thread.GetStopReason(), lldb.eStopReasonSignal)