1"""
2Test number of threads.
3"""
4
5import os, time
6import unittest2
7import lldb
8from lldbtest import *
9import lldbutil
10
11class ThreadExitTestCase(TestBase):
12
13    mydir = os.path.join("functionalities", "thread", "thread_exit")
14
15    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
16    @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained
17    @dsym_test
18    def test_with_dsym(self):
19        """Test thread exit handling."""
20        self.buildDsym(dictionary=self.getBuildFlags())
21        self.thread_exit_test()
22
23    @expectedFailureFreeBSD("llvm.org/pr16696") # threaded inferior not implemented on FreeBSD yet
24    @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained
25    @dwarf_test
26    def test_with_dwarf(self):
27        """Test thread exit handling."""
28        self.buildDwarf(dictionary=self.getBuildFlags())
29        self.thread_exit_test()
30
31    def setUp(self):
32        # Call super's setUp().
33        TestBase.setUp(self)
34        # Find the line numbers for our breakpoints.
35        self.break_1 = line_number('main.cpp', '// Set first breakpoint here')
36        self.break_2 = line_number('main.cpp', '// Set second breakpoint here')
37        self.break_3 = line_number('main.cpp', '// Set third breakpoint here')
38        self.break_4 = line_number('main.cpp', '// Set fourth breakpoint here')
39
40    def thread_exit_test(self):
41        """Test thread exit handling."""
42        exe = os.path.join(os.getcwd(), "a.out")
43        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
44
45        # This should create a breakpoint with 1 location.
46        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1)
47        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1)
48        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_3, num_expected_locations=1)
49        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_4, num_expected_locations=1)
50
51        # The breakpoint list should show 1 locations.
52        self.expect("breakpoint list -f", "Breakpoint location shown correctly",
53            substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.break_1,
54                       "2: file = 'main.cpp', line = %d, locations = 1" % self.break_2,
55                       "3: file = 'main.cpp', line = %d, locations = 1" % self.break_3,
56                       "4: file = 'main.cpp', line = %d, locations = 1" % self.break_4])
57
58        # Run the program.
59        self.runCmd("run", RUN_SUCCEEDED)
60
61        # The stop reason of the thread should be breakpoint 1.
62        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 1",
63            substrs = ['stopped',
64                       '* thread #1',
65                       'stop reason = breakpoint 1',
66                       'thread #2'])
67
68        # Get the target process
69        target = self.dbg.GetSelectedTarget()
70        process = target.GetProcess()
71
72        # Get the number of threads
73        num_threads = process.GetNumThreads()
74
75        self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match at breakpoint 1.')
76
77        # Run to the second breakpoint
78        self.runCmd("continue")
79
80        # The stop reason of the thread should be breakpoint 1.
81        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 2",
82            substrs = ['stopped',
83                       'thread #1',
84                       '* thread #2',
85                       'stop reason = breakpoint 2',
86                       'thread #3'])
87
88        # Update the number of threads
89        num_threads = process.GetNumThreads()
90
91        self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match at breakpoint 2.')
92
93        # Run to the third breakpoint
94        self.runCmd("continue")
95
96        # The stop reason of the thread should be breakpoint 3.
97        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 3",
98            substrs = ['stopped',
99                       '* thread #1',
100                       'stop reason = breakpoint 3',
101                       'thread #3',
102                       ])
103
104        # Update the number of threads
105        num_threads = process.GetNumThreads()
106
107        self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match at breakpoint 3.')
108
109        # Run to the fourth breakpoint
110        self.runCmd("continue")
111
112        # The stop reason of the thread should be breakpoint 4.
113        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 4",
114            substrs = ['stopped',
115                       '* thread #1',
116                       'stop reason = breakpoint 4'])
117
118        # Update the number of threads
119        num_threads = process.GetNumThreads()
120
121        self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match at breakpoint 4.')
122
123        # Run to completion
124        self.runCmd("continue")
125
126        # At this point, the inferior process should have exited.
127        self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED)
128
129if __name__ == '__main__':
130    import atexit
131    lldb.SBDebugger.Initialize()
132    atexit.register(lambda: lldb.SBDebugger.Terminate())
133    unittest2.main()
134