diff --git a/src/debugpy/launcher/debuggee.py b/src/debugpy/launcher/debuggee.py index 5a97c29cb..00b5eaaa7 100644 --- a/src/debugpy/launcher/debuggee.py +++ b/src/debugpy/launcher/debuggee.py @@ -58,6 +58,10 @@ def spawn(process_name, cmdline, env, redirect_output): # all their subprocesses, so that the OS will kill the entire object tree # automatically when launcher exits. _setup_win32_job() + else: + # Start the debuggee in a new process group, so that the launcher can kill + # the entire group later. + kwargs.update(preexec_fn=os.setpgrp) try: global process @@ -69,10 +73,8 @@ def spawn(process_name, cmdline, env, redirect_output): log.info("Spawned {0}.", describe()) + # On Windows, the job object takes care of process tree cleanup. if sys.platform != "win32": - # Assign the debuggee to a new process group, so that we can easily kill - # the entire group including subprocesses later. - os.setpgid(process.pid, 0) atexit.register(kill) launcher.channel.send_event( @@ -109,17 +111,23 @@ def spawn(process_name, cmdline, env, redirect_output): def kill(): if process is None: return + try: if process.poll() is None: log.info("Killing {0}", describe()) process.kill() - if sys.platform != "win32": - # On POSIX, we also kill the process group that we created for the - # debuggee, to kill all its subprocesses. - os.kill(-process.pid, signal.SIGKILL) except Exception: log.swallow_exception("Failed to kill {0}", describe()) + if sys.platform != "win32": + # On POSIX, we also kill debuggee's process group, which kills the whole + # process tree (excepting subprocesses that joined another process group). + try: + log.info("Killing {0} process group", describe()) + os.kill(-process.pid, signal.SIGKILL) + except Exception: + log.swallow_exception("Failed to kill {0} process group", describe()) + def wait_for_exit(): try: diff --git a/src/debugpy/launcher/handlers.py b/src/debugpy/launcher/handlers.py index 14baac493..4f5fef2ec 100644 --- a/src/debugpy/launcher/handlers.py +++ b/src/debugpy/launcher/handlers.py @@ -64,6 +64,7 @@ def property_or_debug_option(prop_name, flag_name): adapter_access_token = request("adapterAccessToken", unicode, optional=True) if adapter_access_token != (): cmdline += ["--adapter-access-token", compat.filename(adapter_access_token)] + debugpy_args = request("debugpyArgs", json.array(unicode)) cmdline += debugpy_args