@@ -110,6 +110,14 @@ get_page_size(void) {
110
110
return page_size ;
111
111
}
112
112
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
113
121
114
122
// Define a platform-independent process handle structure
115
123
typedef struct {
@@ -121,9 +129,27 @@ typedef struct {
121
129
#elif defined(__linux__ )
122
130
int memfd ;
123
131
#endif
132
+ page_cache_entry_t pages [MAX_PAGES ];
124
133
Py_ssize_t page_size ;
125
134
} proc_handle_t ;
126
135
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
+ }
127
153
128
154
#if defined(__APPLE__ ) && defined(TARGET_OS_OSX ) && TARGET_OS_OSX
129
155
static mach_port_t pid_to_task (pid_t pid );
@@ -152,6 +178,10 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) {
152
178
handle -> memfd = -1 ;
153
179
#endif
154
180
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
+ }
155
185
return 0 ;
156
186
}
157
187
@@ -170,6 +200,7 @@ _Py_RemoteDebug_CleanupProcHandle(proc_handle_t *handle) {
170
200
}
171
201
#endif
172
202
handle -> pid = 0 ;
203
+ _Py_RemoteDebug_FreePageCache (handle );
173
204
}
174
205
175
206
#if defined(__APPLE__ ) && defined(TARGET_OS_OSX ) && TARGET_OS_OSX
@@ -1035,6 +1066,53 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
1035
1066
size_t size ,
1036
1067
void * out )
1037
1068
{
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
1038
1116
return _Py_RemoteDebug_ReadRemoteMemory (handle , addr , size , out );
1039
1117
}
1040
1118
0 commit comments