def finalize(self, why, terminate_debuggee=False):
        """Finalizes the debug session.

        If the server is present, sends "disconnect" request with "terminateDebuggee"
        set as specified) request to it; waits for it to disconnect, allowing any
        remaining messages from it to be handled; and closes the server channel.

        If the launcher is present, sends "terminate" request to it, regardless of the
        value of terminate; waits for it to disconnect, allowing any remaining messages
        from it to be handled; and closes the launcher channel.

        If the IDE is present, sends "terminated" event to it.
        """

        if self.is_finalizing:
            return
        self.is_finalizing = True
        log.info("{0}; finalizing {1}.", why, self)

        try:
            self._finalize(why, terminate_debuggee)
        except Exception:
            # Finalization should never fail, and if it does, the session is in an
            # indeterminate and likely unrecoverable state, so just fail fast.
            log.exception("Fatal error while finalizing {0}", self)
            os._exit(1)

        log.info("{0} finalized.", self)
示例#2
0
        def accept_worker():
            log.info(
                "Listening for incoming connection from {0} on port {1}...",
                self,
                self.port,
            )

            server_socket = self._server_socket
            if server_socket is None:
                return  # concurrent close()

            try:
                sock, _ = server_socket.accept()
            except socket.timeout:
                if self._server_socket is None:
                    return
                else:
                    raise log.exception("Timed out waiting for {0} to connect",
                                        self)
            except Exception:
                if self._server_socket is None:
                    return
                else:
                    raise log.exception("Error accepting connection for {0}:",
                                        self)

            sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
            log.info("Incoming connection from {0} accepted.", self)

            self._socket = sock
            self._setup_stream()
示例#3
0
def wait_for_exit():
    try:
        code = process.wait()
        if sys.platform != "win32" and code < 0:
            # On POSIX, if the process was terminated by a signal, Popen will use
            # a negative returncode to indicate that - but the actual exit code of
            # the process is always an unsigned number, and can be determined by
            # taking the lowest 8 bits of that negative returncode.
            code &= 0xFF
    except Exception:
        log.exception("Couldn't determine process exit code:")
        code = -1

    log.info("{0} exited with code {1}", describe(), code)
    output.wait_for_remaining_output()

    # Determine whether we should wait or not before sending "exited", so that any
    # follow-up "terminate" requests don't affect the predicates.
    should_wait = any(pred(code) for pred in wait_on_exit_predicates)

    try:
        launcher.channel.send_event("exited", {"exitCode": code})
    except Exception:
        pass

    if should_wait:
        _wait_for_user_input()

    try:
        launcher.channel.send_event("terminated")
    except Exception:
        pass
示例#4
0
def main():
    original_argv = list(sys.argv)
    try:
        sys.argv[1:] = parse(sys.argv[1:])
    except Exception as ex:
        print(HELP + "\nError: " + str(ex), file=sys.stderr)
        sys.exit(2)

    log.to_file(prefix="ptvsd.server")
    log.describe_environment("ptvsd.server startup environment:")
    log.info(
        "sys.argv before parsing: {0!r}\n"
        "         after parsing:  {1!r}",
        original_argv,
        sys.argv,
    )

    try:
        run = {
            "file": run_file,
            "module": run_module,
            "code": run_code,
            "pid": attach_to_pid,
        }[options.target_kind]
        run()
    except SystemExit as ex:
        log.exception("Debuggee exited via SystemExit: {0!r}",
                      ex.code,
                      level="debug")
        raise
