1"""
2Test number of threads.
3"""
4
5
6
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11
12
13class ExitDuringStepTestCase(TestBase):
14
15    mydir = TestBase.compute_mydir(__file__)
16
17    @skipIfWindows # This is flakey on Windows: llvm.org/pr38373
18    def test(self):
19        """Test thread exit during step handling."""
20        self.build(dictionary=self.getBuildFlags())
21        self.exit_during_step_base(
22            "thread step-inst -m all-threads",
23            'stop reason = instruction step',
24            True)
25
26    @skipIfWindows # This is flakey on Windows: llvm.org/pr38373
27    def test_step_over(self):
28        """Test thread exit during step-over handling."""
29        self.build(dictionary=self.getBuildFlags())
30        self.exit_during_step_base(
31            "thread step-over -m all-threads",
32            'stop reason = step over',
33            False)
34
35    @skipIfWindows # This is flakey on Windows: llvm.org/pr38373
36    def test_step_in(self):
37        """Test thread exit during step-in handling."""
38        self.build(dictionary=self.getBuildFlags())
39        self.exit_during_step_base(
40            "thread step-in -m all-threads",
41            'stop reason = step in',
42            False)
43
44    def setUp(self):
45        # Call super's setUp().
46        TestBase.setUp(self)
47        # Find the line numbers to break and continue.
48        self.breakpoint = line_number('main.cpp', '// Set breakpoint here')
49        self.continuepoint = line_number('main.cpp', '// Continue from here')
50
51    def exit_during_step_base(self, step_cmd, step_stop_reason, by_instruction):
52        """Test thread exit during step handling."""
53        exe = self.getBuildArtifact("a.out")
54        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
55
56        # This should create a breakpoint in the main thread.
57        self.bp_num = lldbutil.run_break_set_by_file_and_line(
58            self, "main.cpp", self.breakpoint, num_expected_locations=1)
59
60        # The breakpoint list should show 1 location.
61        self.expect(
62            "breakpoint list -f",
63            "Breakpoint location shown correctly",
64            substrs=[
65                "1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" %
66                self.breakpoint])
67
68        # Run the program.
69        self.runCmd("run", RUN_SUCCEEDED)
70
71        # The stop reason of the thread should be breakpoint.
72        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
73                    substrs=['stopped',
74                             'stop reason = breakpoint'])
75
76        # Get the target process
77        target = self.dbg.GetSelectedTarget()
78        process = target.GetProcess()
79
80        num_threads = process.GetNumThreads()
81        # Make sure we see all three threads
82        self.assertGreaterEqual(
83            num_threads,
84            3,
85            'Number of expected threads and actual threads do not match.')
86
87        stepping_thread = lldbutil.get_one_thread_stopped_at_breakpoint_id(
88            process, self.bp_num)
89        self.assertIsNotNone(
90            stepping_thread,
91            "Could not find a thread stopped at the breakpoint")
92
93        current_line = self.breakpoint
94        stepping_frame = stepping_thread.GetFrameAtIndex(0)
95        self.assertEqual(
96            current_line,
97            stepping_frame.GetLineEntry().GetLine(),
98            "Starting line for stepping doesn't match breakpoint line.")
99
100        # Keep stepping until we've reached our designated continue point
101        while current_line != self.continuepoint:
102            # Since we're using the command interpreter to issue the thread command
103            # (on the selected thread) we need to ensure the selected thread is the
104            # stepping thread.
105            if stepping_thread != process.GetSelectedThread():
106                process.SetSelectedThread(stepping_thread)
107
108            self.runCmd(step_cmd)
109
110            frame = stepping_thread.GetFrameAtIndex(0)
111
112            current_line = frame.GetLineEntry().GetLine()
113
114            if by_instruction and current_line == 0:
115                continue
116
117            self.assertGreaterEqual(
118                current_line,
119                self.breakpoint,
120                "Stepped to unexpected line, " +
121                str(current_line))
122            self.assertLessEqual(
123                current_line,
124                self.continuepoint,
125                "Stepped to unexpected line, " +
126                str(current_line))
127
128        self.runCmd("thread list")
129
130        # Update the number of threads
131        new_num_threads = process.GetNumThreads()
132
133        # Check to see that we reduced the number of threads as expected
134        self.assertEqual(
135            new_num_threads,
136            num_threads - 1,
137            'Number of threads did not reduce by 1 after thread exit.')
138
139        self.expect("thread list", 'Process state is stopped due to step',
140                    substrs=['stopped',
141                             step_stop_reason])
142
143        # Run to completion
144        self.runCmd("continue")
145
146        # At this point, the inferior process should have exited.
147        self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED)
148