summaryrefslogtreecommitdiffstats
path: root/Objects/object.c
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@redhat.com>2018-10-25 15:31:10 (GMT)
committerGitHub <noreply@github.com>2018-10-25 15:31:10 (GMT)
commit626bff856840f471e98ec627043f780c111a063d (patch)
tree4c8f4da76f3443157e7a06a96d88bbc631650f88 /Objects/object.c
parent18618e652c56e61a134e596b315a13c7cb997a89 (diff)
downloadcpython-626bff856840f471e98ec627043f780c111a063d.zip
cpython-626bff856840f471e98ec627043f780c111a063d.tar.gz
cpython-626bff856840f471e98ec627043f780c111a063d.tar.bz2
bpo-9263: Dump Python object on GC assertion failure (GH-10062)
Changes: * Add _PyObject_AssertFailed() function. * Add _PyObject_ASSERT() and _PyObject_ASSERT_WITH_MSG() macros. * gc_decref(): replace assert() with _PyObject_ASSERT_WITH_MSG() to dump the faulty object if the assertion fails. _PyObject_AssertFailed() calls: * _PyMem_DumpTraceback(): try to log the traceback where the object memory has been allocated if tracemalloc is enabled. * _PyObject_Dump(): log repr(obj). * Py_FatalError(): log the current Python traceback. _PyObject_AssertFailed() uses _PyObject_IsFreed() heuristic to check if the object memory has been freed by a debug hook on Python memory allocators. Initial patch written by David Malcolm. Co-Authored-By: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'Objects/object.c')
-rw-r--r--Objects/object.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/Objects/object.c b/Objects/object.c
index 8256071..2252f98 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -10,6 +10,9 @@
extern "C" {
#endif
+/* Defined in tracemalloc.c */
+extern void _PyMem_DumpTraceback(int fd, const void *ptr);
+
_Py_IDENTIFIER(Py_Repr);
_Py_IDENTIFIER(__bytes__);
_Py_IDENTIFIER(__dir__);
@@ -2212,6 +2215,55 @@ _PyTrash_thread_destroy_chain(void)
--tstate->trash_delete_nesting;
}
+
+void
+_PyObject_AssertFailed(PyObject *obj, const char *msg, const char *expr,
+ const char *file, int line, const char *function)
+{
+ fprintf(stderr,
+ "%s:%d: %s: Assertion \"%s\" failed",
+ file, line, function, expr);
+ fflush(stderr);
+
+ if (msg) {
+ fprintf(stderr, "; %s.\n", msg);
+ }
+ else {
+ fprintf(stderr, ".\n");
+ }
+ fflush(stderr);
+
+ if (obj == NULL) {
+ fprintf(stderr, "<NULL object>\n");
+ }
+ else if (_PyObject_IsFreed(obj)) {
+ /* It seems like the object memory has been freed:
+ don't access it to prevent a segmentation fault. */
+ fprintf(stderr, "<Freed object>\n");
+ }
+ else {
+ /* Diplay the traceback where the object has been allocated.
+ Do it before dumping repr(obj), since repr() is more likely
+ to crash than dumping the traceback. */
+ void *ptr;
+ PyTypeObject *type = Py_TYPE(obj);
+ if (PyType_IS_GC(type)) {
+ ptr = (void *)((char *)obj - sizeof(PyGC_Head));
+ }
+ else {
+ ptr = (void *)obj;
+ }
+ _PyMem_DumpTraceback(fileno(stderr), ptr);
+
+ /* This might succeed or fail, but we're about to abort, so at least
+ try to provide any extra info we can: */
+ _PyObject_Dump(obj);
+ }
+ fflush(stderr);
+
+ Py_FatalError("_PyObject_AssertFailed");
+}
+
#ifndef Py_TRACE_REFS
/* For Py_LIMITED_API, we need an out-of-line version of _Py_Dealloc.
Define this here, so we can undefine the macro. */