示例#5
0
def inject(pid, ptvsd_args):
    host, port = Connection.listener.getsockname()

    cmdline = [
        sys.executable,
        compat.filename(os.path.dirname(ptvsd.__file__)),
        "--client",
        "--host",
        host,
        "--port",
        str(port),
    ]
    cmdline += ptvsd_args
    cmdline += ["--pid", str(pid)]

    log.info("Spawning attach-to-PID debugger injector: {0!r}", cmdline)
    try:
        subprocess.Popen(
            cmdline,
            bufsize=0,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
    except Exception as exc:
        log.exception(
            "Failed to inject debug server into process with PID={0}", pid)
        raise messaging.MessageHandlingError(
            "Failed to inject debug server into process with PID={0}: {1}",
            pid, exc)
    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.
            log.info("{0} waiting for {1} to disconnect...", self,
                     self.launcher)
            if not self.wait_for(lambda: not self.launcher.is_connected,
                                 timeout=5):
                log.warning("{0} timed out waiting for {1} to disconnect.",
                            self, self.launcher)

            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
    def inject_server(self, pid, ptvsd_args):
        with self.accept_connection_from_server() as (host, port):
            cmdline = [
                sys.executable,
                compat.filename(os.path.dirname(ptvsd.__file__)),
                "--client",
                "--host",
                host,
                "--port",
                str(port),
            ]
            cmdline += ptvsd_args
            cmdline += ["--pid", str(pid)]

            log.info("{0} spawning attach-to-PID debugger injector: {1!r}",
                     self, cmdline)

            try:
                subprocess.Popen(
                    cmdline,
                    bufsize=0,
                    stdin=subprocess.PIPE,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                )
            except Exception as exc:
                log.exception("{0} failed to inject debugger", self)
                raise messaging.MessageHandlingError(
                    fmt("Failed to inject debugger: {0}", exc))
示例#8
0
def wait_for_exit():
    from ptvsd.launcher import adapter, output

    try:
        code = process.wait()
        if platform.system() != "Windows" and code < 0:
            # On POSIX, if the process was terminated by a signal, Popen will use
            # a negative returncode to indicate that - but the actual exit code of
            # the process is always an unsigned number, and can be determined by
            # taking the lowest 8 bits of that negative returncode.
            code &= 0xFF
    except Exception:
        log.exception("Couldn't determine process exit code:")
        code = -1

    log.info("{0} exited with code {1}", describe(), code)
    output.wait_for_remaining_output()
    try:
        adapter.channel.send_event("exited", {"exitCode": code})
    except Exception:
        pass

    if any(pred(code) for pred in wait_on_exit_predicates):
        _wait_for_user_input()

    try:
        adapter.channel.send_event("terminated")
    except Exception:
        pass
示例#9
0
文件: session.py 项目: yazici/ptvsd
    def __exit__(self, exc_type, exc_val, exc_tb):
        log.info("Ending {0}.", self)

        if self.timeline.is_frozen:
            self.timeline.unfreeze()

        # Only wait for exit if there was no exception in the test - if there was one,
        # the debuggee might still be waiting for further requests.
        if exc_type is None:
            # If expected_exit_code is set to None, the debuggee is not expected to
            # exit after this Session is closed (e.g. because another Session will
            # attach to it later on).
            if self.expected_exit_code is not None:
                self.wait_for_exit()
        else:
            # Log the error, in case another one happens during shutdown.
            log.exception(exc_info=(exc_type, exc_val, exc_tb))

        if exc_type is None:
            self.disconnect()
            self.timeline.close()
        else:
            # If there was an exception, don't try to send any more messages to avoid
            # spamming log with irrelevant entries - just close the channel and kill
            # all the processes immediately. Don't close or finalize the timeline,
            # either, since it'll likely have unobserved events in it.
            if self.adapter is not None:
                log.info("Killing {0}.", self.adapter_id)
                try:
                    self.adapter.kill()
                except Exception:
                    pass
            if self.debuggee is not None:
                log.info("Killing {0}.", self.debuggee_id)
                try:
                    self.debuggee.kill()
                except Exception:
                    pass
            self.disconnect(force=True)

        if self.adapter_endpoints is not None and self.expected_exit_code is not None:
            log.info("Waiting for {0} to close listener ports ...",
                     self.adapter_id)
            while self.adapter_endpoints.check():
                time.sleep(0.1)

        if self.adapter is not None:
            log.info(
                "Waiting for {0} with PID={1} to exit.",
                self.adapter_id,
                self.adapter.pid,
            )
            self.adapter.wait()
            watchdog.unregister_spawn(self.adapter.pid, self.adapter_id)
            self.adapter = None

        if self.backchannel is not None:
            self.backchannel.close()
            self.backchannel = None
示例#10
0
 def delete_listener_file():
     log.info("Listener ports closed; deleting {0!r}", listener_file)
     try:
         os.remove(listener_file)
     except Exception:
         log.exception("Failed to delete {0!r}",
                       listener_file,
                       level="warning")
示例#11
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
示例#12
0
def kill():
    if process is None:
        return
    try:
        if process.poll() is None:
            log.info("Killing {0}", describe())
            process.kill()
    except Exception:
        log.exception("Failed to kill {0}", describe())
示例#13
0
def stop():
    if _stream is None:
        return

    try:
        _invoke("stop")
        _stream.close()
    except Exception:
        log.exception()
示例#14
0
 def _worker(self, func, *args, **kwargs):
     try:
         self.request = func(self.url, *args, **kwargs)
     except Exception as exc:
         if self.log_errors:
             log.exception("{0} failed:", self)
         self.exception = exc
     else:
         log.info("{0} --> {1} {2}", self, self.request.status_code,
                  self.request.reason)
示例#15
0
    def __init__(self, sock):
        from ptvsd.adapter import sessions

        self.server = None
        """The Server component, if this debug server belongs to Session.
        """

        self.pid = None

        stream = messaging.JsonIOStream.from_socket(sock, str(self))
        self.channel = messaging.JsonMessageChannel(stream, self)
        self.channel.start()

        try:
            info = self.channel.request("pydevdSystemInfo")
            process_info = info("process", json.object())
            self.pid = process_info("pid", int)
            self.ppid = process_info("ppid", int, optional=True)
            if self.ppid == ():
                self.ppid = None

            self.channel.name = stream.name = str(self)
            with _lock:
                if any(conn.pid == self.pid for conn in _connections):
                    raise KeyError(
                        fmt("{0} is already connected to this adapter", self))
                _connections.append(self)
                _connections_changed.set()

        except Exception:
            log.exception("Failed to accept incoming server connection:")
            # If we couldn't retrieve all the necessary info from the debug server,
            # or there's a PID clash, we don't want to track this debuggee anymore,
            # but we want to continue accepting connections.
            self.channel.close()
            return

        parent_session = sessions.get(self.ppid)
        if parent_session is None:
            log.info("No active debug session for parent process of {0}.",
                     self)
        else:
            try:
                parent_session.ide.notify_of_subprocess(self)
            except Exception:
                # This might fail if the IDE concurrently disconnects from the parent
                # session. We still want to keep the connection around, in case the
                # IDE reconnects later. If the parent session was "launch", it'll take
                # care of closing the remaining server connections.
                log.exception("Failed to notify parent session about {0}:",
                              self)
示例#16
0
def inject(pid, ptvsd_args):
    host, port = Connection.listener.getsockname()

    cmdline = [
        sys.executable,
        compat.filename(os.path.dirname(ptvsd.__file__)),
        "--client",
        "--host",
        host,
        "--port",
        str(port),
    ]
    if adapter.access_token is not None:
        cmdline += ["--client-access-token", adapter.access_token]
    cmdline += ptvsd_args
    cmdline += ["--pid", str(pid)]

    log.info("Spawning attach-to-PID debugger injector: {0!r}", cmdline)
    try:
        injector = subprocess.Popen(
            cmdline,
            bufsize=0,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
        )
    except Exception as exc:
        log.exception("Failed to inject debug server into process with PID={0}", pid)
        raise messaging.MessageHandlingError(
            fmt(
                "Failed to inject debug server into process with PID={0}: {1}", pid, exc
            )
        )

    # We need to capture the output of the injector - otherwise it can get blocked
    # on a write() syscall when it tries to print something.

    def capture_output():
        while True:
            line = injector.stdout.readline()
            if not line:
                break
            log.info("Injector[PID={0}] output:\n{1}", pid, line.rstrip())
        log.info("Injector[PID={0}] exited.", pid)

    thread = threading.Thread(
        target=capture_output, name=fmt("Injector[PID={0}] output", pid)
    )
    thread.daemon = True
    thread.start()
示例#17
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)
示例#18
0
文件: api.py 项目: int19h/ptvsd
    def debug(address, log_dir=None, multiprocess=True):
        if log_dir:
            log.log_dir = log_dir

        log.to_file(prefix="ptvsd.server")
        log.describe_environment("ptvsd.server debug start environment:")
        log.debug("{0}{1!r}", func.__name__, (address, log_dir, multiprocess))

        if is_attached():
            log.info("{0}() ignored - already attached.", func.__name__)
            return options.host, options.port

        # Ensure port is int
        if address is not options:
            host, port = address
            options.host, options.port = (host, int(port))

        if multiprocess is not options:
            options.multiprocess = multiprocess

        ptvsd_path, _, _ = get_abs_path_real_path_and_base_from_file(ptvsd.__file__)
        ptvsd_path = os.path.dirname(ptvsd_path)
        start_patterns = (ptvsd_path,)
        end_patterns = ("ptvsd_launcher.py",)
        log.info(
            "Won't trace filenames starting with: {0!j}\n"
            "Won't trace filenames ending with: {1!j}",
            start_patterns,
            end_patterns,
        )

        try:
            return func(start_patterns, end_patterns)
        except Exception:
            raise log.exception("{0}() failed:", func.__name__, level="info")
