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:
Victor Stinner 2018-10-25 17:31:10 +02:00 committed by GitHub
parent 18618e652c
commit 626bff8568
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 180 additions and 14 deletions

View file

@ -1105,6 +1105,53 @@ PyAPI_FUNC(void)
_PyObject_DebugTypeStats(FILE *out);
#endif /* ifndef Py_LIMITED_API */
#ifndef Py_LIMITED_API
/* Define a pair of assertion macros:
_PyObject_ASSERT_WITH_MSG() and _PyObject_ASSERT().
These work like the regular C assert(), in that they will abort the
process with a message on stderr if the given condition fails to hold,
but compile away to nothing if NDEBUG is defined.
However, before aborting, Python will also try to call _PyObject_Dump() on
the given object. This may be of use when investigating bugs in which a
particular object is corrupt (e.g. buggy a tp_visit method in an extension
module breaking the garbage collector), to help locate the broken objects.
The WITH_MSG variant allows you to supply an additional message that Python
will attempt to print to stderr, after the object dump. */
#ifdef NDEBUG
/* No debugging: compile away the assertions: */
# define _PyObject_ASSERT_WITH_MSG(obj, expr, msg) ((void)0)
#else
/* With debugging: generate checks: */
# define _PyObject_ASSERT_WITH_MSG(obj, expr, msg) \
((expr) \
? (void)(0) \
: _PyObject_AssertFailed((obj), \
(msg), \
Py_STRINGIFY(expr), \
__FILE__, \
__LINE__, \
__func__))
#endif
#define _PyObject_ASSERT(obj, expr) _PyObject_ASSERT_WITH_MSG(obj, expr, NULL)
/* Declare and define _PyObject_AssertFailed() even when NDEBUG is defined,
to avoid causing compiler/linker errors when building extensions without
NDEBUG against a Python built with NDEBUG defined. */
PyAPI_FUNC(void) _PyObject_AssertFailed(
PyObject *obj,
const char *msg,
const char *expr,
const char *file,
int line,
const char *function);
#endif /* ifndef Py_LIMITED_API */
#ifdef __cplusplus
}
#endif