1"""
2Test lldb-vscode setBreakpoints request
3"""
4
5
6import unittest2
7import vscode
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11import lldbvscode_testcase
12import os
13import shutil
14import subprocess
15import tempfile
16import threading
17import time
18
19
20def spawn_and_wait(program, delay):
21    if delay:
22        time.sleep(delay)
23    process = subprocess.Popen([program],
24                               stdin=subprocess.PIPE,
25                               stdout=subprocess.PIPE,
26                               stderr=subprocess.PIPE)
27    process.wait()
28
29
30class TestVSCode_attach(lldbvscode_testcase.VSCodeTestCaseBase):
31
32    mydir = TestBase.compute_mydir(__file__)
33
34    def set_and_hit_breakpoint(self, continueToExit=True):
35        source = 'main.c'
36        breakpoint1_line = line_number(source, '// breakpoint 1')
37        lines = [breakpoint1_line]
38        # Set breakpoint in the thread function so we can step the threads
39        breakpoint_ids = self.set_source_breakpoints(source, lines)
40        self.assertEqual(len(breakpoint_ids), len(lines),
41                         "expect correct number of breakpoints")
42        self.continue_to_breakpoints(breakpoint_ids)
43        if continueToExit:
44            self.continue_to_exit()
45
46    @skipIfWindows
47    @skipIfNetBSD # Hangs on NetBSD as well
48    @skipIfRemote
49    def test_by_pid(self):
50        '''
51            Tests attaching to a process by process ID.
52        '''
53        self.build_and_create_debug_adaptor()
54        program = self.getBuildArtifact("a.out")
55        self.process = subprocess.Popen([program],
56                                        stdin=subprocess.PIPE,
57                                        stdout=subprocess.PIPE,
58                                        stderr=subprocess.PIPE)
59        self.attach(pid=self.process.pid)
60        self.set_and_hit_breakpoint(continueToExit=True)
61
62    @skipIfWindows
63    @skipIfNetBSD # Hangs on NetBSD as well
64    @skipIfRemote
65    def test_by_name(self):
66        '''
67            Tests attaching to a process by process name.
68        '''
69        self.build_and_create_debug_adaptor()
70        orig_program = self.getBuildArtifact("a.out")
71        # Since we are going to attach by process name, we need a unique
72        # process name that has minimal chance to match a process that is
73        # already running. To do this we use tempfile.mktemp() to give us a
74        # full path to a location where we can copy our executable. We then
75        # run this copy to ensure we don't get the error "more that one
76        # process matches 'a.out'".
77        program = tempfile.mktemp()
78        shutil.copyfile(orig_program, program)
79        shutil.copymode(orig_program, program)
80
81        # Use a file as a synchronization point between test and inferior.
82        pid_file_path = lldbutil.append_to_process_working_directory(self,
83            "pid_file_%d" % (int(time.time())))
84
85        def cleanup():
86            if os.path.exists(program):
87                os.unlink(program)
88            self.run_platform_command("rm %s" % (pid_file_path))
89        # Execute the cleanup function during test case tear down.
90        self.addTearDownHook(cleanup)
91
92        popen = self.spawnSubprocess(program, [pid_file_path])
93
94        pid = lldbutil.wait_for_file_on_target(self, pid_file_path)
95
96        self.attach(program=program)
97        self.set_and_hit_breakpoint(continueToExit=True)
98
99    @skipUnlessDarwin
100    @skipIfDarwin
101    @skipIfNetBSD # Hangs on NetBSD as well
102    def test_by_name_waitFor(self):
103        '''
104            Tests attaching to a process by process name and waiting for the
105            next instance of a process to be launched, ingoring all current
106            ones.
107        '''
108        self.build_and_create_debug_adaptor()
109        program = self.getBuildArtifact("a.out")
110        self.spawn_thread = threading.Thread(target=spawn_and_wait,
111                                             args=(program, 1.0,))
112        self.spawn_thread.start()
113        self.attach(program=program, waitFor=True)
114        self.set_and_hit_breakpoint(continueToExit=True)
115
116    @skipIfWindows
117    @skipIfDarwin
118    @skipIfNetBSD # Hangs on NetBSD as well
119    @skipIf(archs=["arm", "aarch64"]) # Example of a flaky run http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/5527/steps/test/logs/stdio
120    def test_commands(self):
121        '''
122            Tests the "initCommands", "preRunCommands", "stopCommands",
123            "exitCommands", "terminateCommands" and "attachCommands"
124            that can be passed during attach.
125
126            "initCommands" are a list of LLDB commands that get executed
127            before the targt is created.
128            "preRunCommands" are a list of LLDB commands that get executed
129            after the target has been created and before the launch.
130            "stopCommands" are a list of LLDB commands that get executed each
131            time the program stops.
132            "exitCommands" are a list of LLDB commands that get executed when
133            the process exits
134            "attachCommands" are a list of LLDB commands that get executed and
135            must have a valid process in the selected target in LLDB after
136            they are done executing. This allows custom commands to create any
137            kind of debug session.
138            "terminateCommands" are a list of LLDB commands that get executed when
139            the debugger session terminates.
140        '''
141        self.build_and_create_debug_adaptor()
142        program = self.getBuildArtifact("a.out")
143        # Here we just create a target and launch the process as a way to test
144        # if we are able to use attach commands to create any kind of a target
145        # and use it for debugging
146        attachCommands = [
147            'target create -d "%s"' % (program),
148            'process launch --stop-at-entry'
149        ]
150        initCommands = ['target list', 'platform list']
151        preRunCommands = ['image list a.out', 'image dump sections a.out']
152        postRunCommands = ['help trace', 'help process trace']
153        stopCommands = ['frame variable', 'bt']
154        exitCommands = ['expr 2+3', 'expr 3+4']
155        terminateCommands = ['expr 4+2']
156        self.attach(program=program,
157                    attachCommands=attachCommands,
158                    initCommands=initCommands,
159                    preRunCommands=preRunCommands,
160                    stopCommands=stopCommands,
161                    exitCommands=exitCommands,
162                    terminateCommands=terminateCommands,
163                    postRunCommands=postRunCommands)
164        # Get output from the console. This should contain both the
165        # "initCommands" and the "preRunCommands".
166        output = self.get_console()
167        # Verify all "initCommands" were found in console output
168        self.verify_commands('initCommands', output, initCommands)
169        # Verify all "preRunCommands" were found in console output
170        self.verify_commands('preRunCommands', output, preRunCommands)
171        # Verify all "postRunCommands" were found in console output
172        self.verify_commands('postRunCommands', output, postRunCommands)
173
174        functions = ['main']
175        breakpoint_ids = self.set_function_breakpoints(functions)
176        self.assertEquals(len(breakpoint_ids), len(functions),
177                        "expect one breakpoint")
178        self.continue_to_breakpoints(breakpoint_ids)
179        output = self.get_console(timeout=1.0)
180        self.verify_commands('stopCommands', output, stopCommands)
181
182        # Continue after launch and hit the "pause()" call and stop the target.
183        # Get output from the console. This should contain both the
184        # "stopCommands" that were run after we stop.
185        self.vscode.request_continue()
186        time.sleep(0.5)
187        self.vscode.request_pause()
188        self.vscode.wait_for_stopped()
189        output = self.get_console(timeout=1.0)
190        self.verify_commands('stopCommands', output, stopCommands)
191
192        # Continue until the program exits
193        self.continue_to_exit()
194        # Get output from the console. This should contain both the
195        # "exitCommands" that were run after the second breakpoint was hit
196        # and the "terminateCommands" due to the debugging session ending
197        output = self.collect_console(duration=1.0)
198        self.verify_commands('exitCommands', output, exitCommands)
199        self.verify_commands('terminateCommands', output, terminateCommands)
200
201    @skipIfWindows
202    @skipIfDarwin
203    @skipIfNetBSD # Hangs on NetBSD as well
204    @skipIf(archs=["arm", "aarch64"]) # Example of a flaky run http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/5517/steps/test/logs/stdio
205    def test_terminate_commands(self):
206        '''
207            Tests that the "terminateCommands", that can be passed during
208            attach, are run when the debugger is disconnected.
209        '''
210        self.build_and_create_debug_adaptor()
211        program = self.getBuildArtifact("a.out")
212        # Here we just create a target and launch the process as a way to test
213        # if we are able to use attach commands to create any kind of a target
214        # and use it for debugging
215        attachCommands = [
216            'target create -d "%s"' % (program),
217            'process launch --stop-at-entry'
218        ]
219        terminateCommands = ['expr 4+2']
220        self.attach(program=program,
221                    attachCommands=attachCommands,
222                    terminateCommands=terminateCommands,
223                    disconnectAutomatically=False)
224        self.get_console()
225        # Once it's disconnected the console should contain the
226        # "terminateCommands"
227        self.vscode.request_disconnect(terminateDebuggee=True)
228        output = self.collect_console(duration=1.0)
229        self.verify_commands('terminateCommands', output, terminateCommands)
230