示例#19
0
文件: session.py 项目: yazici/ptvsd
 def _process_request(self, request):
     self.timeline.record_request(request, block=False)
     if request.command == "runInTerminal":
         args = request("args", json.array(unicode))
         cwd = request("cwd", ".")
         env = request("env", json.object(unicode))
         try:
             exe = args.pop(0)
             assert not len(self.spawn_debuggee.env)
             self.spawn_debuggee.env = env
             self.spawn_debuggee(args, cwd, exe=exe)
             return {}
         except OSError as exc:
             log.exception('"runInTerminal" failed:')
             raise request.cant_handle(str(exc))
     else:
         raise request.isnt_valid("not supported")
示例#20
0
def _dump_worker_log(command, problem, exc_info=None):
    reason = fmt("{0}.{1}() {2}", _name, command, problem)
    if _worker_log_filename is None:
        reason += ", but there is no log."
    else:
        try:
            with open(_worker_log_filename) as f:
                worker_log = f.read()
        except Exception:
            reason += fmt(", but log {0} could not be retrieved.",
                          _worker_log_filename)
        else:
            reason += fmt("; watchdog worker process log:\n\n{0}", worker_log)

    if exc_info is None:
        log.error("{0}", reason)
    else:
        log.exception("{0}", reason, exc_info=exc_info)
    return reason
