From 544de1effb6b730d68433fde7828bf90bc50fe35 Mon Sep 17 00:00:00 2001
From: Neil Schemenauer <nascheme@enme.ucalgary.ca>
Date: Fri, 22 Sep 2000 15:22:38 +0000
Subject: - 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.
---
 Doc/lib/libgc.tex  | 12 ++++++++--
 Modules/gcmodule.c | 66 ++++++++++++++++++++++++++++++++----------------------
 2 files changed, 49 insertions(+), 29 deletions(-)

diff --git a/Doc/lib/libgc.tex b/Doc/lib/libgc.tex
index 5a744c3..a39f755 100644
--- a/Doc/lib/libgc.tex
+++ b/Doc/lib/libgc.tex
@@ -85,7 +85,9 @@ The following variable is provided for read-only access:
 A list of objects which the collector found to be unreachable
 but could not be freed (uncollectable objects).  Objects that have
 \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}
 
 
@@ -117,8 +119,14 @@ When \constant{DEBUG_COLLECTABLE} or \constant{DEBUG_UNCOLLECTABLE} is
 set, print information about objects other than instance objects found.
 \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}
 The debugging flags necessary for the collector to print
 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}
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 06f51ed..62bc58e 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -53,10 +53,12 @@ static int allocated;
 #define DEBUG_UNCOLLECTABLE	(1<<2) /* print uncollectable objects */
 #define DEBUG_INSTANCES		(1<<3) /* print instances */
 #define DEBUG_OBJECTS		(1<<4) /* print other objects */
+#define DEBUG_SAVEALL		(1<<5) /* save all garbage in gc.garbage */
 #define DEBUG_LEAK		DEBUG_COLLECTABLE | \
 				DEBUG_UNCOLLECTABLE | \
 				DEBUG_INSTANCES | \
-				DEBUG_OBJECTS
+				DEBUG_OBJECTS | \
+				DEBUG_SAVEALL
 static int debug;
 
 /* list of uncollectable objects */
