Skip to content

Commit 289d868

Browse files
committed
Add debug asserts
1 parent 1622ee3 commit 289d868

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

Modules/_remote_debugging_module.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* HEADERS AND INCLUDES
1212
* ============================================================================ */
1313

14+
#include <assert.h>
1415
#include <errno.h>
1516
#include <fcntl.h>
1617
#include <stddef.h>
@@ -2479,6 +2480,8 @@ unwind_stack_for_thread(
24792480
Py_XDECREF(thread_id);
24802481
Py_XDECREF(result);
24812482
cleanup_stack_chunks(&chunks);
2483+
/* Assert that an exception has been set when returning NULL */
2484+
assert(PyErr_Occurred());
24822485
return NULL;
24832486
}
24842487

@@ -2761,6 +2764,8 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
27612764

27622765
exit:
27632766
_Py_RemoteDebug_ClearCache(&self->handle);
2767+
/* Assert that if we're returning NULL, an exception has been set */
2768+
assert(result != NULL || PyErr_Occurred());
27642769
return result;
27652770
}
27662771

@@ -2849,6 +2854,8 @@ _remote_debugging_RemoteUnwinder_get_all_awaited_by_impl(RemoteUnwinderObject *s
28492854
result_err:
28502855
_Py_RemoteDebug_ClearCache(&self->handle);
28512856
Py_XDECREF(result);
2857+
/* Assert that an exception has been set when returning NULL */
2858+
assert(PyErr_Occurred());
28522859
return NULL;
28532860
}
28542861

@@ -2923,6 +2930,8 @@ _remote_debugging_RemoteUnwinder_get_async_stack_trace_impl(RemoteUnwinderObject
29232930
result_err:
29242931
_Py_RemoteDebug_ClearCache(&self->handle);
29252932
Py_XDECREF(result);
2933+
/* Assert that an exception has been set when returning NULL */
2934+
assert(PyErr_Occurred());
29262935
return NULL;
29272936
}
29282937

Python/remote_debug.h

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ extern "C" {
6767
#include <tlhelp32.h>
6868
#endif
6969

70+
#include <assert.h>
7071
#include <errno.h>
7172
#include <fcntl.h>
7273
#include <stddef.h>
@@ -165,6 +166,7 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) {
165166
handle->task = pid_to_task(handle->pid);
166167
if (handle->task == 0) {
167168
_set_debug_exception_cause(PyExc_RuntimeError, "Failed to initialize macOS process handle");
169+
assert(PyErr_Occurred());
168170
return -1;
169171
}
170172
#elif defined(MS_WINDOWS)
@@ -174,6 +176,7 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) {
174176
if (handle->hProcess == NULL) {
175177
PyErr_SetFromWindowsErr(0);
176178
_set_debug_exception_cause(PyExc_RuntimeError, "Failed to initialize Windows process handle");
179+
assert(PyErr_Occurred());
177180
return -1;
178181
}
179182
#elif defined(__linux__)
@@ -248,6 +251,7 @@ return_section_address64(
248251
"mach_vm_region failed while parsing 64-bit Mach-O binary "
249252
"at base address 0x%lx (kern_return_t: %d)",
250253
base, ret);
254+
assert(PyErr_Occurred());
251255
return 0;
252256
}
253257
}
@@ -311,6 +315,7 @@ return_section_address32(
311315
"mach_vm_region failed while parsing 32-bit Mach-O binary "
312316
"at base address 0x%lx (kern_return_t: %d)",
313317
base, ret);
318+
assert(PyErr_Occurred());
314319
return 0;
315320
}
316321
}
@@ -352,13 +357,15 @@ return_section_address_fat(
352357
"Failed to determine CPU type via sysctlbyname "
353358
"for fat binary analysis at 0x%lx: %s",
354359
base, strerror(errno));
360+
assert(PyErr_Occurred());
355361
return 0;
356362
}
357363
if (sysctlbyname("hw.cpu64bit_capable", &is_abi64, &abi64_size, NULL, 0) != 0) {
358364
PyErr_Format(PyExc_OSError,
359365
"Failed to determine CPU ABI capability via sysctlbyname "
360366
"for fat binary analysis at 0x%lx: %s",
361367
base, strerror(errno));
368+
assert(PyErr_Occurred());
362369
return 0;
363370
}
364371

@@ -403,6 +410,7 @@ return_section_address_fat(
403410
"No matching architecture found for CPU type 0x%x "
404411
"in fat binary at base 0x%lx (%u architectures examined)",
405412
cpu, base, nfat_arch);
413+
assert(PyErr_Occurred());
406414
return 0;
407415
}
408416

@@ -414,6 +422,7 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_
414422
PyErr_Format(PyExc_OSError,
415423
"Cannot open binary file '%s' for section '%s' search: %s",
416424
path, secname, strerror(errno));
425+
assert(PyErr_Occurred());
417426
return 0;
418427
}
419428

@@ -423,6 +432,7 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_
423432
"Cannot get file size for binary '%s' during section '%s' search: %s",
424433
path, secname, strerror(errno));
425434
close(fd);
435+
assert(PyErr_Occurred());
426436
return 0;
427437
}
428438

@@ -432,6 +442,7 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_
432442
"Cannot memory map binary file '%s' (size: %lld bytes) for section '%s' search: %s",
433443
path, (long long)fs.st_size, secname, strerror(errno));
434444
close(fd);
445+
assert(PyErr_Occurred());
435446
return 0;
436447
}
437448

@@ -455,6 +466,7 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_
455466
PyErr_Format(PyExc_RuntimeError,
456467
"Unrecognized Mach-O magic number 0x%x in binary file '%s' for section '%s' search",
457468
magic, path, secname);
469+
assert(PyErr_Occurred());
458470
break;
459471
}
460472

@@ -486,6 +498,7 @@ pid_to_task(pid_t pid)
486498
"Cannot get task port for PID %d (kern_return_t: %d). "
487499
"This typically requires running as root or having the 'com.apple.system-task-ports' entitlement.",
488500
pid, result);
501+
assert(PyErr_Occurred());
489502
return 0;
490503
}
491504
return task;
@@ -506,6 +519,7 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s
506519
"Cannot get task port for PID %d during section search",
507520
handle->pid);
508521
}
522+
assert(PyErr_Occurred());
509523
return 0;
510524
}
511525

@@ -671,6 +685,7 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c
671685
PyErr_Format(PyExc_OSError,
672686
"Cannot open process memory map file '%s' for PID %d section search: %s",
673687
maps_file_path, handle->pid, strerror(errno));
688+
assert(PyErr_Occurred());
674689
return 0;
675690
}
676691

@@ -762,13 +777,15 @@ static int is_process_alive(HANDLE hProcess) {
762777
}
763778

764779
static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char* secname) {
780+
printf("DEBUG: analyze_pe called with mod_path=%ls, remote_base=%p, secname=%s\n", mod_path, remote_base, secname);
765781
HANDLE hFile = CreateFileW(mod_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
766782
if (hFile == INVALID_HANDLE_VALUE) {
767783
PyErr_SetFromWindowsErr(0);
768784
DWORD error = GetLastError();
769785
PyErr_Format(PyExc_OSError,
770786
"Cannot open PE file for section '%s' analysis (error %lu)",
771787
secname, error);
788+
printf("DEBUG: analyze_pe failed to open file, error=%lu\n", error);
772789
return NULL;
773790
}
774791

@@ -820,10 +837,14 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char*
820837
IMAGE_SECTION_HEADER* pSection_header = (IMAGE_SECTION_HEADER*)(mapView + pDOSHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));
821838
void* runtime_addr = NULL;
822839

840+
printf("DEBUG: analyze_pe searching for section '%s', NumberOfSections=%d\n", secname, pNTHeaders->FileHeader.NumberOfSections);
823841
for (int i = 0; i < pNTHeaders->FileHeader.NumberOfSections; i++) {
824842
const char* name = (const char*)pSection_header[i].Name;
843+
printf("DEBUG: analyze_pe section[%d] name='%.8s'\n", i, name);
825844
if (strncmp(name, secname, IMAGE_SIZEOF_SHORT_NAME) == 0) {
826845
runtime_addr = remote_base + pSection_header[i].VirtualAddress;
846+
printf("DEBUG: analyze_pe found matching section at virtual address=0x%x, runtime_addr=%p\n",
847+
pSection_header[i].VirtualAddress, runtime_addr);
827848
break;
828849
}
829850
}
@@ -832,12 +853,25 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char*
832853
CloseHandle(hMap);
833854
CloseHandle(hFile);
834855

856+
if (runtime_addr == NULL) {
857+
printf("DEBUG: analyze_pe section '%s' not found, setting error\n", secname);
858+
// PyErr_Format(PyExc_RuntimeError,
859+
// "Section '%s' not found in PE file",
860+
// secname);
861+
} else {
862+
printf("DEBUG: analyze_pe returning runtime_addr=%p\n", runtime_addr);
863+
}
864+
865+
/* Assert that if we're returning NULL, an exception should be set somewhere up the call stack */
866+
// assert(runtime_addr != NULL || PyErr_Occurred());
835867
return runtime_addr;
836868
}
837869