示例#21
0
def dump(why):
    assert why

    pydevd_debug_file = os.environ.get("PYDEVD_DEBUG_FILE")
    if not pydevd_debug_file:
        return

    try:
        f = open(pydevd_debug_file)
        with f:
            pydevd_log = f.read()
    except Exception:
        log.exception(
            "Test {0}, but pydevd log {1} could not be retrieved.",
            why,
            pydevd_debug_file,
        )
        return

    log.info("Test {0}; pydevd log:\n\n{1}", why, pydevd_log)
示例#22
0
def wait_for_exit():
    from ptvsd.launcher import adapter, output

    try:
        code = process.wait()
    except Exception:
        log.exception("Couldn't determine process exit code:")
        code = -1

    log.info("{0} exited with code {1}", describe(), code)
    output.wait_for_remaining_output()
    try:
        adapter.channel.send_event("exited", {"exitCode": code})
    except Exception:
        pass

    if any(pred(code) for pred in wait_on_exit_predicates):
        _wait_for_user_input()

    try:
        adapter.channel.send_event("terminated")
    except Exception:
        pass
示例#23
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:
            # 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.exception()
            time.sleep(interval)
        else:
            log.info("localhost:{0} is listening - server is up!", port)
            return
        finally:
            sock.close()
示例#24
0
        def accept_worker():
            log.info(
                "Listening for incoming connection from {0} on port {1}...",
                self,
                self.port,
            )

            try:
                self._socket, _ = self._server_socket.accept()
            except socket.timeout:
                raise log.exception("Timed out waiting for {0} to connect",
                                    self)

            self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
            log.info("Incoming connection from {0} accepted.", self)
            self._setup_stream()