@@ -100,7 +102,8 @@ gc_list_move(PyGC_Head *from, PyGC_Head *to)
 	if (from->gc_next == from) {
 		/* empty from list */
 		gc_list_init(to);
-	} else {
+	}
+	else {
 		to->gc_next = from->gc_next;
 		to->gc_next->gc_prev = to;
 		to->gc_prev = from->gc_prev;
@@ -290,7 +293,8 @@ debug_cycle(char *msg, PyObject *op)
 {
 	if ((debug & DEBUG_INSTANCES) && PyInstance_Check(op)) {
 		debug_instance(msg, (PyInstanceObject *)op);
-	} else if (debug & DEBUG_OBJECTS) {
+	}
+	else if (debug & DEBUG_OBJECTS) {
 		PySys_WriteStderr("gc: %.100s <%.100s %p>\n",
 				  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;
 			gc = finalizers->gc_next) {
 		PyObject *op = PyObject_FROM_GC(gc);
-		/* Add all instances to a Python accessible list of garbage */
-		if (PyInstance_Check(op)) {
+		if ((debug & DEBUG_SAVEALL) || 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);
 		}
-		/* We assume that all objects in finalizers are reachable from
-		 * instances.  Once we add the instances to the garbage list
-		 * everything is reachable from Python again. */
+		/* object is now reachable again */ 
 		gc_list_remove(gc);
 		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
  * objects may be freed.  It is possible I screwed something up here. */
 static void
@@ -330,17 +335,18 @@ delete_garbage(PyGC_Head *unreachable, PyGC_Head *old)
 	while (unreachable->gc_next != unreachable) {
 		PyGC_Head *gc = unreachable->gc_next;
 		PyObject *op = PyObject_FROM_GC(gc);
-		/*
-		PyList_Append(garbage, op);
-		*/
-		if ((clear = op->ob_type->tp_clear) != NULL) {
-			Py_INCREF(op);
-			clear((PyObject *)op);
-			Py_DECREF(op);
+		if (debug & DEBUG_SAVEALL) {
+			PyList_Append(garbage, op);
+		}
+		else {
+			if ((clear = op->ob_type->tp_clear) != NULL) {
+				Py_INCREF(op);
+				clear((PyObject *)op);
+				Py_DECREF(op);
+			}
 		}
-		/* only try to call tp_clear once for each object */
 		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_append(gc, old);
 		}
@@ -425,7 +431,8 @@ collect(PyGC_Head *young, PyGC_Head *old)
 	if (debug & DEBUG_STATS) {
 		if (m == 0 && n == 0) {
 			PySys_WriteStderr("gc: done.\n");
-		} else {
+		}
+		else {
 			PySys_WriteStderr(
 			    "gc: done, %ld unreachable, %ld uncollectable.\n",
 			    n+m, n);
@@ -438,6 +445,9 @@ collect(PyGC_Head *young, PyGC_Head *old)
 	handle_finalizers(&finalizers, old);
 
 	if (PyErr_Occurred()) {
+		if (gc_str == NULL) {
+		    gc_str = PyString_FromString("garbage collection");
+		}
 		PyErr_WriteUnraisable(gc_str);
 		Py_FatalError("unexpected exception during garbage collection");
 	}
@@ -461,7 +471,8 @@ collect_generations(void)
 			n = collect(&generation2, &generation2);
 		}
 		collections1 = 0;
-	} else if (collections0 > threshold1) {
+	}
+	else if (collections0 > threshold1) {
 		generation = 1;
 		collections1++;
 		gc_list_merge(&generation0, &generation1);
@@ -469,7 +480,8 @@ collect_generations(void)
 			n = collect(&generation1, &generation2);
 		}
 		collections0 = 0;
-	} else {
+	}
+	else {
 		generation = 0;
 		collections0++;
 		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_INSTANCES - Print instance objects.\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"
 ;
 
@@ -679,14 +692,14 @@ static char gc__doc__ [] =
 ;
 
 static PyMethodDef GcMethods[] = {
-	{"enable",	   gc_enable,     METH_VARARGS, gc_enable__doc__},
-	{"disable",	   gc_disable,    METH_VARARGS, gc_disable__doc__},
+	{"enable",	   gc_enable,	  METH_VARARGS, gc_enable__doc__},
+	{"disable",	   gc_disable,	  METH_VARARGS, gc_disable__doc__},
 	{"isenabled",	   gc_isenabled,  METH_VARARGS, gc_isenabled__doc__},
 	{"set_debug",	   gc_set_debug,  METH_VARARGS, gc_set_debug__doc__},
 	{"get_debug",	   gc_get_debug,  METH_VARARGS, gc_get_debug__doc__},
 	{"set_threshold",  gc_set_thresh, METH_VARARGS, gc_set_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 */
 };
 
@@ -705,9 +718,6 @@ initgc(void)
 	if (garbage == NULL) {
 		garbage = PyList_New(0);
 	}
-	if (gc_str == NULL) {
-		gc_str = PyString_FromString("garbage collection");
-	}
 	PyDict_SetItemString(d, "garbage", garbage);
 	PyDict_SetItemString(d, "DEBUG_STATS",
 			PyInt_FromLong(DEBUG_STATS));
@@ -719,6 +729,8 @@ initgc(void)
 			PyInt_FromLong(DEBUG_INSTANCES));
 	PyDict_SetItemString(d, "DEBUG_OBJECTS",
 			PyInt_FromLong(DEBUG_OBJECTS));
+	PyDict_SetItemString(d, "DEBUG_SAVEALL",
+			PyInt_FromLong(DEBUG_SAVEALL));
 	PyDict_SetItemString(d, "DEBUG_LEAK",
 			PyInt_FromLong(DEBUG_LEAK));
 }
-- 
cgit v0.12

