コード例 #1
0
ファイル: ide.py プロジェクト: Adespinoza/dotfiles
    def notify_of_subprocess(self, conn):
        with self.session:
            if self.start_request is None or conn in self._known_subprocesses:
                return
            if "processId" in self.start_request.arguments:
                log.warning(
                    "Not reporting subprocess for {0}, because the parent process "
                    'was attached to using "processId" rather than "port".',
                    self.session,
                )
                return

            log.info("Notifying {0} about {1}.", self, conn)
            body = dict(self.start_request.arguments)
            self._known_subprocesses.add(conn)

        body["name"] = fmt("Subprocess {0}", conn.pid)
        body["request"] = "attach"
        if "host" not in body:
            body["host"] = "127.0.0.1"
        if "port" not in body:
            _, body["port"] = self.listener.getsockname()
        if "processId" in body:
            del body["processId"]
        body["subProcessId"] = conn.pid

        self.channel.send_event("ptvsd_attach", body)
コード例 #2
0
def wait_until_port_is_listening(port, interval=1, max_attempts=1000):
    """Blocks until the specified TCP port on localhost is listening, and can be
    connected to.

    Tries to connect to the port periodically, and repeats until connection succeeds.
    Connection is immediately closed before returning.
    """

    for i in compat.xrange(1, max_attempts + 1):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            log.info("Probing localhost:{0} (attempt {1})...", port, i)
            sock.connect(("localhost", port))
        except socket.error as exc:
            # The first attempt will almost always fail, because the port isn't
            # open yet. But if it keeps failing after that, we want to know why.
            if i > 1:
                log.warning("Failed to connect to localhost:{0}:\n{1}", port,
                            exc)
            time.sleep(interval)
        else:
            log.info("localhost:{0} is listening - server is up!", port)
            return
        finally:
            sock.close()
コード例 #3
0
    def _finalize(self, why, terminate_debuggee):
        # If the IDE 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.exception()

        # Tell the IDE that debugging is over, but don't close the channel until it
        # tells us to, via the "disconnect" request.
        if self.ide and self.ide.is_connected:
            try:
                self.ide.channel.send_event("terminated")
            except Exception:
                pass
コード例 #4
0
ファイル: session.py プロジェクト: pengjianaixue/ptvsd
    def _finalize(self, why, terminate_debuggee):
        if self.server and self.server.is_connected:
            try:
                self.server.channel.request(
                    "disconnect", {"terminateDebuggee": terminate_debuggee}
                )
            except Exception:
                pass

            try:
                self.server.channel.close()
            except Exception:
                log.exception()

            # Wait until the server message queue fully drains - there won't be any
            # more events after close(), but there may still be pending responses.
            log.info("{0} waiting for {1} to disconnect...", self, self.server)
            if not self.wait_for(lambda: not self.server.is_connected, timeout=5):
                log.warning(
                    "{0} timed out waiting for {1} to disconnect.", self, self.server
                )

        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.
            try:
                self.launcher.channel.request("terminate")
            except Exception:
                pass

            # 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.exception()

        # Tell the IDE that debugging is over, but don't close the channel until it
        # tells us to, via the "disconnect" request.
        if self.ide and self.ide.is_connected:
            try:
                self.ide.channel.send_event("terminated")
            except Exception:
                pass
コード例 #5
0
def run_module():
    # Add current directory to path, like Python itself does for -m. This must
    # be in place before trying to use find_spec below to resolve submodules.
    sys.path.insert(0, "")

    # We want to do the same thing that run_module() would do here, without
    # actually invoking it. On Python 3, it's exposed as a public API, but
    # on Python 2, we have to invoke a private function in runpy for this.
    # Either way, if it fails to resolve for any reason, just leave argv as is.
    argv_0 = sys.argv[0]
    try:
        if sys.version_info >= (3,):
            from importlib.util import find_spec

            spec = find_spec(options.target)
            if spec is not None:
                argv_0 = spec.origin
        else:
            _, _, _, argv_0 = runpy._get_module_details(options.target)
    except Exception:
        log.exception("Error determining module path for sys.argv")

    setup_debug_server(argv_0)

    # On Python 2, module name must be a non-Unicode string, because it ends up
    # a part of module's __package__, and Python will refuse to run the module
    # if __package__ is Unicode.
    target = (
        compat.filename_bytes(options.target)
        if sys.version_info < (3,)
        else options.target
    )

    log.describe_environment("Pre-launch environment:")
    log.info("Running module {0!r}", target)

    # Docs say that runpy.run_module is equivalent to -m, but it's not actually
    # the case for packages - -m sets __name__ to "__main__", but run_module sets
    # it to "pkg.__main__". This breaks everything that uses the standard pattern
    # __name__ == "__main__" to detect being run as a CLI app. On the other hand,
    # runpy._run_module_as_main is a private function that actually implements -m.
    try:
        run_module_as_main = runpy._run_module_as_main
    except AttributeError:
        log.warning("runpy._run_module_as_main is missing, falling back to run_module.")
        runpy.run_module(target, alter_sys=True)
    else:
        run_module_as_main(target, alter_argv=True)