示例#25
0
    def listen(cls, host=None, port=0, timeout=None, name=None):
        """Accepts TCP connections on the specified host and port, and creates a new
        instance of this class wrapping every accepted socket.
        """

        if name is None:
            name = cls.__name__

        assert cls.listener is None
        try:
            cls.listener = create_server(host, port, timeout)
        except Exception:
            raise log.exception(
                "Error listening for incoming {0} connections on {1}:{2}:",
                name,
                host,
                port,
            )
        host, port = cls.listener.getsockname()
        log.info("Listening for incoming {0} connections on {1}:{2}...", name,
                 host, port)

        def accept_worker():
            while True:
                try:
                    sock, (other_host, other_port) = cls.listener.accept()
                except OSError:
                    # Listener socket has been closed.
                    break

                log.info(
                    "Accepted incoming {0} connection from {1}:{2}.",
                    name,
                    other_host,
                    other_port,
                )
                cls(sock)

        thread = threading.Thread(target=accept_worker)
        thread.daemon = True
        thread.pydev_do_not_trace = True
        thread.is_pydev_daemon_thread = True
        thread.start()

        return host, port
示例#26
0
def stop_listening():
    try:
        IDE.listener.close()
    except Exception:
        log.exception(level="warning")
示例#27
0
def spawn(process_name, cmdline, cwd, env, redirect_output):
    log.info(
        "Spawning debuggee process:\n\n"
        "Current directory: {0!j}\n\n"
        "Command line: {1!j}\n\n"
        "Environment variables: {2!j}\n\n",
        cwd,
        cmdline,
        env,
    )

    close_fds = set()
    try:
        if redirect_output:
            # subprocess.PIPE behavior can vary substantially depending on Python version
            # and platform; using our own pipes keeps it simple, predictable, and fast.
            stdout_r, stdout_w = os.pipe()
            stderr_r, stderr_w = os.pipe()
            close_fds |= {stdout_r, stdout_w, stderr_r, stderr_w}
            kwargs = dict(stdout=stdout_w, stderr=stderr_w)
        else:
            kwargs = {}

        try:
            global process
            process = subprocess.Popen(cmdline,
                                       cwd=cwd,
                                       env=env,
                                       bufsize=0,
                                       **kwargs)
        except Exception as exc:
            raise messaging.MessageHandlingError(
                fmt("Couldn't spawn debuggee: {0}\n\nCommand line:{1!r}", exc,
                    cmdline))

        log.info("Spawned {0}.", describe())
        atexit.register(kill)
        launcher.channel.send_event(
            "process",
            {
                "startMethod": "launch",
                "isLocalProcess": True,
                "systemProcessId": process.pid,
                "name": process_name,
                "pointerSize": struct.calcsize(compat.force_str("P")) * 8,
            },
        )

        if redirect_output:
            for category, fd, tee in [
                ("stdout", stdout_r, sys.stdout),
                ("stderr", stderr_r, sys.stderr),
            ]:
                output.CaptureOutput(describe(), category, fd, tee)
                close_fds.remove(fd)

        wait_thread = threading.Thread(target=wait_for_exit,
                                       name="wait_for_exit()")
        wait_thread.daemon = True
        wait_thread.start()

    finally:
        for fd in close_fds:
            try:
                os.close(fd)
            except Exception:
                log.exception()
 def option(name, type, *args):
     try:
         return type(os.environ.pop(name, *args))
     except Exception:
         raise log.exception("Error parsing {0!r}:", name)
