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