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' 149 ] 150 initCommands = ['target list', 'platform list'] 151 preRunCommands = ['image list a.out', 'image dump sections a.out'] 152 stopCommands = ['frame variable', 'bt'] 153 exitCommands = ['expr 2+3', 'expr 3+4'] 154 terminateCommands = ['expr 4+2'] 155 self.attach(program=program, 156 attachCommands=attachCommands, 157 initCommands=initCommands, 158 preRunCommands=preRunCommands, 159 stopCommands=stopCommands, 160 exitCommands=exitCommands, 161 terminateCommands=terminateCommands) 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 170 functions = ['main'] 171 breakpoint_ids = self.set_function_breakpoints(functions) 172 self.assertEquals(len(breakpoint_ids), len(functions), 173 "expect one breakpoint") 174 self.continue_to_breakpoints(breakpoint_ids) 175 output = self.get_console(timeout=1.0) 176 self.verify_commands('stopCommands', output, stopCommands) 177 178 # Continue after launch and hit the "pause()" call and stop the target. 179 # Get output from the console. This should contain both the 180 # "stopCommands" that were run after we stop. 181 self.vscode.request_continue() 182 time.sleep(0.5) 183 self.vscode.request_pause() 184 self.vscode.wait_for_stopped() 185 output = self.get_console(timeout=1.0) 186 self.verify_commands('stopCommands', output, stopCommands) 187 188 # Continue until the program exits 189 self.continue_to_exit() 190 # Get output from the console. This should contain both the 191 # "exitCommands" that were run after the second breakpoint was hit 192 # and the "terminateCommands" due to the debugging session ending 193 output = self.collect_console(duration=1.0) 194 self.verify_commands('exitCommands', output, exitCommands) 195 self.verify_commands('terminateCommands', output, terminateCommands) 196 197 @skipIfWindows 198 @skipIfDarwin 199 @skipIfNetBSD # Hangs on NetBSD as well 200 @skipIf(archs=["arm", "aarch64"]) # Example of a flaky run http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/5517/steps/test/logs/stdio 201 def test_terminate_commands(self): 202 ''' 203 Tests that the "terminateCommands", that can be passed during 204 attach, are run when the debugger is disconnected. 205 ''' 206 self.build_and_create_debug_adaptor() 207 program = self.getBuildArtifact("a.out") 208 # Here we just create a target and launch the process as a way to test 209 # if we are able to use attach commands to create any kind of a target 210 # and use it for debugging 211 attachCommands = [ 212 'target create -d "%s"' % (program), 213 'process launch' 214 ] 215 terminateCommands = ['expr 4+2'] 216 self.attach(program=program, 217 attachCommands=attachCommands, 218 terminateCommands=terminateCommands, 219 disconnectAutomatically=False) 220 self.get_console() 221 # Once it's disconnected the console should contain the 222 # "terminateCommands" 223 self.vscode.request_disconnect(terminateDebuggee=True) 224 output = self.collect_console(duration=1.0) 225 self.verify_commands('terminateCommands', output, terminateCommands) 226