patch to ease reference debugging

Reuben Sumner rasumner at bach.wisdom.weizmann.ac.il
Tue Jun 29 09:50:19 EDT 1999


Attached you will find a patch designed to make debugging references in
Python a bit easier.

Python 1.5.2 comes with the following flags to enable certain debugging
features
	Py_REF_DEBUG keeps a global counter of the total number of references
	in the system.  Very low overhead, but useful for detecting leaks
	due to circular references or incorrect C modules
	
	Py_TRACE_REFS implies Py_REF_DEBUG and keeps more track of things.
	I haven't really used it so I can't tell you what it does.
	
	Py_DEBUG implies both of the above and other paranoid checks meant
	for debugging the interpreter itself.
	
	COUNT_ALLOCS keeps track of how many object of a certain type have
	been created (not number of references created).
	
Unfortunately all of these result in the compiler spitting out debug
information unconditionally, and in the case of Py_TRACE_REF asking an
interactive question after running all scripts.  The included patch
simply makes most of this behavior dependent on a new command line argument
or environment variable.

These defines should be put in config.h.  It is important that all of
python and all binary modules be compiled with the same flags.  With
Py_TRACE_REFS this is absolutely critical.

We have a new environment variable PYTHONREFDEBUG.  Multiple values are
possible:
	0 output is like a non-debug interpreter
	1 print total number of references at end of execution or after an
	  interactive step
	2 If Py_TRACE_REFS ask before printing "left references"
	3 If Py_TRACE_REFS don't even ask, just print "left references"
	
On the command line you just say "-r" multiple times, each time increasing
the value by 1.

The final feature is the sys.getrefcount(obj) which returns the total number
of references to obj can now be caled as sys.getrefcount() in which case
it returns the grand total number of references.

I will put any updates on my homepage at
http://www.wisdom.weizmann.ac.il/~rasumner/


Reuben


diff -ur Python-1.5.2/Include/pydebug.h pydbg/Include/pydebug.h
--- Python-1.5.2/Include/pydebug.h	Fri Dec  4 20:48:15 1998
+++ pydbg/Include/pydebug.h	Fri Jun 25 18:20:03 1999
@@ -36,6 +36,7 @@
 #endif
 
 extern DL_IMPORT(int) Py_DebugFlag;
+extern DL_IMPORT(int) Py_RefDebug;
 extern DL_IMPORT(int) Py_VerboseFlag;
 extern DL_IMPORT(int) Py_InteractiveFlag;
 extern DL_IMPORT(int) Py_OptimizeFlag;
diff -ur Python-1.5.2/Modules/main.c pydbg/Modules/main.c
--- Python-1.5.2/Modules/main.c	Tue Feb  9 20:36:51 1999
+++ pydbg/Modules/main.c	Fri Jun 25 18:20:09 1999
@@ -71,8 +71,12 @@
 -O     : optimize generated bytecode (a tad; also PYTHONOPTIMIZE=x)\n\
 -OO    : remove doc-strings in addition to the -O optimizations\n\
 -S     : don't imply 'import site' on initialization\n\
--t     : issue warnings about inconsistent tab usage (-tt: issue errors)\n\
-";
+-t     : issue warnings about inconsistent tab usage (-tt: issue errors)\n"
+#ifdef Py_REF_DEBUG
+"-r     : debug reference counting\n"
+#endif
+;
+
 static char *usage_mid = "\
 -u     : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)\n\
 -v     : verbose (trace import statements) (also PYTHONVERBOSE=x)\n\
@@ -119,7 +123,11 @@
 	if ((p = getenv("PYTHONUNBUFFERED")) && *p != '\0')
 		unbuffered = 1;
 
+#ifdef Py_REF_DEBUG	
+	while ((c = getopt(argc, argv, "c:diOStuvxXr")) != EOF) {
+#else
 	while ((c = getopt(argc, argv, "c:diOStuvxX")) != EOF) {
+#endif
 		if (c == 'c') {
 			/* -c is the last option; following arguments
 			   that look like options are left for the
@@ -170,6 +178,9 @@
 
 		case 'X':
 			Py_UseClassExceptionsFlag = 0;
+			break;
+		case 'r':
+			Py_RefDebug++;
 			break;
 
 		/* This space reserved for other options */
diff -ur Python-1.5.2/Python/pythonrun.c pydbg/Python/pythonrun.c
--- Python-1.5.2/Python/pythonrun.c	Wed Apr  7 21:32:51 1999
+++ pydbg/Python/pythonrun.c	Sun Jun 27 00:21:15 1999
@@ -73,6 +73,7 @@
 static void call_ll_exitfuncs Py_PROTO((void));
 
 int Py_DebugFlag; /* Needed by parser.c */
+int Py_RefDebug; /* needed by sysmodule.c and main.c */
 int Py_VerboseFlag; /* Needed by import.c */
 int Py_InteractiveFlag; /* Needed by Py_FdIsInteractive() below */
 int Py_NoSiteFlag; /* Suppress 'import site' */
@@ -119,6 +120,10 @@
 		Py_VerboseFlag = 1;
 	if ((p = getenv("PYTHONOPTIMIZE")) && *p != '\0')
 		Py_OptimizeFlag = 1;
+#ifdef Py_REF_DEBUG
+	if ((p = getenv("PYTHONREFDEBUG")) && *p != '\0')
+		Py_RefDebug = atoi(p);
+#endif
 
 	interp = PyInterpreterState_New();
 	if (interp == NULL)
@@ -214,11 +219,13 @@
 #endif
 
 #ifdef Py_REF_DEBUG
-	fprintf(stderr, "[%ld refs]\n", _Py_RefTotal);
+	if (Py_RefDebug)
+		fprintf(stderr, "[%ld refs]\n", _Py_RefTotal);
 #endif
 
 #ifdef Py_TRACE_REFS
-	if (_Py_AskYesNo("Print left references?")) {
+	if (Py_RefDebug > 2
+		|| Py_RefDebug > 1 && _Py_AskYesNo("Print left references?")) {
 		_Py_PrintReferences(stderr);
 	}
 #endif /* Py_TRACE_REFS */
@@ -471,7 +478,8 @@
 	for (;;) {
 		ret = PyRun_InteractiveOne(fp, filename);
 #ifdef Py_REF_DEBUG
-		fprintf(stderr, "[%ld refs]\n", _Py_RefTotal);
+		if (Py_RefDebug)
+			fprintf(stderr, "[%ld refs]\n", _Py_RefTotal);
 #endif
 		if (ret == E_EOF)
 			return 0;
diff -ur Python-1.5.2/Python/sysmodule.c pydbg/Python/sysmodule.c
--- Python-1.5.2/Python/sysmodule.c	Wed Jan 27 18:33:19 1999
+++ pydbg/Python/sysmodule.c	Sun Jun 27 00:24:47 1999
@@ -225,17 +225,36 @@
 	PyObject *self;
 	PyObject *args;
 {
+#ifdef Py_REF_DEBUG
+	PyObject *arg = NULL;
+	if (!PyArg_ParseTuple(args, "|O", &arg))
+		return NULL;
+	if (arg == NULL)
+		return PyInt_FromLong(_Py_RefTotal);
+	else
+		return PyInt_FromLong((long) arg->ob_refcnt);
+#else
 	PyObject *arg;
-	if (!PyArg_Parse(args, "O", &arg))
+	if (!PyArg_ParseTuple(args, "O", &arg))
 		return NULL;
 	return PyInt_FromLong((long) arg->ob_refcnt);
+#endif
 }
 
 static char getrefcount_doc[] =
+#ifdef Py_REF_DEBUG
+"getrefcount([object]) -> integer\n\
+\n\
+Return the current reference count for the object.  This includes the\n\
+temporary reference in the argument list, so it is at least 2.\n\
+\n\
+If no object is supplied, returns the total number of references";
+#else
 "getrefcount(object) -> integer\n\
 \n\
 Return the current reference count for the object.  This includes the\n\
 temporary reference in the argument list, so it is at least 2.";
+#endif
 
 #ifdef COUNT_ALLOCS
 static PyObject *
@@ -273,7 +292,7 @@
 #ifdef Py_TRACE_REFS
 	{"getobjects",	_Py_GetObjects, 1},
 #endif
-	{"getrefcount",	sys_getrefcount, 0, getrefcount_doc},
+	{"getrefcount",	sys_getrefcount, 1, getrefcount_doc},
 #ifdef USE_MALLOPT
 	{"mdebug",	sys_mdebug, 0},
 #endif




More information about the Python-list mailing list