Beispiel #1
0
    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")
Beispiel #2
0
def main():
    from ptvsd.common import log
    from ptvsd import launcher
    from ptvsd.launcher import debuggee

    log.to_file(prefix="ptvsd.launcher")
    log.describe_environment("ptvsd.launcher startup environment:")

    # Disable exceptions on Ctrl+C - we want to allow the debuggee process to handle
    # these, or not, as it sees fit. If the debuggee exits on Ctrl+C, the launcher
    # will also exit, so it doesn't need to observe the signal directly.
    signal.signal(signal.SIGINT, signal.SIG_IGN)

    def option(name, type, *args):
        try:
            return type(os.environ.pop(name, *args))
        except Exception:
            raise log.exception("Error parsing {0!r}:", name)

    launcher_port = option("PTVSD_LAUNCHER_PORT", int)

    launcher.connect(launcher_port)
    launcher.channel.wait()

    if debuggee.process is not None:
        sys.exit(debuggee.process.returncode)
Beispiel #3
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
Beispiel #4
0
def test_wrapper(request, long_tmpdir):
    def write_log(filename, data):
        filename = os.path.join(log.log_dir, filename)
        if not isinstance(data, bytes):
            data = data.encode("utf-8")
        with open(filename, "wb") as f:
            f.write(data)

    session.Session.reset_counter()

    session.Session.tmpdir = long_tmpdir
    original_log_dir = log.log_dir

    try:
        if log.log_dir is None:
            log.log_dir = (long_tmpdir / "ptvsd_logs").strpath
        else:
            log_subdir = request.node.nodeid
            log_subdir = log_subdir.replace("::", "/")
            for ch in r":?*|<>":
                log_subdir = log_subdir.replace(ch, fmt("&#{0};", ord(ch)))
            log.log_dir += "/" + log_subdir

        try:
            py.path.local(log.log_dir).remove()
        except Exception:
            pass

        print("\n")  # make sure on-screen logs start on a new line
        with log.to_file(prefix="tests"):
            timestamp.reset()
            log.info("{0} started.", request.node.nodeid)
            try:
                yield
            finally:
                failed = False
                for report_attr in ("setup_report", "call_report", "teardown_report"):
                    try:
                        report = getattr(request.node, report_attr)
                    except AttributeError:
                        continue

                    failed |= report.failed
                    log.write_format(
                        "error" if report.failed else "info",
                        "pytest {0} phase for {1} {2}.",
                        report.when,
                        request.node.nodeid,
                        report.outcome,
                    )

                    write_log(report_attr + ".log", report.longreprtext)
                    write_log(report_attr + ".stdout.log", report.capstdout)
                    write_log(report_attr + ".stderr.log", report.capstderr)

                if failed:
                    write_log("FAILED.log", "")
                    logs.dump()
    finally:
        log.log_dir = original_log_dir
Beispiel #5
0
def main(args):
    from ptvsd.common import log, options as common_options
    from ptvsd.adapter import ide, servers, sessions, options as adapter_options

    if args.log_stderr:
        log.stderr.levels |= set(log.LEVELS)
        adapter_options.log_stderr = True
    if args.log_dir is not None:
        common_options.log_dir = args.log_dir

    log.to_file(prefix="ptvsd.adapter")
    log.describe_environment("ptvsd.adapter startup environment:")

    if args.for_enable_attach and args.port is None:
        log.error("--for-enable-attach requires --port")
        sys.exit(64)

    server_host, server_port = servers.listen()
    ide_host, ide_port = ide.listen(port=args.port)

    if args.for_enable_attach:
        endpoints = {
            "ide": {
                "host": ide_host,
                "port": ide_port
            },
            "server": {
                "host": server_host,
                "port": server_port
            },
        }
        log.info("Sending endpoints to stdout: {0!r}", endpoints)
        print(json.dumps(endpoints))
        sys.stdout.flush()

    if args.port is None:
        ide.IDE("stdio")

    servers.wait_until_disconnected()
    log.info(
        "All debug servers disconnected; waiting for remaining sessions...")

    sessions.wait_until_ended()
    log.info("All debug sessions have ended; exiting.")
def main():
    from ptvsd.common import log
    from ptvsd.launcher import adapter, debuggee

    log.to_file(prefix="ptvsd.launcher")
    log.describe_environment("ptvsd.launcher startup environment:")

    def option(name, type, *args):
        try:
            return type(os.environ.pop(name, *args))
        except Exception:
            raise log.exception("Error parsing {0!r}:", name)

    session_id = option("PTVSD_SESSION_ID", int)
    launcher_port = option("PTVSD_LAUNCHER_PORT", int)

    adapter.connect(session_id, launcher_port)
    adapter.channel.wait()

    if debuggee.process is not None:
        sys.exit(debuggee.process.returncode)
Beispiel #7
0
def main(args):
    from ptvsd.common import log, options as common_options
    from ptvsd.adapter import session, options as adapter_options

    if args.log_stderr:
        log.stderr_levels |= set(log.LEVELS)
        adapter_options.log_stderr = True
    if args.log_dir is not None:
        common_options.log_dir = args.log_dir

    log.to_file(prefix="ptvsd.adapter")
    log.describe_environment("ptvsd.adapter startup environment:")

    session = session.Session()
    if args.port is None:
        session.connect_to_ide()
    else:
        if args.for_server_on_port is not None:
            session.connect_to_server(("127.0.0.1", args.for_server_on_port))
        with session.accept_connection_from_ide((args.host, args.port)) as (_, port):
            if session.server:
                session.server.set_debugger_property({"adapterPort": port})
    session.wait_for_completion()
Beispiel #8
0
    pytest.register_assert_rewrite(modname)


_register_assert_rewrite("ptvsd.common")
tests_submodules = pkgutil.iter_modules([str(root)])
for _, submodule, _ in tests_submodules:
    submodule = str("{0}.{1}".format(__name__, submodule))
    _register_assert_rewrite(submodule)

# Now we can import these, and pytest will rewrite asserts in them.
from ptvsd.common import json, log
import ptvsd.server  # noqa

# Enable full logging to stderr, and make timestamps shorter to match maximum test
# run time better.
log.stderr.levels = all
log.timestamp_format = "06.3f"
log.to_file(prefix="tests")

# Enable JSON serialization for py.path.local.


def json_default(self, obj):
    if isinstance(obj, py.path.local):
        return obj.strpath
    return self.original_default(obj)


