Blame SOURCES/00170-gc-assertions.patch

6e8c2f
diff -up Python-2.7.3/Lib/test/test_gc.py.gc-assertions Python-2.7.3/Lib/test/test_gc.py
6e8c2f
--- Python-2.7.3/Lib/test/test_gc.py.gc-assertions	2013-02-20 16:28:20.890536607 -0500
6e8c2f
+++ Python-2.7.3/Lib/test/test_gc.py	2013-02-20 16:39:52.720489297 -0500
6e8c2f
@@ -1,6 +1,7 @@
6e8c2f
 import unittest
6e8c2f
-from test.test_support import verbose, run_unittest
6e8c2f
+from test.test_support import verbose, run_unittest, import_module
6e8c2f
 import sys
6e8c2f
+import sysconfig
6e8c2f
 import time
6e8c2f
 import gc
6e8c2f
 import weakref
6e8c2f
@@ -32,6 +33,8 @@ class GC_Detector(object):
6e8c2f
         self.wr = weakref.ref(C1055820(666), it_happened)
6e8c2f
 
6e8c2f
 
6e8c2f
+BUILT_WITH_NDEBUG = ('-DNDEBUG' in sysconfig.get_config_vars()['PY_CFLAGS'])
6e8c2f
+
6e8c2f
 ### Tests
6e8c2f
 ###############################################################################
6e8c2f
 
6e8c2f
@@ -476,6 +479,49 @@ class GCTests(unittest.TestCase):
6e8c2f
             # would be damaged, with an empty __dict__.
6e8c2f
             self.assertEqual(x, None)
6e8c2f
 
6e8c2f
+    @unittest.skipIf(BUILT_WITH_NDEBUG,
6e8c2f
+                     'built with -NDEBUG')
6e8c2f
+    def test_refcount_errors(self):
6e8c2f
+        # Verify the "handling" of objects with broken refcounts
6e8c2f
+ 
6e8c2f
+        import_module("ctypes") #skip if not supported
6e8c2f
+
6e8c2f
+        import subprocess
6e8c2f
+        code = '''if 1:
6e8c2f
+        a = []
6e8c2f
+        b = [a]
6e8c2f
+
6e8c2f
+        # Simulate the refcount of "a" being too low (compared to the
6e8c2f
+        # references held on it by live data), but keeping it above zero
6e8c2f
+        # (to avoid deallocating it):
6e8c2f
+        import ctypes
6e8c2f
+        ctypes.pythonapi.Py_DecRef(ctypes.py_object(a))
6e8c2f
+
6e8c2f
+        # The garbage collector should now have a fatal error when it reaches
6e8c2f
+        # the broken object:
6e8c2f
+        import gc
6e8c2f
+        gc.collect()
6e8c2f
+        '''
6e8c2f
+        p = subprocess.Popen([sys.executable, "-c", code],
6e8c2f
+                             stdout=subprocess.PIPE,
6e8c2f
+                             stderr=subprocess.PIPE)
6e8c2f
+        stdout, stderr = p.communicate()
6e8c2f
+        p.stdout.close()
6e8c2f
+        p.stderr.close()
6e8c2f
+        # Verify that stderr has a useful error message:
6e8c2f
+        self.assertRegexpMatches(stderr,
6e8c2f
+            b'Modules/gcmodule.c:[0-9]+: visit_decref: Assertion "gc->gc.gc_refs != 0" failed.')
6e8c2f
+        self.assertRegexpMatches(stderr,
6e8c2f
+            b'refcount was too small')
6e8c2f
+        self.assertRegexpMatches(stderr,
6e8c2f
+            b'object  : \[\]')
6e8c2f
+        self.assertRegexpMatches(stderr,
6e8c2f
+            b'type    : list')
6e8c2f
+        self.assertRegexpMatches(stderr,
6e8c2f
+            b'refcount: 1')
6e8c2f
+        self.assertRegexpMatches(stderr,
6e8c2f
+            b'address : 0x[0-9a-f]+')
6e8c2f
+
6e8c2f
 class GCTogglingTests(unittest.TestCase):
6e8c2f
     def setUp(self):
6e8c2f
         gc.enable()
