1"""
2Test stepping out from a function in a multi-threaded program.
3"""
4
5import os, time
6import unittest2
7import lldb
8from lldbtest import *
9import lldbutil
10
11class ThreadStepOutTestCase(TestBase):
12
13    mydir = os.path.join("functionalities", "thread", "step_out")
14
15    @dsym_test
16    def test_step_single_thread_with_dsym(self):
17        """Test thread step out on one thread via command interpreter. """
18        self.buildDsym(dictionary=self.getBuildFlags())
19        self.step_out_test(self.step_out_single_thread_with_cmd)
20
21    @expectedFailureFreeBSD("llvm.org/pr16696") # threaded inferior not yet implemented on FreeBSD
22    @dwarf_test
23    def test_step_single_thread_with_dwarf(self):
24        """Test thread step out on one thread via command interpreter. """
25        self.buildDwarf(dictionary=self.getBuildFlags())
26        self.step_out_test(self.step_out_single_thread_with_cmd)
27
28    @dsym_test
29    def test_step_all_threads_with_dsym(self):
30        """Test thread step out on all threads via command interpreter. """
31        self.buildDsym(dictionary=self.getBuildFlags())
32        self.step_out_test(self.step_out_all_threads_with_cmd)
33
34    @expectedFailureFreeBSD("llvm.org/pr16696") # threaded inferior not yet implemented on FreeBSD
35    @dwarf_test
36    def test_step_all_threads_with_dwarf(self):
37        """Test thread step out on all threads via command interpreter. """
38        self.buildDwarf(dictionary=self.getBuildFlags())
39        self.step_out_test(self.step_out_all_threads_with_cmd)
40
41    @dsym_test
42    def test_python_with_dsym(self):
43        """Test thread step out on one threads via Python API (dsym)."""
44        self.buildDsym(dictionary=self.getBuildFlags())
45        self.step_out_test(self.step_out_with_python)
46
47    @expectedFailureFreeBSD("llvm.org/pr16696") # threaded inferior not yet implemented on FreeBSD
48    @dwarf_test
49    def test_python_with_dwarf(self):
50        """Test thread step out on one thread via Python API (dwarf)."""
51        self.buildDwarf(dictionary=self.getBuildFlags())
52        self.step_out_test(self.step_out_with_python)
53
54    def setUp(self):
55        # Call super's setUp().
56        TestBase.setUp(self)
57        # Find the line number for our breakpoint.
58        self.breakpoint = line_number('main.cpp', '// Set breakpoint here')
59        if "gcc" in self.getCompiler() or self.isIntelCompiler():
60            self.step_out_destination = line_number('main.cpp', '// Expect to stop here after step-out (icc and gcc)')
61        else:
62            self.step_out_destination = line_number('main.cpp', '// Expect to stop here after step-out (clang)')
63
64    def step_out_single_thread_with_cmd(self):
65        self.step_out_with_cmd("this-thread")
66        self.expect("thread backtrace all", "Thread location after step out is correct",
67            substrs = ["main.cpp:%d" % self.step_out_destination,
68                       "main.cpp:%d" % self.breakpoint])
69
70    def step_out_all_threads_with_cmd(self):
71        self.step_out_with_cmd("all-threads")
72        self.expect("thread backtrace all", "Thread location after step out is correct",
73            substrs = ["main.cpp:%d" % self.step_out_destination])
74
75    def step_out_with_cmd(self, run_mode):
76        self.runCmd("thread select %d" % self.step_out_thread.GetIndexID())
77        self.runCmd("thread step-out -m %s" % run_mode)
78        self.expect("process status", "Expected stop reason to be step-out",
79            substrs = ["stop reason = step out"])
80
81        self.expect("thread list", "Selected thread did not change during step-out",
82            substrs = ["* thread #%d" % self.step_out_thread.GetIndexID()])
83
84    def step_out_with_python(self):
85        self.step_out_thread.StepOut()
86
87        reason = self.step_out_thread.GetStopReason()
88        self.assertEqual(lldb.eStopReasonPlanComplete, reason,
89            "Expected thread stop reason 'plancomplete', but got '%s'" % lldbutil.stop_reason_to_str(reason))
90
91        # Verify location after stepping out
92        frame = self.step_out_thread.GetFrameAtIndex(0)
93        desc = lldbutil.get_description(frame.GetLineEntry())
94        expect = "main.cpp:%d" % self.step_out_destination
95        self.assertTrue(expect in desc, "Expected %s but thread stopped at %s" % (expect, desc))
96
97    def step_out_test(self, step_out_func):
98        """Test single thread step out of a function."""
99        exe = os.path.join(os.getcwd(), "a.out")
100        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
101
102        # This should create a breakpoint in the main thread.
103        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1)
104
105        # The breakpoint list should show 1 location.
106        self.expect("breakpoint list -f", "Breakpoint location shown correctly",
107            substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.breakpoint])
108
109        # Run the program.
110        self.runCmd("run", RUN_SUCCEEDED)
111
112        # Get the target process
113        self.inferior_target = self.dbg.GetSelectedTarget()
114        self.inferior_process = self.inferior_target.GetProcess()
115
116        # Get the number of threads, ensure we see all three.
117        num_threads = self.inferior_process.GetNumThreads()
118        self.assertEqual(num_threads, 3, 'Number of expected threads and actual threads do not match.')
119
120        (breakpoint_threads, other_threads) = ([], [])
121        lldbutil.sort_stopped_threads(self.inferior_process,
122                                      breakpoint_threads=breakpoint_threads,
123                                      other_threads=other_threads)
124
125        while len(breakpoint_threads) < 2:
126            self.runCmd("thread continue %s" % " ".join([str(x.GetIndexID()) for x in other_threads]))
127            lldbutil.sort_stopped_threads(self.inferior_process,
128                                          breakpoint_threads=breakpoint_threads,
129                                          other_threads=other_threads)
130
131        self.step_out_thread = breakpoint_threads[0]
132
133        # Step out of thread stopped at breakpoint
134        step_out_func()
135
136        # Run to completion
137        self.runCmd("continue")
138
139        # At this point, the inferior process should have exited.
140        self.assertTrue(self.inferior_process.GetState() == lldb.eStateExited, PROCESS_EXITED)
141
142if __name__ == '__main__':
143    import atexit
144    lldb.SBDebugger.Initialize()
145    atexit.register(lambda: lldb.SBDebugger.Terminate())
146    unittest2.main()
147