summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrank Benkstein <frank.benkstein@sap.com>2016-01-25 14:36:37 +0100
committerFrank Benkstein <frank.benkstein@sap.com>2016-01-27 14:18:59 +0100
commitcbccd44acbedb5e839f54b00243191d553ab8d36 (patch)
tree3d6b8d3455f97ec103a5060a11e3050e37ebaa8d
parent4ceb5ecbf3f086ebe629ca567bafaff7e18ce4ec (diff)
downloadpsutil-cbccd44acbedb5e839f54b00243191d553ab8d36.tar.gz
address 32bit/64bit confusion in psutil_get_parameters
Replace the hard-coded offsets into compiler generated ones by providing struct definitions for the data to be fetched from the target process. This allows a 64 bit process to query both other 64 bit and 32 bit processes. A 32 bit process currently can only query other 32 bit processes.
-rw-r--r--HISTORY.rst3
-rw-r--r--psutil/_psutil_windows.c15
-rw-r--r--psutil/arch/windows/process_info.c502
-rw-r--r--test/_windows.py74
-rw-r--r--test/test_psutil.py2
5 files changed, 471 insertions, 125 deletions
diff --git a/HISTORY.rst b/HISTORY.rst
index 29c6b80c..05bfad59 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -14,6 +14,9 @@ Bug tracker at https://github.com/giampaolo/psutil/issues
UnicodeDecodeError exceptions. 'surrogateescape' error handler is now
used as a workaround for replacing the corrupted data.
- #741: [OpenBSD] fix compilation on mips64.
+- #737: [Windows] when the bitness of psutil and the target process was
+ different cmdline() and cwd() could return a wrong result or incorrectly
+ report an AccessDenied error.
3.4.2 - 2016-01-20
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index 186587ac..c351a2ba 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -575,7 +575,6 @@ static PyObject *
psutil_proc_cmdline(PyObject *self, PyObject *args) {
long pid;
int pid_return;
- PyObject *py_retlist;
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
@@ -588,19 +587,7 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) {
if (pid_return == -1)
return NULL;
- // XXX the assumptio below probably needs to go away
-
- // May fail any of several ReadProcessMemory calls etc. and
- // not indicate a real problem so we ignore any errors and
- // just live without commandline.
- py_retlist = psutil_get_cmdline(pid);
- if ( NULL == py_retlist ) {
- // carry on anyway, clear any exceptions too
- PyErr_Clear();
- return Py_BuildValue("[]");
- }
-
- return py_retlist;
+ return psutil_get_cmdline(pid);
}
diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c
index f6ee7df5..e66166c0 100644
--- a/psutil/arch/windows/process_info.c
+++ b/psutil/arch/windows/process_info.c
@@ -67,19 +67,6 @@ psutil_handle_from_pid(DWORD pid) {
}
-// fetch the PEB base address from NtQueryInformationProcess()
-PVOID
-psutil_get_peb_address(HANDLE ProcessHandle) {
- _NtQueryInformationProcess NtQueryInformationProcess =
- (_NtQueryInformationProcess)GetProcAddress(
- GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
- PROCESS_BASIC_INFORMATION pbi;
-
- NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);
- return pbi.PebBaseAddress;
-}
-
-
DWORD *
psutil_get_pids(DWORD *numberOfReturnedPIDs) {
// Win32 SDK says the only way to know if our process array
@@ -203,6 +190,124 @@ handlep_is_running(HANDLE hProcess) {
return 0;
}
+// Helper structures to access the memory correctly. Some of these might also
+// be defined in the winternl.h header file 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 {
+ BYTE Reserved1[2];
+ BYTE BeingDebugged;
+ BYTE Reserved2[21];
+ PVOID LoaderData;
+ PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters;
+ /* More fields ... */
+} PEB_;
+#else
+typedef struct {
+ BYTE Reserved1[2];
+ BYTE BeingDebugged;
+ BYTE Reserved2[1];
+ PVOID Reserved3[2];
+ PVOID Ldr;
+ PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters;
+ /* More fields ... */
+} PEB_;
+#endif
+
+#ifdef _WIN64
+/* When we are a 64 bit process accessing a 32 bit (WoW64) process we need to
+ use the 32 bit structure layout. */
+typedef struct {
+ USHORT Length;
+ USHORT MaxLength;
+ DWORD Buffer;
+} UNICODE_STRING32;
+
+typedef struct {
+ BYTE Reserved1[16];
+ DWORD Reserved2[5];
+ UNICODE_STRING32 CurrentDirectoryPath;
+ DWORD CurrentDirectoryHandle;
+ UNICODE_STRING32 DllPath;
+ UNICODE_STRING32 ImagePathName;
+ UNICODE_STRING32 CommandLine;
+ DWORD env;
+} RTL_USER_PROCESS_PARAMETERS32;
+
+typedef struct {
+ BYTE Reserved1[2];
+ BYTE BeingDebugged;
+ BYTE Reserved2[1];
+ DWORD Reserved3[2];
+ DWORD Ldr;
+ DWORD ProcessParameters;
+ /* More fields ... */
+} PEB32;
+#else
+/* When we are a 32 bit (WoW64) process accessing a 64 bit process we need to
+ 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 struct {
+ PVOID Reserved1[2];
+ PVOID64 PebBaseAddress;
+ PVOID Reserved2[4];
+ PVOID UniqueProcessId[2];
+ PVOID Reserved3[2];
+} PROCESS_BASIC_INFORMATION64;
+
+typedef struct {
+ USHORT Length;
+ USHORT MaxLength;
+ PVOID64 Buffer;
+} UNICODE_STRING64;
+
+typedef struct {
+ BYTE Reserved1[16];
+ PVOID64 Reserved2[5];
+ UNICODE_STRING64 CurrentDirectoryPath;
+ PVOID64 CurrentDirectoryHandle;
+ UNICODE_STRING64 DllPath;
+ UNICODE_STRING64 ImagePathName;
+ UNICODE_STRING64 CommandLine;
+ PVOID64 env;
+} RTL_USER_PROCESS_PARAMETERS64;
+
+typedef struct {
+ BYTE Reserved1[2];
+ BYTE BeingDebugged;
+ BYTE Reserved2[21];
+ PVOID64 LoaderData;
+ PVOID64 ProcessParameters;
+ /* More fields ... */
+} PEB64;
+#endif
+
/* Get one or more parameters of the process with the given pid:
pcmdline: the command line as a Python list
@@ -210,85 +315,311 @@ handlep_is_running(HANDLE hProcess) {
On success 0 is returned. On error the given output parameters are not
touched, -1 is returned, and an appropriate Python exception is set. */
-static int psutil_get_parameters(long pid, PyObject **pcmdline, PyObject **pcwd) {
+static int psutil_get_parameters(long pid, PyObject **pcmdline,
+ PyObject **pcwd) {
+ /* This function is quite complex because there are several cases to be
+ considered:
+
+ Two cases are really simple: we (i.e. the python interpreter) and the
+ target process are both 32 bit or both 64 bit. In that case the memory
+ layout of the structures matches up and all is well.
+
+ When we are 64 bit and the target process is 32 bit we need to use
+ custom 32 bit versions of the structures.
+
+ When we are 32 bit and the target process is 64 bit we need to use
+ custom 64 bit version of the structures. Also we need to use separate
+ Wow64 functions to get the information.
+
+ A few helper structs are defined above so that the compiler can handle
+ calculating the correct offsets.
+
+ Additional help also came from the following sources:
+
+ https://github.com/kohsuke/winp and
+ http://wj32.org/wp/2009/01/24/howto-get-the-command-line-of-processes/
+ http://stackoverflow.com/a/14012919
+ http://www.drdobbs.com/embracing-64-bit-windows/184401966
+ */
+ static _NtQueryInformationProcess NtQueryInformationProcess = NULL;
+#ifndef _WIN64
+ static _NtQueryInformationProcess NtWow64QueryInformationProcess64 = NULL;
+ static _NtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64 = NULL;
+#endif
int nArgs, i;
LPWSTR *szArglist = NULL;
HANDLE hProcess = NULL;
- PVOID pebAddress;
- PVOID rtlUserProcParamsAddress;
- UNICODE_STRING commandLine;
WCHAR *commandLineContents = NULL;
- UNICODE_STRING currentDirectory;
WCHAR *currentDirectoryContent = NULL;
+#ifdef _WIN64
+ LPVOID ppeb32 = NULL;
+#else
+ BOOL weAreWow64;
+ BOOL theyAreWow64;
+#endif
PyObject *py_unicode = NULL;
PyObject *py_retlist = NULL;
hProcess = psutil_handle_from_pid(pid);
if (hProcess == NULL)
return -1;
- pebAddress = psutil_get_peb_address(hProcess);
- // get the address of ProcessParameters
+ if (NtQueryInformationProcess == NULL) {
+ NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(
+ GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
+ }
+
#ifdef _WIN64
- if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32,
- &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
+ /* 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))) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
#else
- if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10,
- &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
+ /* 32 bit case. Check if the target is also 32 bit. */
+ if (!IsWow64Process(GetCurrentProcess(), &weAreWow64) ||
+ !IsWow64Process(hProcess, &theyAreWow64)) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
#endif
- {
- if (GetLastError() == ERROR_PARTIAL_COPY) {
- // this occurs quite often with system processes
- AccessDenied();
+
+#ifdef _WIN64
+ if (ppeb32 != NULL) {
+ /* We are 64 bit. Target process is 32 bit running in WoW64 mode. */
+ PEB32 peb32;
+ RTL_USER_PROCESS_PARAMETERS32 procParameters32;
+
+ // read PEB
+ if(!ReadProcessMemory(hProcess, ppeb32, &peb32, sizeof(peb32), NULL)) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
}
- else {
+
+ // read process parameters
+ if(!ReadProcessMemory(hProcess,
+ UlongToPtr(peb32.ProcessParameters),
+ &procParameters32,
+ sizeof(procParameters32),
+ NULL)) {
PyErr_SetFromWindowsErr(0);
+ goto error;
}
- goto error;
- }
- if (pcmdline != NULL) {
- // read the CommandLine UNICODE_STRING structure
-#ifdef _WIN64
- if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112,
- &commandLine, sizeof(commandLine), NULL))
+ if (pcmdline != NULL) {
+ // read command line aguments
+ commandLineContents =
+ calloc(procParameters32.CommandLine.Length + 2, 1);
+ if (commandLineContents == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if(!ReadProcessMemory(
+ hProcess,
+ UlongToPtr(procParameters32.CommandLine.Buffer),
+ commandLineContents,
+ procParameters32.CommandLine.Length,
+ NULL)) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+ }
+
+ if (pcwd != NULL) {
+ // read cwd
+ currentDirectoryContent =
+ calloc(procParameters32.CurrentDirectoryPath.Length + 2, 1);
+ if (currentDirectoryContent == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if (!ReadProcessMemory(
+ hProcess,
+ UlongToPtr(procParameters32.CurrentDirectoryPath.Buffer),
+ currentDirectoryContent,
+ procParameters32.CurrentDirectoryPath.Length,
+ NULL)) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+ }
+ } else
#else
- if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40,
- &commandLine, sizeof(commandLine), NULL))
-#endif
- {
- if (GetLastError() == ERROR_PARTIAL_COPY) {
- // this occurs quite often with system processes
- AccessDenied();
+ if (weAreWow64 && !theyAreWow64) {
+ /* We are 32 bit running in WoW64 mode. Target process is 64 bit. */
+ PROCESS_BASIC_INFORMATION64 pbi64;
+ PEB64 peb64;
+ RTL_USER_PROCESS_PARAMETERS64 procParameters64;
+
+ if (NtWow64QueryInformationProcess64 == NULL) {
+ NtWow64QueryInformationProcess64 =
+ (_NtQueryInformationProcess)GetProcAddress(
+ GetModuleHandleA("ntdll.dll"),
+ "NtWow64QueryInformationProcess64");
+ }
+
+ if (!NT_SUCCESS(NtWow64QueryInformationProcess64(
+ hProcess,
+ ProcessBasicInformation,
+ &pbi64,
+ sizeof(pbi64),
+ NULL))) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+
+ // read peb
+ if (NtWow64ReadVirtualMemory64 == NULL) {
+ NtWow64ReadVirtualMemory64 =
+ (_NtWow64ReadVirtualMemory64)GetProcAddress(
+ GetModuleHandleA("ntdll.dll"),
+ "NtWow64ReadVirtualMemory64");
+ }
+
+ 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))) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+
+ if (pcmdline != NULL) {
+ // read command line aguments
+ commandLineContents =
+ calloc(procParameters64.CommandLine.Length + 2, 1);
+ if(!commandLineContents) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(
+ hProcess,
+ procParameters64.CommandLine.Buffer,
+ commandLineContents,
+ procParameters64.CommandLine.Length,
+ NULL))) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+ }
+
+ if (pcwd != NULL) {
+ // read cwd
+ currentDirectoryContent =
+ calloc(procParameters64.CurrentDirectoryPath.Length + 2, 1);
+ if (!currentDirectoryContent) {
+ PyErr_NoMemory();
+ goto error;
}
- else {
+
+ if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(
+ hProcess,
+ procParameters64.CurrentDirectoryPath.Buffer,
+ currentDirectoryContent,
+ procParameters64.CurrentDirectoryPath.Length,
+ NULL))) {
PyErr_SetFromWindowsErr(0);
+ goto error;
}
+ }
+ } 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))) {
+ PyErr_SetFromWindowsErr(0);
goto error;
}
- // allocate memory to hold the command line
- commandLineContents = (WCHAR *)malloc(commandLine.Length + 1);
- if (commandLineContents == NULL) {
- PyErr_NoMemory();
+ // read peb
+ if(!ReadProcessMemory(hProcess,
+ pbi.PebBaseAddress,
+ &peb,
+ sizeof(peb),
+ NULL)) {
+ PyErr_SetFromWindowsErr(0);
goto error;
}
- // read the command line
- if (!ReadProcessMemory(hProcess, commandLine.Buffer,
- commandLineContents, commandLine.Length, NULL))
- {
+ // read process parameters
+ if(!ReadProcessMemory(hProcess,
+ peb.ProcessParameters,
+ &procParameters,
+ sizeof(procParameters),
+ NULL)) {
PyErr_SetFromWindowsErr(0);
goto error;
}
- // Null-terminate the string to prevent wcslen from returning
- // incorrect length the length specifier is in characters, but
- // commandLine.Length is in bytes.
- commandLineContents[(commandLine.Length / sizeof(WCHAR))] = '\0';
+ if (pcmdline != NULL) {
+ // read command line aguments
+ commandLineContents =
+ calloc(procParameters.CommandLine.Length + 2, 1);
+ if(!commandLineContents) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if(!ReadProcessMemory(hProcess,
+ procParameters.CommandLine.Buffer,
+ commandLineContents,
+ procParameters.CommandLine.Length,
+ NULL)) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+ }
+
+ if (pcwd != NULL) {
+ // read cwd
+ currentDirectoryContent =
+ calloc(procParameters.CurrentDirectoryPath.Length + 2, 1);
+ if (!currentDirectoryContent) {
+ PyErr_NoMemory();
+ goto error;
+ }
- // attempt to parse the command line using Win32 API, fall back
- // on string cmdline version otherwise
+ if (!ReadProcessMemory(hProcess,
+ procParameters.CurrentDirectoryPath.Buffer,
+ currentDirectoryContent,
+ procParameters.CurrentDirectoryPath.Length,
+ NULL)) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+ }
+ }
+
+ if (pcmdline != NULL) {
+ // attempt to parse the command line using Win32 API
szArglist = CommandLineToArgvW(commandLineContents, &nArgs);
if (szArglist == NULL) {
PyErr_SetFromWindowsErr(0);
@@ -320,60 +651,9 @@ static int psutil_get_parameters(long pid, PyObject **pcmdline, PyObject **pcwd)
}
if (pcwd != NULL) {
- // Read the currentDirectory UNICODE_STRING structure.
- // 0x24 refers to "CurrentDirectoryPath" of RTL_USER_PROCESS_PARAMETERS
- // structure, see:
- // http://wj32.wordpress.com/2009/01/24/
- // howto-get-the-command-line-of-processes/
-#ifdef _WIN64
- if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 56,
- &currentDirectory, sizeof(currentDirectory), NULL))
-#else
- if (!ReadProcessMemory(hProcess,
- (PCHAR)rtlUserProcParamsAddress + 0x24,
- &currentDirectory, sizeof(currentDirectory), NULL))
-#endif
- {
- if (GetLastError() == ERROR_PARTIAL_COPY) {
- // this occurs quite often with system processes
- AccessDenied();
- }
- else {
- PyErr_SetFromWindowsErr(0);
- }
- goto error;
- }
-
- // allocate memory to hold cwd
- currentDirectoryContent = (WCHAR *)malloc(currentDirectory.Length + 1);
- if (currentDirectoryContent == NULL) {
- PyErr_NoMemory();
- goto error;
- }
-
- // read cwd
- if (!ReadProcessMemory(hProcess, currentDirectory.Buffer,
- currentDirectoryContent, currentDirectory.Length,
- NULL))
- {
- if (GetLastError() == ERROR_PARTIAL_COPY) {
- // this occurs quite often with system processes
- AccessDenied();
- }
- else {
- PyErr_SetFromWindowsErr(0);
- }
- goto error;
- }
-
- // null-terminate the string to prevent wcslen from returning
- // incorrect length the length specifier is in characters, but
- // currentDirectory.Length is in bytes
- currentDirectoryContent[(currentDirectory.Length / sizeof(WCHAR))] = '\0';
-
// convert wchar array to a Python unicode string
- py_unicode = PyUnicode_FromWideChar(
- currentDirectoryContent, wcslen(currentDirectoryContent));
+ py_unicode = PyUnicode_FromWideChar(currentDirectoryContent,
+ wcslen(currentDirectoryContent));
if (py_unicode == NULL)
goto error;
CloseHandle(hProcess);
diff --git a/test/_windows.py b/test/_windows.py
index 2dbc2a3b..90b3bd97 100644
--- a/test/_windows.py
+++ b/test/_windows.py
@@ -8,6 +8,7 @@
"""Windows specific tests. These are implicitly run by test_psutil.py."""
import errno
+import glob
import os
import platform
import signal
@@ -42,6 +43,9 @@ from test_psutil import WINDOWS
cext = psutil._psplatform.cext
+# are we a 64 bit process
+IS_64_BIT = sys.maxsize > 2**32
+
def wrap_exceptions(fun):
def wrapper(self, *args, **kwargs):
@@ -484,10 +488,80 @@ class TestDualProcessImplementation(unittest.TestCase):
self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID)
+class RemoteProcessTestCase(unittest.TestCase):
+ """Certain functions require calling ReadProcessMemory. This trivially
+ works when called on the current process. Check that this works on other
+ processes, especially when they have a different bitness."""
+
+ @staticmethod
+ def find_other_interpreter():
+ # find a python interpreter that is of the opposite bitness from us
+ code = "import sys; sys.stdout.write(str(sys.maxsize > 2**32))"
+
+ # XXX: a different and probably more stable approach might be to access
+ # the registry but accessing 64 bit paths from a 32 bit process
+ for filename in glob.glob(r"C:\Python*\python.exe"):
+ proc = subprocess.Popen(args=[filename, "-c", code],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ output, _ = proc.communicate()
+ if output == str(not IS_64_BIT):
+ return filename
+
+ @classmethod
+ def setUpClass(cls):
+ other_python = cls.find_other_interpreter()
+
+ if other_python is None:
+ raise unittest.SkipTest(
+ "could not find interpreter with opposite bitness")
+
+ if IS_64_BIT:
+ cls.python64 = sys.executable
+ cls.python32 = other_python
+ else:
+ cls.python64 = other_python
+ cls.python32 = sys.executable
+
+ test_args = ["-c", "import sys; sys.stdin.read()"]
+
+ def setUp(self):
+ self.proc32 = get_test_subprocess([self.python32] + self.test_args)
+ self.proc64 = get_test_subprocess([self.python64] + self.test_args)
+
+ def tearDown(self):
+ self.proc32.communicate()
+ self.proc64.communicate()
+ reap_children()
+
+ @classmethod
+ def tearDownClass(cls):
+ reap_children()
+
+ def test_cmdline_32(self):
+ p = psutil.Process(self.proc32.pid)
+ self.assertEqual(len(p.cmdline()), 3)
+ self.assertEqual(p.cmdline()[1:], self.test_args)
+
+ def test_cmdline_64(self):
+ p = psutil.Process(self.proc64.pid)
+ self.assertEqual(len(p.cmdline()), 3)
+ self.assertEqual(p.cmdline()[1:], self.test_args)
+
+ def test_cwd_32(self):
+ p = psutil.Process(self.proc32.pid)
+ self.assertEqual(p.cwd(), os.getcwd())
+
+ def test_cwd_64(self):
+ p = psutil.Process(self.proc64.pid)
+ self.assertEqual(p.cwd(), os.getcwd())
+
+
def main():
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase))
test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation))
+ test_suite.addTest(unittest.makeSuite(RemoteProcessTestCase))
result = unittest.TextTestRunner(verbosity=2).run(test_suite)
return result.wasSuccessful()
diff --git a/test/test_psutil.py b/test/test_psutil.py
index d5f52e56..a1dcd082 100644
--- a/test/test_psutil.py
+++ b/test/test_psutil.py
@@ -3454,7 +3454,9 @@ def main():
elif WINDOWS:
from _windows import WindowsSpecificTestCase as stc
from _windows import TestDualProcessImplementation
+ from _windows import RemoteProcessTestCase
tests.append(TestDualProcessImplementation)
+ tests.append(RemoteProcessTestCase)
elif OSX:
from _osx import OSXSpecificTestCase as stc
elif SUNOS: