Skip to content

Commit dbe2c0a

Browse files
committed
More fixes
1 parent f0de45a commit dbe2c0a

File tree

2 files changed

+24
-6
lines changed

2 files changed

+24
-6
lines changed

Lib/test/test_sample_profiler.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,15 +1634,18 @@ def test_invalid_output_format_with_mocked_profiler(self):
16341634
)
16351635

16361636
def test_is_process_running(self):
1637-
with (test_subprocess("import time; time.sleep(1000)") as proc,
1638-
mock.patch("_remote_debugging.RemoteUnwinder") as mock_unwinder_class):
1639-
mock_unwinder_class.return_value = mock.MagicMock()
1640-
profiler = SampleProfiler(pid=proc.pid, sample_interval_usec=1000, all_threads=False)
1641-
self.assertTrue(profiler._is_process_running())
1642-
proc.kill()
1637+
with test_subprocess("import time; time.sleep(1000)") as proc:
1638+
profiler = SampleProfiler(pid=proc.pid, sample_interval_usec=1000, all_threads=False)
1639+
self.assertTrue(profiler._is_process_running())
1640+
self.assertIsNotNone(profiler.unwinder.get_stack_trace())
1641+
proc.kill()
1642+
proc.wait()
1643+
# ValueError on MacOS (yeah I know), ProcessLookupError on Linux and Windows
1644+
self.assertRaises((ValueError, ProcessLookupError), profiler.unwinder.get_stack_trace)
16431645

16441646
# Exit the context manager to ensure the process is terminated
16451647
self.assertFalse(profiler._is_process_running())
1648+
self.assertRaises((ValueError, ProcessLookupError), profiler.unwinder.get_stack_trace)
16461649

16471650
@unittest.skipUnless(sys.platform == "linux", "Only valid on Linux")
16481651
def test_esrch_signal_handling(self):

Python/remote_debug.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,14 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c
720720

721721
#ifdef MS_WINDOWS
722722

723+
static int is_process_alive(HANDLE hProcess) {
724+
DWORD exitCode;
725+
if (GetExitCodeProcess(hProcess, &exitCode)) {
726+
return exitCode == STILL_ACTIVE;
727+
}
728+
return 0;
729+
}
730+
723731
static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char* secname) {
724732
HANDLE hFile = CreateFileW(mod_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
725733
if (hFile == INVALID_HANDLE_VALUE) {
@@ -950,6 +958,13 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
950958
SIZE_T result = 0;
951959
do {
952960
if (!ReadProcessMemory(handle->hProcess, (LPCVOID)(remote_address + result), (char*)dst + result, len - result, &read_bytes)) {
961+
// Check if the process is still alive: we need to be able to tell our caller
962+
// that the process is dead and not just that the read failed.
963+
if (!is_process_alive(handle->hProcess)) {
964+
_set_errno(ESRCH);
965+
PyErr_SetFromErrno(PyExc_OSError);
966+
return -1;
967+
}
953968
PyErr_SetFromWindowsErr(0);
954969
DWORD error = GetLastError();
955970
_set_debug_exception_cause(PyExc_OSError,

0 commit comments

Comments
 (0)