@@ -265,18 +265,22 @@ process_frame_chain(
265265 int * stopped_at_cached_frame ,
266266 uintptr_t * frame_addrs , // optional: C array to receive frame addresses
267267 Py_ssize_t * num_addrs , // in/out: current count / updated count
268- Py_ssize_t max_addrs ) // max capacity of frame_addrs array
268+ Py_ssize_t max_addrs , // max capacity of frame_addrs array
269+ uintptr_t * out_last_frame_addr ) // optional: receives last frame address visited
269270{
270271 uintptr_t frame_addr = initial_frame_addr ;
271272 uintptr_t prev_frame_addr = 0 ;
272273 uintptr_t last_frame_addr = 0 ; // Track last frame visited for validation
273274 const size_t MAX_FRAMES = 1024 + 512 ;
274275 size_t frame_count = 0 ;
275276
276- // Initialize output flag
277+ // Initialize output parameters
277278 if (stopped_at_cached_frame ) {
278279 * stopped_at_cached_frame = 0 ;
279280 }
281+ if (out_last_frame_addr ) {
282+ * out_last_frame_addr = 0 ;
283+ }
280284
281285 // Quick check: if current_frame == last_profiled_frame, entire stack is unchanged
282286 if (last_profiled_frame != 0 && initial_frame_addr == last_profiled_frame ) {
@@ -390,6 +394,11 @@ process_frame_chain(
390394 return -1 ;
391395 }
392396
397+ // Set output parameter for caller (needed for cache validation)
398+ if (out_last_frame_addr ) {
399+ * out_last_frame_addr = last_frame_addr ;
400+ }
401+
393402 return 0 ;
394403}
395404
@@ -537,6 +546,7 @@ collect_frames_with_cache(
537546 uintptr_t frame_addr ,
538547 StackChunkList * chunks ,
539548 PyObject * frame_info ,
549+ uintptr_t base_frame_addr ,
540550 uintptr_t gc_frame ,
541551 uintptr_t last_profiled_frame ,
542552 uint64_t thread_id )
@@ -551,11 +561,13 @@ collect_frames_with_cache(
551561 uintptr_t addrs [FRAME_CACHE_MAX_FRAMES ];
552562 Py_ssize_t num_addrs = 0 ;
553563 Py_ssize_t frames_before = PyList_GET_SIZE (frame_info );
564+ uintptr_t last_frame_visited = 0 ;
554565
555566 int stopped_at_cached = 0 ;
556- if (process_frame_chain (unwinder , frame_addr , chunks , frame_info , 0 , gc_frame ,
567+ if (process_frame_chain (unwinder , frame_addr , chunks , frame_info , base_frame_addr , gc_frame ,
557568 last_profiled_frame , & stopped_at_cached ,
558- addrs , & num_addrs , FRAME_CACHE_MAX_FRAMES ) < 0 ) {
569+ addrs , & num_addrs , FRAME_CACHE_MAX_FRAMES ,
570+ & last_frame_visited ) < 0 ) {
559571 return -1 ;
560572 }
561573
@@ -575,23 +587,28 @@ collect_frames_with_cache(
575587 // Cache miss - continue walking from last_profiled_frame to get the rest
576588 STATS_INC (unwinder , frame_cache_misses );
577589 Py_ssize_t frames_before_walk = PyList_GET_SIZE (frame_info );
578- if (process_frame_chain (unwinder , last_profiled_frame , chunks , frame_info , 0 , gc_frame ,
579- 0 , NULL , addrs , & num_addrs , FRAME_CACHE_MAX_FRAMES ) < 0 ) {
590+ if (process_frame_chain (unwinder , last_profiled_frame , chunks , frame_info , base_frame_addr , gc_frame ,
591+ 0 , NULL , addrs , & num_addrs , FRAME_CACHE_MAX_FRAMES ,
592+ & last_frame_visited ) < 0 ) {
580593 return -1 ;
581594 }
582595 STATS_ADD (unwinder , frames_read_from_memory , PyList_GET_SIZE (frame_info ) - frames_before_walk );
583596 } else {
584- // Partial cache hit
597+ // Partial cache hit - cache was validated when stored, so we trust it
585598 STATS_INC (unwinder , frame_cache_partial_hits );
586599 STATS_ADD (unwinder , frames_read_from_cache , PyList_GET_SIZE (frame_info ) - frames_before_cache );
587600 }
588- } else if (last_profiled_frame == 0 ) {
589- // No cache involvement (no last_profiled_frame or cache disabled)
590- STATS_INC (unwinder , frame_cache_misses );
601+ } else {
602+ if (last_profiled_frame == 0 ) {
603+ // No cache involvement (no last_profiled_frame or cache disabled)
604+ STATS_INC (unwinder , frame_cache_misses );
605+ }
591606 }
592607
593- // Store in cache (frame_cache_store handles truncation if num_addrs > FRAME_CACHE_MAX_FRAMES)
594- if (frame_cache_store (unwinder , thread_id , frame_info , addrs , num_addrs ) < 0 ) {
608+ // Store in cache - frame_cache_store validates internally that we have a
609+ // complete stack (reached base_frame_addr) before actually storing
610+ if (frame_cache_store (unwinder , thread_id , frame_info , addrs , num_addrs ,
611+ base_frame_addr , last_frame_visited ) < 0 ) {
595612 return -1 ;
596613 }
597614
0 commit comments