|
4 | 4 |
|
5 | 5 |
|
6 | 6 | import os
|
| 7 | +import threading |
| 8 | +import time |
7 | 9 | import lldb
|
8 | 10 | import shutil
|
9 | 11 | from lldbsuite.test.decorators import *
|
@@ -128,3 +130,84 @@ def tearDown(self):
|
128 | 130 |
|
129 | 131 | # Call super's tearDown().
|
130 | 132 | TestBase.tearDown(self)
|
| 133 | + |
| 134 | + def test_run_then_attach_wait_interrupt(self): |
| 135 | + # Test that having run one process doesn't cause us to be unable |
| 136 | + # to interrupt a subsequent attach attempt. |
| 137 | + self.build() |
| 138 | + exe = self.getBuildArtifact(exe_name) |
| 139 | + |
| 140 | + target = lldbutil.run_to_breakpoint_make_target(self, exe_name, True) |
| 141 | + launch_info = target.GetLaunchInfo() |
| 142 | + launch_info.SetArguments(["q"], True) |
| 143 | + error = lldb.SBError() |
| 144 | + target.Launch(launch_info, error) |
| 145 | + self.assertSuccess(error, "Launched a process") |
| 146 | + self.assertState(target.process.state, lldb.eStateExited, "and it exited.") |
| 147 | + |
| 148 | + # Okay now we've run a process, try to attach/wait to something |
| 149 | + # and make sure that we can interrupt that. |
| 150 | + |
| 151 | + options = lldb.SBCommandInterpreterRunOptions() |
| 152 | + options.SetPrintResults(True) |
| 153 | + options.SetEchoCommands(False) |
| 154 | + |
| 155 | + self.stdin_path = self.getBuildArtifact("stdin.txt") |
| 156 | + |
| 157 | + with open(self.stdin_path, "w") as input_handle: |
| 158 | + input_handle.write("process attach -w -n noone_would_use_this_name\nquit") |
| 159 | + |
| 160 | + # Python will close the file descriptor if all references |
| 161 | + # to the filehandle object lapse, so we need to keep one |
| 162 | + # around. |
| 163 | + self.filehandle = open(self.stdin_path, "r") |
| 164 | + self.dbg.SetInputFileHandle(self.filehandle, False) |
| 165 | + |
| 166 | + # No need to track the output |
| 167 | + self.stdout_path = self.getBuildArtifact("stdout.txt") |
| 168 | + self.out_filehandle = open(self.stdout_path, "w") |
| 169 | + self.dbg.SetOutputFileHandle(self.out_filehandle, False) |
| 170 | + self.dbg.SetErrorFileHandle(self.out_filehandle, False) |
| 171 | + |
| 172 | + n_errors, quit_req, crashed = self.dbg.RunCommandInterpreter( |
| 173 | + True, True, options, 0, False, False) |
| 174 | + |
| 175 | + while 1: |
| 176 | + time.sleep(1) |
| 177 | + if target.process.state == lldb.eStateAttaching: |
| 178 | + break |
| 179 | + |
| 180 | + self.dbg.DispatchInputInterrupt() |
| 181 | + self.dbg.DispatchInputInterrupt() |
| 182 | + |
| 183 | + # cycle waiting for the process state to change before trying |
| 184 | + # to read the command output. I don't want to spin forever. |
| 185 | + counter = 0 |
| 186 | + got_exit = False |
| 187 | + while counter < 20: |
| 188 | + if target.process.state == lldb.eStateExited: |
| 189 | + got_exit = True |
| 190 | + break |
| 191 | + counter += 1 |
| 192 | + time.sleep(1) |
| 193 | + |
| 194 | + self.assertTrue(got_exit, "The process never switched to eStateExited") |
| 195 | + # Even if the state has flipped, we still need to wait for the |
| 196 | + # command to complete to see the result. We don't have a way to |
| 197 | + # synchronize on "command completed" right now, but sleeping just |
| 198 | + # a bit should be enough, all that's left is passing this error |
| 199 | + # result to the command, and printing it to the debugger output. |
| 200 | + time.sleep(1) |
| 201 | + |
| 202 | + self.out_filehandle.flush() |
| 203 | + reader = open(self.stdout_path, "r") |
| 204 | + results = reader.readlines() |
| 205 | + found_result = False |
| 206 | + for line in results: |
| 207 | + if "Cancelled async attach" in line: |
| 208 | + found_result = True |
| 209 | + break |
| 210 | + if not found_result: |
| 211 | + print(f"Results: {results}") |
| 212 | + |
| 213 | + self.assertTrue(found_result, "Found async error in results") |
0 commit comments