@@ -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 
115123typedef  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 
129155static  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