(self, why, terminate_debuggee)
| 168 | log.info("{0} finalized.", self) |
| 169 | |
| 170 | def _finalize(self, why, terminate_debuggee): |
| 171 | # If the client started a session, and then disconnected before issuing "launch" |
| 172 | # or "attach", the main thread will be blocked waiting for the first server |
| 173 | # connection to come in - unblock it, so that we can exit. |
| 174 | servers.dont_wait_for_first_connection() |
| 175 | |
| 176 | if self.server: |
| 177 | if self.server.is_connected: |
| 178 | if terminate_debuggee and self.launcher and self.launcher.is_connected: |
| 179 | # If we were specifically asked to terminate the debuggee, and we |
| 180 | # can ask the launcher to kill it, do so instead of disconnecting |
| 181 | # from the server to prevent debuggee from running any more code. |
| 182 | self.launcher.terminate_debuggee() |
| 183 | else: |
| 184 | # Otherwise, let the server handle it the best it can. |
| 185 | try: |
| 186 | self.server.channel.request( |
| 187 | "disconnect", {"terminateDebuggee": terminate_debuggee} |
| 188 | ) |
| 189 | except Exception: |
| 190 | pass |
| 191 | self.server.detach_from_session() |
| 192 | |
| 193 | if self.launcher and self.launcher.is_connected: |
| 194 | # If there was a server, we just disconnected from it above, which should |
| 195 | # cause the debuggee process to exit, unless it is being replaced in situ - |
| 196 | # so let's wait for that first. |
| 197 | if self.server and not self.server.connection.process_replaced: |
| 198 | log.info('{0} waiting for "exited" event...', self) |
| 199 | if not self.wait_for( |
| 200 | lambda: self.launcher.exit_code is not None, |
| 201 | timeout=common.PROCESS_EXIT_TIMEOUT, |
| 202 | ): |
| 203 | log.warning('{0} timed out waiting for "exited" event.', self) |
| 204 | |
| 205 | # Terminate the debuggee process if it's still alive for any reason - |
| 206 | # whether it's because there was no server to handle graceful shutdown, |
| 207 | # or because the server couldn't handle it for some reason - unless the |
| 208 | # process is being replaced in situ. |
| 209 | if not (self.server and self.server.connection.process_replaced): |
| 210 | self.launcher.terminate_debuggee() |
| 211 | |
| 212 | # Wait until the launcher message queue fully drains. There is no timeout |
| 213 | # here, because the final "terminated" event will only come after reading |
| 214 | # user input in wait-on-exit scenarios. In addition, if the process was |
| 215 | # replaced in situ, the launcher might still have more output to capture |
| 216 | # from its replacement. |
| 217 | log.info("{0} waiting for {1} to disconnect...", self, self.launcher) |
| 218 | self.wait_for(lambda: not self.launcher.is_connected) |
| 219 | |
| 220 | try: |
| 221 | self.launcher.channel.close() |
| 222 | except Exception: |
| 223 | log.swallow_exception() |
| 224 | |
| 225 | if self.client: |
| 226 | if self.client.is_connected: |
| 227 | # Tell the client that debugging is over, but don't close the channel until it |
no test coverage detected