@@ -598,6 +598,32 @@ refresh_generation_caches_for_interpreter(
598598 return 0 ;
599599}
600600
601+ static int
602+ iterate_unwinder_interpreters (
603+ RemoteUnwinderObject * self ,
604+ unwinder_interpreter_func func ,
605+ void * context )
606+ {
607+ uintptr_t interp = self -> interpreter_addr ;
608+ for (size_t count = 0 ; interp != 0 && count < MAX_INTERPRETERS ; count ++ ) {
609+ if (refresh_generation_caches_for_interpreter (self , interp ) < 0 ) {
610+ return -1 ;
611+ }
612+ if (func (self , interp , context ) < 0 ) {
613+ return -1 ;
614+ }
615+ if (_Py_RemoteDebug_PagedReadRemoteMemory (
616+ & self -> handle ,
617+ interp + (uintptr_t )self -> debug_offsets .interpreter_state .next ,
618+ sizeof (void * ),
619+ & interp ) < 0 ) {
620+ set_exception_cause (self , PyExc_RuntimeError , "Failed to read next interpreter address" );
621+ return -1 ;
622+ }
623+ }
624+ return 0 ;
625+ }
626+
601627static int
602628read_interp_state_and_maybe_thread_frame (
603629 RemoteUnwinderObject * unwinder ,
@@ -903,6 +929,35 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
903929 return result ;
904930}
905931
932+ static int
933+ process_interpreter_for_awaited_by (
934+ RemoteUnwinderObject * self ,
935+ uintptr_t interpreter_addr ,
936+ void * context )
937+ {
938+ PyObject * result = (PyObject * )context ;
939+
940+ // Process all threads
941+ if (iterate_threads (self , interpreter_addr , process_thread_for_awaited_by , result ) < 0 ) {
942+ return -1 ;
943+ }
944+
945+ uintptr_t head_addr = interpreter_addr
946+ + (uintptr_t )self -> async_debug_offsets .asyncio_interpreter_state .asyncio_tasks_head ;
947+
948+ // On top of a per-thread task lists used by default by asyncio to avoid
949+ // contention, there is also a fallback per-interpreter list of tasks;
950+ // any tasks still pending when a thread is destroyed will be moved to the
951+ // per-interpreter task list. It's unlikely we'll find anything here, but
952+ // interesting for debugging.
953+ if (append_awaited_by (self , 0 , head_addr , result ))
954+ {
955+ set_exception_cause (self , PyExc_RuntimeError , "Failed to append interpreter awaited_by in get_all_awaited_by" );
956+ return -1 ;
957+ }
958+ return 0 ;
959+ }
960+
906961/*[clinic input]
907962@permit_long_docstring_body
908963@critical_section
@@ -966,39 +1021,9 @@ _remote_debugging_RemoteUnwinder_get_all_awaited_by_impl(RemoteUnwinderObject *s
9661021 }
9671022
9681023 // gh-158880: Tasks live in every interpreter, not only the one at the list head
969- uintptr_t interp = self -> interpreter_addr ;
970- while (interp != 0 ) {
971- if (refresh_generation_caches_for_interpreter (self , interp ) < 0 ) {
972- goto result_err ;
973- }
974-
975- // Process all threads
976- if (iterate_threads (self , interp , process_thread_for_awaited_by , result ) < 0 ) {
977- goto result_err ;
978- }
979-
980- uintptr_t head_addr = interp
981- + (uintptr_t )self -> async_debug_offsets .asyncio_interpreter_state .asyncio_tasks_head ;
982-
983- // On top of a per-thread task lists used by default by asyncio to avoid
984- // contention, there is also a fallback per-interpreter list of tasks;
985- // any tasks still pending when a thread is destroyed will be moved to
986- // the per-interpreter task list. It's unlikely we'll find anything
987- // here, but interesting for debugging.
988- if (append_awaited_by (self , 0 , head_addr , result ))
989- {
990- set_exception_cause (self , PyExc_RuntimeError , "Failed to append interpreter awaited_by in get_all_awaited_by" );
991- goto result_err ;
992- }
993-
994- if (_Py_RemoteDebug_PagedReadRemoteMemory (
995- & self -> handle ,
996- interp + (uintptr_t )self -> debug_offsets .interpreter_state .next ,
997- sizeof (void * ),
998- & interp ) < 0 ) {
999- set_exception_cause (self , PyExc_RuntimeError , "Failed to read next interpreter address" );
1000- goto result_err ;
1001- }
1024+ if (iterate_unwinder_interpreters (self , process_interpreter_for_awaited_by ,
1025+ result ) < 0 ) {
1026+ goto result_err ;
10021027 }
10031028
10041029 _Py_RemoteDebug_ClearCache (& self -> handle );
0 commit comments