Skip to content

Commit 8c485ea

Browse files
committed
Buffer debug cycles output before printing it via stderr
1 parent 0a64683 commit 8c485ea

1 file changed

Lines changed: 38 additions & 23 deletions

File tree

‎Python/gc.c‎

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -996,38 +996,53 @@ clear_weakrefs(PyGC_Head *unreachable)
996996
}
997997
}
998998

999-
static void
999+
static char *
10001000
debug_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
*/
10121021
static Py_ssize_t
10131022
debug_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

Comments
 (0)