diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2019-02-21 07:39:54 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-02-21 07:39:54 -0800 |
commit | 0a30dc9dc2693ffe9aa54126eb684711d950c3f1 (patch) | |
tree | c163b5492c33bee1a57145a81ce2a89cdc405032 | |
parent | ba649e832140fd69e0adb3958ad298b8f7434e69 (diff) | |
download | psutil-0a30dc9dc2693ffe9aa54126eb684711d950c3f1.tar.gz |
Windows / speeup: dynamically load libraries on startup and never again (#1422)
Windows / speeup: dynamically load libraries on startup and never again.
-rw-r--r-- | psutil/_psutil_windows.c | 289 | ||||
-rw-r--r-- | psutil/arch/windows/global.c | 129 | ||||
-rw-r--r-- | psutil/arch/windows/global.h | 11 | ||||
-rw-r--r-- | psutil/arch/windows/ntextapi.h | 293 | ||||
-rw-r--r-- | psutil/arch/windows/process_handles.c | 198 | ||||
-rw-r--r-- | psutil/arch/windows/process_handles.h | 101 | ||||
-rw-r--r-- | psutil/arch/windows/process_info.c | 183 | ||||
-rw-r--r-- | psutil/arch/windows/process_info.h | 2 | ||||
-rwxr-xr-x | psutil/tests/test_windows.py | 5 | ||||
-rwxr-xr-x | setup.py | 1 |
10 files changed, 581 insertions, 631 deletions
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index d6058b8b..3908884e 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -22,6 +22,8 @@ #include <ws2tcpip.h> #endif #include <iphlpapi.h> +#include <iprtrmib.h> +#include <udpmib.h> #include <wtsapi32.h> #include <Winsvc.h> #include <PowrProf.h> @@ -30,13 +32,14 @@ // Link with Iphlpapi.lib #pragma comment(lib, "IPHLPAPI.lib") -#include "_psutil_common.h" +#include "arch/windows/ntextapi.h" +#include "arch/windows/global.h" #include "arch/windows/security.h" #include "arch/windows/process_info.h" #include "arch/windows/process_handles.h" -#include "arch/windows/ntextapi.h" #include "arch/windows/inet_ntop.h" #include "arch/windows/services.h" +#include "_psutil_common.h" /* @@ -53,109 +56,6 @@ #ifndef AF_INET6 #define AF_INET6 23 #endif -#define _psutil_conn_decref_objs() \ - Py_DECREF(_AF_INET); \ - Py_DECREF(_AF_INET6);\ - Py_DECREF(_SOCK_STREAM);\ - Py_DECREF(_SOCK_DGRAM); - -#if (_WIN32_WINNT >= 0x0601) // Windows 7 -typedef BOOL (WINAPI *PFN_GETLOGICALPROCESSORINFORMATIONEX)( - LOGICAL_PROCESSOR_RELATIONSHIP relationship, - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer, - PDWORD ReturnLength); -static PFN_GETLOGICALPROCESSORINFORMATIONEX _GetLogicalProcessorInformationEx; -#endif - -// Fix for mingw32, see: -// https://github.com/giampaolo/psutil/issues/351#c2 -// This is actually a DISK_PERFORMANCE struct: -// https://msdn.microsoft.com/en-us/library/windows/desktop/ -// aa363991(v=vs.85).aspx -typedef struct _DISK_PERFORMANCE_WIN_2008 { - LARGE_INTEGER BytesRead; - LARGE_INTEGER BytesWritten; - LARGE_INTEGER ReadTime; - LARGE_INTEGER WriteTime; - LARGE_INTEGER IdleTime; - DWORD ReadCount; - DWORD WriteCount; - DWORD QueueDepth; - DWORD SplitCount; - LARGE_INTEGER QueryTime; - DWORD StorageDeviceNumber; - WCHAR StorageManagerName[8]; -} DISK_PERFORMANCE_WIN_2008; - -// --- network connections mingw32 support -#ifndef _IPRTRMIB_H -#if (_WIN32_WINNT < 0x0600) // Windows XP -typedef struct _MIB_TCP6ROW_OWNER_PID { - UCHAR ucLocalAddr[16]; - DWORD dwLocalScopeId; - DWORD dwLocalPort; - UCHAR ucRemoteAddr[16]; - DWORD dwRemoteScopeId; - DWORD dwRemotePort; - DWORD dwState; - DWORD dwOwningPid; -} MIB_TCP6ROW_OWNER_PID, *PMIB_TCP6ROW_OWNER_PID; - -typedef struct _MIB_TCP6TABLE_OWNER_PID { - DWORD dwNumEntries; - MIB_TCP6ROW_OWNER_PID table[ANY_SIZE]; -} MIB_TCP6TABLE_OWNER_PID, *PMIB_TCP6TABLE_OWNER_PID; -#endif -#endif - -#ifndef __IPHLPAPI_H__ -typedef struct in6_addr { - union { - UCHAR Byte[16]; - USHORT Word[8]; - } u; -} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR; - -typedef enum _UDP_TABLE_CLASS { - UDP_TABLE_BASIC, - UDP_TABLE_OWNER_PID, - UDP_TABLE_OWNER_MODULE -} UDP_TABLE_CLASS, *PUDP_TABLE_CLASS; - -typedef struct _MIB_UDPROW_OWNER_PID { - DWORD dwLocalAddr; - DWORD dwLocalPort; - DWORD dwOwningPid; -} MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID; - -typedef struct _MIB_UDPTABLE_OWNER_PID { - DWORD dwNumEntries; - MIB_UDPROW_OWNER_PID table[ANY_SIZE]; -} MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID; -#endif - -#if (_WIN32_WINNT < 0x0600) // Windows XP -typedef struct _MIB_UDP6ROW_OWNER_PID { - UCHAR ucLocalAddr[16]; - DWORD dwLocalScopeId; - DWORD dwLocalPort; - DWORD dwOwningPid; -} MIB_UDP6ROW_OWNER_PID, *PMIB_UDP6ROW_OWNER_PID; - -typedef struct _MIB_UDP6TABLE_OWNER_PID { - DWORD dwNumEntries; - MIB_UDP6ROW_OWNER_PID table[ANY_SIZE]; -} MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; -#endif - -typedef struct _PROCESSOR_POWER_INFORMATION { - ULONG Number; - ULONG MaxMhz; - ULONG CurrentMhz; - ULONG MhzLimit; - ULONG MaxIdleState; - ULONG CurrentIdleState; -} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION; PIP_ADAPTER_ADDRESSES @@ -204,15 +104,10 @@ unsigned int psutil_get_num_cpus(int fail_on_err) { unsigned int ncpus = 0; SYSTEM_INFO sysinfo; - static DWORD(CALLBACK *_GetActiveProcessorCount)(WORD) = NULL; - - // GetActiveProcessorCount is available only on 64 bit versions - // of Windows from Windows 7 onward. - // Windows Vista 64 bit and Windows XP don't have it. - _GetActiveProcessorCount = \ - psutil_GetProcAddress("kernel32", "GetActiveProcessorCount"); - if (_GetActiveProcessorCount != NULL) { - ncpus = _GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + + // Minimum requirement: Windows 7 + if (psutil_GetActiveProcessorCount != NULL) { + ncpus = psutil_GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); if ((ncpus == 0) && (fail_on_err == 1)) { PyErr_SetFromWindowsErr(0); } @@ -242,8 +137,6 @@ psutil_get_num_cpus(int fail_on_err) { static PyObject *TimeoutExpired; static PyObject *TimeoutAbandoned; -static ULONGLONG (*psutil_GetTickCount64)(void) = NULL; - /* * Return a Python float representing the system uptime expressed in seconds * since the epoch. @@ -258,7 +151,6 @@ psutil_boot_time(PyObject *self, PyObject *args) { time_t pt; FILETIME fileTime; long long ll; - psutil_GetTickCount64; GetSystemTimeAsFileTime(&fileTime); /* @@ -287,7 +179,6 @@ psutil_boot_time(PyObject *self, PyObject *args) { // "#if (_WIN32_WINNT >= 0x0600)" pre-processor but that way // the produced exe/wheels cannot be used on Windows XP, see: // https://github.com/giampaolo/psutil/issues/811#issuecomment-230639178 - psutil_GetTickCount64 = psutil_GetProcAddress("kernel32", "GetTickCount64"); if (psutil_GetTickCount64 != NULL) { // Windows >= Vista uptime = psutil_GetTickCount64() / (ULONGLONG)1000.00f; @@ -297,6 +188,8 @@ psutil_boot_time(PyObject *self, PyObject *args) { // Windows XP. // GetTickCount() time will wrap around to zero if the // system is run continuously for 49.7 days. + psutil_debug("Windows < Vista; using GetTickCount() instead of " + "GetTickCount64()"); uptime = GetTickCount() / (LONGLONG)1000.00f; return Py_BuildValue("L", pt - uptime); } @@ -611,17 +504,6 @@ psutil_cpu_count_logical(PyObject *self, PyObject *args) { * Return the number of physical CPU cores (hyper-thread CPUs count * is excluded). */ -#if (_WIN32_WINNT < 0x0601) // < Windows 7 (namely Vista and XP) -static PyObject * -psutil_cpu_count_phys(PyObject *self, PyObject *args) { - // Note: we may have used GetLogicalProcessorInformation() - // but I don't want to prolong support for Windows XP and Vista. - // On such old systems psutil will compile but this API will - // just return None. - psutil_debug("Win < 7; cpu_count_phys() forced to None"); - Py_RETURN_NONE; -} -#else // Windows >= 7 static PyObject * psutil_cpu_count_phys(PyObject *self, PyObject *args) { DWORD rc; @@ -636,13 +518,13 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) { // it supports process groups, meaning this is able to report more // than 64 CPUs. See: // https://bugs.python.org/issue33166 - _GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib( - "kernel32", "GetLogicalProcessorInformationEx"); - if (_GetLogicalProcessorInformationEx == NULL) - return NULL; + if (psutil_GetLogicalProcessorInformationEx == NULL) { + psutil_debug("Win < 7; cpu_count_phys() forced to None"); + Py_RETURN_NONE; + } while (1) { - rc = _GetLogicalProcessorInformationEx( + rc = psutil_GetLogicalProcessorInformationEx( RelationAll, buffer, &length); if (rc == FALSE) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { @@ -691,7 +573,6 @@ return_none: free(buffer); Py_RETURN_NONE; } -#endif /* @@ -1044,10 +925,6 @@ psutil_cpu_times(PyObject *self, PyObject *args) { */ static PyObject * psutil_per_cpu_times(PyObject *self, PyObject *args) { - // NtQuerySystemInformation stuff - typedef DWORD (_stdcall * NTQSI_PROC) (int, PVOID, ULONG, PULONG); - NTQSI_PROC NtQuerySystemInformation; - double idle, kernel, systemt, user, interrupt, dpc; NTSTATUS status; _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; @@ -1058,10 +935,6 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - NtQuerySystemInformation = \ - psutil_GetProcAddressFromLib("ntdll.dll", "NtQuerySystemInformation"); - if (NtQuerySystemInformation == NULL) - goto error; // retrieves number of processors ncpus = psutil_get_num_cpus(1); @@ -1078,7 +951,7 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) { } // gets cpu time informations - status = NtQuerySystemInformation( + status = psutil_NtQuerySystemInformation( SystemProcessorPerformanceInformation, sppi, ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), @@ -1537,10 +1410,6 @@ error: } -typedef DWORD (WINAPI * _GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG, - TCP_TABLE_CLASS, ULONG); - - // https://msdn.microsoft.com/library/aa365928.aspx static DWORD __GetExtendedTcpTable(_GetExtendedTcpTable call, ULONG address_family, @@ -1575,10 +1444,6 @@ static DWORD __GetExtendedTcpTable(_GetExtendedTcpTable call, } -typedef DWORD (WINAPI * _GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG, - UDP_TABLE_CLASS, ULONG); - - // https://msdn.microsoft.com/library/aa365930.aspx static DWORD __GetExtendedUdpTable(_GetExtendedUdpTable call, ULONG address_family, @@ -1613,6 +1478,13 @@ static DWORD __GetExtendedUdpTable(_GetExtendedUdpTable call, } +#define psutil_conn_decref_objs() \ + Py_DECREF(_AF_INET); \ + Py_DECREF(_AF_INET6);\ + Py_DECREF(_SOCK_STREAM);\ + Py_DECREF(_SOCK_DGRAM); + + /* * Return a list of network connections opened by a process */ @@ -1621,12 +1493,6 @@ psutil_net_connections(PyObject *self, PyObject *args) { static long null_address[4] = { 0, 0, 0, 0 }; unsigned long pid; int pid_return; - typedef PSTR (NTAPI * _RtlIpv4AddressToStringA)(struct in_addr *, PSTR); - _RtlIpv4AddressToStringA rtlIpv4AddressToStringA; - typedef PSTR (NTAPI * _RtlIpv6AddressToStringA)(struct in6_addr *, PSTR); - _RtlIpv6AddressToStringA rtlIpv6AddressToStringA; - _GetExtendedTcpTable getExtendedTcpTable; - _GetExtendedUdpTable getExtendedUdpTable; PVOID table = NULL; DWORD tableSize; DWORD error; @@ -1650,28 +1516,11 @@ psutil_net_connections(PyObject *self, PyObject *args) { PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM); // Import some functions. - rtlIpv4AddressToStringA = psutil_GetProcAddressFromLib( - "ntdll.dll", "RtlIpv4AddressToStringA"); - if (rtlIpv4AddressToStringA == NULL) - goto error; - rtlIpv6AddressToStringA = psutil_GetProcAddressFromLib( - "ntdll.dll", "RtlIpv6AddressToStringA"); - if (rtlIpv6AddressToStringA == NULL) - goto error; - getExtendedTcpTable = psutil_GetProcAddressFromLib( - "iphlpapi.dll", "GetExtendedTcpTable"); - if (getExtendedTcpTable == NULL) - goto error; - getExtendedUdpTable = psutil_GetProcAddressFromLib( - "iphlpapi.dll", "GetExtendedUdpTable"); - if (getExtendedUdpTable == NULL) - goto error; - if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter)) goto error; if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { - _psutil_conn_decref_objs(); + psutil_conn_decref_objs(); PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); return NULL; } @@ -1679,25 +1528,18 @@ psutil_net_connections(PyObject *self, PyObject *args) { if (pid != -1) { pid_return = psutil_pid_is_running(pid); if (pid_return == 0) { - _psutil_conn_decref_objs(); + psutil_conn_decref_objs(); return NoSuchProcess(""); } else if (pid_return == -1) { - _psutil_conn_decref_objs(); + psutil_conn_decref_objs(); return NULL; } } - if ((getExtendedTcpTable == NULL) || (getExtendedUdpTable == NULL)) { - PyErr_SetString(PyExc_NotImplementedError, - "feature not supported on this Windows version"); - _psutil_conn_decref_objs(); - return NULL; - } - py_retlist = PyList_New(0); if (py_retlist == NULL) { - _psutil_conn_decref_objs(); + psutil_conn_decref_objs(); return NULL; } @@ -1712,7 +1554,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { py_addr_tuple_remote = NULL; tableSize = 0; - error = __GetExtendedTcpTable(getExtendedTcpTable, + error = __GetExtendedTcpTable(psutil_GetExtendedTcpTable, AF_INET, &table, &tableSize); if (error == ERROR_NOT_ENOUGH_MEMORY) { PyErr_NoMemory(); @@ -1737,7 +1579,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { struct in_addr addr; addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; - rtlIpv4AddressToStringA(&addr, addressBufferLocal); + psutil_rtlIpv4AddressToStringA(&addr, addressBufferLocal); py_addr_tuple_local = Py_BuildValue( "(si)", addressBufferLocal, @@ -1759,7 +1601,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { struct in_addr addr; addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr; - rtlIpv4AddressToStringA(&addr, addressBufferRemote); + psutil_rtlIpv4AddressToStringA(&addr, addressBufferRemote); py_addr_tuple_remote = Py_BuildValue( "(si)", addressBufferRemote, @@ -1809,7 +1651,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { py_addr_tuple_remote = NULL; tableSize = 0; - error = __GetExtendedTcpTable(getExtendedTcpTable, + error = __GetExtendedTcpTable(psutil_GetExtendedTcpTable, AF_INET6, &table, &tableSize); if (error == ERROR_NOT_ENOUGH_MEMORY) { PyErr_NoMemory(); @@ -1834,7 +1676,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { struct in6_addr addr; memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16); - rtlIpv6AddressToStringA(&addr, addressBufferLocal); + psutil_rtlIpv6AddressToStringA(&addr, addressBufferLocal); py_addr_tuple_local = Py_BuildValue( "(si)", addressBufferLocal, @@ -1857,7 +1699,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { struct in6_addr addr; memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16); - rtlIpv6AddressToStringA(&addr, addressBufferRemote); + psutil_rtlIpv6AddressToStringA(&addr, addressBufferRemote); py_addr_tuple_remote = Py_BuildValue( "(si)", addressBufferRemote, @@ -1906,7 +1748,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { py_addr_tuple_local = NULL; py_addr_tuple_remote = NULL; tableSize = 0; - error = __GetExtendedUdpTable(getExtendedUdpTable, + error = __GetExtendedUdpTable(psutil_GetExtendedUdpTable, AF_INET, &table, &tableSize); if (error == ERROR_NOT_ENOUGH_MEMORY) { PyErr_NoMemory(); @@ -1931,7 +1773,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { struct in_addr addr; addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; - rtlIpv4AddressToStringA(&addr, addressBufferLocal); + psutil_rtlIpv4AddressToStringA(&addr, addressBufferLocal); py_addr_tuple_local = Py_BuildValue( "(si)", addressBufferLocal, @@ -1980,7 +1822,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { py_addr_tuple_local = NULL; py_addr_tuple_remote = NULL; tableSize = 0; - error = __GetExtendedUdpTable(getExtendedUdpTable, + error = __GetExtendedUdpTable(psutil_GetExtendedUdpTable, AF_INET6, &table, &tableSize); if (error == ERROR_NOT_ENOUGH_MEMORY) { PyErr_NoMemory(); @@ -2004,7 +1846,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { struct in6_addr addr; memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16); - rtlIpv6AddressToStringA(&addr, addressBufferLocal); + psutil_rtlIpv6AddressToStringA(&addr, addressBufferLocal); py_addr_tuple_local = Py_BuildValue( "(si)", addressBufferLocal, @@ -2043,11 +1885,11 @@ psutil_net_connections(PyObject *self, PyObject *args) { tableSize = 0; } - _psutil_conn_decref_objs(); + psutil_conn_decref_objs(); return py_retlist; error: - _psutil_conn_decref_objs(); + psutil_conn_decref_objs(); Py_XDECREF(py_conn_tuple); Py_XDECREF(py_addr_tuple_local); Py_XDECREF(py_addr_tuple_remote); @@ -2123,19 +1965,13 @@ psutil_proc_io_priority_get(PyObject *self, PyObject *args) { long pid; HANDLE hProcess; DWORD IoPriority; - _NtQueryInformationProcess NtQueryInformationProcess; - NtQueryInformationProcess = \ - psutil_GetProcAddress("ntdll.dll", "NtQueryInformationProcess"); - if (NtQueryInformationProcess == NULL) - return NULL; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); if (hProcess == NULL) return NULL; - - NtQueryInformationProcess( + psutil_NtQueryInformationProcess( hProcess, ProcessIoPriority, &IoPriority, @@ -2156,19 +1992,14 @@ psutil_proc_io_priority_set(PyObject *self, PyObject *args) { DWORD prio; HANDLE hProcess; DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; - _NtSetInformationProcess NtSetInformationProcess; - NtSetInformationProcess = \ - psutil_GetProcAddress("ntdll.dll", "NtSetInformationProcess"); - if (NtSetInformationProcess == NULL) - return NULL; if (! PyArg_ParseTuple(args, "li", &pid, &prio)) return NULL; hProcess = psutil_handle_from_pid(pid, access); if (hProcess == NULL) return NULL; - NtSetInformationProcess( + psutil_NtSetInformationProcess( hProcess, ProcessIoPriority, (PVOID)&prio, @@ -2461,7 +2292,7 @@ error: */ static PyObject * psutil_disk_io_counters(PyObject *self, PyObject *args) { - DISK_PERFORMANCE_WIN_2008 diskPerformance; + DISK_PERFORMANCE diskPerformance; DWORD dwSize; HANDLE hDevice = NULL; char szDevice[MAX_PATH]; @@ -2764,7 +2595,6 @@ psutil_users(PyObject *self, PyObject *args) { PWTS_CLIENT_ADDRESS address; char address_str[50]; long long unix_time; - PWINSTATIONQUERYINFORMATIONW WinStationQueryInformationW; WINSTATION_INFO station_info; ULONG returnLen; PyObject *py_tuple = NULL; @@ -2775,11 +2605,6 @@ psutil_users(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - WinStationQueryInformationW = psutil_GetProcAddressFromLib( - "winsta.dll", "WinStationQueryInformationW"); - if (WinStationQueryInformationW == NULL) - goto error; - if (WTSEnumerateSessions(hServer, 0, 1, &sessions, &count) == 0) { PyErr_SetFromWindowsErr(0); goto error; @@ -2833,12 +2658,13 @@ psutil_users(PyObject *self, PyObject *args) { } // login time - if (!WinStationQueryInformationW(hServer, - sessionId, - WinStationInformation, - &station_info, - sizeof(station_info), - &returnLen)) + if (! psutil_WinStationQueryInformationW( + hServer, + sessionId, + WinStationInformation, + &station_info, + sizeof(station_info), + &returnLen)) { goto error; } @@ -3478,8 +3304,6 @@ error: */ static PyObject * psutil_cpu_stats(PyObject *self, PyObject *args) { - typedef DWORD (_stdcall * NTQSI_PROC) (int, PVOID, ULONG, PULONG); - NTQSI_PROC NtQuerySystemInformation; NTSTATUS status; _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL; _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; @@ -3489,11 +3313,6 @@ psutil_cpu_stats(PyObject *self, PyObject *args) { ULONG64 dpcs = 0; ULONG interrupts = 0; - NtQuerySystemInformation = \ - psutil_GetProcAddressFromLib("ntdll.dll", "NtQuerySystemInformation"); - if (NtQuerySystemInformation == NULL) - return NULL; - // retrieves number of processors ncpus = psutil_get_num_cpus(1); if (ncpus == 0) @@ -3506,7 +3325,7 @@ psutil_cpu_stats(PyObject *self, PyObject *args) { PyErr_NoMemory(); goto error; } - status = NtQuerySystemInformation( + status = psutil_NtQuerySystemInformation( SystemPerformanceInformation, spi, ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION), @@ -3524,7 +3343,7 @@ psutil_cpu_stats(PyObject *self, PyObject *args) { goto error; } - status = NtQuerySystemInformation( + status = psutil_NtQuerySystemInformation( SystemInterruptInformation, InterruptInformation, ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION), @@ -3545,7 +3364,7 @@ psutil_cpu_stats(PyObject *self, PyObject *args) { goto error; } - status = NtQuerySystemInformation( + status = psutil_NtQuerySystemInformation( SystemProcessorPerformanceInformation, sppi, ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), @@ -3939,6 +3758,8 @@ void init_psutil_windows(void) // set SeDebug for the current process psutil_set_se_debug(); psutil_setup(); + if (psutil_loadlibs() != 0) + return NULL; #if PY_MAJOR_VERSION >= 3 return module; diff --git a/psutil/arch/windows/global.c b/psutil/arch/windows/global.c new file mode 100644 index 00000000..bb9970fe --- /dev/null +++ b/psutil/arch/windows/global.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <windows.h> +#include <Python.h> +#include "ntextapi.h" +#include "global.h" + + +// A wrapper around GetModuleHandle and GetProcAddress. +PVOID +psutil_GetProcAddress(LPCSTR libname, LPCSTR procname) { + HMODULE mod; + FARPROC addr; + + if ((mod = GetModuleHandleA(libname)) == NULL) { + PyErr_SetFromWindowsErrWithFilename(0, libname); + return NULL; + } + if ((addr = GetProcAddress(mod, procname)) == NULL) { + PyErr_SetFromWindowsErrWithFilename(0, procname); + return NULL; + } + return addr; +} + + +// A wrapper around LoadLibrary and GetProcAddress. +PVOID +psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname) { + HMODULE mod; + FARPROC addr; + + Py_BEGIN_ALLOW_THREADS + mod = LoadLibraryA(libname); + Py_END_ALLOW_THREADS + if (mod == NULL) { + PyErr_SetFromWindowsErrWithFilename(0, libname); + return NULL; + } + if ((addr = GetProcAddress(mod, procname)) == NULL) { + PyErr_SetFromWindowsErrWithFilename(0, procname); + FreeLibrary(mod); + return NULL; + } + // Causes crash. + // FreeLibrary(mod); + return addr; +} + + +/* + * This is executed on import and loads Windows APIs so that they + * are available globally. + */ +psutil_loadlibs() { + /* + * Mandatory. + */ + + psutil_NtQuerySystemInformation = psutil_GetProcAddressFromLib( + "ntdll.dll", "NtQuerySystemInformation"); + if (psutil_NtQuerySystemInformation == NULL) + return 1; + + psutil_NtQueryInformationProcess = psutil_GetProcAddress( + "ntdll.dll", "NtQueryInformationProcess"); + if (! psutil_NtQueryInformationProcess) + return 1; + + psutil_NtSetInformationProcess = psutil_GetProcAddress( + "ntdll.dll", "NtSetInformationProcess"); + if (! psutil_NtSetInformationProcess) + return 1; + + psutil_WinStationQueryInformationW = psutil_GetProcAddressFromLib( + "winsta.dll", "WinStationQueryInformationW"); + if (! psutil_WinStationQueryInformationW) + return 1; + + psutil_NtQueryObject = psutil_GetProcAddressFromLib( + "ntdll.dll", "NtQueryObject"); + if (! psutil_NtQueryObject) + return 1; + + psutil_rtlIpv4AddressToStringA = psutil_GetProcAddressFromLib( + "ntdll.dll", "RtlIpv4AddressToStringA"); + if (! psutil_rtlIpv4AddressToStringA) + return 1; + + psutil_rtlIpv6AddressToStringA = psutil_GetProcAddressFromLib( + "ntdll.dll", "RtlIpv6AddressToStringA"); + if (! psutil_rtlIpv6AddressToStringA) + return 1; + + // minimum requirement: Win XP SP3 + psutil_GetExtendedTcpTable = psutil_GetProcAddressFromLib( + "iphlpapi.dll", "GetExtendedTcpTable"); + if (! psutil_GetExtendedTcpTable) + return 1; + + // minimum requirement: Win XP SP3 + psutil_GetExtendedUdpTable = psutil_GetProcAddressFromLib( + "iphlpapi.dll", "GetExtendedUdpTable"); + if (! psutil_GetExtendedUdpTable) + return 1; + + /* + * Optional. + */ + + // minimum requirement: Win Vista + psutil_GetTickCount64 = psutil_GetProcAddress( + "kernel32", "GetTickCount64"); + + // minimum requirement: Win 7 + psutil_GetActiveProcessorCount = psutil_GetProcAddress( + "kernel32", "GetActiveProcessorCount"); + + // minumum requirement: Win 7 + psutil_GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib( + "kernel32", "GetLogicalProcessorInformationEx"); + + PyErr_Clear(); + return 0; +} diff --git a/psutil/arch/windows/global.h b/psutil/arch/windows/global.h new file mode 100644 index 00000000..8d828776 --- /dev/null +++ b/psutil/arch/windows/global.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <windows.h> + +int psutil_loadlibs(); +PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname); +PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname); diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index ea23ddb7..e0006c7b 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -6,7 +6,75 @@ #if !defined(__NTEXTAPI_H__) #define __NTEXTAPI_H__ #include <winternl.h> +#include <iphlpapi.h> +typedef LONG NTSTATUS; + +#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 +#define STATUS_BUFFER_TOO_SMALL 0xC0000023L +#define SystemExtendedHandleInformation 64 + +/* + * ================================================================ + * Enums. + * ================================================================ + */ + +typedef enum _PROCESSINFOCLASS2 { + _ProcessBasicInformation, + ProcessQuotaLimits, + ProcessIoCounters, + ProcessVmCounters, + ProcessTimes, + ProcessBasePriority, + ProcessRaisePriority, + _ProcessDebugPort, + ProcessExceptionPort, + ProcessAccessToken, + ProcessLdtInformation, + ProcessLdtSize, + ProcessDefaultHardErrorMode, + ProcessIoPortHandlers, + ProcessPooledUsageAndLimits, + ProcessWorkingSetWatch, + ProcessUserModeIOPL, + ProcessEnableAlignmentFaultFixup, + ProcessPriorityClass, + ProcessWx86Information, + ProcessHandleCount, + ProcessAffinityMask, + ProcessPriorityBoost, + ProcessDeviceMap, + ProcessSessionInformation, + ProcessForegroundInformation, + _ProcessWow64Information, + /* added after XP+ */ + _ProcessImageFileName, + ProcessLUIDDeviceMapsEnabled, + _ProcessBreakOnTermination, + ProcessDebugObjectHandle, + ProcessDebugFlags, + ProcessHandleTracing, + ProcessIoPriority, + ProcessExecuteFlags, + ProcessResourceManagement, + ProcessCookie, + ProcessImageInformation, + MaxProcessInfoClass +} PROCESSINFOCLASS2; + +#define PROCESSINFOCLASS PROCESSINFOCLASS2 +#define ProcessBasicInformation _ProcessBasicInformation +#define ProcessWow64Information _ProcessWow64Information +#define ProcessDebugPort _ProcessDebugPort +#define ProcessImageFileName _ProcessImageFileName +#define ProcessBreakOnTermination _ProcessBreakOnTermination + +/* + * ================================================================ + * Structs. + * ================================================================ + */ typedef struct { LARGE_INTEGER IdleTime; @@ -17,7 +85,6 @@ typedef struct { ULONG InterruptCount; } _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; - typedef struct { LARGE_INTEGER IdleProcessTime; LARGE_INTEGER IoReadTransferCount; @@ -93,10 +160,8 @@ typedef struct { ULONG FirstLevelTbFills; ULONG SecondLevelTbFills; ULONG SystemCalls; - } _SYSTEM_PERFORMANCE_INFORMATION; - typedef struct { ULONG ContextSwitches; ULONG DpcCount; @@ -106,7 +171,6 @@ typedef struct { ULONG ApcBypassCount; } _SYSTEM_INTERRUPT_INFORMATION; - typedef enum _KTHREAD_STATE { Initialized, Ready, @@ -120,7 +184,6 @@ typedef enum _KTHREAD_STATE { MaximumThreadState } KTHREAD_STATE, *PKTHREAD_STATE; - typedef enum _KWAIT_REASON { Executive = 0, FreePage = 1, @@ -162,6 +225,22 @@ typedef enum _KWAIT_REASON { MaximumWaitReason = 37 } KWAIT_REASON, *PKWAIT_REASON; +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { + PVOID Object; + HANDLE UniqueProcessId; + HANDLE HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX { + ULONG_PTR NumberOfHandles; + ULONG_PTR Reserved; + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; typedef struct _CLIENT_ID2 { HANDLE UniqueProcess; @@ -190,8 +269,6 @@ typedef struct _SYSTEM_THREAD_INFORMATION2 { typedef struct _TEB *PTEB; - -// private typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION { SYSTEM_THREAD_INFORMATION ThreadInfo; PVOID StackBase; @@ -203,7 +280,6 @@ typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION { ULONG_PTR Reserved4; } SYSTEM_EXTENDED_THREAD_INFORMATION, *PSYSTEM_EXTENDED_THREAD_INFORMATION; - typedef struct _SYSTEM_PROCESS_INFORMATION2 { ULONG NextEntryOffset; ULONG NumberOfThreads; @@ -244,10 +320,35 @@ typedef struct _SYSTEM_PROCESS_INFORMATION2 { #define SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION2 #define PSYSTEM_PROCESS_INFORMATION PSYSTEM_PROCESS_INFORMATION2 - -// ================================================ -// psutil.users() support -// ================================================ +typedef struct _PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION; + +#ifndef __IPHLPAPI_H__ +typedef struct in6_addr { + union { + UCHAR Byte[16]; + USHORT Word[8]; + } u; +} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR; +#endif + +// http://msdn.microsoft.com/en-us/library/aa813741(VS.85).aspx +typedef struct { + BYTE Reserved1[16]; + PVOID Reserved2[5]; + UNICODE_STRING CurrentDirectoryPath; + PVOID CurrentDirectoryHandle; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + LPCWSTR env; +} RTL_USER_PROCESS_PARAMETERS_, *PRTL_USER_PROCESS_PARAMETERS_; typedef struct _WINSTATION_INFO { BYTE Reserved1[72]; @@ -261,18 +362,38 @@ typedef struct _WINSTATION_INFO { FILETIME CurrentTime; } WINSTATION_INFO, *PWINSTATION_INFO; - -typedef BOOLEAN (WINAPI * PWINSTATIONQUERYINFORMATIONW) - (HANDLE,ULONG,WINSTATIONINFOCLASS,PVOID,ULONG,PULONG); - +#if (_WIN32_WINNT < 0x0601) // Windows < 7 (Vista and XP) +typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { + LOGICAL_PROCESSOR_RELATIONSHIP Relationship; + DWORD Size; + _ANONYMOUS_UNION + union { + PROCESSOR_RELATIONSHIP Processor; + NUMA_NODE_RELATIONSHIP NumaNode; + CACHE_RELATIONSHIP Cache; + GROUP_RELATIONSHIP Group; + } DUMMYUNIONNAME; +} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; +#endif /* - * NtQueryInformationProcess code taken from - * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/ - * typedefs needed to compile against ntdll functions not exposted in the API + * ================================================================ + * Type defs for modules loaded at runtime. + * ================================================================ */ -typedef LONG NTSTATUS; +typedef BOOL (WINAPI *_GetLogicalProcessorInformationEx)( + LOGICAL_PROCESSOR_RELATIONSHIP relationship, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer, + PDWORD ReturnLength); + +typedef BOOLEAN (WINAPI * _WinStationQueryInformationW)( + HANDLE ServerHandle, + ULONG SessionId, + WINSTATIONINFOCLASS WinStationInformationClass, + PVOID pWinStationInformation, + ULONG WinStationInformationLength, + PULONG pReturnLength); typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( HANDLE ProcessHandle, @@ -282,6 +403,12 @@ typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( PDWORD ReturnLength ); +typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength +); typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( HANDLE ProcessHandle, @@ -290,56 +417,86 @@ typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( DWORD ProcessInformationLength ); +typedef PSTR (NTAPI * _RtlIpv4AddressToStringA)( + struct in_addr *Addr, + PSTR S); + +typedef PSTR (NTAPI * _RtlIpv6AddressToStringA)( + struct in6_addr *Addr, + PSTR P); + +typedef DWORD (WINAPI * _GetExtendedTcpTable)( + PVOID pTcpTable, + PDWORD pdwSize, + BOOL bOrder, + ULONG ulAf, + TCP_TABLE_CLASS TableClass, + ULONG Reserved +); -typedef enum _PROCESSINFOCLASS2 { - _ProcessBasicInformation, - ProcessQuotaLimits, - ProcessIoCounters, - ProcessVmCounters, - ProcessTimes, - ProcessBasePriority, - ProcessRaisePriority, - _ProcessDebugPort, - ProcessExceptionPort, - ProcessAccessToken, - ProcessLdtInformation, - ProcessLdtSize, - ProcessDefaultHardErrorMode, - ProcessIoPortHandlers, - ProcessPooledUsageAndLimits, - ProcessWorkingSetWatch, - ProcessUserModeIOPL, - ProcessEnableAlignmentFaultFixup, - ProcessPriorityClass, - ProcessWx86Information, - ProcessHandleCount, - ProcessAffinityMask, - ProcessPriorityBoost, - ProcessDeviceMap, - ProcessSessionInformation, - ProcessForegroundInformation, - _ProcessWow64Information, - /* added after XP+ */ - _ProcessImageFileName, - ProcessLUIDDeviceMapsEnabled, - _ProcessBreakOnTermination, - ProcessDebugObjectHandle, - ProcessDebugFlags, - ProcessHandleTracing, - ProcessIoPriority, - ProcessExecuteFlags, - ProcessResourceManagement, - ProcessCookie, - ProcessImageInformation, - MaxProcessInfoClass -} PROCESSINFOCLASS2; +typedef DWORD (WINAPI * _GetExtendedUdpTable)( + PVOID pUdpTable, + PDWORD pdwSize, + BOOL bOrder, + ULONG ulAf, + UDP_TABLE_CLASS TableClass, + ULONG Reserved +); +typedef DWORD (CALLBACK *_GetActiveProcessorCount)( + WORD GroupNumber); -#define PROCESSINFOCLASS PROCESSINFOCLASS2 -#define ProcessBasicInformation _ProcessBasicInformation -#define ProcessWow64Information _ProcessWow64Information -#define ProcessDebugPort _ProcessDebugPort -#define ProcessImageFileName _ProcessImageFileName -#define ProcessBreakOnTermination _ProcessBreakOnTermination +typedef ULONGLONG (CALLBACK *_GetTickCount64)( + void); + +typedef NTSTATUS (NTAPI *_NtQueryObject)( + HANDLE Handle, + OBJECT_INFORMATION_CLASS ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength +); + +/* + * ================================================================ + * Custom psutil definitions for modules loaded at runtime. + * ================================================================ + */ + +_NtQuerySystemInformation \ + psutil_NtQuerySystemInformation; + +_NtQueryInformationProcess \ + psutil_NtQueryInformationProcess; + +_NtSetInformationProcess + psutil_NtSetInformationProcess; + +_WinStationQueryInformationW \ + psutil_WinStationQueryInformationW; + +_RtlIpv4AddressToStringA \ + psutil_rtlIpv4AddressToStringA; + +_RtlIpv6AddressToStringA \ + psutil_rtlIpv6AddressToStringA; + +_GetExtendedTcpTable \ + psutil_GetExtendedTcpTable; + +_GetExtendedUdpTable \ + psutil_GetExtendedUdpTable; + +_GetActiveProcessorCount \ + psutil_GetActiveProcessorCount; + +_GetTickCount64 \ + psutil_GetTickCount64; + +_NtQueryObject \ + psutil_NtQueryObject; + +_GetLogicalProcessorInformationEx \ + psutil_GetLogicalProcessorInformationEx; #endif // __NTEXTAPI_H__ diff --git a/psutil/arch/windows/process_handles.c b/psutil/arch/windows/process_handles.c index 4ac02b91..f295f18d 100644 --- a/psutil/arch/windows/process_handles.c +++ b/psutil/arch/windows/process_handles.c @@ -4,13 +4,16 @@ * found in the LICENSE file. * */ + +#include <windows.h> +#include <Psapi.h> +#include <Python.h> +#include "ntextapi.h" +#include "global.h" #include "process_handles.h" #include "process_info.h" #include "../../_psutil_common.h" -static _NtQuerySystemInformation __NtQuerySystemInformation = NULL; -static _NtQueryObject __NtQueryObject = NULL; - CRITICAL_SECTION g_cs; BOOL g_initialized = FALSE; NTSTATUS g_status; @@ -23,33 +26,15 @@ ULONG g_dwSize = 0; ULONG g_dwLength = 0; -PyObject * -psutil_get_open_files(long dwPid, HANDLE hProcess) { - OSVERSIONINFO osvi; - - ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osvi); - - // Threaded version only works for Vista+ - if (osvi.dwMajorVersion >= 6) - return psutil_get_open_files_ntqueryobject(dwPid, hProcess); - else - return psutil_get_open_files_getmappedfilename(dwPid, hProcess); -} +#define ObjectNameInformation 1 +#define NTQO_TIMEOUT 100 -VOID +static VOID psutil_get_open_files_init(BOOL threaded) { if (g_initialized == TRUE) return; - // Resolve the Windows API calls - __NtQuerySystemInformation = psutil_GetProcAddressFromLib( - "ntdll.dll", "NtQuerySystemInformation"); - __NtQueryObject = psutil_GetProcAddressFromLib( - "ntdll.dll", "NtQueryObject"); - // Create events for signalling work between threads if (threaded == TRUE) { g_hEvtStart = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -61,7 +46,59 @@ psutil_get_open_files_init(BOOL threaded) { } -PyObject * +static DWORD WINAPI +psutil_wait_thread(LPVOID lpvParam) { + // Loop infinitely waiting for work + while (TRUE) { + WaitForSingleObject(g_hEvtStart, INFINITE); + + g_status = psutil_NtQueryObject( + g_hFile, + ObjectNameInformation, + g_pNameBuffer, + g_dwSize, + &g_dwLength); + SetEvent(g_hEvtFinish); + } +} + + +static DWORD +psutil_create_thread() { + DWORD dwWait = 0; + + if (g_hThread == NULL) + g_hThread = CreateThread( + NULL, + 0, + psutil_wait_thread, + NULL, + 0, + NULL); + if (g_hThread == NULL) + return GetLastError(); + + // Signal the worker thread to start + SetEvent(g_hEvtStart); + + // Wait for the worker thread to finish + dwWait = WaitForSingleObject(g_hEvtFinish, NTQO_TIMEOUT); + + // If the thread hangs, kill it and cleanup + if (dwWait == WAIT_TIMEOUT) { + SuspendThread(g_hThread); + TerminateThread(g_hThread, 1); + WaitForSingleObject(g_hThread, INFINITE); + CloseHandle(g_hThread); + + g_hThread = NULL; + } + + return dwWait; +} + + +static PyObject * psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) { NTSTATUS status; PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; @@ -81,9 +118,7 @@ psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) { // to psutil_get_open_files() is running EnterCriticalSection(&g_cs); - if (__NtQuerySystemInformation == NULL || - __NtQueryObject == NULL || - g_hEvtStart == NULL || + if (g_hEvtStart == NULL || g_hEvtFinish == NULL) { @@ -117,7 +152,7 @@ psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) { error = TRUE; goto cleanup; } - } while ((status = __NtQuerySystemInformation( + } while ((status = psutil_NtQuerySystemInformation( SystemExtendedHandleInformation, pHandleInfo, dwInfoSize, @@ -180,7 +215,7 @@ psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) { goto loop_cleanup; } - dwWait = psutil_NtQueryObject(); + dwWait = psutil_create_thread(); // If the call does not return, skip this handle if (dwWait != WAIT_OBJECT_0) @@ -228,19 +263,19 @@ psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) { } loop_cleanup: - Py_XDECREF(py_path); - py_path = NULL; + Py_XDECREF(py_path); + py_path = NULL; - if (g_pNameBuffer != NULL) - HeapFree(GetProcessHeap(), 0, g_pNameBuffer); - g_pNameBuffer = NULL; - g_dwSize = 0; - g_dwLength = 0; + if (g_pNameBuffer != NULL) + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + g_dwLength = 0; - if (g_hFile != NULL) - CloseHandle(g_hFile); - g_hFile = NULL; - } + if (g_hFile != NULL) + CloseHandle(g_hFile); + g_hFile = NULL; +} cleanup: if (g_pNameBuffer != NULL) @@ -268,58 +303,7 @@ cleanup: } -DWORD -psutil_NtQueryObject() { - DWORD dwWait = 0; - - if (g_hThread == NULL) - g_hThread = CreateThread( - NULL, - 0, - psutil_NtQueryObjectThread, - NULL, - 0, - NULL); - if (g_hThread == NULL) - return GetLastError(); - - // Signal the worker thread to start - SetEvent(g_hEvtStart); - - // Wait for the worker thread to finish - dwWait = WaitForSingleObject(g_hEvtFinish, NTQO_TIMEOUT); - - // If the thread hangs, kill it and cleanup - if (dwWait == WAIT_TIMEOUT) { - SuspendThread(g_hThread); - TerminateThread(g_hThread, 1); - WaitForSingleObject(g_hThread, INFINITE); - CloseHandle(g_hThread); - - g_hThread = NULL; - } - - return dwWait; -} - - -DWORD WINAPI -psutil_NtQueryObjectThread(LPVOID lpvParam) { - // Loop infinitely waiting for work - while (TRUE) { - WaitForSingleObject(g_hEvtStart, INFINITE); - - g_status = __NtQueryObject(g_hFile, - ObjectNameInformation, - g_pNameBuffer, - g_dwSize, - &g_dwLength); - SetEvent(g_hEvtFinish); - } -} - - -PyObject * +static PyObject * psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) { NTSTATUS status; PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; @@ -339,12 +323,6 @@ psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) { if (g_initialized == FALSE) psutil_get_open_files_init(FALSE); - if (__NtQuerySystemInformation == NULL || __NtQueryObject == NULL) { - PyErr_SetFromWindowsErr(0); - error = TRUE; - goto cleanup; - } - // Py_BuildValue raises an exception if NULL is returned py_retlist = PyList_New(0); if (py_retlist == NULL) { @@ -370,7 +348,7 @@ psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) { error = TRUE; goto cleanup; } - } while ((status = __NtQuerySystemInformation( + } while ((status = psutil_NtQuerySystemInformation( SystemExtendedHandleInformation, pHandleInfo, dwInfoSize, @@ -517,3 +495,23 @@ cleanup: return py_retlist; } + + +/* + * The public function. + */ +PyObject * +psutil_get_open_files(long dwPid, HANDLE hProcess) { + OSVERSIONINFO osvi; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + + // Threaded version only works for Vista+ + if (osvi.dwMajorVersion >= 6) + return psutil_get_open_files_ntqueryobject(dwPid, hProcess); + else + return psutil_get_open_files_getmappedfilename(dwPid, hProcess); +} + diff --git a/psutil/arch/windows/process_handles.h b/psutil/arch/windows/process_handles.h index 4a022c1c..342ce8fd 100644 --- a/psutil/arch/windows/process_handles.h +++ b/psutil/arch/windows/process_handles.h @@ -4,108 +4,7 @@ * found in the LICENSE file. */ -#ifndef __PROCESS_HANDLES_H__ -#define __PROCESS_HANDLES_H__ - -#ifndef UNICODE -#define UNICODE -#endif - #include <Python.h> -#include <stdio.h> #include <windows.h> -#include <strsafe.h> -#include <winternl.h> -#include <psapi.h> - - -#ifndef NT_SUCCESS -#define NT_SUCCESS(x) ((x) >= 0) -#endif - -#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 -#define ObjectBasicInformation 0 -#define ObjectNameInformation 1 -#define ObjectTypeInformation 2 -#define HANDLE_TYPE_FILE 28 -#define NTQO_TIMEOUT 100 - -typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( - ULONG SystemInformationClass, - PVOID SystemInformation, - ULONG SystemInformationLength, - PULONG ReturnLength -); - -typedef NTSTATUS (NTAPI *_NtQueryObject)( - HANDLE ObjectHandle, - ULONG ObjectInformationClass, - PVOID ObjectInformation, - ULONG ObjectInformationLength, - PULONG ReturnLength -); -// Undocumented FILE_INFORMATION_CLASS: FileNameInformation -static const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64; - -typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { - PVOID Object; - HANDLE UniqueProcessId; - HANDLE HandleValue; - ULONG GrantedAccess; - USHORT CreatorBackTraceIndex; - USHORT ObjectTypeIndex; - ULONG HandleAttributes; - ULONG Reserved; -} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; - -typedef struct _SYSTEM_HANDLE_INFORMATION_EX { - ULONG_PTR NumberOfHandles; - ULONG_PTR Reserved; - SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; -} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; - -typedef enum _POOL_TYPE { - NonPagedPool, - PagedPool, - NonPagedPoolMustSucceed, - DontUseThisType, - NonPagedPoolCacheAligned, - PagedPoolCacheAligned, - NonPagedPoolCacheAlignedMustS -} POOL_TYPE, *PPOOL_TYPE; - -typedef struct _OBJECT_TYPE_INFORMATION { - UNICODE_STRING Name; - ULONG TotalNumberOfObjects; - ULONG TotalNumberOfHandles; - ULONG TotalPagedPoolUsage; - ULONG TotalNonPagedPoolUsage; - ULONG TotalNamePoolUsage; - ULONG TotalHandleTableUsage; - ULONG HighWaterNumberOfObjects; - ULONG HighWaterNumberOfHandles; - ULONG HighWaterPagedPoolUsage; - ULONG HighWaterNonPagedPoolUsage; - ULONG HighWaterNamePoolUsage; - ULONG HighWaterHandleTableUsage; - ULONG InvalidAttributes; - GENERIC_MAPPING GenericMapping; - ULONG ValidAccess; - BOOLEAN SecurityRequired; - BOOLEAN MaintainHandleCount; - USHORT MaintainTypeList; - POOL_TYPE PoolType; - ULONG PagedPoolUsage; - ULONG NonPagedPoolUsage; -} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; - -PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName); -VOID psutil_get_open_files_init(BOOL threaded); PyObject* psutil_get_open_files(long pid, HANDLE processHandle); -PyObject* psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess); -PyObject* psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess); -DWORD psutil_NtQueryObject(void); -DWORD WINAPI psutil_NtQueryObjectThread(LPVOID lpvParam); - -#endif // __PROCESS_HANDLES_H__ diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c index a2edca58..9a698e42 100644 --- a/psutil/arch/windows/process_info.c +++ b/psutil/arch/windows/process_info.c @@ -12,9 +12,10 @@ #include <Psapi.h> #include <tlhelp32.h> +#include "ntextapi.h" +#include "global.h" #include "security.h" #include "process_info.h" -#include "ntextapi.h" #include "../../_psutil_common.h" @@ -24,23 +25,6 @@ // but unfortunately not in a usable way. // ==================================================================== -// see http://msdn2.microsoft.com/en-us/library/aa489609.aspx -#ifndef NT_SUCCESS -#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) -#endif - -// http://msdn.microsoft.com/en-us/library/aa813741(VS.85).aspx -typedef struct { - BYTE Reserved1[16]; - PVOID Reserved2[5]; - UNICODE_STRING CurrentDirectoryPath; - PVOID CurrentDirectoryHandle; - UNICODE_STRING DllPath; - UNICODE_STRING ImagePathName; - UNICODE_STRING CommandLine; - LPCWSTR env; -} RTL_USER_PROCESS_PARAMETERS_, *PRTL_USER_PROCESS_PARAMETERS_; - // https://msdn.microsoft.com/en-us/library/aa813706(v=vs.85).aspx #ifdef _WIN64 typedef struct { @@ -97,15 +81,11 @@ typedef struct { use the 64 bit structure layout and a special function to read its memory. */ typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)( - IN HANDLE ProcessHandle, - IN PVOID64 BaseAddress, - OUT PVOID Buffer, - IN ULONG64 Size, - OUT PULONG64 NumberOfBytesRead); - -typedef enum { - MemoryInformationBasic -} MEMORY_INFORMATION_CLASS; + HANDLE ProcessHandle, + PVOID64 BaseAddress, + PVOID Buffer, + ULONG64 Size, + PULONG64 NumberOfBytesRead); typedef struct { PVOID Reserved1[2]; @@ -150,47 +130,6 @@ typedef struct { (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : NULL) -const int STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; -const int STATUS_BUFFER_TOO_SMALL = 0xC0000023L; - - -// A wrapper around GetModuleHandle and GetProcAddress. -PVOID -psutil_GetProcAddress(LPCSTR libname, LPCSTR procname) { - HMODULE mod; - FARPROC addr; - - if ((mod = GetModuleHandleA(libname)) == NULL) { - PyErr_SetFromWindowsErrWithFilename(0, libname); - return NULL; - } - if ((addr = GetProcAddress(mod, procname)) == NULL) { - PyErr_SetFromWindowsErrWithFilename(0, procname); - return NULL; - } - return addr; -} - - -// A wrapper around LoadLibrary and GetProcAddress. -PVOID -psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname) { - HMODULE mod; - FARPROC addr; - - if ((mod = LoadLibraryA(libname)) == NULL) { - PyErr_SetFromWindowsErrWithFilename(0, libname); - return NULL; - } - if ((addr = GetProcAddress(mod, procname)) == NULL) { - PyErr_SetFromWindowsErrWithFilename(0, procname); - FreeLibrary(mod); - return NULL; - } - FreeLibrary(mod); - return addr; -} - // ==================================================================== // Process and PIDs utiilties. @@ -535,11 +474,6 @@ psutil_get_process_data(long pid, #endif DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; - NtQueryInformationProcess = \ - psutil_GetProcAddress("ntdll.dll", "NtQueryInformationProcess"); - if (NtQueryInformationProcess == NULL) - return -1; - hProcess = psutil_handle_from_pid(pid, access); if (hProcess == NULL) return -1; @@ -547,11 +481,13 @@ psutil_get_process_data(long pid, #ifdef _WIN64 /* 64 bit case. Check if the target is a 32 bit process running in WoW64 * mode. */ - if (!NT_SUCCESS(NtQueryInformationProcess(hProcess, - ProcessWow64Information, - &ppeb32, - sizeof(LPVOID), - NULL))) { + if (! NT_SUCCESS(psutil_NtQueryInformationProcess( + hProcess, + ProcessWow64Information, + &ppeb32, + sizeof(LPVOID), + NULL))) + { PyErr_SetFromWindowsErr(0); goto error; } @@ -610,7 +546,7 @@ psutil_get_process_data(long pid, psutil_GetProcAddressFromLib( "ntdll.dll", "NtWow64QueryInformationProcess64"); if (NtWow64QueryInformationProcess64 == NULL) { - // Too complicated. Give up. + PyErr_Clear(); AccessDenied("can't query 64-bit process in 32-bit-WoW mode"); goto error; } @@ -620,38 +556,44 @@ psutil_get_process_data(long pid, psutil_GetProcAddressFromLib( "ntdll.dll", "NtWow64ReadVirtualMemory64"); if (NtWow64ReadVirtualMemory64 == NULL) { - // Too complicated. Give up. + PyErr_Clear(); AccessDenied("can't query 64-bit process in 32-bit-WoW mode"); goto error; } } - if (!NT_SUCCESS(NtWow64QueryInformationProcess64( - hProcess, - ProcessBasicInformation, - &pbi64, - sizeof(pbi64), - NULL))) { + if (! NT_SUCCESS( + NtWow64QueryInformationProcess64( + hProcess, + ProcessBasicInformation, + &pbi64, + sizeof(pbi64), + NULL))) + { PyErr_SetFromWindowsErr(0); goto error; } // read peb - if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(hProcess, - pbi64.PebBaseAddress, - &peb64, - sizeof(peb64), - NULL))) { + if (! NT_SUCCESS(NtWow64ReadVirtualMemory64( + hProcess, + pbi64.PebBaseAddress, + &peb64, + sizeof(peb64), + NULL))) + { PyErr_SetFromWindowsErr(0); goto error; } // read process parameters - if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(hProcess, - peb64.ProcessParameters, - &procParameters64, - sizeof(procParameters64), - NULL))) { + if (! NT_SUCCESS(NtWow64ReadVirtualMemory64( + hProcess, + peb64.ProcessParameters, + &procParameters64, + sizeof(procParameters64), + NULL))) + { PyErr_SetFromWindowsErr(0); goto error; } @@ -671,18 +613,19 @@ psutil_get_process_data(long pid, } } else #endif - /* Target process is of the same bitness as us. */ { PROCESS_BASIC_INFORMATION pbi; PEB_ peb; RTL_USER_PROCESS_PARAMETERS_ procParameters; - if (!NT_SUCCESS(NtQueryInformationProcess(hProcess, - ProcessBasicInformation, - &pbi, - sizeof(pbi), - NULL))) { + if (! NT_SUCCESS(psutil_NtQueryInformationProcess( + hProcess, + ProcessBasicInformation, + &pbi, + sizeof(pbi), + NULL))) + { PyErr_SetFromWindowsErr(0); goto error; } @@ -743,11 +686,13 @@ psutil_get_process_data(long pid, #ifndef _WIN64 if (weAreWow64 && !theyAreWow64) { - if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(hProcess, - src64, - buffer, - size, - NULL))) { + if (! NT_SUCCESS(NtWow64ReadVirtualMemory64( + hProcess, + src64, + buffer, + size, + NULL))) + { PyErr_SetFromWindowsErr(0); goto error; } @@ -788,12 +733,6 @@ psutil_get_cmdline_data(long pid, WCHAR **pdata, SIZE_T *psize) { WCHAR * cmdline_buffer_wchar = NULL; PUNICODE_STRING tmp = NULL; DWORD string_size; - _NtQueryInformationProcess NtQueryInformationProcess; - - NtQueryInformationProcess = \ - psutil_GetProcAddress("ntdll.dll", "NtQueryInformationProcess"); - if (NtQueryInformationProcess == NULL) - goto error; cmdline_buffer = calloc(ret_length, 1); if (cmdline_buffer == NULL) { @@ -804,14 +743,14 @@ psutil_get_cmdline_data(long pid, WCHAR **pdata, SIZE_T *psize) { hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); if (hProcess == NULL) goto error; - status = NtQueryInformationProcess( + status = psutil_NtQueryInformationProcess( hProcess, 60, // ProcessCommandLineInformation cmdline_buffer, ret_length, &ret_length ); - if (!NT_SUCCESS(status)) { + if (! NT_SUCCESS(status)) { PyErr_SetFromWindowsErr(0); goto error; } @@ -971,13 +910,6 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, PVOID buffer; ULONG bufferSize; PSYSTEM_PROCESS_INFORMATION process; - typedef DWORD (_stdcall * NTQSI_PROC) (int, PVOID, ULONG, PULONG); - NTQSI_PROC NtQuerySystemInformation; - - NtQuerySystemInformation = \ - psutil_GetProcAddressFromLib("ntdll.dll", "NtQuerySystemInformation"); - if (NtQuerySystemInformation == NULL) - goto error; bufferSize = initialBufferSize; buffer = malloc(bufferSize); @@ -987,8 +919,11 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, } while (TRUE) { - status = NtQuerySystemInformation(SystemProcessInformation, buffer, - bufferSize, &bufferSize); + status = psutil_NtQuerySystemInformation( + SystemProcessInformation, + buffer, + bufferSize, + &bufferSize); if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) { diff --git a/psutil/arch/windows/process_info.h b/psutil/arch/windows/process_info.h index d2e60b7c..4278c4df 100644 --- a/psutil/arch/windows/process_info.h +++ b/psutil/arch/windows/process_info.h @@ -23,8 +23,6 @@ int psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, int psutil_assert_pid_exists(DWORD pid, char *err); int psutil_assert_pid_not_exists(DWORD pid, char *err); -PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname); -PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname); PyObject* psutil_get_cmdline(long pid, int use_peb); PyObject* psutil_get_cwd(long pid); diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py index 7be12d9c..c98d892c 100755 --- a/psutil/tests/test_windows.py +++ b/psutil/tests/test_windows.py @@ -778,10 +778,11 @@ class RemoteProcessTestCase(unittest.TestCase): self.assertEquals(e["THINK_OF_A_NUMBER"], str(os.getpid())) def test_environ_64(self): - # Environ 32 is not supported. p = psutil.Process(self.proc64.pid) - with self.assertRaises(psutil.AccessDenied): + try: p.environ() + except psutil.AccessDenied: + pass # =================================================================== @@ -134,6 +134,7 @@ if WINDOWS: 'psutil/arch/windows/security.c', 'psutil/arch/windows/inet_ntop.c', 'psutil/arch/windows/services.c', + 'psutil/arch/windows/global.c', ], define_macros=macros, libraries=[ |