From 49474e1e3ac0285462bdc5d255e010ee68c8d79c Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Fri, 1 Mar 2024 21:16:18 +1000 Subject: [PATCH 01/10] Re-implement win32_getppid efficiently. Use the NtQueryInformationProcess system call to efficiently retrieve the parent process ID in a single step, rather than using the process snapshots API which retrieves large amounts of unnecessary information and is more prone to failure (since it makes heap allocations). --- Modules/posixmodule.c | 73 ++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index fd70b38bddec79..d16d7e9b867a86 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9115,33 +9115,70 @@ os_setpgrp_impl(PyObject *module) #ifdef HAVE_GETPPID #ifdef MS_WINDOWS -#include +#include + +// The structure definition in winternl.h may be incomplete. +// This structure is the full version from the MSDN documentation. +typedef struct _PROCESS_BASIC_INFORMATION_FULL { + NTSTATUS ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR AffinityMask; + LONG BasePriority; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION_FULL; static PyObject* win32_getppid(void) { - DWORD error; - PyObject* result = NULL; - HANDLE process = GetCurrentProcess(); + NTSTATUS Status; + PROCESS_BASIC_INFORMATION_FULL BasicInformation; + HMODULE Ntdll; + NTSTATUS (NTAPI *pNtQueryInformationProcess) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); - HPSS snapshot = NULL; - error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot); - if (error != ERROR_SUCCESS) { - return PyErr_SetFromWindowsErr(error); - } + // A GetModuleHandle call with NTDLL as the parameter is guaranteed to succeed on + // all versions of Windows. + Ntdll = GetModuleHandleW(L"ntdll.dll"); + pNtQueryInformationProcess = (NTSTATUS (NTAPI *)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG)) GetProcAddress(Ntdll, "NtQueryInformationProcess"); - PSS_PROCESS_INFORMATION info; - error = PssQuerySnapshot(snapshot, PSS_QUERY_PROCESS_INFORMATION, &info, - sizeof(info)); - if (error == ERROR_SUCCESS) { - result = PyLong_FromUnsignedLong(info.ParentProcessId); + if (!pNtQueryInformationProcess) { + // This code is never going to be executed. It is just here as insurance. + return PyErr_SetFromWindowsErr(GetLastError()); } - else { - result = PyErr_SetFromWindowsErr(error); + + // The NtQueryInformationProcess system call, the ProcessBasicInformation info class + // and the PROCESS_BASIC_INFORMATION structure are all documented on MSDN. + Status = pNtQueryInformationProcess( + GetCurrentProcess(), + ProcessBasicInformation, + &BasicInformation, + sizeof(BasicInformation), + NULL); + + // If the NtQueryInformationProcess fails for any reason (it should never fail given + // the parameters passed to it), we will convert the returned NTSTATUS error code into + // a normal Win32 error code. + if (!NT_SUCCESS(Status)) { + ULONG ErrorCode; + ULONG (NTAPI *pRtlNtStatusToDosError) (NTSTATUS); + + pRtlNtStatusToDosError = (ULONG (NTAPI *) (NTSTATUS)) GetProcAddress(Ntdll, "RtlNtStatusToDosError"); + + if (!pRtlNtStatusToDosError) { + return PyErr_SetFromWindowsErr(GetLastError()); + } + + // Convert the NTSTATUS error code into a Win32 error code. + ErrorCode = pRtlNtStatusToDosError(Status); + return PyErr_SetFromWindowsErr(ErrorCode); } - PssFreeSnapshot(process, snapshot); - return result; + // Now that we have reached this point, the BasicInformation.InheritedFromUniqueProcessId + // structure member contains a ULONG_PTR which represents the process ID of our parent + // process. This process ID will be correctly returned even if the parent process has + // terminated. + + return PyLong_FromUnsignedLong((ULONG) BasicInformation.InheritedFromUniqueProcessId); } #endif /*MS_WINDOWS*/ From 4bb90fb87788e4ca250d0db31f07d56fb0839cd7 Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Sat, 2 Mar 2024 00:42:29 +1000 Subject: [PATCH 02/10] Include fallback for win32_getppid, split new implementation into a separate function --- Modules/posixmodule.c | 101 +++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d16d7e9b867a86..1cd6094046ef31 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9128,26 +9128,39 @@ typedef struct _PROCESS_BASIC_INFORMATION_FULL { ULONG_PTR InheritedFromUniqueProcessId; } PROCESS_BASIC_INFORMATION_FULL; -static PyObject* -win32_getppid(void) +typedef NTSTATUS (NTAPI *PNT_QUERY_INFORMATION_PROCESS) ( + IN HANDLE ProcessHandle, + IN PROCESSINFOCLASS ProcessInformationClass, + OUT PVOID ProcessInformation, + IN ULONG ProcessInformationLength, + OUT PULONG ReturnLength OPTIONAL); + +// This function returns the process ID of the parent process. +// Returns 0 on failure. +static ULONG +win32_getppid_fast(void) { NTSTATUS Status; - PROCESS_BASIC_INFORMATION_FULL BasicInformation; HMODULE Ntdll; - NTSTATUS (NTAPI *pNtQueryInformationProcess) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); + PNT_QUERY_INFORMATION_PROCESS pNtQueryInformationProcess; + PROCESS_BASIC_INFORMATION_FULL BasicInformation; + static ULONG CachedParentProcessId = 0; + + if (CachedParentProcessId) { + // No need to query the kernel again. + return CachedParentProcessId; + } - // A GetModuleHandle call with NTDLL as the parameter is guaranteed to succeed on - // all versions of Windows. Ntdll = GetModuleHandleW(L"ntdll.dll"); - pNtQueryInformationProcess = (NTSTATUS (NTAPI *)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG)) GetProcAddress(Ntdll, "NtQueryInformationProcess"); + if (!Ntdll) { + return 0; + } + pNtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS) GetProcAddress(Ntdll, "NtQueryInformationProcess"); if (!pNtQueryInformationProcess) { - // This code is never going to be executed. It is just here as insurance. - return PyErr_SetFromWindowsErr(GetLastError()); + return 0; } - // The NtQueryInformationProcess system call, the ProcessBasicInformation info class - // and the PROCESS_BASIC_INFORMATION structure are all documented on MSDN. Status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, @@ -9155,30 +9168,68 @@ win32_getppid(void) sizeof(BasicInformation), NULL); - // If the NtQueryInformationProcess fails for any reason (it should never fail given - // the parameters passed to it), we will convert the returned NTSTATUS error code into - // a normal Win32 error code. if (!NT_SUCCESS(Status)) { - ULONG ErrorCode; - ULONG (NTAPI *pRtlNtStatusToDosError) (NTSTATUS); + return 0; + } - pRtlNtStatusToDosError = (ULONG (NTAPI *) (NTSTATUS)) GetProcAddress(Ntdll, "RtlNtStatusToDosError"); + // + // Perform sanity check on the parent process ID we received from NtQueryInformationProcess. + // The check covers values which exceed the 32-bit range (if running on x64) as well as + // zero and (ULONG) -1. + // - if (!pRtlNtStatusToDosError) { - return PyErr_SetFromWindowsErr(GetLastError()); - } + if (BasicInformation.InheritedFromUniqueProcessId == 0 || + BasicInformation.InheritedFromUniqueProcessId >= ULONG_MAX) { - // Convert the NTSTATUS error code into a Win32 error code. - ErrorCode = pRtlNtStatusToDosError(Status); - return PyErr_SetFromWindowsErr(ErrorCode); + return 0; } + // // Now that we have reached this point, the BasicInformation.InheritedFromUniqueProcessId // structure member contains a ULONG_PTR which represents the process ID of our parent // process. This process ID will be correctly returned even if the parent process has - // terminated. + // exited or been terminated. + // - return PyLong_FromUnsignedLong((ULONG) BasicInformation.InheritedFromUniqueProcessId); + CachedParentProcessId = (ULONG) BasicInformation.InheritedFromUniqueProcessId; + return CachedParentProcessId; +} + +static PyObject* +win32_getppid(void) +{ + DWORD error; + PyObject* result = NULL; + HANDLE process = GetCurrentProcess(); + HPSS snapshot = NULL; + ULONG pid; + + pid = win32_getppid_fast(); + if (pid != 0) { + return PyLong_FromUnsignedLong(pid); + } + + // + // If failure occurs in win32_getppid_fast(), fall back to using the PSS API. + // + + error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot); + if (error != ERROR_SUCCESS) { + return PyErr_SetFromWindowsErr(error); + } + + PSS_PROCESS_INFORMATION info; + error = PssQuerySnapshot(snapshot, PSS_QUERY_PROCESS_INFORMATION, &info, + sizeof(info)); + if (error == ERROR_SUCCESS) { + result = PyLong_FromUnsignedLong(info.ParentProcessId); + } + else { + result = PyErr_SetFromWindowsErr(error); + } + + PssFreeSnapshot(process, snapshot); + return result; } #endif /*MS_WINDOWS*/ From 615900282da5cf7c970c47ea029823cc0ece13c1 Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Sat, 2 Mar 2024 01:03:19 +1000 Subject: [PATCH 03/10] Fix a header file that was left out --- Modules/posixmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 1cd6094046ef31..7ebdf1976bb0f9 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9116,6 +9116,7 @@ os_setpgrp_impl(PyObject *module) #ifdef MS_WINDOWS #include +#include // The structure definition in winternl.h may be incomplete. // This structure is the full version from the MSDN documentation. From 466c4d4810ac053955e2b23f1722b2c3b9597c21 Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Sat, 2 Mar 2024 01:26:07 +1000 Subject: [PATCH 04/10] comply with PEP7 Co-authored-by: AN Long --- Modules/posixmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 7ebdf1976bb0f9..5906069fbf317d 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9180,8 +9180,8 @@ win32_getppid_fast(void) // if (BasicInformation.InheritedFromUniqueProcessId == 0 || - BasicInformation.InheritedFromUniqueProcessId >= ULONG_MAX) { - + BasicInformation.InheritedFromUniqueProcessId >= ULONG_MAX) + { return 0; } From c42d5e48d9e78c8db702a4a9609895390cee3076 Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Sun, 3 Mar 2024 20:01:37 +1000 Subject: [PATCH 05/10] Make local variable naming consistent with the rest of the file --- Modules/posixmodule.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5906069fbf317d..bcd255efdeaa36 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9141,35 +9141,35 @@ typedef NTSTATUS (NTAPI *PNT_QUERY_INFORMATION_PROCESS) ( static ULONG win32_getppid_fast(void) { - NTSTATUS Status; - HMODULE Ntdll; + NTSTATUS status; + HMODULE ntdll; PNT_QUERY_INFORMATION_PROCESS pNtQueryInformationProcess; - PROCESS_BASIC_INFORMATION_FULL BasicInformation; - static ULONG CachedParentProcessId = 0; + PROCESS_BASIC_INFORMATION_FULL basic_information; + static ULONG cached_ppid = 0; - if (CachedParentProcessId) { + if (cached_ppid) { // No need to query the kernel again. - return CachedParentProcessId; + return cached_ppid; } - Ntdll = GetModuleHandleW(L"ntdll.dll"); - if (!Ntdll) { + ntdll = GetModuleHandleW(L"ntdll.dll"); + if (!ntdll) { return 0; } - pNtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS) GetProcAddress(Ntdll, "NtQueryInformationProcess"); + pNtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS) GetProcAddress(ntdll, "NtQueryInformationProcess"); if (!pNtQueryInformationProcess) { return 0; } - Status = pNtQueryInformationProcess( + status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, - &BasicInformation, - sizeof(BasicInformation), + &basic_information, + sizeof(basic_information), NULL); - if (!NT_SUCCESS(Status)) { + if (!NT_SUCCESS(status)) { return 0; } @@ -9179,8 +9179,8 @@ win32_getppid_fast(void) // zero and (ULONG) -1. // - if (BasicInformation.InheritedFromUniqueProcessId == 0 || - BasicInformation.InheritedFromUniqueProcessId >= ULONG_MAX) + if (basic_information.InheritedFromUniqueProcessId == 0 || + basic_information.InheritedFromUniqueProcessId >= ULONG_MAX) { return 0; } @@ -9192,8 +9192,8 @@ win32_getppid_fast(void) // exited or been terminated. // - CachedParentProcessId = (ULONG) BasicInformation.InheritedFromUniqueProcessId; - return CachedParentProcessId; + cached_ppid = (ULONG) basic_information.InheritedFromUniqueProcessId; + return cached_ppid; } static PyObject* From 7c06c9dfb22ec9b544489cbc1a5c55d777a2d814 Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Sun, 3 Mar 2024 21:28:26 +1000 Subject: [PATCH 06/10] Update Modules/posixmodule.c Co-authored-by: AN Long --- Modules/posixmodule.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index bcd255efdeaa36..0ebf7ad022bc69 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9162,12 +9162,11 @@ win32_getppid_fast(void) return 0; } - status = pNtQueryInformationProcess( - GetCurrentProcess(), - ProcessBasicInformation, - &basic_information, - sizeof(basic_information), - NULL); + status = pNtQueryInformationProcess(GetCurrentProcess(), + ProcessBasicInformation, + &basic_information, + sizeof(basic_information), + NULL); if (!NT_SUCCESS(status)) { return 0; From 4bcb564eea7a83ec316fc9bf8ed29af05987f501 Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Sun, 3 Mar 2024 21:28:50 +1000 Subject: [PATCH 07/10] make comments more consistent Co-authored-by: AN Long --- Modules/posixmodule.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 0ebf7ad022bc69..266624aee64c6c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9172,11 +9172,9 @@ win32_getppid_fast(void) return 0; } - // // Perform sanity check on the parent process ID we received from NtQueryInformationProcess. // The check covers values which exceed the 32-bit range (if running on x64) as well as // zero and (ULONG) -1. - // if (basic_information.InheritedFromUniqueProcessId == 0 || basic_information.InheritedFromUniqueProcessId >= ULONG_MAX) From 918872aa7d6a6bd4c2e723c49badfd1f14aa88df Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Sun, 3 Mar 2024 21:29:06 +1000 Subject: [PATCH 08/10] make comments more consistent Co-authored-by: AN Long --- Modules/posixmodule.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 266624aee64c6c..14c051af38acea 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9207,9 +9207,7 @@ win32_getppid(void) return PyLong_FromUnsignedLong(pid); } - // // If failure occurs in win32_getppid_fast(), fall back to using the PSS API. - // error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot); if (error != ERROR_SUCCESS) { From cccd46d08115296b06ac3a13df69fa0862bb63a0 Mon Sep 17 00:00:00 2001 From: vxiiduu <73044267+vxiiduu@users.noreply.github.com> Date: Sun, 3 Mar 2024 21:29:29 +1000 Subject: [PATCH 09/10] make comments more consistent Co-authored-by: AN Long --- Modules/posixmodule.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 14c051af38acea..e3456ab3d3ec24 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9182,12 +9182,10 @@ win32_getppid_fast(void) return 0; } - // // Now that we have reached this point, the BasicInformation.InheritedFromUniqueProcessId // structure member contains a ULONG_PTR which represents the process ID of our parent // process. This process ID will be correctly returned even if the parent process has // exited or been terminated. - // cached_ppid = (ULONG) basic_information.InheritedFromUniqueProcessId; return cached_ppid; From 2d081f45335a1e315028df2432aedc50a148243d Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 20:46:24 +0000 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Windows/2024-03-14-20-46-23.gh-issue-116195.Cu_rYs.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Windows/2024-03-14-20-46-23.gh-issue-116195.Cu_rYs.rst diff --git a/Misc/NEWS.d/next/Windows/2024-03-14-20-46-23.gh-issue-116195.Cu_rYs.rst b/Misc/NEWS.d/next/Windows/2024-03-14-20-46-23.gh-issue-116195.Cu_rYs.rst new file mode 100644 index 00000000000000..32122d764e870a --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-03-14-20-46-23.gh-issue-116195.Cu_rYs.rst @@ -0,0 +1 @@ +Improves performance of :func:`os.getppid` by using an alternate system API when available. Contributed by vxiiduu.