@@ -996,38 +996,53 @@ clear_weakrefs(PyGC_Head *unreachable)
996996 }
997997}
998998
999- static void
999+ static char *
10001000debug_cycle (const char * msg , PyObject * op )
10011001{
1002- PySys_FormatStderr ("gc: %s <%s %p>\n" ,
1003- msg , Py_TYPE (op )-> tp_name , op );
1002+ const char * name = Py_TYPE (op )-> tp_name ;
1003+ int len = PyOS_snprintf (NULL , 0 , "gc: %s <%s %p>\n" ,
1004+ msg , name , (void * )op );
1005+ if (len < 0 ) {
1006+ return NULL ;
1007+ }
1008+ char * line = PyMem_RawMalloc ((size_t )len + 1 );
1009+ if (line == NULL ) {
1010+ return NULL ;
1011+ }
1012+ PyOS_snprintf (line , (size_t )len + 1 , "gc: %s <%s %p>\n" ,
1013+ msg , name , (void * )op );
1014+ return line ;
10041015}
10051016
1006- /* Call debug_cycle() on each object in `list`. sys.stderr.write() can run
1007- * Python code that frees any object in the list, including the one just
1008- * printed, so always take the first object and move it to a temporary
1009- * `seen` list, as finalize_garbage() does. Return the number of seen
1010- * items.
1017+ /* Format a line for each object in `list`, then print them.
1018+ * sys.stderr.write(). Stores into buffers so can iterate
1019+ * to avoid calling into Python whilst we iterate the objects
10111020 */
10121021static Py_ssize_t
10131022debug_cycles (const char * msg , PyGC_Head * list )
10141023{
1015- PyGC_Head seen ;
1016- Py_ssize_t n = 0 ;
1017- /* Always take the next object from the head of `list` rather than from
1018- * the current one, which may be freed (along with others) by debug_cycle().
1019- */
1020- gc_list_init (& seen );
1021- while (!gc_list_is_empty (list )) {
1022- PyGC_Head * gc = GC_NEXT (list );
1023- PyObject * op = FROM_GC (gc );
1024- gc_list_move (gc , & seen );
1025- n ++ ;
1026- Py_INCREF (op );
1027- debug_cycle (msg , op );
1028- Py_DECREF (op );
1024+ Py_ssize_t n = gc_list_size (list );
1025+ if (n == 0 ) {
1026+ return n ;
1027+ }
1028+
1029+ char * * lines = PyMem_RawCalloc (n , sizeof (char * ));
1030+ if (lines == NULL ) {
1031+ return n ;
1032+ }
1033+
1034+ Py_ssize_t i = 0 ;
1035+ for (PyGC_Head * gc = GC_NEXT (list ); gc != list ; gc = GC_NEXT (gc ), i ++ ) {
1036+ lines [i ] = debug_cycle (msg , FROM_GC (gc ));
1037+ }
1038+
1039+ for (i = 0 ; i < n ; i ++ ) {
1040+ if (lines [i ] != NULL ) {
1041+ PySys_FormatStderr ("%s" , lines [i ]);
1042+ }
1043+ PyMem_RawFree (lines [i ]);
10291044 }
1030- gc_list_merge ( & seen , list );
1045+ PyMem_RawFree ( lines );
10311046 return n ;
10321047}
10331048
0 commit comments