@@ -361,6 +361,8 @@ _remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self,
361361 self -> cache_frames = cache_frames ;
362362 self -> collect_stats = stats ;
363363 self -> stale_invalidation_counter = 0 ;
364+ self -> cached_tstate_interpreter_addr = 0 ;
365+ self -> cached_tstate_addr = 0 ;
364366 memset (self -> cached_tstates , 0 , sizeof (self -> cached_tstates ));
365367 self -> debug = debug ;
366368 self -> only_active_thread = only_active_thread ;
@@ -478,13 +480,10 @@ _remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self,
478480static size_t
479481interpreter_thread_cache_index (uintptr_t interpreter_addr )
480482{
481- // The interpreter ID lives in PyInterpreterState, which is the state we are
482- // trying to prefetch. At this point the cheap stable key is the remote
483- // interpreter address, so use it for a small direct-mapped prediction cache.
484- // The full address is stored in each entry and checked on lookup, so hash
485- // collisions are detected as misses; storing a colliding entry only replaces
486- // the previous prediction and cannot return the wrong thread state.
487- return ((interpreter_addr >> 4 ) ^ (interpreter_addr >> 12 ))
483+ // Direct-mapped table indexed by the remote interpreter address. Each entry
484+ // stores the full address and verifies it on lookup, so hash collisions
485+ // degrade to misses and cannot return a wrong tstate.
486+ return (size_t )_Py_HashPointerRaw ((const void * )interpreter_addr )
488487 & (INTERPRETER_THREAD_CACHE_SIZE - 1 );
489488}
490489
@@ -497,9 +496,15 @@ get_cached_tstate_for_interpreter(
497496 return 0 ;
498497 }
499498
499+ if (self -> cached_tstate_interpreter_addr == interpreter_addr ) {
500+ return self -> cached_tstate_addr ;
501+ }
502+
500503 InterpreterThreadCacheEntry * entry =
501504 & self -> cached_tstates [interpreter_thread_cache_index (interpreter_addr )];
502505 if (entry -> interpreter_addr == interpreter_addr ) {
506+ self -> cached_tstate_interpreter_addr = interpreter_addr ;
507+ self -> cached_tstate_addr = entry -> thread_state_addr ;
503508 return entry -> thread_state_addr ;
504509 }
505510 return 0 ;
@@ -515,6 +520,9 @@ set_cached_tstate_for_interpreter(
515520 return ;
516521 }
517522
523+ self -> cached_tstate_interpreter_addr = interpreter_addr ;
524+ self -> cached_tstate_addr = thread_state_addr ;
525+
518526 InterpreterThreadCacheEntry * entry =
519527 & self -> cached_tstates [interpreter_thread_cache_index (interpreter_addr )];
520528 entry -> interpreter_addr = interpreter_addr ;
@@ -584,8 +592,19 @@ read_interp_state_and_maybe_thread_frame(
584592 int nsegs = prefetch -> frame_addr != 0 ? 3 : 2 ;
585593 Py_ssize_t nread = _Py_RemoteDebug_BatchedReadRemoteMemory (
586594 & unwinder -> handle , segments , nsegs );
587- int completed = _Py_RemoteDebug_CountCompletedSegments (
588- segments , nsegs , nread );
595+ int completed = 0 ;
596+ if (nread >= (Py_ssize_t )INTERP_STATE_BUFFER_SIZE ) {
597+ completed = 1 ;
598+ Py_ssize_t with_tstate = (Py_ssize_t )INTERP_STATE_BUFFER_SIZE
599+ + (Py_ssize_t )tstate_size ;
600+ if (nread >= with_tstate ) {
601+ completed = 2 ;
602+ }
603+ if (nsegs == 3
604+ && nread == with_tstate + (Py_ssize_t )SIZEOF_INTERP_FRAME ) {
605+ completed = 3 ;
606+ }
607+ }
589608 STATS_BATCHED_READ (unwinder , nsegs , completed );
590609 if (completed >= 1 ) {
591610 if (completed >= 2 ) {
0 commit comments