838870

839871
static uintptr_t
840872
search_windows_map_for_section(proc_handle_t* handle, const char* secname, const wchar_t* substr) {
873+
printf("DEBUG: search_windows_map_for_section called with pid=%d, secname=%s, substr=%ls\n",
874+
handle->pid, secname, substr);
841875
HANDLE hProcSnap;
842876
do {
843877
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, handle->pid);
@@ -850,25 +884,34 @@ search_windows_map_for_section(proc_handle_t* handle, const char* secname, const
850884
"Unable to create module snapshot for PID %d section '%s' "
851885
"search (error %lu). Check permissions or PID validity",
852886
handle->pid, secname, error);
887+
assert(PyErr_Occurred());
853888
return 0;
854889
}
855890

856891
MODULEENTRY32W moduleEntry;
857892
moduleEntry.dwSize = sizeof(moduleEntry);
858893
void* runtime_addr = NULL;
859894

895+
printf("DEBUG: search_windows_map_for_section enumerating modules\n");
860896
for (BOOL hasModule = Module32FirstW(hProcSnap, &moduleEntry); hasModule; hasModule = Module32NextW(hProcSnap, &moduleEntry)) {
897+
printf("DEBUG: search_windows_map_for_section checking module: %ls\n", moduleEntry.szModule);
861898
// Look for either python executable or DLL
862899
if (wcsstr(moduleEntry.szModule, substr)) {
900+
printf("DEBUG: search_windows_map_for_section found matching module: %ls, path: %ls\n",
901+
moduleEntry.szModule, moduleEntry.szExePath);
863902
runtime_addr = analyze_pe(moduleEntry.szExePath, moduleEntry.modBaseAddr, secname);
864903
if (runtime_addr != NULL) {
904+
printf("DEBUG: search_windows_map_for_section analyze_pe succeeded, runtime_addr=%p\n", runtime_addr);
865905
break;
906+
} else {
907+
printf("DEBUG: search_windows_map_for_section analyze_pe failed for module %ls\n", moduleEntry.szModule);
866908
}
867909
}
868910
}
869911

870912
CloseHandle(hProcSnap);
871913

914+
printf("DEBUG: search_windows_map_for_section returning runtime_addr=%p\n", runtime_addr);
872915
return (uintptr_t)runtime_addr;
873916
}
874917

@@ -878,12 +921,16 @@ search_windows_map_for_section(proc_handle_t* handle, const char* secname, const
878921
static uintptr_t
879922
_Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
880923
{
924+
printf("DEBUG: _Py_RemoteDebug_GetPyRuntimeAddress called for pid=%d\n", handle->pid);
881925
uintptr_t address;
882926

883927
#ifdef MS_WINDOWS
884928
// On Windows, search for 'python' in executable or DLL
929+
printf("DEBUG: _Py_RemoteDebug_GetPyRuntimeAddress calling search_windows_map_for_section\n");
885930
address = search_windows_map_for_section(handle, "PyRuntime", L"python");
931+
printf("DEBUG: _Py_RemoteDebug_GetPyRuntimeAddress search_windows_map_for_section returned address=0x%lx\n", address);
886932
if (address == 0) {
933+
printf("DEBUG: _Py_RemoteDebug_GetPyRuntimeAddress address is 0, setting error\n");
887934
// Error out: 'python' substring covers both executable and DLL
888935
PyObject *exc = PyErr_GetRaisedException();
889936
PyErr_Format(PyExc_RuntimeError,
@@ -923,9 +970,11 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
923970
#else
924971
_set_debug_exception_cause(PyExc_RuntimeError,
925972
"Reading the PyRuntime section is not supported on this platform");
973+
assert(PyErr_Occurred());
926974
return 0;
927975
#endif
928976

977+
printf("DEBUG: _Py_RemoteDebug_GetPyRuntimeAddress returning address=0x%lx\n", address);
929978
return address;
930979
}
931980

@@ -942,6 +991,7 @@ open_proc_mem_fd(proc_handle_t *handle)
942991
PyErr_SetFromErrno(PyExc_OSError);
943992
_set_debug_exception_cause(PyExc_OSError,
944993
"failed to open file %s: %s", mem_file_path, strerror(errno));
994+
assert(PyErr_Occurred());
945995
return -1;
946996
}
947997
return 0;
@@ -974,6 +1024,7 @@ read_remote_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, siz
9741024
"preadv failed for PID %d at address 0x%lx "
9751025
"(size %zu, partial read %zd bytes): %s",
9761026
handle->pid, remote_address + result, len - result, result, strerror(errno));
1027+
assert(PyErr_Occurred());
9771028
return -1;
9781029
}
9791030

