Skip to content

Commit 8847d02

Browse files
committed
gh-91048: Reintroduce the memory cache for remote debugging
1 parent da79ac9 commit 8847d02

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

Modules/_remote_debugging_module.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,6 +2762,7 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
27622762
}
27632763

27642764
exit:
2765+
_Py_RemoteDebug_ClearCache(&self->handle);
27652766
return result;
27662767
}
27672768

@@ -2885,9 +2886,11 @@ _remote_debugging_RemoteUnwinder_get_all_awaited_by_impl(RemoteUnwinderObject *s
28852886
goto result_err;
28862887
}
28872888

2889+
_Py_RemoteDebug_ClearCache(&self->handle);
28882890
return result;
28892891

28902892
result_err:
2893+
_Py_RemoteDebug_ClearCache(&self->handle);
28912894
Py_XDECREF(result);
28922895
return NULL;
28932896
}
@@ -2954,9 +2957,11 @@ _remote_debugging_RemoteUnwinder_get_async_stack_trace_impl(RemoteUnwinderObject
29542957
goto cleanup;
29552958
}
29562959

2960+
_Py_RemoteDebug_ClearCache(&self->handle);
29572961
return result;
29582962

29592963
cleanup:
2964+
_Py_RemoteDebug_ClearCache(&self->handle);
29602965
Py_XDECREF(result);
29612966
return NULL;
29622967
}
@@ -2982,6 +2987,7 @@ RemoteUnwinder_dealloc(PyObject *op)
29822987
}
29832988
#endif
29842989
if (self->handle.pid != 0) {
2990+
_Py_RemoteDebug_ClearCache(&self->handle);
29852991
_Py_RemoteDebug_CleanupProcHandle(&self->handle);
29862992
}
29872993
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy