Skip to content

Commit 77d25e5

Browse files
authored
gh-91048: Revert the memory cache removal for remote debugging (#136440)
gh-91048: Reintroduce the memory cache for remote debugging
1 parent a6566e4 commit 77d25e5

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

Modules/_remote_debugging_module.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,10 @@ parse_coro_chain(
945945
return -1;
946946
}
947947

948+
if (name == NULL) {
949+
return 0;
950+
}
951+
948952
if (PyList_Append(render_to, name)) {
949953
Py_DECREF(name);
950954
set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame to coro chain");
@@ -2762,6 +2766,7 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
27622766
}
27632767

27642768
exit:
2769+
_Py_RemoteDebug_ClearCache(&self->handle);
27652770
return result;
27662771
}
27672772

@@ -2885,9 +2890,11 @@ _remote_debugging_RemoteUnwinder_get_all_awaited_by_impl(RemoteUnwinderObject *s
28852890
goto result_err;
28862891
}
28872892

2893+
_Py_RemoteDebug_ClearCache(&self->handle);
28882894
return result;
28892895

28902896
result_err:
2897+
_Py_RemoteDebug_ClearCache(&self->handle);
28912898
Py_XDECREF(result);
28922899
return NULL;
28932900
}
@@ -2954,9 +2961,11 @@ _remote_debugging_RemoteUnwinder_get_async_stack_trace_impl(RemoteUnwinderObject
29542961
goto cleanup;
29552962
}
29562963

2964+
_Py_RemoteDebug_ClearCache(&self->handle);
29572965
return result;
29582966

29592967
cleanup:
2968+
_Py_RemoteDebug_ClearCache(&self->handle);
29602969
Py_XDECREF(result);
29612970
return NULL;
29622971
}
@@ -2982,6 +2991,7 @@ RemoteUnwinder_dealloc(PyObject *op)
29822991
}
29832992
#endif
29842993
if (self->handle.pid != 0) {
2994+
_Py_RemoteDebug_ClearCache(&self->handle);
29852995
_Py_RemoteDebug_CleanupProcHandle(&self->handle);
29862996
}
29872997
PyObject_Del(self);

Python/remote_debug.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ get_page_size(void) {
110110
return page_size;
111111
}
112112

113+
typedef struct page_cache_entry {
114+
uintptr_t page_addr; // page-aligned base address
115+
char *data;
116+
int valid;
117+
struct page_cache_entry *next;
118+
} page_cache_entry_t;
119+
120+
#define MAX_PAGES 1024
113121

114122
// Define a platform-independent process handle structure
115123
typedef struct {
@@ -121,9 +129,27 @@ typedef struct {
121129
#elif defined(__linux__)
122130
int memfd;
123131
#endif
132+
page_cache_entry_t pages[MAX_PAGES];
124133
Py_ssize_t page_size;
125134
} proc_handle_t;
126135

136+
static void
137+
_Py_RemoteDebug_FreePageCache(proc_handle_t *handle)
138+
{
139+
for (int i = 0; i < MAX_PAGES; i++) {
140+
PyMem_RawFree(handle->pages[i].data);
141+
handle->pages[i].data = NULL;
142+
handle->pages[i].valid = 0;
143+
}
144+
}
145+
146+
UNUSED static void
147+
_Py_RemoteDebug_ClearCache(proc_handle_t *handle)
148+
{
149+
for (int i = 0; i < MAX_PAGES; i++) {
150+
handle->pages[i].valid = 0;
151+
}
152+
}
127153

128154
#if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX
129155
static mach_port_t pid_to_task(pid_t pid);
@@ -152,6 +178,10 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) {
152178
handle->memfd = -1;
153179
#endif
154180
handle->page_size = get_page_size();
181+
for (int i = 0; i < MAX_PAGES; i++) {
182+
handle->pages[i].data = NULL;
183+
handle->pages[i].valid = 0;
184+
}
155185
return 0;
156186
}
157187

@@ -170,6 +200,7 @@ _Py_RemoteDebug_CleanupProcHandle(proc_handle_t *handle) {
170200
}
171201
#endif
172202
handle->pid = 0;
203+
_Py_RemoteDebug_FreePageCache(handle);
173204
}
174205

175206
#if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX
@@ -1035,6 +1066,53 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
10351066
size_t size,
10361067
void *out)
10371068
{
1069+
size_t page_size = handle->page_size;
1070+
uintptr_t page_base = addr & ~(page_size - 1);
1071+
size_t offset_in_page = addr - page_base;
1072+
1073+
if (offset_in_page + size > page_size) {
1074+
return _Py_RemoteDebug_ReadRemoteMemory(handle, addr, size, out);
1075+
}
1076+
1077+
// Search for valid cached page
1078+
for (int i = 0; i < MAX_PAGES; i++) {
1079+
page_cache_entry_t *entry = &handle->pages[i];
1080+
if (entry->valid && entry->page_addr == page_base) {
1081+
memcpy(out, entry->data + offset_in_page, size);
1082+
return 0;
1083+
}
1084+
}
1085+
1086+
// Find reusable slot
1087+
for (int i = 0; i < MAX_PAGES; i++) {
1088+
page_cache_entry_t *entry = &handle->pages[i];
1089+
if (!entry->valid) {
1090+
if (entry->data == NULL) {
1091+
entry->data = PyMem_RawMalloc(page_size);
1092+
if (entry->data == NULL) {
1093+
_set_debug_exception_cause(PyExc_MemoryError,
1094+
"Cannot allocate %zu bytes for page cache entry "
1095+
"during read from PID %d at address 0x%lx",
1096+
page_size, handle->pid, addr);
1097+
return -1;
1098+
}
1099+
}
1100+
1101+
if (_Py_RemoteDebug_ReadRemoteMemory(handle, page_base, page_size, entry->data) < 0) {
1102+
// Try to just copy the exact ammount as a fallback
1103+
PyErr_Clear();
1104+
goto fallback;
1105+
}
1106+
1107+
entry->page_addr = page_base;
1108+
entry->valid = 1;
1109+
memcpy(out, entry->data + offset_in_page, size);
1110+
return 0;
1111+
}
1112+
}
1113+
1114+
fallback:
1115+
// Cache full — fallback to uncached read
10381116
return _Py_RemoteDebug_ReadRemoteMemory(handle, addr, size, out);
10391117
}
10401118

0 commit comments

Comments
 (0)