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