@@ -998,6 +1049,7 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
9981049
if (!is_process_alive(handle->hProcess)) {
9991050
_set_errno(ESRCH);
10001051
PyErr_SetFromErrno(PyExc_OSError);
1052+
assert(PyErr_Occurred());
10011053
return -1;
10021054
}
10031055
PyErr_SetFromWindowsErr(0);
@@ -1006,6 +1058,7 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
10061058
"ReadProcessMemory failed for PID %d at address 0x%lx "
10071059
"(size %zu, partial read %zu bytes): Windows error %lu",
10081060
handle->pid, remote_address + result, len - result, result, error);
1061+
assert(PyErr_Occurred());
10091062
return -1;
10101063
}
10111064
result += read_bytes;
@@ -1033,12 +1086,14 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
10331086
}
10341087
PyErr_SetFromErrno(PyExc_OSError);
10351088
if (errno == ESRCH) {
1089+
assert(PyErr_Occurred());
10361090
return -1;
10371091
}
10381092
_set_debug_exception_cause(PyExc_OSError,
10391093
"process_vm_readv failed for PID %d at address 0x%lx "
10401094
"(size %zu, partial read %zd bytes): %s",
10411095
handle->pid, remote_address + result, len - result, result, strerror(errno));
1096+
assert(PyErr_Occurred());
10421097
return -1;
10431098
}
10441099

@@ -1094,6 +1149,7 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
10941149
"(size %zu): kern_return_t %d",
10951150
handle->pid, remote_address, len, kr);
10961151
}
1152+
assert(PyErr_Occurred());
10971153
return -1;
10981154
}
10991155
return 0;
@@ -1108,6 +1164,7 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
11081164
size_t size,
11091165
void *out)
11101166
{
1167+
int result = 0;
11111168
size_t page_size = handle->page_size;
11121169
uintptr_t page_base = addr & ~(page_size - 1);
11131170
size_t offset_in_page = addr - page_base;
@@ -1136,6 +1193,7 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
11361193
"Cannot allocate %zu bytes for page cache entry "
11371194
"during read from PID %d at address 0x%lx",
11381195
page_size, handle->pid, addr);
1196+
assert(PyErr_Occurred());
11391197
return -1;
11401198
}
11411199
}
@@ -1155,7 +1213,9 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
11551213

11561214
fallback:
11571215
// Cache full — fallback to uncached read
1158-
return _Py_RemoteDebug_ReadRemoteMemory(handle, addr, size, out);
1216+
result = _Py_RemoteDebug_ReadRemoteMemory(handle, addr, size, out);
1217+
assert(result == 0 || PyErr_Occurred());
1218+
return result;
11591219
}
11601220

11611221
static int
@@ -1172,11 +1232,13 @@ _Py_RemoteDebug_ReadDebugOffsets(
11721232
handle->pid);
11731233
}
11741234
_set_debug_exception_cause(PyExc_RuntimeError, "PyRuntime address lookup failed during debug offsets initialization");
1235+
assert(PyErr_Occurred());
11751236
return -1;
11761237
}
11771238
size_t size = sizeof(struct _Py_DebugOffsets);
11781239
if (0 != _Py_RemoteDebug_ReadRemoteMemory(handle, *runtime_start_address, size, debug_offsets)) {
11791240
_set_debug_exception_cause(PyExc_RuntimeError, "Failed to read debug offsets structure from remote process");
1241+
assert(PyErr_Occurred());
11801242
return -1;
11811243
}
11821244
return 0;

0 commit comments

Comments
 (0)