json.JsonEncoder.original_default = json.JsonEncoder.default
json.JsonEncoder.default = json_default
Beispiel #9
0
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)
Beispiel #10
0
def main(args):
    from ptvsd import adapter
    from ptvsd.common import compat, log, sockets
    from ptvsd.adapter import ide, servers, sessions

    if args.for_server is not None:
        if os.name == "posix":
            # On POSIX, we need to leave the process group and its session, and then
            # daemonize properly by double-forking (first fork already happened when
            # this process was spawned).
            os.setsid()
            if os.fork() != 0:
                sys.exit(0)

        for stdio in sys.stdin, sys.stdout, sys.stderr:
            if stdio is not None:
                stdio.close()

    if args.log_stderr:
        log.stderr.levels |= set(log.LEVELS)
    if args.log_dir is not None:
        log.log_dir = args.log_dir

    log.to_file(prefix="ptvsd.adapter")
    log.describe_environment("ptvsd.adapter startup environment:")

    servers.access_token = args.server_access_token
    if args.for_server is None:
        adapter.access_token = compat.force_str(
            codecs.encode(os.urandom(32), "hex"))

    try:
        server_host, server_port = servers.listen()
    except Exception as exc:
        if args.for_server is None:
            raise
        endpoints = {
            "error": "Can't listen for server connections: " + str(exc)
        }
    else:
        endpoints = {"server": {"host": server_host, "port": server_port}}
        try:
            ide_host, ide_port = ide.listen(port=args.port)
        except Exception as exc:
            if args.for_server is None:
                raise
            endpoints = {
                "error": "Can't listen for IDE connections: " + str(exc)
            }
        else:
            endpoints["ide"] = {"host": ide_host, "port": ide_port}

    if args.for_server is not None:
        log.info(
            "Sending endpoints info to debug server at localhost:{0}:\n{1!j}",
            args.for_server,
            endpoints,
        )

        try:
            sock = sockets.create_client()
            try:
                sock.settimeout(None)
                sock.connect(("127.0.0.1", args.for_server))
                sock_io = sock.makefile("wb", 0)
                try:
                    sock_io.write(json.dumps(endpoints).encode("utf-8"))
                finally:
                    sock_io.close()
            finally:
                sockets.close_socket(sock)
        except Exception:
            raise log.exception(
                "Error sending endpoints info to debug server:")

        if "error" in endpoints:
            log.error("Couldn't set up endpoints; exiting.")
            sys.exit(1)

    listener_file = os.getenv("PTVSD_ADAPTER_ENDPOINTS")
    if listener_file is not None:
        log.info("Writing endpoints info to {0!r}:\n{1!j}", listener_file,
                 endpoints)

        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")

        try:
            with open(listener_file, "w") as f:
                atexit.register(delete_listener_file)
                print(json.dumps(endpoints), file=f)
        except Exception:
            raise log.exception("Error writing endpoints info to file:")

    if args.port is None:
        ide.IDE("stdio")

    # These must be registered after the one above, to ensure that the listener sockets
    # are closed before the endpoint info file is deleted - this way, another process
    # can wait for the file to go away as a signal that the ports are no longer in use.
    atexit.register(servers.stop_listening)
    atexit.register(ide.stop_listening)

    servers.wait_until_disconnected()
    log.info(
        "All debug servers disconnected; waiting for remaining sessions...")

    sessions.wait_until_ended()
    log.info("All debug sessions have ended; exiting.")
Beispiel #11
0
def main(args):
    from ptvsd import adapter
    from ptvsd.common import compat, log
    from ptvsd.adapter import ide, servers, sessions

    if args.log_stderr:
        log.stderr.levels |= set(log.LEVELS)
    if args.log_dir is not None:
        log.log_dir = args.log_dir

    log.to_file(prefix="ptvsd.adapter")
    log.describe_environment("ptvsd.adapter startup environment:")

    if args.for_server and args.port is None:
        log.error("--for-server requires --port")
        sys.exit(64)

    servers.access_token = args.server_access_token
    if not args.for_server:
        adapter.access_token = compat.force_str(
            codecs.encode(os.urandom(32), "hex"))

    server_host, server_port = servers.listen()
    ide_host, ide_port = ide.listen(port=args.port)
    endpoints_info = {
        "ide": {
            "host": ide_host,
            "port": ide_port
        },
        "server": {
            "host": server_host,
            "port": server_port
        },
    }

    if args.for_server:
        log.info("Writing endpoints info to stdout:\n{0!r}", endpoints_info)
        print(json.dumps(endpoints_info))
        sys.stdout.flush()

    if args.port is None:
        ide.IDE("stdio")

    listener_file = os.getenv("PTVSD_ADAPTER_ENDPOINTS")
    if listener_file is not None:
        log.info("Writing endpoints info to {0!r}:\n{1!r}", listener_file,
                 endpoints_info)

        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")

        with open(listener_file, "w") as f:
            atexit.register(delete_listener_file)
            print(json.dumps(endpoints_info), file=f)

    # These must be registered after the one above, to ensure that the listener sockets
    # are closed before the endpoint info file is deleted - this way, another process
    # can wait for the file to go away as a signal that the ports are no longer in use.
    atexit.register(servers.stop_listening)
    atexit.register(ide.stop_listening)

    servers.wait_until_disconnected()
    log.info(
        "All debug servers disconnected; waiting for remaining sessions...")

    sessions.wait_until_ended()
    log.info("All debug sessions have ended; exiting.")