示例#29
0
    def __init__(self, sock):
        from ptvsd.adapter import sessions

        self.disconnected = False

        self.server = None
        """The Server component, if this debug server belongs to Session.
        """

        self.pid = None

        stream = messaging.JsonIOStream.from_socket(sock, str(self))
        self.channel = messaging.JsonMessageChannel(stream, self)
        self.channel.start()

        try:
            self.authenticate()
            info = self.channel.request("pydevdSystemInfo")
            process_info = info("process", json.object())
            self.pid = process_info("pid", int)
            self.ppid = process_info("ppid", int, optional=True)
            if self.ppid == ():
                self.ppid = None
            self.channel.name = stream.name = str(self)

            ptvsd_dir = os.path.dirname(os.path.dirname(ptvsd.__file__))
            # Note: we must check if 'ptvsd' is not already in sys.modules because the
            # evaluation of an import at the wrong time could deadlock Python due to
            # its import lock.
            #
            # So, in general this evaluation shouldn't do anything. It's only
            # important when pydevd attaches automatically to a subprocess. In this
            # case, we have to make sure that ptvsd is properly put back in the game
            # for users to be able to use it.v
            #
            # In this case (when the import is needed), this evaluation *must* be done
            # before the configurationDone request is sent -- if this is not respected
            # it's possible that pydevd already started secondary threads to handle
            # commands, in which case it's very likely that this command would be
            # evaluated at the wrong thread and the import could potentially deadlock
            # the program.
            #
            # Note 2: the sys module is guaranteed to be in the frame globals and
            # doesn't need to be imported.
            inject_ptvsd = """
if 'ptvsd' not in sys.modules:
    sys.path.insert(0, {ptvsd_dir!r})
    try:
        import ptvsd
    finally:
        del sys.path[0]
"""
            inject_ptvsd = fmt(inject_ptvsd, ptvsd_dir=ptvsd_dir)

            try:
                self.channel.request("evaluate", {"expression": inject_ptvsd})
            except messaging.MessageHandlingError:
                # Failure to inject is not a fatal error - such a subprocess can
                # still be debugged, it just won't support "import ptvsd" in user
                # code - so don't terminate the session.
                log.exception("Failed to inject ptvsd into {0}:",
                              self,
                              level="warning")

            with _lock:
                # The server can disconnect concurrently before we get here, e.g. if
                # it was force-killed. If the disconnect() handler has already run,
                # don't register this server or report it, since there's nothing to
                # deregister it.
                if self.disconnected:
                    return

                if any(conn.pid == self.pid for conn in _connections):
                    raise KeyError(
                        fmt("{0} is already connected to this adapter", self))
                _connections.append(self)
                _connections_changed.set()

        except Exception:
            log.exception("Failed to accept incoming server connection:")
            self.channel.close()

            # If this was the first server to connect, and the main thread is inside
            # wait_until_disconnected(), we want to unblock it and allow it to exit.
            dont_wait_for_first_connection()

            # If we couldn't retrieve all the necessary info from the debug server,
            # or there's a PID clash, we don't want to track this debuggee anymore,
            # but we want to continue accepting connections.
            return

        parent_session = sessions.get(self.ppid)
        if parent_session is None:
            log.info("No active debug session for parent process of {0}.",
                     self)
        else:
            try:
                parent_session.ide.notify_of_subprocess(self)
            except Exception:
                # This might fail if the IDE concurrently disconnects from the parent
                # session. We still want to keep the connection around, in case the
                # IDE reconnects later. If the parent session was "launch", it'll take
                # care of closing the remaining server connections.
                log.exception("Failed to notify parent session about {0}:",
                              self)
def attach(host, port, client, log_dir=None):
    try:
        import sys
        if 'threading' not in sys.modules:
            try:

                def on_warn(msg):
                    print(msg, file=sys.stderr)

                def on_exception(msg):
                    print(msg, file=sys.stderr)

                def on_critical(msg):
                    print(msg, file=sys.stderr)

                pydevd_attach_to_process_path = os.path.join(
                    _ptvsd_dir, 'ptvsd', '_vendored', 'pydevd',
                    'pydevd_attach_to_process')
                assert os.path.exists(pydevd_attach_to_process_path)
                sys.path.insert(0, pydevd_attach_to_process_path)

                # NOTE: that it's not a part of the pydevd PYTHONPATH
                import attach_script
                attach_script.fix_main_thread_id(on_warn=on_warn,
                                                 on_exception=on_exception,
                                                 on_critical=on_critical)

                # NOTE: At this point it should be safe to remove this.
                sys.path.remove(pydevd_attach_to_process_path)
            except:
                import traceback
                traceback.print_exc()
                raise

        sys.path.insert(0, _ptvsd_dir)
        import ptvsd

        # NOTE: Don't do sys.path.remove here it will remove all instances of that path
        # and the user may have set that to ptvsd path via PYTHONPATH
        assert sys.path[0] == _ptvsd_dir
        del sys.path[0]

        from ptvsd.common import options as common_opts
        from ptvsd.server import options
        if log_dir is not None:
            common_opts.log_dir = log_dir
        options.client = client
        options.host = host
        options.port = port

        if options.client:
            ptvsd.attach((options.host, options.port))
        else:
            ptvsd.enable_attach((options.host, options.port))

        from ptvsd.common import log
        log.info("Debugger successfully injected")

    except:
        import traceback
        traceback.print_exc()
        raise log.exception()