]> granicus.if.org Git - python/commitdiff
Improved new Py_TRACE_REFS gimmicks.
authorTim Peters <tim.peters@gmail.com>
Sun, 23 Mar 2003 17:52:28 +0000 (17:52 +0000)
committerTim Peters <tim.peters@gmail.com>
Sun, 23 Mar 2003 17:52:28 +0000 (17:52 +0000)
Arranged that all the objects exposed by __builtin__ appear in the list
of all objects.  I basically peed away two days tracking down a mystery
leak in sys.gettotalrefcount() in a ZODB app (== tons of code), because
the object leaking the references didn't appear in the sys.getobjects(0)
list.  The object happened to be False.  Now False is in the list, along
with other popular & previously missing leak candidates (like None).
Alas, we still don't have a choke point covering *all* Python objects,
so the list of all objects may still be incomplete.

Include/object.h
Objects/object.c
Objects/typeobject.c
Python/bltinmodule.c

index 89445c0976b83fa4cd5861c749488c3c555c1683..3ac75380a204ba147ad28f63a3ba1fa159a84abc 100644 (file)
@@ -582,7 +582,7 @@ PyAPI_FUNC(void) _Py_NewReference(PyObject *);
 PyAPI_FUNC(void) _Py_ForgetReference(PyObject *);
 PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
 PyAPI_FUNC(void) _Py_PrintReferences(FILE *);
-PyAPI_FUNC(void) _Py_AddToAllObjects(PyObject *);
+PyAPI_FUNC(void) _Py_AddToAllObjects(PyObject *, int force);
 
 #else
 /* Without Py_TRACE_REFS, there's little enough to do that we expand code
index 059b36ab0a60700281766429681b229a42ce7d91..0a8d2f12a76e4718cd9886b02cc804116af74e46 100644 (file)
@@ -18,19 +18,42 @@ int Py_DivisionWarningFlag;
    Do not call them otherwise, they do not initialize the object! */
 
 #ifdef Py_TRACE_REFS
-/* Head of doubly-linked list of all objects. */
+/* Head of circular doubly-linked list of all objects.  These are linked
+ * together via the _ob_prev and _ob_next members of a PyObject, which
+ * exist only in a Py_TRACE_REFS build.
+ */
 static PyObject refchain = {&refchain, &refchain};
 
-/* Insert op at the fron of the doubly-linked list of all objects. */
+/* Insert op at the front of the list of all objects.  If force is true,
+ * op is added even if _ob_prev and _ob_next are non-NULL already.  If
+ * force is false amd _ob_prev or _ob_next are non-NULL, do nothing.
+ * force should be true if and only if op points to freshly allocated,
+ * uninitialized memory, or you've unlinked op from the list and are
+ * relinking it into the font.
+ * Note that objects are normally added to the list via _Py_NewReference,
+ * which is called by PyObject_Init.  Not all objects are initialized that
+ * way, though; exceptions include statically allocated type objects, and
+ * statically allocated singletons (like Py_True and Py_None).
+ */
 void
-_Py_AddToAllObjects(PyObject *op)
+_Py_AddToAllObjects(PyObject *op, int force)
 {
-       op->_ob_next = refchain._ob_next;
-       op->_ob_prev = &refchain;
-       refchain._ob_next->_ob_prev = op;
-       refchain._ob_next = op;
-}
+#ifdef  Py_DEBUG
+       if (!force) {
+               /* If it's initialized memory, op must be in or out of
+                * the list unambiguously.
+                */
+               assert((op->_ob_prev == NULL) == (op->_ob_next == NULL));
+       }
 #endif
+       if (force || op->_ob_prev == NULL) {
+               op->_ob_next = refchain._ob_next;
+               op->_ob_prev = &refchain;
+               refchain._ob_next->_ob_prev = op;
+               refchain._ob_next = op;
+       }
+}
+#endif /* Py_TRACE_REFS */
 
 #ifdef COUNT_ALLOCS
 static PyTypeObject *type_list;
@@ -100,11 +123,10 @@ inc_count(PyTypeObject *tp)
                Py_INCREF(tp);
                type_list = tp;
 #ifdef Py_TRACE_REFS
-               /* Also insert in the doubly-linked list of all objects. */
-               if (tp->_ob_prev == NULL) {
-                       assert(tp->_ob_next == NULL);
-                       _Py_AddToAllObjects((PyObject *)tp);
-               }
+               /* Also insert in the doubly-linked list of all objects,
+                * if not already there.
+                */
+               _Py_AddToAllObjects((PyObject *)tp, 0);
 #endif
        }
        tp->tp_allocs++;
@@ -1963,7 +1985,7 @@ _Py_NewReference(PyObject *op)
 {
        _Py_INC_REFTOTAL;
        op->ob_refcnt = 1;
-       _Py_AddToAllObjects(op);
+       _Py_AddToAllObjects(op, 1);
        _Py_INC_TPALLOCS(op);
 }
 
index 8dfb939b709080f7176923c43a3a4441878d13c6..4acee5a8c9254106b11b698882dfb15c666e04da 100644 (file)
@@ -3058,10 +3058,7 @@ PyType_Ready(PyTypeObject *type)
         * to get type objects into the doubly-linked list of all objects.
         * Still, not all type objects go thru PyType_Ready.
         */
-        if (type->_ob_next == NULL) {
-               assert(type->_ob_prev == NULL);
-               _Py_AddToAllObjects((PyObject *)type);
-       }
+       _Py_AddToAllObjects((PyObject *)type, 0);
 #endif
 
        /* Initialize tp_base (defaults to BaseObject unless that's us) */
index e2465916ee496b1b524519ede4e6089e8d35e49d..29e11e5aca887473b430eea5c4c195dcb9d491c2 100644 (file)
@@ -1818,9 +1818,22 @@ _PyBuiltin_Init(void)
                return NULL;
        dict = PyModule_GetDict(mod);
 
+#ifdef Py_TRACE_REFS
+       /* __builtin__ exposes a number of statically allocated objects
+        * that, before this code was added in 2.3, never showed up in
+        * the list of "all objects" maintained by Py_TRACE_REFS.  As a
+        * result, programs leaking references to None and False (etc)
+        * couldn't be diagnosed by examining sys.getobjects(0).
+        */
+#define ADD_TO_ALL(OBJECT) _Py_AddToAllObjects((PyObject *)(OBJECT), 0)
+#else
+#define ADD_TO_ALL(OBJECT) (void)0
+#endif
+
 #define SETBUILTIN(NAME, OBJECT) \
-       if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0) \
-               return NULL
+       if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0)   \
+               return NULL;                                            \
+       ADD_TO_ALL(OBJECT)
 
        SETBUILTIN("None",              Py_None);
        SETBUILTIN("Ellipsis",          Py_Ellipsis);
@@ -1864,6 +1877,7 @@ _PyBuiltin_Init(void)
        Py_XDECREF(debug);
 
        return mod;
+#undef ADD_TO_ALL
 #undef SETBUILTIN
 }