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
47    @skipIfWindows
48    @skipIfNetBSD # Hangs on NetBSD as well
49    @skipIfRemote
50    def test_by_pid(self):
51        '''
52            Tests attaching to a process by process ID.
53        '''
54        self.build_and_create_debug_adaptor()
55        program = self.getBuildArtifact("a.out")
56        self.process = subprocess.Popen([program],
57                                        stdin=subprocess.PIPE,
58                                        stdout=subprocess.PIPE,
59                                        stderr=subprocess.PIPE)
60        self.attach(pid=self.process.pid)
61        self.set_and_hit_breakpoint(continueToExit=True)
62
63    @skipIfWindows
64    @skipIfNetBSD # Hangs on NetBSD as well
65    @skipIfRemote
66    def test_by_name(self):
67        '''
68            Tests attaching to a process by process name.
69        '''
70        self.build_and_create_debug_adaptor()
71        orig_program = self.getBuildArtifact("a.out")
72        # Since we are going to attach by process name, we need a unique
73        # process name that has minimal chance to match a process that is
74        # already running. To do this we use tempfile.mktemp() to give us a
75        # full path to a location where we can copy our executable. We then
76        # run this copy to ensure we don't get the error "more that one
77        # process matches 'a.out'".
78        program = tempfile.mktemp()
79        shutil.copyfile(orig_program, program)
80        shutil.copymode(orig_program, program)
81
82        # Use a file as a synchronization point between test and inferior.
83        pid_file_path = lldbutil.append_to_process_working_directory(self,
84            "pid_file_%d" % (int(time.time())))
85
86        def cleanup():
87            if os.path.exists(program):
88                os.unlink(program)
89            self.run_platform_command("rm %s" % (pid_file_path))
90        # Execute the cleanup function during test case tear down.
91        self.addTearDownHook(cleanup)
92
93        popen = self.spawnSubprocess(program, [pid_file_path])
94        self.addTearDownHook(self.cleanupSubprocesses)
95
96        pid = lldbutil.wait_for_file_on_target(self, pid_file_path)
97
98        self.attach(program=program)
99        self.set_and_hit_breakpoint(continueToExit=True)
100
101    @skipUnlessDarwin
102    @skipIfDarwin
103    @skipIfNetBSD # Hangs on NetBSD as well
104    def test_by_name_waitFor(self):
105        '''
106            Tests attaching to a process by process name and waiting for the
107            next instance of a process to be launched, ingoring all current
108            ones.
109        '''
110        self.build_and_create_debug_adaptor()
111        program = self.getBuildArtifact("a.out")
112        self.spawn_thread = threading.Thread(target=spawn_and_wait,
113                                             args=(program, 1.0,))
114        self.spawn_thread.start()
115        self.attach(program=program, waitFor=True)
116        self.set_and_hit_breakpoint(continueToExit=True)
117
118    @skipIfWindows
119    @skipIfDarwin
120    @skipIfNetBSD # Hangs on NetBSD as well
121    def test_commands(self):
122        '''
123            Tests the "initCommands", "preRunCommands", "stopCommands",
124            "exitCommands", and "attachCommands" that can be passed during
125            attach.
126
127            "initCommands" are a list of LLDB commands that get executed
128            before the targt is created.
129            "preRunCommands" are a list of LLDB commands that get executed
130            after the target has been created and before the launch.
131            "stopCommands" are a list of LLDB commands that get executed each
132            time the program stops.
133            "exitCommands" are a list of LLDB commands that get executed when
134            the process exits
135            "attachCommands" are a list of LLDB commands that get executed and
136            must have a valid process in the selected target in LLDB after
137            they are done executing. This allows custom commands to create any
138            kind of debug session.
139        '''
140        self.build_and_create_debug_adaptor()
141        program = self.getBuildArtifact("a.out")
142        # Here we just create a target and launch the process as a way to test
143        # if we are able to use attach commands to create any kind of a target
144        # and use it for debugging
145        attachCommands = [
146            'target create -d "%s"' % (program),
147            'process launch'
148        ]
149        initCommands = ['target list', 'platform list']
150        preRunCommands = ['image list a.out', 'image dump sections a.out']
151        stopCommands = ['frame variable', 'bt']
152        exitCommands = ['expr 2+3', 'expr 3+4']
153        self.attach(program=program,
154                    attachCommands=attachCommands,
155                    initCommands=initCommands,
156                    preRunCommands=preRunCommands,
157                    stopCommands=stopCommands,
158                    exitCommands=exitCommands)
159
160        # Get output from the console. This should contain both the
161        # "initCommands" and the "preRunCommands".
162        output = self.get_console()
163        # Verify all "initCommands" were found in console output
164        self.verify_commands('initCommands', output, initCommands)
165        # Verify all "preRunCommands" were found in console output
166        self.verify_commands('preRunCommands', output, preRunCommands)
167
168        functions = ['main']
169        breakpoint_ids = self.set_function_breakpoints(functions)
170        self.assertEquals(len(breakpoint_ids), len(functions),
171                        "expect one breakpoint")
172        self.continue_to_breakpoints(breakpoint_ids)
173        output = self.get_console(timeout=1.0)
174        self.verify_commands('stopCommands', output, stopCommands)
175
176        # Continue after launch and hit the "pause()" call and stop the target.
177        # Get output from the console. This should contain both the
178        # "stopCommands" that were run after we stop.
179        self.vscode.request_continue()
180        time.sleep(0.5)
181        self.vscode.request_pause()
182        self.vscode.wait_for_stopped()
183        output = self.get_console(timeout=1.0)
184        self.verify_commands('stopCommands', output, stopCommands)
185
186        # Continue until the program exits
187        self.continue_to_exit()
188        # Get output from the console. This should contain both the
189        # "exitCommands" that were run after the second breakpoint was hit
190        output = self.get_console(timeout=1.0)
191        self.verify_commands('exitCommands', output, exitCommands)
192