6e8c2f
diff -up Python-2.7.3/Modules/gcmodule.c.gc-assertions Python-2.7.3/Modules/gcmodule.c
6e8c2f
--- Python-2.7.3/Modules/gcmodule.c.gc-assertions	2012-04-09 19:07:34.000000000 -0400
6e8c2f
+++ Python-2.7.3/Modules/gcmodule.c	2013-02-20 16:28:21.029536600 -0500
6e8c2f
@@ -21,6 +21,73 @@
6e8c2f
 #include "Python.h"
6e8c2f
 #include "frameobject.h"        /* for PyFrame_ClearFreeList */
6e8c2f
 
6e8c2f
+/* 
6e8c2f
+   Define a pair of assertion macros.
6e8c2f
+
6e8c2f
+   These work like the regular C assert(), in that they will abort the
6e8c2f
+   process with a message on stderr if the given condition fails to hold,
6e8c2f
+   but compile away to nothing if NDEBUG is defined.
6e8c2f
+
6e8c2f
+   However, before aborting, Python will also try to call _PyObject_Dump() on
6e8c2f
+   the given object.  This may be of use when investigating bugs in which a
6e8c2f
+   particular object is corrupt (e.g. buggy a tp_visit method in an extension
6e8c2f
+   module breaking the garbage collector), to help locate the broken objects.
6e8c2f
+
6e8c2f
+   The WITH_MSG variant allows you to supply an additional message that Python
6e8c2f
+   will attempt to print to stderr, after the object dump.
6e8c2f
+*/
6e8c2f
+#ifdef NDEBUG
6e8c2f
+/* No debugging: compile away the assertions: */
6e8c2f
+#define PyObject_ASSERT_WITH_MSG(obj, expr, msg) ((void)0)
6e8c2f
+#else
6e8c2f
+/* With debugging: generate checks: */
6e8c2f
+#define PyObject_ASSERT_WITH_MSG(obj, expr, msg) \
6e8c2f
+  ((expr)                                           \
6e8c2f
+   ? (void)(0)                                      \
6e8c2f
+   : _PyObject_AssertFailed((obj),                  \
6e8c2f
+                            (msg),                  \
6e8c2f
+                            (__STRING(expr)),       \
6e8c2f
+                            (__FILE__),             \
6e8c2f
+                            (__LINE__),             \
6e8c2f
+                            (__PRETTY_FUNCTION__)))
6e8c2f
+#endif
6e8c2f
+
6e8c2f
+#define PyObject_ASSERT(obj, expr) \
6e8c2f
+  PyObject_ASSERT_WITH_MSG(obj, expr, NULL)
6e8c2f
+
6e8c2f
+static void _PyObject_AssertFailed(PyObject *,  const char *,
6e8c2f
+				   const char *, const char *, int,
6e8c2f
+				   const char *);
6e8c2f
+
6e8c2f
+static void
6e8c2f
+_PyObject_AssertFailed(PyObject *obj, const char *msg, const char *expr,
6e8c2f
+		       const char *file, int line, const char *function)
6e8c2f
+{
6e8c2f
+    fprintf(stderr,
6e8c2f
+            "%s:%d: %s: Assertion \"%s\" failed.\n",
6e8c2f
+            file, line, function, expr);
6e8c2f
+    if (msg) {
6e8c2f
+        fprintf(stderr, "%s\n", msg);
6e8c2f
+    }
6e8c2f
+
6e8c2f
+    fflush(stderr);
6e8c2f
+
6e8c2f
+    if (obj) {
6e8c2f
+        /* This might succeed or fail, but we're about to abort, so at least
6e8c2f
+           try to provide any extra info we can: */
6e8c2f
+        _PyObject_Dump(obj);
6e8c2f
+    }
6e8c2f
+    else {
6e8c2f
+        fprintf(stderr, "NULL object\n");
6e8c2f
+    }
6e8c2f
+
6e8c2f
+    fflush(stdout);
6e8c2f
+    fflush(stderr);
6e8c2f
+
6e8c2f
+    /* Terminate the process: */
6e8c2f
+    abort();
6e8c2f
+}
6e8c2f
+
6e8c2f
 /* Get an object's GC head */
6e8c2f
 #define AS_GC(o) ((PyGC_Head *)(o)-1)
