Beispiel #1
0
def spawn_debuggee(session, start_request, sudo, args, console, console_title):
    cmdline = ["sudo"] if sudo else []
    cmdline += [sys.executable, os.path.dirname(ptvsd.launcher.__file__)]
    cmdline += args
    env = {str("PTVSD_SESSION_ID"): str(session.id)}

    def spawn_launcher():
        with session.accept_connection_from_launcher() as (_, launcher_port):
            env[str("PTVSD_LAUNCHER_PORT")] = str(launcher_port)
            if common_options.log_dir is not None:
                env[str("PTVSD_LOG_DIR")] = compat.filename_str(
                    common_options.log_dir)
            if adapter_options.log_stderr:
                env[str("PTVSD_LOG_STDERR")] = str("debug info warning error")

            if console == "internalConsole":
                log.info("{0} spawning launcher: {1!r}", session, cmdline)

                # If we are talking to the IDE over stdio, sys.stdin and sys.stdout are
                # redirected to avoid mangling the DAP message stream. Make sure the
                # launcher also respects that.
                subprocess.Popen(
                    cmdline,
                    env=dict(list(os.environ.items()) + list(env.items())),
                    stdin=sys.stdin,
                    stdout=sys.stdout,
                    stderr=sys.stderr,
                )

            else:
                log.info('{0} spawning launcher via "runInTerminal" request.',
                         session)
                session.ide.capabilities.require(
                    "supportsRunInTerminalRequest")
                kinds = {
                    "integratedTerminal": "integrated",
                    "externalTerminal": "external",
                }
                session.ide.channel.request(
                    "runInTerminal",
                    {
                        "kind": kinds[console],
                        "title": console_title,
                        "args": cmdline,
                        "env": env,
                    },
                )

        try:
            session.launcher.channel.request(start_request.command, arguments)
        except messaging.MessageHandlingError as exc:
            exc.propagate(start_request)

    if session.no_debug:
        arguments = start_request.arguments
        spawn_launcher()
    else:
        _, port = servers.Connection.listener.getsockname()
        arguments = dict(start_request.arguments)
        arguments["port"] = port
        spawn_launcher()

        if not session.wait_for(lambda: session.pid is not None, timeout=5):
            raise start_request.cant_handle(
                '{0} timed out waiting for "process" event from {1}',
                session,
                session.launcher,
            )

        conn = servers.wait_for_connection(session.pid, timeout=10)
        if conn is None:
            raise start_request.cant_handle(
                "{0} timed out waiting for debuggee to spawn", session)
        conn.attach_to_session(session)
Beispiel #2
0
    def attach_request(self, request):
        if self.session.no_debug:
            raise request.isnt_valid('"noDebug" is not supported for "attach"')

        # There are four distinct possibilities here.
        #
        # If "processId" is specified, this is attach-by-PID. We need to inject the
        # debug server into the designated process, and then wait until it connects
        # back to us. Since the injected server can crash, there must be a timeout.
        #
        # If "subProcessId" is specified, this is attach to a known subprocess, likely
        # in response to a "ptvsd_attach" event. If so, the debug server should be
        # connected already, and thus the wait timeout is zero.
        #
        # If neither is specified, and "waitForAttach" is true, this is attach-by-socket
        # with the server expected to connect to the adapter via ptvsd.attach(). There
        # is no PID known in advance, so just wait until the first server connection
        # indefinitely, with no timeout.
        #
        # If neither is specified, and "waitForAttach" is false, this is attach-by-socket
        # in which the server has spawned the adapter via ptvsd.enable_attach(). There
        # is no PID known to the IDE in advance, but the server connection should be
        # either be there already, or the server should be connecting shortly, so there
        # must be a timeout.
        #
        # In the last two cases, if there's more than one server connection already,
        # this is a multiprocess re-attach. The IDE doesn't know the PID, so we just
        # connect it to the oldest server connection that we have - in most cases, it
        # will be the one for the root debuggee process, but if it has exited already,
        # it will be some subprocess.

        pid = request("processId", (int, unicode), optional=True)
        sub_pid = request("subProcessId", int, optional=True)
        if pid != ():
            if sub_pid != ():
                raise request.isnt_valid(
                    '"processId" and "subProcessId" are mutually exclusive')
            if not isinstance(pid, int):
                try:
                    pid = int(pid)
                except Exception:
                    raise request.isnt_valid(
                        '"processId" must be parseable as int')
            ptvsd_args = request("ptvsdArgs", json.array(unicode))
            servers.inject(pid, ptvsd_args)
            timeout = 10
            pred = lambda conn: conn.pid == pid
        else:
            if sub_pid == ():
                pred = lambda conn: True
                timeout = None if request("waitForAttach", False) else 10
            else:
                pred = lambda conn: conn.pid == sub_pid
                timeout = 0

        conn = servers.wait_for_connection(self.session, pred, timeout)
        if conn is None:
            raise request.cant_handle(
                ("Timed out waiting for debug server to connect." if timeout
                 else "There is no debug server connected to this adapter."
                 if sub_pid == () else
                 'No known subprocess with "subProcessId":{0}'),
                sub_pid,
            )

        try:
            conn.attach_to_session(self.session)
        except ValueError:
            request.cant_handle("{0} is already being debugged.", conn)
Beispiel #3
0
def spawn_debuggee(session, start_request, sudo, args, console, console_title):
    cmdline = ["sudo"] if sudo else []
    cmdline += [sys.executable, os.path.dirname(launcher.__file__)]
    cmdline += args
    env = {}

    def spawn_launcher():
        with session.accept_connection_from_launcher() as (_, launcher_port):
            env[str("PTVSD_LAUNCHER_PORT")] = str(launcher_port)
            if log.log_dir is not None:
                env[str("PTVSD_LOG_DIR")] = compat.filename_str(log.log_dir)
            if log.stderr.levels != {"warning", "error"}:
                env[str("PTVSD_LOG_STDERR")] = str(" ".join(log.stderr.levels))

            if console == "internalConsole":
                log.info("{0} spawning launcher: {1!r}", session, cmdline)

                # If we are talking to the IDE over stdio, sys.stdin and sys.stdout are
                # redirected to avoid mangling the DAP message stream. Make sure the
                # launcher also respects that.
                subprocess.Popen(
                    cmdline,
                    env=dict(list(os.environ.items()) + list(env.items())),
                    stdin=sys.stdin,
                    stdout=sys.stdout,
                    stderr=sys.stderr,
                )

            else:
                log.info('{0} spawning launcher via "runInTerminal" request.',
                         session)
                session.ide.capabilities.require(
                    "supportsRunInTerminalRequest")
                kinds = {
                    "integratedTerminal": "integrated",
                    "externalTerminal": "external",
                }
                session.ide.channel.request(
                    "runInTerminal",
                    {
                        "kind": kinds[console],
                        "title": console_title,
                        "args": cmdline,
                        "env": env,
                    },
                )

        try:
            session.launcher.channel.request(start_request.command, arguments)
        except messaging.MessageHandlingError as exc:
            exc.propagate(start_request)

    if session.no_debug:
        arguments = start_request.arguments
        spawn_launcher()
    else:
        _, port = servers.Connection.listener.getsockname()
        arguments = dict(start_request.arguments)
        arguments["port"] = port
        arguments["clientAccessToken"] = adapter.access_token
        spawn_launcher()

        if not session.wait_for(
                lambda: session.launcher is not None and session.launcher.pid
                is not None,
                timeout=5,
        ):
            raise start_request.cant_handle(
                '{0} timed out waiting for "process" event from {1}',
                session,
                session.launcher,
            )

        # Wait for the first incoming connection regardless of the PID - it won't
        # necessarily match due to the use of stubs like py.exe or "conda run".
        conn = servers.wait_for_connection(session,
                                           lambda conn: True,
                                           timeout=10)
        if conn is None:
            raise start_request.cant_handle(
                "{0} timed out waiting for debuggee to spawn", session)
        conn.attach_to_session(session)