mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
- Add DEBUG_SAVEALL option. When enabled all garbage objects found by the
collector will be saved in gc.garbage. This is useful for debugging a program that creates reference cycles. - Fix else statements in gcmodule.c to conform to Python coding standards.
This commit is contained in:
parent
676940b497
commit
544de1effb
2 changed files with 49 additions and 29 deletions
|
@ -85,7 +85,9 @@ The following variable is provided for read-only access:
|
||||||
A list of objects which the collector found to be unreachable
|
A list of objects which the collector found to be unreachable
|
||||||
but could not be freed (uncollectable objects). Objects that have
|
but could not be freed (uncollectable objects). Objects that have
|
||||||
\method{__del__()} methods and create part of a reference cycle cause
|
\method{__del__()} methods and create part of a reference cycle cause
|
||||||
the entire reference cycle to be uncollectable.
|
the entire reference cycle to be uncollectable. If
|
||||||
|
\constant{DEBUG_SAVEALL} is set, then all unreachable objects will
|
||||||
|
be added to this list rather than freed.
|
||||||
\end{datadesc}
|
\end{datadesc}
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,8 +119,14 @@ When \constant{DEBUG_COLLECTABLE} or \constant{DEBUG_UNCOLLECTABLE} is
|
||||||
set, print information about objects other than instance objects found.
|
set, print information about objects other than instance objects found.
|
||||||
\end{datadesc}
|
\end{datadesc}
|
||||||
|
|
||||||
|
\begin{datadesc}{DEBUG_SAVEALL}
|
||||||
|
When set, all unreachable objects found will be appended to
|
||||||
|
\var{garbage} rather than being freed. This can be useful for debugging
|
||||||
|
a leaking program.
|
||||||
|
\end{datadesc}
|
||||||
|
|
||||||
\begin{datadesc}{DEBUG_LEAK}
|
\begin{datadesc}{DEBUG_LEAK}
|
||||||
The debugging flags necessary for the collector to print
|
The debugging flags necessary for the collector to print
|
||||||
information about a leaking program (equal to \code{DEBUG_COLLECTABLE |
|
information about a leaking program (equal to \code{DEBUG_COLLECTABLE |
|
||||||
DEBUG_UNCOLLECTABLE | DEBUG_INSTANCES | DEBUG_OBJECTS}).
|
DEBUG_UNCOLLECTABLE | DEBUG_INSTANCES | DEBUG_OBJECTS | DEBUG_SAVEALL}).
|
||||||
\end{datadesc}
|
\end{datadesc}
|
||||||
|
|
|
@ -53,10 +53,12 @@ static int allocated;
|
||||||
#define DEBUG_UNCOLLECTABLE (1<<2) /* print uncollectable objects */
|
#define DEBUG_UNCOLLECTABLE (1<<2) /* print uncollectable objects */
|
||||||
#define DEBUG_INSTANCES (1<<3) /* print instances */
|
#define DEBUG_INSTANCES (1<<3) /* print instances */
|
||||||
#define DEBUG_OBJECTS (1<<4) /* print other objects */
|
#define DEBUG_OBJECTS (1<<4) /* print other objects */
|
||||||
|
#define DEBUG_SAVEALL (1<<5) /* save all garbage in gc.garbage */
|
||||||
#define DEBUG_LEAK DEBUG_COLLECTABLE | \
|
#define DEBUG_LEAK DEBUG_COLLECTABLE | \
|
||||||
DEBUG_UNCOLLECTABLE | \
|
DEBUG_UNCOLLECTABLE | \
|
||||||
DEBUG_INSTANCES | \
|
DEBUG_INSTANCES | \
|
||||||
DEBUG_OBJECTS
|
DEBUG_OBJECTS | \
|
||||||
|
DEBUG_SAVEALL
|
||||||
static int debug;
|
static int debug;
|
||||||
|
|
||||||
/* list of uncollectable objects */
|
/* list of uncollectable objects */
|
||||||
|
@ -100,7 +102,8 @@ gc_list_move(PyGC_Head *from, PyGC_Head *to)
|
||||||
if (from->gc_next == from) {
|
if (from->gc_next == from) {
|
||||||
/* empty from list */
|
/* empty from list */
|
||||||
gc_list_init(to);
|
gc_list_init(to);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
to->gc_next = from->gc_next;
|
to->gc_next = from->gc_next;
|
||||||
to->gc_next->gc_prev = to;
|
to->gc_next->gc_prev = to;
|
||||||
to->gc_prev = from->gc_prev;
|
to->gc_prev = from->gc_prev;
|
||||||
|
@ -290,7 +293,8 @@ debug_cycle(char *msg, PyObject *op)
|
||||||
{
|
{
|
||||||
if ((debug & DEBUG_INSTANCES) && PyInstance_Check(op)) {
|
if ((debug & DEBUG_INSTANCES) && PyInstance_Check(op)) {
|
||||||
debug_instance(msg, (PyInstanceObject *)op);
|
debug_instance(msg, (PyInstanceObject *)op);
|
||||||
} else if (debug & DEBUG_OBJECTS) {
|
}
|
||||||
|
else if (debug & DEBUG_OBJECTS) {
|
||||||
PySys_WriteStderr("gc: %.100s <%.100s %p>\n",
|
PySys_WriteStderr("gc: %.100s <%.100s %p>\n",
|
||||||
msg, op->ob_type->tp_name, op);
|
msg, op->ob_type->tp_name, op);
|
||||||
}
|
}
|
||||||
|
@ -307,19 +311,20 @@ handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
|
||||||
for (gc = finalizers->gc_next; gc != finalizers;
|
for (gc = finalizers->gc_next; gc != finalizers;
|
||||||
gc = finalizers->gc_next) {
|
gc = finalizers->gc_next) {
|
||||||
PyObject *op = PyObject_FROM_GC(gc);
|
PyObject *op = PyObject_FROM_GC(gc);
|
||||||
/* Add all instances to a Python accessible list of garbage */
|
if ((debug & DEBUG_SAVEALL) || PyInstance_Check(op)) {
|
||||||
if (PyInstance_Check(op)) {
|
/* If SAVEALL is not set then just append
|
||||||
|
* instances to the list of garbage. We assume
|
||||||
|
* that all objects in the finalizers list are
|
||||||
|
* reachable from instances. */
|
||||||
PyList_Append(garbage, op);
|
PyList_Append(garbage, op);
|
||||||
}
|
}
|
||||||
/* We assume that all objects in finalizers are reachable from
|
/* object is now reachable again */
|
||||||
* instances. Once we add the instances to the garbage list
|
|
||||||
* everything is reachable from Python again. */
|
|
||||||
gc_list_remove(gc);
|
gc_list_remove(gc);
|
||||||
gc_list_append(gc, old);
|
gc_list_append(gc, old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Break reference cycles by clearing the containers involved. This is
|
/* Break reference cycles by clearing the containers involved. This is
|
||||||
* tricky business as the lists can be changing and we don't know which
|
* tricky business as the lists can be changing and we don't know which
|
||||||
* objects may be freed. It is possible I screwed something up here. */
|
* objects may be freed. It is possible I screwed something up here. */
|
||||||
static void
|
static void
|
||||||
|
@ -330,17 +335,18 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
|
||||||
while (unreachable->gc_next != unreachable) {
|
while (unreachable->gc_next != unreachable) {
|
||||||
PyGC_Head *gc = unreachable->gc_next;
|
PyGC_Head *gc = unreachable->gc_next;
|
||||||
PyObject *op = PyObject_FROM_GC(gc);
|
PyObject *op = PyObject_FROM_GC(gc);
|
||||||
/*
|
if (debug & DEBUG_SAVEALL) {
|
||||||
PyList_Append(garbage, op);
|
PyList_Append(garbage, op);
|
||||||
*/
|
}
|
||||||
if ((clear = op->ob_type->tp_clear) != NULL) {
|
else {
|
||||||
Py_INCREF(op);
|
if ((clear = op->ob_type->tp_clear) != NULL) {
|
||||||
clear((PyObject *)op);
|
Py_INCREF(op);
|
||||||
Py_DECREF(op);
|
clear((PyObject *)op);
|
||||||
|
Py_DECREF(op);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* only try to call tp_clear once for each object */
|
|
||||||
if (unreachable->gc_next == gc) {
|
if (unreachable->gc_next == gc) {
|
||||||
/* still alive, move it, it may die later */
|
/* object is still alive, move it, it may die later */
|
||||||
gc_list_remove(gc);
|
gc_list_remove(gc);
|
||||||
gc_list_append(gc, old);
|
gc_list_append(gc, old);
|
||||||
}
|
}
|
||||||
|
@ -425,7 +431,8 @@ collect(PyGC_Head *young, PyGC_Head *old)
|
||||||
if (debug & DEBUG_STATS) {
|
if (debug & DEBUG_STATS) {
|
||||||
if (m == 0 && n == 0) {
|
if (m == 0 && n == 0) {
|
||||||
PySys_WriteStderr("gc: done.\n");
|
PySys_WriteStderr("gc: done.\n");
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
PySys_WriteStderr(
|
PySys_WriteStderr(
|
||||||
"gc: done, %ld unreachable, %ld uncollectable.\n",
|
"gc: done, %ld unreachable, %ld uncollectable.\n",
|
||||||
n+m, n);
|
n+m, n);
|
||||||
|
@ -438,6 +445,9 @@ collect(PyGC_Head *young, PyGC_Head *old)
|
||||||
handle_finalizers(&finalizers, old);
|
handle_finalizers(&finalizers, old);
|
||||||
|
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
|
if (gc_str == NULL) {
|
||||||
|
gc_str = PyString_FromString("garbage collection");
|
||||||
|
}
|
||||||
PyErr_WriteUnraisable(gc_str);
|
PyErr_WriteUnraisable(gc_str);
|
||||||
Py_FatalError("unexpected exception during garbage collection");
|
Py_FatalError("unexpected exception during garbage collection");
|
||||||
}
|
}
|
||||||
|
@ -461,7 +471,8 @@ collect_generations(void)
|
||||||
n = collect(&generation2, &generation2);
|
n = collect(&generation2, &generation2);
|
||||||
}
|
}
|
||||||
collections1 = 0;
|
collections1 = 0;
|
||||||
} else if (collections0 > threshold1) {
|
}
|
||||||
|
else if (collections0 > threshold1) {
|
||||||
generation = 1;
|
generation = 1;
|
||||||
collections1++;
|
collections1++;
|
||||||
gc_list_merge(&generation0, &generation1);
|
gc_list_merge(&generation0, &generation1);
|
||||||
|
@ -469,7 +480,8 @@ collect_generations(void)
|
||||||
n = collect(&generation1, &generation2);
|
n = collect(&generation1, &generation2);
|
||||||
}
|
}
|
||||||
collections0 = 0;
|
collections0 = 0;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
generation = 0;
|
generation = 0;
|
||||||
collections0++;
|
collections0++;
|
||||||
if (generation0.gc_next != &generation0) {
|
if (generation0.gc_next != &generation0) {
|
||||||
|
@ -603,6 +615,7 @@ static char gc_set_debug__doc__[] =
|
||||||
" DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n"
|
" DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n"
|
||||||
" DEBUG_INSTANCES - Print instance objects.\n"
|
" DEBUG_INSTANCES - Print instance objects.\n"
|
||||||
" DEBUG_OBJECTS - Print objects other than instances.\n"
|
" DEBUG_OBJECTS - Print objects other than instances.\n"
|
||||||
|
" DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.\n"
|
||||||
" DEBUG_LEAK - Debug leaking programs (everything but STATS).\n"
|
" DEBUG_LEAK - Debug leaking programs (everything but STATS).\n"
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -679,14 +692,14 @@ static char gc__doc__ [] =
|
||||||
;
|
;
|
||||||
|
|
||||||
static PyMethodDef GcMethods[] = {
|
static PyMethodDef GcMethods[] = {
|
||||||
{"enable", gc_enable, METH_VARARGS, gc_enable__doc__},
|
{"enable", gc_enable, METH_VARARGS, gc_enable__doc__},
|
||||||
{"disable", gc_disable, METH_VARARGS, gc_disable__doc__},
|
{"disable", gc_disable, METH_VARARGS, gc_disable__doc__},
|
||||||
{"isenabled", gc_isenabled, METH_VARARGS, gc_isenabled__doc__},
|
{"isenabled", gc_isenabled, METH_VARARGS, gc_isenabled__doc__},
|
||||||
{"set_debug", gc_set_debug, METH_VARARGS, gc_set_debug__doc__},
|
{"set_debug", gc_set_debug, METH_VARARGS, gc_set_debug__doc__},
|
||||||
{"get_debug", gc_get_debug, METH_VARARGS, gc_get_debug__doc__},
|
{"get_debug", gc_get_debug, METH_VARARGS, gc_get_debug__doc__},
|
||||||
{"set_threshold", gc_set_thresh, METH_VARARGS, gc_set_thresh__doc__},
|
{"set_threshold", gc_set_thresh, METH_VARARGS, gc_set_thresh__doc__},
|
||||||
{"get_threshold", gc_get_thresh, METH_VARARGS, gc_get_thresh__doc__},
|
{"get_threshold", gc_get_thresh, METH_VARARGS, gc_get_thresh__doc__},
|
||||||
{"collect", gc_collect, METH_VARARGS, gc_collect__doc__},
|
{"collect", gc_collect, METH_VARARGS, gc_collect__doc__},
|
||||||
{NULL, NULL} /* Sentinel */
|
{NULL, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -705,9 +718,6 @@ initgc(void)
|
||||||
if (garbage == NULL) {
|
if (garbage == NULL) {
|
||||||
garbage = PyList_New(0);
|
garbage = PyList_New(0);
|
||||||
}
|
}
|
||||||
if (gc_str == NULL) {
|
|
||||||
gc_str = PyString_FromString("garbage collection");
|
|
||||||
}
|
|
||||||
PyDict_SetItemString(d, "garbage", garbage);
|
PyDict_SetItemString(d, "garbage", garbage);
|
||||||
PyDict_SetItemString(d, "DEBUG_STATS",
|
PyDict_SetItemString(d, "DEBUG_STATS",
|
||||||
PyInt_FromLong(DEBUG_STATS));
|
PyInt_FromLong(DEBUG_STATS));
|
||||||
|
@ -719,6 +729,8 @@ initgc(void)
|
||||||
PyInt_FromLong(DEBUG_INSTANCES));
|
PyInt_FromLong(DEBUG_INSTANCES));
|
||||||
PyDict_SetItemString(d, "DEBUG_OBJECTS",
|
PyDict_SetItemString(d, "DEBUG_OBJECTS",
|
||||||
PyInt_FromLong(DEBUG_OBJECTS));
|
PyInt_FromLong(DEBUG_OBJECTS));
|
||||||
|
PyDict_SetItemString(d, "DEBUG_SAVEALL",
|
||||||
|
PyInt_FromLong(DEBUG_SAVEALL));
|
||||||
PyDict_SetItemString(d, "DEBUG_LEAK",
|
PyDict_SetItemString(d, "DEBUG_LEAK",
|
||||||
PyInt_FromLong(DEBUG_LEAK));
|
PyInt_FromLong(DEBUG_LEAK));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue