@@ -147,19 +147,59 @@ frame_cache_invalidate_stale(RemoteUnwinderObject *unwinder, PyObject *result)
147147 Py_CLEAR (unwinder -> frame_cache [i ].frame_list );
148148 unwinder -> frame_cache [i ].thread_id = 0 ;
149149 unwinder -> frame_cache [i ].thread_state_addr = 0 ;
150+ unwinder -> frame_cache [i ].last_profiled_frame_seq = 0 ;
150151 unwinder -> frame_cache [i ].num_addrs = 0 ;
151152 STATS_INC (unwinder , stale_cache_invalidations );
152153 }
153154 }
154155}
155156
157+ int
158+ frame_cache_anchor_matches (
159+ RemoteUnwinderObject * unwinder ,
160+ uintptr_t thread_state_addr ,
161+ uintptr_t last_profiled_frame ,
162+ uintptr_t last_profiled_frame_seq )
163+ {
164+ uintptr_t live_frame = 0 ;
165+ uintptr_t live_seq = 0 ;
166+ uintptr_t frame_offset = (uintptr_t )unwinder -> debug_offsets .thread_state .last_profiled_frame ;
167+ uintptr_t seq_offset = (uintptr_t )unwinder -> debug_offsets .thread_state .last_profiled_frame_seq ;
168+
169+ if (seq_offset == frame_offset + sizeof (uintptr_t )) {
170+ uintptr_t live_anchor [2 ];
171+ if (_Py_RemoteDebug_PagedReadRemoteMemory (& unwinder -> handle ,
172+ thread_state_addr + frame_offset ,
173+ sizeof (live_anchor ),
174+ live_anchor ) < 0 ) {
175+ PyErr_Clear ();
176+ return 0 ;
177+ }
178+ live_frame = live_anchor [0 ];
179+ live_seq = live_anchor [1 ];
180+ }
181+ else {
182+ if (read_ptr (unwinder , thread_state_addr + frame_offset , & live_frame ) < 0 ) {
183+ PyErr_Clear ();
184+ return 0 ;
185+ }
186+ if (read_ptr (unwinder , thread_state_addr + seq_offset , & live_seq ) < 0 ) {
187+ PyErr_Clear ();
188+ return 0 ;
189+ }
190+ }
191+ return live_frame == last_profiled_frame && live_seq == last_profiled_frame_seq ;
192+ }
193+
156194// Find last_profiled_frame in cache and extend frame_info with cached continuation
157195// If frame_addrs is provided (not NULL), also extends it with cached addresses
158196int
159197frame_cache_lookup_and_extend (
160198 RemoteUnwinderObject * unwinder ,
161199 uint64_t thread_id ,
200+ uintptr_t thread_state_addr ,
162201 uintptr_t last_profiled_frame ,
202+ uintptr_t last_profiled_frame_seq ,
163203 PyObject * frame_info ,
164204 uintptr_t * frame_addrs ,
165205 Py_ssize_t * num_addrs ,
@@ -173,6 +213,9 @@ frame_cache_lookup_and_extend(
173213 if (!entry || !entry -> frame_list ) {
174214 return 0 ;
175215 }
216+ if (entry -> thread_state_addr != thread_state_addr ) {
217+ return 0 ;
218+ }
176219
177220 assert (entry -> num_addrs >= 0 && entry -> num_addrs <= FRAME_CACHE_MAX_FRAMES );
178221
@@ -189,8 +232,16 @@ frame_cache_lookup_and_extend(
189232 return 0 ; // Not found
190233 }
191234 assert (start_idx < entry -> num_addrs );
235+ if (entry -> last_profiled_frame_seq + (uintptr_t )start_idx !=
236+ last_profiled_frame_seq )
237+ {
238+ return 0 ;
239+ }
192240
193241 Py_ssize_t num_frames = PyList_GET_SIZE (entry -> frame_list );
242+ if (start_idx >= num_frames ) {
243+ return 0 ;
244+ }
194245
195246 // Extend frame_info with frames ABOVE start_idx (not including it).
196247 // The frame at start_idx (last_profiled_frame) was the executing frame
@@ -200,6 +251,11 @@ frame_cache_lookup_and_extend(
200251 if (cache_start >= num_frames ) {
201252 return 0 ; // Nothing above last_profiled_frame to extend with
202253 }
254+ if (!frame_cache_anchor_matches (unwinder , thread_state_addr ,
255+ last_profiled_frame ,
256+ last_profiled_frame_seq )) {
257+ return 0 ;
258+ }
203259
204260 PyObject * slice = PyList_GetSlice (entry -> frame_list , cache_start , num_frames );
205261 if (!slice ) {
@@ -235,6 +291,7 @@ frame_cache_store(
235291 const uintptr_t * addrs ,
236292 Py_ssize_t num_addrs ,
237293 uintptr_t thread_state_addr ,
294+ uintptr_t last_profiled_frame_seq ,
238295 uintptr_t base_frame_addr ,
239296 uintptr_t last_frame_visited )
240297{
@@ -277,6 +334,7 @@ frame_cache_store(
277334 }
278335 entry -> thread_id = thread_id ;
279336 entry -> thread_state_addr = thread_state_addr ;
337+ entry -> last_profiled_frame_seq = last_profiled_frame_seq ;
280338 if (entry -> thread_id_obj == NULL ) {
281339 entry -> thread_id_obj = PyLong_FromUnsignedLongLong (thread_id );
282340 if (entry -> thread_id_obj == NULL ) {
0 commit comments