mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
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>
This commit is contained in:
parent
18618e652c
commit
626bff8568
6 changed files with 180 additions and 14 deletions
|
@ -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. */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue