Index: Modules/gcmodule.c |
=================================================================== |
--- Modules/gcmodule.c (revision 74542) |
+++ Modules/gcmodule.c (working copy) |
@@ -127,6 +127,12 @@ |
static int debug; |
static PyObject *tmod = NULL; |
+/* |
+ Defines where debugging output is written. |
+ Should be a write(str) callable. If NULL, output is written to stderr. |
+*/ |
+static PyObject* debug_writer = NULL; |
+ |
/*-------------------------------------------------------------------------- |
gc_refs values. |
@@ -689,6 +695,25 @@ |
} |
static void |
+debug_print(const char* format, ...) |
+{ |
+ va_list va; |
+ |
+ va_start(va, format); |
+ |
+ if (debug_writer == NULL) PySys_WriteStderr(format, va); |
+ else { |
+ |
+ PyObject* msg = PyString_FromFormatV(format, va); |
+ if (NULL == PyObject_CallFunctionObjArgs(debug_writer, msg, NULL)) |
+ PyErr_WriteUnraisable(debug_writer); |
+ |
+ Py_DECREF(msg); |
+ } |
+ va_end(va); |
+} |
+ |
+static void |
debug_instance(char *msg, PyInstanceObject *inst) |
{ |
char *cname; |
@@ -698,7 +723,7 @@ |
cname = PyString_AsString(classname); |
else |
cname = "?"; |
- PySys_WriteStderr("gc: %.100s <%.100s instance at %p>\n", |
+ debug_print("gc: %.100s <%.100s instance at %p>\n", |
msg, cname, inst); |
} |
@@ -709,7 +734,7 @@ |
debug_instance(msg, (PyInstanceObject *)op); |
} |
else if (debug & DEBUG_OBJECTS) { |
- PySys_WriteStderr("gc: %.100s <%.100s %p>\n", |
+ debug_print("gc: %.100s <%.100s %p>\n", |
msg, Py_TYPE(op)->tp_name, op); |
} |
} |
@@ -838,13 +863,13 @@ |
if (debug & DEBUG_STATS) { |
t1 = get_time(); |
- PySys_WriteStderr("gc: collecting generation %d...\n", |
+ debug_print("gc: collecting generation %d...\n", |
generation); |
- PySys_WriteStderr("gc: objects in each generation:"); |
+ debug_print("gc: objects in each generation:"); |
for (i = 0; i < NUM_GENERATIONS; i++) |
- PySys_WriteStderr(" %" PY_FORMAT_SIZE_T "d", |
+ debug_print(" %" PY_FORMAT_SIZE_T "d", |
gc_list_size(GEN_HEAD(i))); |
- PySys_WriteStderr("\n"); |
+ debug_print("\n"); |
} |
/* update collection and allocation counters */ |
@@ -941,17 +966,17 @@ |
if (debug & DEBUG_STATS) { |
double t2 = get_time(); |
if (m == 0 && n == 0) |
- PySys_WriteStderr("gc: done"); |
+ debug_print("gc: done"); |
else |
- PySys_WriteStderr( |
+ debug_print( |
"gc: done, " |
"%" PY_FORMAT_SIZE_T "d unreachable, " |
"%" PY_FORMAT_SIZE_T "d uncollectable", |
n+m, n); |
if (t1 && t2) { |
- PySys_WriteStderr(", %.4fs elapsed", t2-t1); |
+ debug_print(", %.4fs elapsed", t2-t1); |
} |
- PySys_WriteStderr(".\n"); |
+ debug_print(".\n"); |
} |
/* Append instances in the uncollectable set to a Python |
@@ -1072,7 +1097,7 @@ |
} |
PyDoc_STRVAR(gc_set_debug__doc__, |
-"set_debug(flags) -> None\n" |
+"set_debug(flags[, stream=sys.stderr]) -> None\n" |
"\n" |
"Set the garbage collection debugging flags. Debugging information is\n" |
"written to sys.stderr.\n" |
@@ -1085,14 +1110,41 @@ |
" 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"); |
+" DEBUG_LEAK - Debug leaking programs (everything but STATS).\n" |
+"\n" |
+"stream is a file object to which debugging output will be written.\n"); |
static PyObject * |
gc_set_debug(PyObject *self, PyObject *args) |
{ |
- if (!PyArg_ParseTuple(args, "i:set_debug", &debug)) |
+ PyObject* stream = NULL; |
+ int flags; |
+ |
+ if (!PyArg_ParseTuple(args, "i|O:set_debug", &flags, &stream)) |
return NULL; |
+ PyObject* writer = NULL; |
+ if (stream != NULL) { |
+ /* Be defensive, we don't want errors to occur during collection */ |
+ writer = PyObject_GetAttrString(stream, "write"); |
+ if (writer == NULL) |
+ return NULL; |
+ |
+ if (!PyCallable_Check(writer)) { |
+ PyErr_SetString(PyExc_TypeError, |
+ "attribute write of parameter stream must be callable"); |
+ return NULL; |
+ } |
+ |
+ } |
+ |
+ if (debug_writer != NULL) { |
+ Py_DECREF(debug_writer); |
+ debug_writer = NULL; |
+ } |
+ debug_writer = writer; |
+ debug = flags; |
+ |
Py_INCREF(Py_None); |
return Py_None; |
} |