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))
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)
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()
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
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()
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
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 {}