https://github.com/python/cpython/commit/9a79c5128f6d10118b9c2a402f2f9582f7a2c489
commit: 9a79c5128f6d10118b9c2a402f2f9582f7a2c489
branch: 3.14
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: pablogsal <pablog...@gmail.com>
date: 2025-07-09T02:21:56+01:00
summary:

[3.14] gh-91048: Revert the memory cache removal for remote debugging 
(GH-136440) (#136443)

gh-91048: Revert the memory cache removal for remote debugging (GH-136440)
(cherry picked from commit 77d25e5b169f7c306d3a6d9ca6777c0a0be80d8f)


gh-91048: Reintroduce the memory cache for remote debugging

Co-authored-by: Pablo Galindo Salgado <pablog...@gmail.com>

files:
M Modules/_remote_debugging_module.c
M Python/remote_debug.h

diff --git a/Modules/_remote_debugging_module.c 
b/Modules/_remote_debugging_module.c
index e744fe65815985..7b8d272e63b160 100644
--- a/Modules/_remote_debugging_module.c
+++ b/Modules/_remote_debugging_module.c
@@ -945,6 +945,10 @@ parse_coro_chain(
         return -1;
     }
 
+    if (name == NULL) {
+        return 0;
+    }
+
     if (PyList_Append(render_to, name)) {
         Py_DECREF(name);
         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
     }
 
 exit:
+   _Py_RemoteDebug_ClearCache(&self->handle);
     return result;
 }
 
@@ -2885,9 +2890,11 @@ 
_remote_debugging_RemoteUnwinder_get_all_awaited_by_impl(RemoteUnwinderObject *s
         goto result_err;
     }
 
+    _Py_RemoteDebug_ClearCache(&self->handle);
     return result;
 
 result_err:
+    _Py_RemoteDebug_ClearCache(&self->handle);
     Py_XDECREF(result);
     return NULL;
 }
@@ -2954,9 +2961,11 @@ 
_remote_debugging_RemoteUnwinder_get_async_stack_trace_impl(RemoteUnwinderObject
         goto cleanup;
     }
 
+    _Py_RemoteDebug_ClearCache(&self->handle);
     return result;
 
 cleanup:
+    _Py_RemoteDebug_ClearCache(&self->handle);
     Py_XDECREF(result);
     return NULL;
 }
@@ -2982,6 +2991,7 @@ RemoteUnwinder_dealloc(PyObject *op)
     }
 #endif
     if (self->handle.pid != 0) {
+        _Py_RemoteDebug_ClearCache(&self->handle);
         _Py_RemoteDebug_CleanupProcHandle(&self->handle);
     }
     PyObject_Del(self);
diff --git a/Python/remote_debug.h b/Python/remote_debug.h
index d1fcb478d2b035..8f9b6cd4c4960f 100644
--- a/Python/remote_debug.h
+++ b/Python/remote_debug.h
@@ -110,6 +110,14 @@ get_page_size(void) {
     return page_size;
 }
 
+typedef struct page_cache_entry {
+    uintptr_t page_addr; // page-aligned base address
+    char *data;
+    int valid;
+    struct page_cache_entry *next;
+} page_cache_entry_t;
+
+#define MAX_PAGES 1024
 
 // Define a platform-independent process handle structure
 typedef struct {
@@ -121,9 +129,27 @@ typedef struct {
 #elif defined(__linux__)
     int memfd;
 #endif
+    page_cache_entry_t pages[MAX_PAGES];
     Py_ssize_t page_size;
 } proc_handle_t;
 
+static void
+_Py_RemoteDebug_FreePageCache(proc_handle_t *handle)
+{
+    for (int i = 0; i < MAX_PAGES; i++) {
+        PyMem_RawFree(handle->pages[i].data);
+        handle->pages[i].data = NULL;
+        handle->pages[i].valid = 0;
+    }
+}
+
+UNUSED static void
+_Py_RemoteDebug_ClearCache(proc_handle_t *handle)
+{
+    for (int i = 0; i < MAX_PAGES; i++) {
+        handle->pages[i].valid = 0;
+    }
+}
 
 #if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX
 static mach_port_t pid_to_task(pid_t pid);
@@ -152,6 +178,10 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, 
pid_t pid) {
     handle->memfd = -1;
 #endif
     handle->page_size = get_page_size();
+    for (int i = 0; i < MAX_PAGES; i++) {
+        handle->pages[i].data = NULL;
+        handle->pages[i].valid = 0;
+    }
     return 0;
 }
 
@@ -170,6 +200,7 @@ _Py_RemoteDebug_CleanupProcHandle(proc_handle_t *handle) {
     }
 #endif
     handle->pid = 0;
+    _Py_RemoteDebug_FreePageCache(handle);
 }
 
 #if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX
@@ -1035,6 +1066,53 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t 
*handle,
                                       size_t size,
                                       void *out)
 {
+    size_t page_size = handle->page_size;
+    uintptr_t page_base = addr & ~(page_size - 1);
+    size_t offset_in_page = addr - page_base;
+
+    if (offset_in_page + size > page_size) {
+        return _Py_RemoteDebug_ReadRemoteMemory(handle, addr, size, out);
+    }
+
+    // Search for valid cached page
+    for (int i = 0; i < MAX_PAGES; i++) {
+        page_cache_entry_t *entry = &handle->pages[i];
+        if (entry->valid && entry->page_addr == page_base) {
+            memcpy(out, entry->data + offset_in_page, size);
+            return 0;
+        }
+    }
+
+    // Find reusable slot
+    for (int i = 0; i < MAX_PAGES; i++) {
+        page_cache_entry_t *entry = &handle->pages[i];
+        if (!entry->valid) {
+            if (entry->data == NULL) {
+                entry->data = PyMem_RawMalloc(page_size);
+                if (entry->data == NULL) {
+                    _set_debug_exception_cause(PyExc_MemoryError,
+                        "Cannot allocate %zu bytes for page cache entry "
+                        "during read from PID %d at address 0x%lx",
+                        page_size, handle->pid, addr);
+                    return -1;
+                }
+            }
+
+            if (_Py_RemoteDebug_ReadRemoteMemory(handle, page_base, page_size, 
entry->data) < 0) {
+                // Try to just copy the exact ammount as a fallback
+                PyErr_Clear();
+                goto fallback;
+            }
+
+            entry->page_addr = page_base;
+            entry->valid = 1;
+            memcpy(out, entry->data + offset_in_page, size);
+            return 0;
+        }
+    }
+
+fallback:
+    // Cache full — fallback to uncached read
     return _Py_RemoteDebug_ReadRemoteMemory(handle, addr, size, out);
 }
 

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to