Esempio n. 1
0
    def _finalize(self, why, terminate_debuggee):
        # If the client started a session, and then disconnected before issuing "launch"
        # or "attach", the main thread will be blocked waiting for the first server
        # connection to come in - unblock it, so that we can exit.
        servers.dont_wait_for_first_connection()

        if self.server:
            if self.server.is_connected:
                if terminate_debuggee and self.launcher and self.launcher.is_connected:
                    # If we were specifically asked to terminate the debuggee, and we
                    # can ask the launcher to kill it, do so instead of disconnecting
                    # from the server to prevent debuggee from running any more code.
                    self.launcher.terminate_debuggee()
                else:
                    # Otherwise, let the server handle it the best it can.
                    try:
                        self.server.channel.request(
                            "disconnect",
                            {"terminateDebuggee": terminate_debuggee})
                    except Exception:
                        pass
            self.server.detach_from_session()

        if self.launcher and self.launcher.is_connected:
            # If there was a server, we just disconnected from it above, which should
            # cause the debuggee process to exit - so let's wait for that first.
            if self.server:
                log.info('{0} waiting for "exited" event...', self)
                if not self.wait_for(
                        lambda: self.launcher.exit_code is not None,
                        timeout=5):
                    log.warning('{0} timed out waiting for "exited" event.',
                                self)

            # Terminate the debuggee process if it's still alive for any reason -
            # whether it's because there was no server to handle graceful shutdown,
            # or because the server couldn't handle it for some reason.
            self.launcher.terminate_debuggee()

            # Wait until the launcher message queue fully drains. There is no timeout
            # here, because the final "terminated" event will only come after reading
            # user input in wait-on-exit scenarios.
            log.info("{0} waiting for {1} to disconnect...", self,
                     self.launcher)
            self.wait_for(lambda: not self.launcher.is_connected)

            try:
                self.launcher.channel.close()
            except Exception:
                log.swallow_exception()

        if self.client:
            if self.client.is_connected:
                # Tell the client that debugging is over, but don't close the channel until it
                # tells us to, via the "disconnect" request.
                try:
                    self.client.channel.send_event("terminated")
                except Exception:
                    pass

            if (self.client.start_request is not None
                    and self.client.start_request.command == "launch"):
                servers.stop_serving()
                log.info(
                    '"launch" session ended - killing remaining debuggee processes.'
                )

                pids_killed = set()
                if self.launcher and self.launcher.pid is not None:
                    # Already killed above.
                    pids_killed.add(self.launcher.pid)

                while True:
                    conns = [
                        conn for conn in servers.connections()
                        if conn.pid not in pids_killed
                    ]
                    if not len(conns):
                        break
                    for conn in conns:
                        log.info("Killing {0}", conn)
                        try:
                            os.kill(conn.pid, signal.SIGTERM)
                        except Exception:
                            log.swallow_exception("Failed to kill {0}", conn)
                        pids_killed.add(conn.pid)
Esempio n. 2
0
        def handle(self, request):
            assert request.is_request("launch", "attach")
            if self._initialize_request is None:
                raise request.isnt_valid("Session is not initialized yet")
            if self.launcher or self.server:
                raise request.isnt_valid("Session is already started")

            self.session.no_debug = request("noDebug", json.default(False))
            if self.session.no_debug:
                servers.dont_wait_for_first_connection()

            self.session.debug_options = debug_options = set(
                request("debugOptions", json.array(unicode)))

            f(self, request)

            if self.server:
                self.server.initialize(self._initialize_request)
                self._initialize_request = None

                arguments = request.arguments
                if self.launcher:
                    if "RedirectOutput" in debug_options:
                        # The launcher is doing output redirection, so we don't need the
                        # server to do it, as well.
                        arguments = dict(arguments)
                        arguments["debugOptions"] = list(debug_options -
                                                         {"RedirectOutput"})

                    if arguments.get("redirectOutput"):
                        arguments = dict(arguments)
                        del arguments["redirectOutput"]

                # pydevd doesn't send "initialized", and responds to the start request
                # immediately, without waiting for "configurationDone". If it changes
                # to conform to the DAP spec, we'll need to defer waiting for response.
                try:
                    self.server.channel.request(request.command, arguments)
                except messaging.MessageHandlingError as exc:
                    exc.propagate(request)

            if self.session.no_debug:
                self.start_request = request
                self.has_started = True
                request.respond({})
                self._propagate_deferred_events()
                return

            if {"WindowsClient", "Windows"} & debug_options:
                client_os_type = "WINDOWS"
            elif {"UnixClient", "UNIX"} & debug_options:
                client_os_type = "UNIX"
            else:
                client_os_type = "WINDOWS" if sys.platform == "win32" else "UNIX"
            self.server.channel.request(
                "setDebuggerProperty",
                {
                    "skipSuspendOnBreakpointException": ("BaseException", ),
                    "skipPrintBreakpointException": ("NameError", ),
                    "multiThreadsSingleNotification": True,
                    "ideOS": client_os_type,
                },
            )

            # Let the client know that it can begin configuring the adapter.
            self.channel.send_event("initialized")

            self.start_request = request
            return messaging.NO_RESPONSE  # will respond on "configurationDone"
Esempio n. 3
0
        def handle(self, request):
            assert request.is_request("launch", "attach")
            if self._initialize_request is None:
                raise request.isnt_valid("Session is not initialized yet")
            if self.launcher or self.server:
                raise request.isnt_valid("Session is already started")

            self.session.no_debug = request("noDebug", json.default(False))
            if self.session.no_debug:
                servers.dont_wait_for_first_connection()

            self.session.debug_options = debug_options = set(
                request("debugOptions", json.array(unicode))
            )

            f(self, request)
            if request.response is not None:
                return

            if self.server:
                self.server.initialize(self._initialize_request)
                self._initialize_request = None

                arguments = request.arguments
                if self.launcher:
                    redirecting = arguments.get("console") == "internalConsole"
                    if "RedirectOutput" in debug_options:
                        # The launcher is doing output redirection, so we don't need the
                        # server to do it, as well.
                        arguments = dict(arguments)
                        arguments["debugOptions"] = list(
                            debug_options - {"RedirectOutput"}
                        )
                        redirecting = True

                    if arguments.get("redirectOutput"):
                        arguments = dict(arguments)
                        del arguments["redirectOutput"]
                        redirecting = True

                    arguments["isOutputRedirected"] = redirecting

                # pydevd doesn't send "initialized", and responds to the start request
                # immediately, without waiting for "configurationDone". If it changes
                # to conform to the DAP spec, we'll need to defer waiting for response.
                try:
                    self.server.channel.request(request.command, arguments)
                except messaging.NoMoreMessages:
                    # Server closed connection before we could receive the response to
                    # "attach" or "launch" - this can happen when debuggee exits shortly
                    # after starting. It's not an error, but we can't do anything useful
                    # here at this point, either, so just bail out.
                    request.respond({})
                    self.session.finalize(
                        fmt(
                            "{0} disconnected before responding to {1!j}",
                            self.server,
                            request.command,
                        )
                    )
                    return
                except messaging.MessageHandlingError as exc:
                    exc.propagate(request)

            if self.session.no_debug:
                self.start_request = request
                self.has_started = True
                request.respond({})
                self._propagate_deferred_events()
                return

            if "clientOS" in request:
                client_os = request("clientOS", json.enum("windows", "unix")).upper()
            elif {"WindowsClient", "Windows"} & debug_options:
                client_os = "WINDOWS"
            elif {"UnixClient", "UNIX"} & debug_options:
                client_os = "UNIX"
            else:
                client_os = "WINDOWS" if sys.platform == "win32" else "UNIX"
            self.server.channel.request(
                "setDebuggerProperty",
                {
                    "skipSuspendOnBreakpointException": ("BaseException",),
                    "skipPrintBreakpointException": ("NameError",),
                    "multiThreadsSingleNotification": True,
                    "ideOS": client_os,
                },
            )

            # Let the client know that it can begin configuring the adapter.
            self.channel.send_event("initialized")

            self.start_request = request
            return messaging.NO_RESPONSE  # will respond on "configurationDone"