mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Report ppid on pydevdSystemInfo on Python 2.7 on Windows. Fixes #1877
This commit is contained in:
parent
88db1db271
commit
fe988d8149
3 changed files with 79 additions and 26 deletions
|
|
@ -24,8 +24,7 @@ from _pydevd_bundle.pydevd_breakpoints import LineBreakpoint
|
|||
from pydevd_tracing import get_exception_traceback_str
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
|
||||
import ctypes
|
||||
|
||||
try:
|
||||
import dis
|
||||
|
|
@ -808,32 +807,42 @@ class PyDevdAPI(object):
|
|||
self.reapply_breakpoints(py_db)
|
||||
return ''
|
||||
|
||||
def get_ppid(self):
|
||||
'''
|
||||
Provides the parent pid (even for older versions of Python on Windows).
|
||||
'''
|
||||
ppid = None
|
||||
|
||||
try:
|
||||
ppid = os.getppid()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if ppid is None and IS_WINDOWS:
|
||||
ppid = self._get_windows_ppid()
|
||||
|
||||
return ppid
|
||||
|
||||
def _get_windows_ppid(self):
|
||||
this_pid = os.getpid()
|
||||
for ppid, pid in _list_ppid_and_pid():
|
||||
if pid == this_pid:
|
||||
return ppid
|
||||
|
||||
return None
|
||||
|
||||
def _terminate_child_processes_windows(self, dont_terminate_child_pids):
|
||||
this_pid = os.getpid()
|
||||
for _ in range(50): # Try this at most 50 times before giving up.
|
||||
|
||||
# Note: we can't kill the process itself with taskkill, so, we
|
||||
# list immediate children, kill that tree and then exit this process.
|
||||
list_popen = self._popen(
|
||||
['wmic', 'process', 'where', '(ParentProcessId=%s)' % (this_pid,), 'get', 'ProcessId'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
if list_popen is None:
|
||||
break # We couldn't create the process.
|
||||
|
||||
stdout, _ = list_popen.communicate()
|
||||
children_pids = []
|
||||
for line in stdout.splitlines():
|
||||
line = line.strip()
|
||||
if line and line != b'ProcessId':
|
||||
try:
|
||||
pid = int(line)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
if pid != list_popen.pid and pid not in dont_terminate_child_pids:
|
||||
children_pids.append(pid)
|
||||
for ppid, pid in _list_ppid_and_pid():
|
||||
if ppid == this_pid:
|
||||
if pid not in dont_terminate_child_pids:
|
||||
children_pids.append(pid)
|
||||
|
||||
if not children_pids:
|
||||
break
|
||||
|
|
@ -943,3 +952,36 @@ class PyDevdAPI(object):
|
|||
py_db.terminate_requested = True
|
||||
run_as_pydevd_daemon_thread(py_db, self._terminate_if_commands_processed, py_db)
|
||||
|
||||
|
||||
def _list_ppid_and_pid():
|
||||
_TH32CS_SNAPPROCESS = 0x00000002
|
||||
|
||||
class PROCESSENTRY32(ctypes.Structure):
|
||||
_fields_ = [("dwSize", ctypes.c_ulong),
|
||||
("cntUsage", ctypes.c_ulong),
|
||||
("th32ProcessID", ctypes.c_ulong),
|
||||
("th32DefaultHeapID", ctypes.c_ulong),
|
||||
("th32ModuleID", ctypes.c_ulong),
|
||||
("cntThreads", ctypes.c_ulong),
|
||||
("th32ParentProcessID", ctypes.c_ulong),
|
||||
("pcPriClassBase", ctypes.c_ulong),
|
||||
("dwFlags", ctypes.c_ulong),
|
||||
("szExeFile", ctypes.c_char * 260)]
|
||||
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
snapshot = kernel32.CreateToolhelp32Snapshot(_TH32CS_SNAPPROCESS, 0)
|
||||
ppid_and_pids = []
|
||||
try:
|
||||
process_entry = PROCESSENTRY32()
|
||||
process_entry.dwSize = ctypes.sizeof(PROCESSENTRY32)
|
||||
if not kernel32.Process32First(snapshot, ctypes.byref(process_entry)):
|
||||
pydev_log.critical('Process32First failed (getting process from CreateToolhelp32Snapshot).')
|
||||
else:
|
||||
while True:
|
||||
ppid_and_pids.append((process_entry.th32ParentProcessID, process_entry.th32ProcessID))
|
||||
if not kernel32.Process32Next(snapshot, ctypes.byref(process_entry)):
|
||||
break
|
||||
finally:
|
||||
kernel32.CloseHandle(snapshot)
|
||||
|
||||
return ppid_and_pids
|
||||
|
|
|
|||
|
|
@ -969,10 +969,7 @@ class PyDevJsonCommandProcessor(object):
|
|||
except AttributeError:
|
||||
pid = None
|
||||
|
||||
try:
|
||||
ppid = os.getppid()
|
||||
except AttributeError:
|
||||
ppid = None
|
||||
ppid = self.api.get_ppid()
|
||||
|
||||
try:
|
||||
impl_desc = platform.python_implementation()
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ from _pydevd_bundle.pydevd_comm import pydevd_find_thread_by_id
|
|||
from _pydevd_bundle.pydevd_utils import convert_dap_log_message_to_expression
|
||||
from tests_python.debug_constants import IS_PY26, IS_PY3K
|
||||
import sys
|
||||
from _pydevd_bundle.pydevd_constants import IS_CPYTHON
|
||||
from _pydevd_bundle.pydevd_constants import IS_CPYTHON, IS_WINDOWS
|
||||
import pytest
|
||||
import os
|
||||
|
||||
|
||||
def test_is_main_thread():
|
||||
|
|
@ -305,7 +306,6 @@ def test_find_main_thread_id():
|
|||
_check_in_separate_process('check_fix_main_thread_id_multiple_threads', '_pydevd_test_find_main_thread_id')
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import pydevd
|
||||
cwd, environ = _build_launch_env()
|
||||
|
||||
|
|
@ -322,3 +322,17 @@ def test_find_main_thread_id():
|
|||
env=environ,
|
||||
cwd=cwd
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not IS_WINDOWS, reason='Windows-only test.')
|
||||
def test_get_ppid():
|
||||
from _pydevd_bundle.pydevd_api import PyDevdAPI
|
||||
api = PyDevdAPI()
|
||||
if IS_PY3K:
|
||||
# On python 3 we can check that our internal api which is used for Python 2 gives the
|
||||
# same result as os.getppid.
|
||||
ppid = os.getppid()
|
||||
assert api._get_windows_ppid() == ppid
|
||||
else:
|
||||
assert api._get_windows_ppid() is not None
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue