예제 #1
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 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))
예제 #3
0
 def long_tmpdir(request, tmpdir):
     """Like tmpdir, but ensures that it's a long rather than short filename on Win32.
     """
     path = compat.filename(tmpdir.strpath)
     buffer = ctypes.create_unicode_buffer(512)
     if GetLongPathNameW(path, buffer, len(buffer)):
         path = buffer.value
     return py.path.local(path)
예제 #4
0
파일: cli.py 프로젝트: Adespinoza/dotfiles
def setup_debug_server(argv_0):
    # We need to set up sys.argv[0] before invoking attach() or enable_attach(),
    # because they use it to report the "process" event. Thus, we can't rely on
    # run_path() and run_module() doing that, even though they will eventually.
    sys.argv[0] = compat.filename(argv_0)
    log.debug("sys.argv after patching: {0!r}", sys.argv)

    debug = ptvsd.attach if options.client else ptvsd.enable_attach
    debug(address=options, multiprocess=options)

    if options.wait:
        ptvsd.wait_for_attach()
예제 #5
0
파일: cli.py 프로젝트: Adespinoza/dotfiles
def parse(args, options=options):
    seen = set()
    it = (compat.filename(arg) for arg in args)

    while True:
        try:
            arg = next(it)
        except StopIteration:
            raise ValueError("missing target: " + TARGET)

        switch = arg if arg.startswith("-") else ""
        for i, (sw, placeholder, action, _) in enumerate(switches):
            if not isinstance(sw, tuple):
                sw = (sw, )
            if switch in sw:
                break
        else:
            raise ValueError("unrecognized switch " + switch)

        if i in seen:
            raise ValueError("duplicate switch " + switch)
        else:
            seen.add(i)

        try:
            action(arg, it)
        except StopIteration:
            assert placeholder is not None
            raise ValueError(fmt("{0}: missing {1}", switch, placeholder))
        except Exception as exc:
            raise ValueError(
                fmt("invalid {0} {1}: {2}", switch, placeholder, exc))

        if options.target is not None:
            break

    for i, (sw, placeholder, _, required) in enumerate(switches):
        if not required or i in seen:
            continue
        if isinstance(sw, tuple):
            sw = sw[0]
        message = fmt("missing required {0}", sw)
        if placeholder is not None:
            message += " " + placeholder
        raise ValueError(message)

    if options.target_kind == "pid" and options.wait:
        raise ValueError("--pid does not support --wait")

    return it
예제 #6
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()
예제 #7
0
    def factory(source):
        assert isinstance(source, types.FunctionType)
        name = source.__name__
        source, _ = inspect.getsourcelines(source)

        # First, find the "def" line.
        def_lineno = 0
        for line in source:
            line = line.strip()
            if line.startswith("def") and line.endswith(":"):
                break
            def_lineno += 1
        else:
            raise ValueError("Failed to locate function header.")

        # Remove everything up to and including "def".
        source = source[def_lineno + 1 :]
        assert source

        # Now we need to adjust indentation. Compute how much the first line of
        # the body is indented by, then dedent all lines by that amount. Blank
        # lines don't matter indentation-wise, and might not be indented to begin
        # with, so just replace them with a simple newline.
        line = source[0]
        indent = len(line) - len(line.lstrip())
        source = [l[indent:] if l.strip() else "\n" for l in source]
        source = "".join(source)

        # Write it to file.
        tmpfile = long_tmpdir / (name + ".py")
        tmpfile.strpath = compat.filename(tmpfile.strpath)
        assert not tmpfile.check()
        tmpfile.write(source)

        tmpfile.lines = code.get_marked_line_numbers(tmpfile)
        return tmpfile
