@@ -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,24 @@ frame_cache_lookup_and_extend(
189232 return 0 ; // Not found
190233 }
191234 assert (start_idx < entry -> num_addrs );
235+ // Synthetic marker frames (<native>/<GC>) are stored as addr-0 entries but
236+ // never increment last_profiled_frame_seq in the target (only real frame
237+ // pops do). Count the real frames before start_idx so the sequence check is
238+ // not thrown off by markers sitting between the leaf and the anchor.
239+ uintptr_t real_pops = 0 ;
240+ for (Py_ssize_t i = 0 ; i < start_idx ; i ++ ) {
241+ if (entry -> addrs [i ] != 0 ) {
242+ real_pops ++ ;
243+ }
244+ }
245+ if (entry -> last_profiled_frame_seq + real_pops != last_profiled_frame_seq ) {
246+ return 0 ;
247+ }
192248
193249 Py_ssize_t num_frames = PyList_GET_SIZE (entry -> frame_list );
250+ if (start_idx >= num_frames ) {
251+ return 0 ;
252+ }
194253
195254 // Extend frame_info with frames ABOVE start_idx (not including it).
196255 // The frame at start_idx (last_profiled_frame) was the executing frame
@@ -200,6 +259,11 @@ frame_cache_lookup_and_extend(
200259 if (cache_start >= num_frames ) {
201260 return 0 ; // Nothing above last_profiled_frame to extend with
202261 }
262+ if (!frame_cache_anchor_matches (unwinder , thread_state_addr ,
263+ last_profiled_frame ,
264+ last_profiled_frame_seq )) {
265+ return 0 ;
266+ }
203267
204268 PyObject * slice = PyList_GetSlice (entry -> frame_list , cache_start , num_frames );
205269 if (!slice ) {
@@ -235,6 +299,7 @@ frame_cache_store(
235299 const uintptr_t * addrs ,
236300 Py_ssize_t num_addrs ,
237301 uintptr_t thread_state_addr ,
302+ uintptr_t last_profiled_frame_seq ,
238303 uintptr_t base_frame_addr ,
239304 uintptr_t last_frame_visited )
240305{
@@ -277,6 +342,7 @@ frame_cache_store(
277342 }
278343 entry -> thread_id = thread_id ;
279344 entry -> thread_state_addr = thread_state_addr ;
345+ entry -> last_profiled_frame_seq = last_profiled_frame_seq ;
280346 if (entry -> thread_id_obj == NULL ) {
281347 entry -> thread_id_obj = PyLong_FromUnsignedLongLong (thread_id );
282348 if (entry -> thread_id_obj == NULL ) {
0 commit comments