6e8c2f
 
6e8c2f
@@ -288,7 +355,8 @@ update_refs(PyGC_Head *containers)
6e8c2f
 {
6e8c2f
     PyGC_Head *gc = containers->gc.gc_next;
6e8c2f
     for (; gc != containers; gc = gc->gc.gc_next) {
6e8c2f
-        assert(gc->gc.gc_refs == GC_REACHABLE);
6e8c2f
+        PyObject_ASSERT(FROM_GC(gc),
6e8c2f
+                        gc->gc.gc_refs == GC_REACHABLE);
6e8c2f
         gc->gc.gc_refs = Py_REFCNT(FROM_GC(gc));
6e8c2f
         /* Python's cyclic gc should never see an incoming refcount
6e8c2f
          * of 0:  if something decref'ed to 0, it should have been
6e8c2f
@@ -308,7 +376,8 @@ update_refs(PyGC_Head *containers)
6e8c2f
          * so serious that maybe this should be a release-build
6e8c2f
          * check instead of an assert?
6e8c2f
          */
6e8c2f
-        assert(gc->gc.gc_refs != 0);
6e8c2f
+        PyObject_ASSERT(FROM_GC(gc),
6e8c2f
+                        gc->gc.gc_refs != 0);
6e8c2f
     }
6e8c2f
 }
6e8c2f
 
6e8c2f
@@ -323,7 +392,9 @@ visit_decref(PyObject *op, void *data)
6e8c2f
          * generation being collected, which can be recognized
6e8c2f
          * because only they have positive gc_refs.
6e8c2f
          */
6e8c2f
-        assert(gc->gc.gc_refs != 0); /* else refcount was too small */
6e8c2f
+        PyObject_ASSERT_WITH_MSG(FROM_GC(gc),
6e8c2f
+                                 gc->gc.gc_refs != 0,
6e8c2f
+                                 "refcount was too small");
6e8c2f
         if (gc->gc.gc_refs > 0)
6e8c2f
             gc->gc.gc_refs--;
6e8c2f
     }
6e8c2f
@@ -383,9 +454,10 @@ visit_reachable(PyObject *op, PyGC_Head
6e8c2f
          * If gc_refs == GC_UNTRACKED, it must be ignored.
6e8c2f
          */
6e8c2f
          else {
6e8c2f
-            assert(gc_refs > 0
6e8c2f
-                   || gc_refs == GC_REACHABLE
6e8c2f
-                   || gc_refs == GC_UNTRACKED);
6e8c2f
+             PyObject_ASSERT(FROM_GC(gc),
6e8c2f
+                             gc_refs > 0
6e8c2f
+                             || gc_refs == GC_REACHABLE
6e8c2f
+                             || gc_refs == GC_UNTRACKED);
6e8c2f
          }
6e8c2f
     }
6e8c2f
     return 0;
