]> git.ipfire.org Git - people/pmueller/ipfire-3.x.git/blame - python/patches/00170-gc-assertions.patch
python: Update to 2.7.15
[people/pmueller/ipfire-3.x.git] / python / patches / 00170-gc-assertions.patch
CommitLineData
ee14ee66
MT
1diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
2index 7e47b2d..12a210d 100644
3--- a/Lib/test/test_gc.py
4+++ b/Lib/test/test_gc.py
5@@ -1,7 +1,8 @@
48d9a6a0 6 import unittest
ee14ee66
MT
7 from test.support import (verbose, run_unittest, start_threads,
8- requires_type_collecting)
9+ requires_type_collecting, import_module)
48d9a6a0
MT
10 import sys
11+import sysconfig
12 import time
13 import gc
14 import weakref
ee14ee66 15@@ -39,6 +40,8 @@ class GC_Detector(object):
48d9a6a0
MT
16 self.wr = weakref.ref(C1055820(666), it_happened)
17
18
19+BUILT_WITH_NDEBUG = ('-DNDEBUG' in sysconfig.get_config_vars()['PY_CFLAGS'])
20+
21 ### Tests
22 ###############################################################################
23
ee14ee66 24@@ -537,6 +540,49 @@ class GCTests(unittest.TestCase):
48d9a6a0
MT
25 # would be damaged, with an empty __dict__.
26 self.assertEqual(x, None)
27
28+ @unittest.skipIf(BUILT_WITH_NDEBUG,
29+ 'built with -NDEBUG')
30+ def test_refcount_errors(self):
31+ # Verify the "handling" of objects with broken refcounts
32+
33+ import_module("ctypes") #skip if not supported
34+
35+ import subprocess
36+ code = '''if 1:
37+ a = []
38+ b = [a]
39+
40+ # Simulate the refcount of "a" being too low (compared to the
41+ # references held on it by live data), but keeping it above zero
42+ # (to avoid deallocating it):
43+ import ctypes
44+ ctypes.pythonapi.Py_DecRef(ctypes.py_object(a))
45+
46+ # The garbage collector should now have a fatal error when it reaches
47+ # the broken object:
48+ import gc
49+ gc.collect()
50+ '''
51+ p = subprocess.Popen([sys.executable, "-c", code],
52+ stdout=subprocess.PIPE,
53+ stderr=subprocess.PIPE)
54+ stdout, stderr = p.communicate()
55+ p.stdout.close()
56+ p.stderr.close()
57+ # Verify that stderr has a useful error message:
58+ self.assertRegexpMatches(stderr,
59+ b'Modules/gcmodule.c:[0-9]+: visit_decref: Assertion "gc->gc.gc_refs != 0" failed.')
60+ self.assertRegexpMatches(stderr,
61+ b'refcount was too small')
62+ self.assertRegexpMatches(stderr,
63+ b'object : \[\]')
64+ self.assertRegexpMatches(stderr,
65+ b'type : list')
66+ self.assertRegexpMatches(stderr,
67+ b'refcount: 1')
68+ self.assertRegexpMatches(stderr,
69+ b'address : 0x[0-9a-f]+')
70+
71 class GCTogglingTests(unittest.TestCase):
72 def setUp(self):
73 gc.enable()
ee14ee66
MT
74diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
75index 916e481..0233ce2 100644
76--- a/Modules/gcmodule.c
77+++ b/Modules/gcmodule.c
48d9a6a0
MT
78@@ -21,6 +21,73 @@
79 #include "Python.h"
80 #include "frameobject.h" /* for PyFrame_ClearFreeList */
81
82+/*
83+ Define a pair of assertion macros.
84+
85+ These work like the regular C assert(), in that they will abort the
86+ process with a message on stderr if the given condition fails to hold,
87+ but compile away to nothing if NDEBUG is defined.
88+
89+ However, before aborting, Python will also try to call _PyObject_Dump() on
90+ the given object. This may be of use when investigating bugs in which a
91+ particular object is corrupt (e.g. buggy a tp_visit method in an extension
92+ module breaking the garbage collector), to help locate the broken objects.
93+
94+ The WITH_MSG variant allows you to supply an additional message that Python
95+ will attempt to print to stderr, after the object dump.
96+*/
97+#ifdef NDEBUG
98+/* No debugging: compile away the assertions: */
99+#define PyObject_ASSERT_WITH_MSG(obj, expr, msg) ((void)0)
100+#else
101+/* With debugging: generate checks: */
102+#define PyObject_ASSERT_WITH_MSG(obj, expr, msg) \
103+ ((expr) \
104+ ? (void)(0) \
105+ : _PyObject_AssertFailed((obj), \
106+ (msg), \
107+ (__STRING(expr)), \
108+ (__FILE__), \
109+ (__LINE__), \
110+ (__PRETTY_FUNCTION__)))
111+#endif
112+
113+#define PyObject_ASSERT(obj, expr) \
114+ PyObject_ASSERT_WITH_MSG(obj, expr, NULL)
115+
116+static void _PyObject_AssertFailed(PyObject *, const char *,
117+ const char *, const char *, int,
118+ const char *);
119+
120+static void
121+_PyObject_AssertFailed(PyObject *obj, const char *msg, const char *expr,
122+ const char *file, int line, const char *function)
123+{
124+ fprintf(stderr,
125+ "%s:%d: %s: Assertion \"%s\" failed.\n",
126+ file, line, function, expr);
127+ if (msg) {
128+ fprintf(stderr, "%s\n", msg);
129+ }
130+
131+ fflush(stderr);
132+
133+ if (obj) {
134+ /* This might succeed or fail, but we're about to abort, so at least
135+ try to provide any extra info we can: */
136+ _PyObject_Dump(obj);
137+ }
138+ else {
139+ fprintf(stderr, "NULL object\n");
140+ }
141+
142+ fflush(stdout);
143+ fflush(stderr);
144+
145+ /* Terminate the process: */
146+ abort();
147+}
148+
149 /* Get an object's GC head */
150 #define AS_GC(o) ((PyGC_Head *)(o)-1)
151
ee14ee66 152@@ -328,7 +395,8 @@ update_refs(PyGC_Head *containers)
48d9a6a0
MT
153 {
154 PyGC_Head *gc = containers->gc.gc_next;
155 for (; gc != containers; gc = gc->gc.gc_next) {
156- assert(gc->gc.gc_refs == GC_REACHABLE);
157+ PyObject_ASSERT(FROM_GC(gc),
158+ gc->gc.gc_refs == GC_REACHABLE);
159 gc->gc.gc_refs = Py_REFCNT(FROM_GC(gc));
160 /* Python's cyclic gc should never see an incoming refcount
161 * of 0: if something decref'ed to 0, it should have been
ee14ee66 162@@ -348,7 +416,8 @@ update_refs(PyGC_Head *containers)
48d9a6a0
MT
163 * so serious that maybe this should be a release-build
164 * check instead of an assert?
165 */
166- assert(gc->gc.gc_refs != 0);
167+ PyObject_ASSERT(FROM_GC(gc),
168+ gc->gc.gc_refs != 0);
169 }
170 }
171
ee14ee66 172@@ -363,7 +432,9 @@ visit_decref(PyObject *op, void *data)
48d9a6a0
MT
173 * generation being collected, which can be recognized
174 * because only they have positive gc_refs.
175 */
176- assert(gc->gc.gc_refs != 0); /* else refcount was too small */
177+ PyObject_ASSERT_WITH_MSG(FROM_GC(gc),
178+ gc->gc.gc_refs != 0,
179+ "refcount was too small");
180 if (gc->gc.gc_refs > 0)
181 gc->gc.gc_refs--;
182 }
ee14ee66 183@@ -423,9 +494,10 @@ visit_reachable(PyObject *op, PyGC_Head *reachable)
48d9a6a0
MT
184 * If gc_refs == GC_UNTRACKED, it must be ignored.
185 */
186 else {
187- assert(gc_refs > 0
188- || gc_refs == GC_REACHABLE
189- || gc_refs == GC_UNTRACKED);
190+ PyObject_ASSERT(FROM_GC(gc),
191+ gc_refs > 0
192+ || gc_refs == GC_REACHABLE
193+ || gc_refs == GC_UNTRACKED);
194 }
195 }
196 return 0;
ee14ee66 197@@ -467,7 +539,7 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
48d9a6a0
MT
198 */
199 PyObject *op = FROM_GC(gc);
200 traverseproc traverse = Py_TYPE(op)->tp_traverse;
201- assert(gc->gc.gc_refs > 0);
202+ PyObject_ASSERT(op, gc->gc.gc_refs > 0);
203 gc->gc.gc_refs = GC_REACHABLE;
204 (void) traverse(op,
205 (visitproc)visit_reachable,
ee14ee66 206@@ -545,7 +617,8 @@ move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
48d9a6a0
MT
207 for (gc = unreachable->gc.gc_next; gc != unreachable; gc = next) {
208 PyObject *op = FROM_GC(gc);
209
210- assert(IS_TENTATIVELY_UNREACHABLE(op));
211+ PyObject_ASSERT(op, IS_TENTATIVELY_UNREACHABLE(op));
212+
213 next = gc->gc.gc_next;
214
215 if (has_finalizer(op)) {
ee14ee66 216@@ -621,7 +694,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
48d9a6a0
MT
217 PyWeakReference **wrlist;
218
219 op = FROM_GC(gc);
220- assert(IS_TENTATIVELY_UNREACHABLE(op));
221+ PyObject_ASSERT(op, IS_TENTATIVELY_UNREACHABLE(op));
222 next = gc->gc.gc_next;
223
224 if (! PyType_SUPPORTS_WEAKREFS(Py_TYPE(op)))
ee14ee66 225@@ -642,9 +715,9 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
48d9a6a0
MT
226 * the callback pointer intact. Obscure: it also
227 * changes *wrlist.
228 */
229- assert(wr->wr_object == op);
230+ PyObject_ASSERT(wr->wr_object, wr->wr_object == op);
231 _PyWeakref_ClearRef(wr);
232- assert(wr->wr_object == Py_None);
233+ PyObject_ASSERT(wr->wr_object, wr->wr_object == Py_None);
234 if (wr->wr_callback == NULL)
235 continue; /* no callback */
236
ee14ee66 237@@ -678,7 +751,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
48d9a6a0
MT
238 */
239 if (IS_TENTATIVELY_UNREACHABLE(wr))
240 continue;
241- assert(IS_REACHABLE(wr));
242+ PyObject_ASSERT(op, IS_REACHABLE(wr));
243
244 /* Create a new reference so that wr can't go away
245 * before we can process it again.
ee14ee66 246@@ -687,7 +760,8 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
48d9a6a0
MT
247
248 /* Move wr to wrcb_to_call, for the next pass. */
249 wrasgc = AS_GC(wr);
250- assert(wrasgc != next); /* wrasgc is reachable, but
251+ PyObject_ASSERT(op, wrasgc != next);
252+ /* wrasgc is reachable, but
253 next isn't, so they can't
254 be the same */
255 gc_list_move(wrasgc, &wrcb_to_call);
ee14ee66 256@@ -703,11 +777,11 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
48d9a6a0
MT
257
258 gc = wrcb_to_call.gc.gc_next;
259 op = FROM_GC(gc);
260- assert(IS_REACHABLE(op));
261- assert(PyWeakref_Check(op));
262+ PyObject_ASSERT(op, IS_REACHABLE(op));
263+ PyObject_ASSERT(op, PyWeakref_Check(op));
264 wr = (PyWeakReference *)op;
265 callback = wr->wr_callback;
266- assert(callback != NULL);
267+ PyObject_ASSERT(op, callback != NULL);
268
269 /* copy-paste of weakrefobject.c's handle_callback() */
270 temp = PyObject_CallFunctionObjArgs(callback, wr, NULL);
ee14ee66 271@@ -810,7 +884,7 @@ delete_garbage(PyGC_Head *collectable, PyGC_Head *old)
48d9a6a0
MT
272 PyGC_Head *gc = collectable->gc.gc_next;
273 PyObject *op = FROM_GC(gc);
274
275- assert(IS_TENTATIVELY_UNREACHABLE(op));
276+ PyObject_ASSERT(op, IS_TENTATIVELY_UNREACHABLE(op));
277 if (debug & DEBUG_SAVEALL) {
278 PyList_Append(garbage, op);
279 }