예제 #8
0
def launch_request(request):
    debug_options = set(request("debugOptions", json.array(unicode)))

    # Handling of properties that can also be specified as legacy "debugOptions" flags.
    # If property is explicitly set to false, but the flag is in "debugOptions", treat
    # it as an error. Returns None if the property wasn't explicitly set either way.
    def property_or_debug_option(prop_name, flag_name):
        assert prop_name[0].islower() and flag_name[0].isupper()

        value = request(prop_name, bool, optional=True)
        if value == ():
            value = None

        if flag_name in debug_options:
            if value is False:
                raise request.isnt_valid(
                    '{0!j}:false and "debugOptions":[{1!j}] are mutually exclusive',
                    prop_name,
                    flag_name,
                )
            value = True

        return value

    cmdline = []
    if property_or_debug_option("sudo", "Sudo"):
        if sys.platform == "win32":
            raise request.cant_handle('"sudo":true is not supported on Windows.')
        else:
            cmdline += ["sudo"]

    # "pythonPath" is a deprecated legacy spelling. If "python" is missing, then try
    # the alternative. But if both are missing, the error message should say "python".
    python_key = "python"
    if python_key in request:
        if "pythonPath" in request:
            raise request.isnt_valid(
                '"pythonPath" is not valid if "python" is specified'
            )
    elif "pythonPath" in request:
        python_key = "pythonPath"
    python = request(python_key, json.array(unicode, vectorize=True, size=(1,)))
    if not len(python):
        python = [compat.filename(sys.executable)]
    cmdline += python

    if not request("noDebug", json.default(False)):
        port = request("port", int)
        cmdline += [
            compat.filename(os.path.dirname(ptvsd.__file__)),
            "--client",
            "--host",
            "127.0.0.1",
            "--port",
            str(port),
        ]
        client_access_token = request("clientAccessToken", unicode, optional=True)
        if client_access_token != ():
            cmdline += ["--client-access-token", compat.filename(client_access_token)]
        ptvsd_args = request("ptvsdArgs", json.array(unicode))
        cmdline += ptvsd_args

    program = module = code = ()
    if "program" in request:
        program = request("program", json.array(unicode, vectorize=True, size=(1,)))
        cmdline += program
        process_name = program[0]
    if "module" in request:
        module = request("module", json.array(unicode, vectorize=True, size=(1,)))
        cmdline += ["-m"] + module
        process_name = module[0]
    if "code" in request:
        code = request("code", json.array(unicode, vectorize=True, size=(1,)))
        cmdline += ["-c"] + code
        process_name = python[0]

    num_targets = len([x for x in (program, module, code) if x != ()])
    if num_targets == 0:
        raise request.isnt_valid(
            'either "program", "module", or "code" must be specified'
        )
    elif num_targets != 1:
        raise request.isnt_valid(
            '"program", "module", and "code" are mutually exclusive'
        )

    cmdline += request("args", json.array(unicode))

    cwd = request("cwd", unicode, optional=True)
    if cwd == ():
        # If it's not specified, but we're launching a file rather than a module,
        # and the specified path has a directory in it, use that.
        cwd = None if program == () else (os.path.dirname(program[0]) or None)

    env = os.environ.copy()
    if "PTVSD_TEST" in env:
        # If we're running as part of a ptvsd test, make sure that codecov is not
        # applied to the debuggee, since it will conflict with pydevd.
        env.pop("COV_CORE_SOURCE", None)
    env.update(request("env", json.object(unicode)))

    if request("gevent", False):
        env["GEVENT_SUPPORT"] = "True"

    redirect_output = property_or_debug_option("redirectOutput", "RedirectOutput")
    if redirect_output is None:
        # If neither the property nor the option were specified explicitly, choose
        # the default depending on console type - "internalConsole" needs it to
        # provide any output at all, but it's unnecessary for the terminals.
        redirect_output = request("console", unicode) == "internalConsole"
    if redirect_output:
        # sys.stdout buffering must be disabled - otherwise we won't see the output
        # at all until the buffer fills up.
        env["PYTHONUNBUFFERED"] = "1"
        # Force UTF-8 output to minimize data loss due to re-encoding.
        env["PYTHONIOENCODING"] = "utf-8"

    if property_or_debug_option("waitOnNormalExit", "WaitOnNormalExit"):
        debuggee.wait_on_exit_predicates.append(lambda code: code == 0)
    if property_or_debug_option("waitOnAbnormalExit", "WaitOnAbnormalExit"):
        debuggee.wait_on_exit_predicates.append(lambda code: code != 0)

    if sys.version_info < (3,):
        # Popen() expects command line and environment to be bytes, not Unicode.
        # Assume that values are filenames - it's usually either that, or numbers -
        # but don't allow encoding to fail if we guessed wrong.
        encode = functools.partial(compat.filename_bytes, errors="replace")
        cmdline = [encode(s) for s in cmdline]
        env = {encode(k): encode(v) for k, v in env.items()}

    debuggee.spawn(process_name, cmdline, cwd, env, redirect_output)
    return {}