• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 time
13import os
14
15
16class TestVSCode_launch(lldbvscode_testcase.VSCodeTestCaseBase):
17
18    mydir = TestBase.compute_mydir(__file__)
19
20    @skipIfWindows
21    @skipIfDarwin # Flaky
22    @skipIfRemote
23    def test_default(self):
24        '''
25            Tests the default launch of a simple program. No arguments,
26            environment, or anything else is specified.
27        '''
28        program = self.getBuildArtifact("a.out")
29        self.build_and_launch(program)
30        self.continue_to_exit()
31        # Now get the STDOUT and verify our program argument is correct
32        output = self.get_stdout()
33        self.assertTrue(output and len(output) > 0,
34                        "expect program output")
35        lines = output.splitlines()
36        self.assertTrue(program in lines[0],
37                        "make sure program path is in first argument")
38
39    @skipIfWindows
40    @skipIfRemote
41    def test_termination(self):
42        '''
43            Tests the correct termination of lldb-vscode upon a 'disconnect'
44            request.
45        '''
46        self.create_debug_adaptor()
47        # The underlying lldb-vscode process must be alive
48        self.assertEqual(self.vscode.process.poll(), None)
49
50        # The lldb-vscode process should finish even though
51        # we didn't close the communication socket explicitly
52        self.vscode.request_disconnect()
53
54        # Wait until the underlying lldb-vscode process dies.
55        # We need to do this because the popen.wait function in python2.7
56        # doesn't have a timeout argument.
57        for _ in range(10):
58            time.sleep(1)
59            if self.vscode.process.poll() is not None:
60                break
61        # Check the return code
62        self.assertEqual(self.vscode.process.poll(), 0)
63
64    @skipIfWindows
65    @skipIfRemote
66    def test_stopOnEntry(self):
67        '''
68            Tests the default launch of a simple program that stops at the
69            entry point instead of continuing.
70        '''
71        program = self.getBuildArtifact("a.out")
72        self.build_and_launch(program, stopOnEntry=True)
73        self.set_function_breakpoints(['main'])
74        stopped_events = self.continue_to_next_stop()
75        for stopped_event in stopped_events:
76            if 'body' in stopped_event:
77                body = stopped_event['body']
78                if 'reason' in body:
79                    reason = body['reason']
80                    self.assertTrue(
81                        reason != 'breakpoint',
82                        'verify stop isn\'t "main" breakpoint')
83
84    @skipIfWindows
85    @skipIfRemote
86    def test_cwd(self):
87        '''
88            Tests the default launch of a simple program with a current working
89            directory.
90        '''
91        program = self.getBuildArtifact("a.out")
92        program_parent_dir = os.path.realpath(
93            os.path.dirname(os.path.dirname(program)))
94        self.build_and_launch(program,
95                              cwd=program_parent_dir)
96        self.continue_to_exit()
97        # Now get the STDOUT and verify our program argument is correct
98        output = self.get_stdout()
99        self.assertTrue(output and len(output) > 0,
100                        "expect program output")
101        lines = output.splitlines()
102        found = False
103        for line in lines:
104            if line.startswith('cwd = \"'):
105                quote_path = '"%s"' % (program_parent_dir)
106                found = True
107                self.assertTrue(quote_path in line,
108                                "working directory '%s' not in '%s'" % (
109                                    program_parent_dir, line))
110        self.assertTrue(found, "verified program working directory")
111
112    @skipIfWindows
113    @skipIfRemote
114    def test_debuggerRoot(self):
115        '''
116            Tests the "debuggerRoot" will change the working directory of
117            the lldb-vscode debug adaptor.
118        '''
119        program = self.getBuildArtifact("a.out")
120        program_parent_dir = os.path.realpath(
121            os.path.dirname(os.path.dirname(program)))
122        commands = ['platform shell echo cwd = $PWD']
123        self.build_and_launch(program,
124                              debuggerRoot=program_parent_dir,
125                              initCommands=commands)
126        output = self.get_console()
127        self.assertTrue(output and len(output) > 0,
128                        "expect console output")
129        lines = output.splitlines()
130        prefix = 'cwd = '
131        found = False
132        for line in lines:
133            if line.startswith(prefix):
134                found = True
135                self.assertEquals(program_parent_dir, line[len(prefix):],
136                                "lldb-vscode working dir '%s' == '%s'" % (
137                                    program_parent_dir, line[6:]))
138        self.assertTrue(found, "verified lldb-vscode working directory")
139        self.continue_to_exit()
140
141    @skipIfWindows
142    @skipIfRemote
143    def test_sourcePath(self):
144        '''
145            Tests the "sourcePath" will set the target.source-map.
146        '''
147        program = self.getBuildArtifact("a.out")
148        program_dir = os.path.dirname(program)
149        self.build_and_launch(program,
150                              sourcePath=program_dir)
151        output = self.get_console()
152        self.assertTrue(output and len(output) > 0,
153                        "expect console output")
154        lines = output.splitlines()
155        prefix = '(lldb) settings set target.source-map "." '
156        found = False
157        for line in lines:
158            if line.startswith(prefix):
159                found = True
160                quoted_path = '"%s"' % (program_dir)
161                self.assertEquals(quoted_path, line[len(prefix):],
162                                "lldb-vscode working dir %s == %s" % (
163                                    quoted_path, line[6:]))
164        self.assertTrue(found, 'found "sourcePath" in console output')
165        self.continue_to_exit()
166
167    @skipIfWindows
168    @skipIfRemote
169    def test_disableSTDIO(self):
170        '''
171            Tests the default launch of a simple program with STDIO disabled.
172        '''
173        program = self.getBuildArtifact("a.out")
174        self.build_and_launch(program,
175                              disableSTDIO=True)
176        self.continue_to_exit()
177        # Now get the STDOUT and verify our program argument is correct
178        output = self.get_stdout()
179        self.assertEquals(output, None,
180                        "expect no program output")
181
182    @skipIfWindows
183    @skipIfLinux # shell argument expansion doesn't seem to work on Linux
184    @expectedFailureAll(oslist=["freebsd", "netbsd"],
185                        bugnumber="llvm.org/pr48349")
186    @skipIfRemote
187    def test_shellExpandArguments_enabled(self):
188        '''
189            Tests the default launch of a simple program with shell expansion
190            enabled.
191        '''
192        program = self.getBuildArtifact("a.out")
193        program_dir = os.path.dirname(program)
194        glob = os.path.join(program_dir, '*.out')
195        self.build_and_launch(program, args=[glob], shellExpandArguments=True)
196        self.continue_to_exit()
197        # Now get the STDOUT and verify our program argument is correct
198        output = self.get_stdout()
199        self.assertTrue(output and len(output) > 0,
200                        "expect no program output")
201        lines = output.splitlines()
202        for line in lines:
203            quote_path = '"%s"' % (program)
204            if line.startswith("arg[1] ="):
205                self.assertTrue(quote_path in line,
206                                'verify "%s" expanded to "%s"' % (
207                                    glob, program))
208
209    @skipIfWindows
210    @skipIfRemote
211    def test_shellExpandArguments_disabled(self):
212        '''
213            Tests the default launch of a simple program with shell expansion
214            disabled.
215        '''
216        program = self.getBuildArtifact("a.out")
217        program_dir = os.path.dirname(program)
218        glob = os.path.join(program_dir, '*.out')
219        self.build_and_launch(program,
220                              args=[glob],
221                              shellExpandArguments=False)
222        self.continue_to_exit()
223        # Now get the STDOUT and verify our program argument is correct
224        output = self.get_stdout()
225        self.assertTrue(output and len(output) > 0,
226                        "expect no program output")
227        lines = output.splitlines()
228        for line in lines:
229            quote_path = '"%s"' % (glob)
230            if line.startswith("arg[1] ="):
231                self.assertTrue(quote_path in line,
232                                'verify "%s" stayed to "%s"' % (
233                                    glob, glob))
234
235    @skipIfWindows
236    @skipIfRemote
237    def test_args(self):
238        '''
239            Tests launch of a simple program with arguments
240        '''
241        program = self.getBuildArtifact("a.out")
242        args = ["one", "with space", "'with single quotes'",
243                '"with double quotes"']
244        self.build_and_launch(program,
245                              args=args)
246        self.continue_to_exit()
247
248        # Now get the STDOUT and verify our arguments got passed correctly
249        output = self.get_stdout()
250        self.assertTrue(output and len(output) > 0,
251                        "expect program output")
252        lines = output.splitlines()
253        # Skip the first argument that contains the program name
254        lines.pop(0)
255        # Make sure arguments we specified are correct
256        for (i, arg) in enumerate(args):
257            quoted_arg = '"%s"' % (arg)
258            self.assertTrue(quoted_arg in lines[i],
259                            'arg[%i] "%s" not in "%s"' % (i+1, quoted_arg, lines[i]))
260
261    @skipIfWindows
262    @skipIfRemote
263    def test_environment(self):
264        '''
265            Tests launch of a simple program with environment variables
266        '''
267        program = self.getBuildArtifact("a.out")
268        env = ["NO_VALUE", "WITH_VALUE=BAR", "EMPTY_VALUE=",
269               "SPACE=Hello World"]
270        self.build_and_launch(program,
271                              env=env)
272        self.continue_to_exit()
273
274        # Now get the STDOUT and verify our arguments got passed correctly
275        output = self.get_stdout()
276        self.assertTrue(output and len(output) > 0,
277                        "expect program output")
278        lines = output.splitlines()
279        # Skip the all arguments so we have only environment vars left
280        while len(lines) and lines[0].startswith("arg["):
281            lines.pop(0)
282        # Make sure each environment variable in "env" is actually set in the
283        # program environment that was printed to STDOUT
284        for var in env:
285            found = False
286            for program_var in lines:
287                if var in program_var:
288                    found = True
289                    break
290            self.assertTrue(found,
291                            '"%s" must exist in program environment (%s)' % (
292                                var, lines))
293
294    @skipIfWindows
295    @skipIfRemote
296    def test_commands(self):
297        '''
298            Tests the "initCommands", "preRunCommands", "stopCommands",
299            "terminateCommands" and "exitCommands" that can be passed during
300            launch.
301
302            "initCommands" are a list of LLDB commands that get executed
303            before the targt is created.
304            "preRunCommands" are a list of LLDB commands that get executed
305            after the target has been created and before the launch.
306            "stopCommands" are a list of LLDB commands that get executed each
307            time the program stops.
308            "exitCommands" are a list of LLDB commands that get executed when
309            the process exits
310            "terminateCommands" are a list of LLDB commands that get executed when
311            the debugger session terminates.
312        '''
313        program = self.getBuildArtifact("a.out")
314        initCommands = ['target list', 'platform list']
315        preRunCommands = ['image list a.out', 'image dump sections a.out']
316        stopCommands = ['frame variable', 'bt']
317        exitCommands = ['expr 2+3', 'expr 3+4']
318        terminateCommands = ['expr 4+2']
319        self.build_and_launch(program,
320                              initCommands=initCommands,
321                              preRunCommands=preRunCommands,
322                              stopCommands=stopCommands,
323                              exitCommands=exitCommands,
324                              terminateCommands=terminateCommands)
325
326        # Get output from the console. This should contain both the
327        # "initCommands" and the "preRunCommands".
328        output = self.get_console()
329        # Verify all "initCommands" were found in console output
330        self.verify_commands('initCommands', output, initCommands)
331        # Verify all "preRunCommands" were found in console output
332        self.verify_commands('preRunCommands', output, preRunCommands)
333
334        source = 'main.c'
335        first_line = line_number(source, '// breakpoint 1')
336        second_line = line_number(source, '// breakpoint 2')
337        lines = [first_line, second_line]
338
339        # Set 2 breakpoints so we can verify that "stopCommands" get run as the
340        # breakpoints get hit
341        breakpoint_ids = self.set_source_breakpoints(source, lines)
342        self.assertEquals(len(breakpoint_ids), len(lines),
343                        "expect correct number of breakpoints")
344
345        # Continue after launch and hit the first breakpoint.
346        # Get output from the console. This should contain both the
347        # "stopCommands" that were run after the first breakpoint was hit
348        self.continue_to_breakpoints(breakpoint_ids)
349        output = self.get_console(timeout=1.0)
350        self.verify_commands('stopCommands', output, stopCommands)
351
352        # Continue again and hit the second breakpoint.
353        # Get output from the console. This should contain both the
354        # "stopCommands" that were run after the second breakpoint was hit
355        self.continue_to_breakpoints(breakpoint_ids)
356        output = self.get_console(timeout=1.0)
357        self.verify_commands('stopCommands', output, stopCommands)
358
359        # Continue until the program exits
360        self.continue_to_exit()
361        # Get output from the console. This should contain both the
362        # "exitCommands" that were run after the second breakpoint was hit
363        # and the "terminateCommands" due to the debugging session ending
364        output = self.collect_console(duration=1.0)
365        self.verify_commands('exitCommands', output, exitCommands)
366        self.verify_commands('terminateCommands', output, terminateCommands)
367
368    @skipIfWindows
369    @skipIfRemote
370    def test_extra_launch_commands(self):
371        '''
372            Tests the "luanchCommands" with extra launching settings
373        '''
374        self.build_and_create_debug_adaptor()
375        program = self.getBuildArtifact("a.out")
376
377        source = 'main.c'
378        first_line = line_number(source, '// breakpoint 1')
379        second_line = line_number(source, '// breakpoint 2')
380        # Set target binary and 2 breakpoints
381        # then we can varify the "launchCommands" get run
382        # also we can verify that "stopCommands" get run as the
383        # breakpoints get hit
384        launchCommands = [
385            'target create "%s"' % (program),
386            'br s -f main.c -l %d' % first_line,
387            'br s -f main.c -l %d' % second_line,
388            'process launch --stop-at-entry'
389        ]
390
391        initCommands = ['target list', 'platform list']
392        preRunCommands = ['image list a.out', 'image dump sections a.out']
393        stopCommands = ['frame variable', 'bt']
394        exitCommands = ['expr 2+3', 'expr 3+4']
395        self.launch(program,
396                    initCommands=initCommands,
397                    preRunCommands=preRunCommands,
398                    stopCommands=stopCommands,
399                    exitCommands=exitCommands,
400                    launchCommands=launchCommands)
401
402        # Get output from the console. This should contain both the
403        # "initCommands" and the "preRunCommands".
404        output = self.get_console()
405        # Verify all "initCommands" were found in console output
406        self.verify_commands('initCommands', output, initCommands)
407        # Verify all "preRunCommands" were found in console output
408        self.verify_commands('preRunCommands', output, preRunCommands)
409
410        # Verify all "launchCommands" were founc in console output
411        # After execution, program should launch
412        self.verify_commands('launchCommands', output, launchCommands)
413        # Verify the "stopCommands" here
414        self.continue_to_next_stop()
415        output = self.get_console(timeout=1.0)
416        self.verify_commands('stopCommands', output, stopCommands)
417
418        # Continue and hit the second breakpoint.
419        # Get output from the console. This should contain both the
420        # "stopCommands" that were run after the first breakpoint was hit
421        self.continue_to_next_stop()
422        output = self.get_console(timeout=1.0)
423        self.verify_commands('stopCommands', output, stopCommands)
424
425        # Continue until the program exits
426        self.continue_to_exit()
427        # Get output from the console. This should contain both the
428        # "exitCommands" that were run after the second breakpoint was hit
429        output = self.get_console(timeout=1.0)
430        self.verify_commands('exitCommands', output, exitCommands)
431
432    @skipIfWindows
433    @skipIfNetBSD # Hangs on NetBSD as well
434    @skipIfDarwin
435    @skipIf(archs=["arm", "aarch64"]) # Example of a flaky run http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/5540/steps/test/logs/stdio
436    def test_terminate_commands(self):
437        '''
438            Tests that the "terminateCommands", that can be passed during
439            launch, are run when the debugger is disconnected.
440        '''
441        self.build_and_create_debug_adaptor()
442        program = self.getBuildArtifact("a.out")
443
444        terminateCommands = ['expr 4+2']
445        self.launch(program=program,
446                    terminateCommands=terminateCommands)
447        self.get_console()
448        # Once it's disconnected the console should contain the
449        # "terminateCommands"
450        self.vscode.request_disconnect(terminateDebuggee=True)
451        output = self.collect_console(duration=1.0)
452        self.verify_commands('terminateCommands', output, terminateCommands)
453