Mailing List Archive

patch to ease reference debugging
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