コード例 #6
0
    def __init__(self, session, connection):
        assert connection.server is None
        with session:
            assert not session.server
            super(Server, self).__init__(session, channel=connection.channel)

            self.connection = connection

            assert self.session.pid is None
            if self.session.launcher and self.session.launcher.pid != self.pid:
                log.warning(
                    "Launcher reported PID={0}, but server reported PID={1}",
                    self.session.pid,
                    self.pid,
                )
            self.session.pid = self.pid

            session.server = self
コード例 #7
0
    def __init__(self, category, fd, tee_fd, encoding):
        assert category not in self.instances
        self.instances[category] = self
        log.info("Capturing {0} of {1}.", category, debuggee.describe())

        self.category = category
        self._fd = fd
        self._tee_fd = tee_fd

        try:
            self._decoder = codecs.getincrementaldecoder(encoding)(
                errors="replace")
        except LookupError:
            self._decoder = None
            log.warning(
                'Unable to generate "output" events for {0} - unknown encoding {1!r}',
                category,
                encoding,
            )

        self._worker_thread = threading.Thread(target=self._worker,
                                               name=category)
        self._worker_thread.start()
コード例 #8
0
ファイル: worker.py プロジェクト: pengjianaixue/ptvsd
def main(tests_pid):
    # To import ptvsd, the "" entry in sys.path - which is added automatically on
    # Python 2 - must be removed first; otherwise, we end up importing tests/ptvsd.
    if "" in sys.path:
        sys.path.remove("")

    from ptvsd.common import fmt, log, messaging

    # log.stderr_levels |= {"info"}
    log.timestamp_format = "06.3f"
    log_file = log.to_file(prefix="tests.watchdog")

    stream = messaging.JsonIOStream.from_stdio(fmt("tests-{0}", tests_pid))
    log.info("Spawned WatchDog-{0} for tests-{0}", tests_pid)
    tests_process = psutil.Process(tests_pid)
    stream.write_json(["watchdog", log_file.filename])

    spawned_processes = {}  # pid -> ProcessInfo
    try:
        stop = False
        while not stop:
            try:
                message = stream.read_json()
            except Exception:
                break

            command = message[0]
            args = message[1:]

            if command == "stop":
                assert not args
                stop = True

            elif command == "register_spawn":
                pid, name = args
                pid = int(pid)

                log.info(
                    "WatchDog-{0} registering spawned process {1} (pid={2})",
                    tests_pid,
                    name,
                    pid,
                )
                try:
                    _, old_name = spawned_processes[pid]
                except KeyError:
                    pass
                else:
                    log.warning(
                        "WatchDog-{0} already tracks a process with pid={1}: {2}",
                        tests_pid,
                        pid,
                        old_name,
                    )
                spawned_processes[pid] = ProcessInfo(psutil.Process(pid), name)

            elif command == "unregister_spawn":
                pid, name = args
                pid = int(pid)

                log.info(
                    "WatchDog-{0} unregistering spawned process {1} (pid={2})",
                    tests_pid,
                    name,
                    pid,
                )
                spawned_processes.pop(pid, None)

            else:
                raise AssertionError(
                    fmt("Unknown watchdog command: {0!r}", command))

            stream.write_json(["ok"])

    except Exception as ex:
        stream.write_json(["error", str(ex)])
        raise log.exception()

    finally:
        try:
            stream.close()
        except Exception:
            log.exception()

        # If the test runner becomes a zombie process, it is still considered alive,
        # and wait() will block indefinitely. Poll status instead.
        while True:
            try:
                status = tests_process.status()
            except Exception:
                # If we can't even get its status, assume that it's dead.
                break

            # If it's dead or a zombie, time to clean it up.
            if status in (psutil.STATUS_DEAD, psutil.STATUS_ZOMBIE):
                break

            # Otherwise, let's wait a bit to see if anything changes.
            try:
                tests_process.wait(0.1)
            except Exception:
                pass

        leftover_processes = {proc for proc, _ in spawned_processes.values()}
        for proc, _ in spawned_processes.values():
            try:
                leftover_processes |= proc.children(recursive=True)
            except Exception:
                pass

        leftover_processes = {
            proc
            for proc in leftover_processes if proc.is_running()
        }
        if not leftover_processes:
            return

        # Wait a bit to allow the terminal to catch up on the test runner output.
        time.sleep(0.3)

        log.newline(level="warning")
        log.warning(
            "tests-{0} process terminated unexpectedly, and left some orphan child "
            "processes behind: {1!r}",
            tests_pid,
            sorted({proc.pid
                    for proc in leftover_processes}),
        )

        for proc in leftover_processes:
            log.warning(
                "WatchDog-{0} killing orphaned test child process (pid={1})",
                tests_pid,
                proc.pid,
            )

            if platform.system() == "Linux":
                try:
                    # gcore will automatically add pid to the filename
                    core_file = os.path.join(tempfile.gettempdir(),
                                             "ptvsd_core")
                    gcore_cmd = fmt("gcore -o {0} {1}", core_file, proc.pid)
                    log.warning("WatchDog-{0}: {1}", tests_pid, gcore_cmd)
                    os.system(gcore_cmd)
                except Exception:
                    log.exception()

            try:
                proc.kill()
            except psutil.NoSuchProcess:
                pass
            except Exception:
                log.exception()

        log.info("WatchDog-{0} exiting", tests_pid)