Exemple #1
0
def spawn(process_name, cmdline, env, redirect_output):
    log.info(
        "Spawning debuggee process:\n\n"
        "Command line: {0!r}\n\n"
        "Environment variables: {1!r}\n\n",
        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, 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.swallow_exception()
Exemple #2
0
def inject(pid, debugpy_args):
    host, port = listener.getsockname()

    cmdline = [
        sys.executable,
        compat.filename(os.path.dirname(debugpy.__file__)),
        "--connect",
        host + ":" + str(port),
    ]
    if adapter.access_token is not None:
        cmdline += ["--adapter-access-token", adapter.access_token]
    cmdline += debugpy_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.swallow_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()
 def require(self, *keys):
     for key in keys:
         if not self[key]:
             raise messaging.MessageHandlingError(
                 fmt("{0} does not have capability {1!j}", self.component, key)
             )
def spawn(process_name, cmdline, env, redirect_output):
    log.info(
        "Spawning debuggee process:\n\n"
        "Command line: {0!r}\n\n"
        "Environment variables: {1!r}\n\n",
        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 = {}

        if sys.platform != "win32":

            def preexec_fn():
                try:
                    # Start the debuggee in a new process group, so that the launcher can
                    # kill the entire process tree later.
                    os.setpgrp()

                    # Make the new process group the foreground group in its session, so
                    # that it can interact with the terminal. The debuggee will receive
                    # SIGTTOU when tcsetpgrp() is called, and must ignore it.
                    old_handler = signal.signal(signal.SIGTTOU, signal.SIG_IGN)
                    try:
                        tty = os.open("/dev/tty", os.O_RDWR)
                        try:
                            os.tcsetpgrp(tty, os.getpgrp())
                        finally:
                            os.close(tty)
                    finally:
                        signal.signal(signal.SIGTTOU, old_handler)
                except Exception:
                    # Not an error - /dev/tty doesn't work when there's no terminal.
                    log.swallow_exception(
                        "Failed to set up process group", level="info"
                    )

            kwargs.update(preexec_fn=preexec_fn)

        try:
            global process
            process = subprocess.Popen(cmdline, 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())

        if sys.platform == "win32":
            # Assign the debuggee to a new job object, so that the launcher can kill
            # the entire process tree later.
            try:
                global job_handle
                job_handle = winapi.kernel32.CreateJobObjectA(None, None)

                job_info = winapi.JOBOBJECT_EXTENDED_LIMIT_INFORMATION()
                job_info_size = winapi.DWORD(ctypes.sizeof(job_info))
                winapi.kernel32.QueryInformationJobObject(
                    job_handle,
                    winapi.JobObjectExtendedLimitInformation,
                    ctypes.pointer(job_info),
                    job_info_size,
                    ctypes.pointer(job_info_size),
                )

                # Setting this flag ensures that the job will be terminated by the OS once the
                # launcher exits, even if it doesn't terminate the job explicitly.
                job_info.BasicLimitInformation.LimitFlags |= (
                    winapi.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
                )
                winapi.kernel32.SetInformationJobObject(
                    job_handle,
                    winapi.JobObjectExtendedLimitInformation,
                    ctypes.pointer(job_info),
                    job_info_size,
                )

                process_handle = winapi.kernel32.OpenProcess(
                    winapi.PROCESS_TERMINATE | winapi.PROCESS_SET_QUOTA,
                    False,
                    process.pid,
                )

                winapi.kernel32.AssignProcessToJobObject(job_handle, process_handle)

            except Exception:
                log.swallow_exception("Failed to set up job object", level="warning")

        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.swallow_exception(level="warning")