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