6e8c2f
@@ -427,7 +499,7 @@ move_unreachable(PyGC_Head *young, PyGC_
6e8c2f
              */
6e8c2f
             PyObject *op = FROM_GC(gc);
6e8c2f
             traverseproc traverse = Py_TYPE(op)->tp_traverse;
6e8c2f
-            assert(gc->gc.gc_refs > 0);
6e8c2f
+            PyObject_ASSERT(op, gc->gc.gc_refs > 0);
6e8c2f
             gc->gc.gc_refs = GC_REACHABLE;
6e8c2f
             (void) traverse(op,
6e8c2f
                             (visitproc)visit_reachable,
6e8c2f
@@ -494,7 +566,8 @@ move_finalizers(PyGC_Head *unreachable,
6e8c2f
     for (gc = unreachable->gc.gc_next; gc != unreachable; gc = next) {
6e8c2f
         PyObject *op = FROM_GC(gc);
6e8c2f
 
6e8c2f
-        assert(IS_TENTATIVELY_UNREACHABLE(op));
6e8c2f
+        PyObject_ASSERT(op, IS_TENTATIVELY_UNREACHABLE(op));
6e8c2f
+
6e8c2f
         next = gc->gc.gc_next;
6e8c2f
 
6e8c2f
         if (has_finalizer(op)) {
6e8c2f
@@ -570,7 +643,7 @@ handle_weakrefs(PyGC_Head *unreachable,
6e8c2f
         PyWeakReference **wrlist;
6e8c2f
 
6e8c2f
         op = FROM_GC(gc);
6e8c2f
-        assert(IS_TENTATIVELY_UNREACHABLE(op));
6e8c2f
+        PyObject_ASSERT(op, IS_TENTATIVELY_UNREACHABLE(op));
6e8c2f
         next = gc->gc.gc_next;
6e8c2f
 
6e8c2f
         if (! PyType_SUPPORTS_WEAKREFS(Py_TYPE(op)))
6e8c2f
@@ -591,9 +664,9 @@ handle_weakrefs(PyGC_Head *unreachable,
6e8c2f
              * the callback pointer intact.  Obscure:  it also
6e8c2f
              * changes *wrlist.
6e8c2f
              */
6e8c2f
-            assert(wr->wr_object == op);
6e8c2f
+            PyObject_ASSERT(wr->wr_object, wr->wr_object == op);
6e8c2f
             _PyWeakref_ClearRef(wr);
6e8c2f
-            assert(wr->wr_object == Py_None);
6e8c2f
+            PyObject_ASSERT(wr->wr_object, wr->wr_object == Py_None);
6e8c2f
             if (wr->wr_callback == NULL)
6e8c2f
                 continue;                       /* no callback */
6e8c2f
 
6e8c2f
@@ -627,7 +700,7 @@ handle_weakrefs(PyGC_Head *unreachable,
6e8c2f
      */
6e8c2f
             if (IS_TENTATIVELY_UNREACHABLE(wr))
6e8c2f
                 continue;
6e8c2f
-            assert(IS_REACHABLE(wr));
6e8c2f
+            PyObject_ASSERT(op, IS_REACHABLE(wr));
6e8c2f
 
6e8c2f
             /* Create a new reference so that wr can't go away
6e8c2f
              * before we can process it again.
6e8c2f
@@ -636,7 +709,8 @@ handle_weakrefs(PyGC_Head *unreachable,
6e8c2f
 
6e8c2f
             /* Move wr to wrcb_to_call, for the next pass. */
6e8c2f
             wrasgc = AS_GC(wr);
6e8c2f
-            assert(wrasgc != next); /* wrasgc is reachable, but
6e8c2f
+            PyObject_ASSERT(op, wrasgc != next);
6e8c2f
+                                    /* wrasgc is reachable, but
6e8c2f
                                        next isn't, so they can't
6e8c2f
                                        be the same */
6e8c2f
             gc_list_move(wrasgc, &wrcb_to_call);
6e8c2f
@@ -652,11 +726,11 @@ handle_weakrefs(PyGC_Head *unreachable,
6e8c2f
 
6e8c2f
         gc = wrcb_to_call.gc.gc_next;
6e8c2f
         op = FROM_GC(gc);
6e8c2f
-        assert(IS_REACHABLE(op));
6e8c2f
-        assert(PyWeakref_Check(op));
6e8c2f
+        PyObject_ASSERT(op, IS_REACHABLE(op));
6e8c2f
+        PyObject_ASSERT(op, PyWeakref_Check(op));
6e8c2f
         wr = (PyWeakReference *)op;
6e8c2f
         callback = wr->wr_callback;
6e8c2f
-        assert(callback != NULL);
6e8c2f
+        PyObject_ASSERT(op, callback != NULL);
6e8c2f
 
6e8c2f
         /* copy-paste of weakrefobject.c's handle_callback() */
6e8c2f
         temp = PyObject_CallFunctionObjArgs(callback, wr, NULL);
6e8c2f
@@ -759,7 +833,7 @@ delete_garbage(PyGC_Head *collectable, P
6e8c2f
         PyGC_Head *gc = collectable->gc.gc_next;
6e8c2f
         PyObject *op = FROM_GC(gc);
6e8c2f
 
6e8c2f
-        assert(IS_TENTATIVELY_UNREACHABLE(op));
6e8c2f
+        PyObject_ASSERT(op, IS_TENTATIVELY_UNREACHABLE(op));
6e8c2f
         if (debug & DEBUG_SAVEALL) {
6e8c2f
             PyList_Append(garbage, op);
6e8c2f
         }