mirror of https://github.com/python/cpython
gh-108634: Py_TRACE_REFS uses a hash table (#108663)
Python built with "configure --with-trace-refs" (tracing references) is now ABI compatible with Python release build and debug build. Moreover, it now also supports the Limited API. Change Py_TRACE_REFS build: * Remove _PyObject_EXTRA_INIT macro. * The PyObject structure no longer has two extra members (_ob_prev and _ob_next). * Use a hash table (_Py_hashtable_t) to trace references (all objects): PyInterpreterState.object_state.refchain. * Py_TRACE_REFS build is now ABI compatible with release build and debug build. * Limited C API extensions can now be built with Py_TRACE_REFS: xxlimited, xxlimited_35, _testclinic_limited. * No longer rename PyModule_Create2() and PyModule_FromDefAndSpec2() functions to PyModule_Create2TraceRefs() and PyModule_FromDefAndSpec2TraceRefs(). * _Py_PrintReferenceAddresses() is now called before finalize_interp_delete() which deletes the refchain hash table. * test_tracemalloc find_trace() now also filters by size to ignore the memory allocated by _PyRefchain_Trace(). Test changes for Py_TRACE_REFS: * Add test.support.Py_TRACE_REFS constant. * Add test_sys.test_getobjects() to test sys.getobjects() function. * test_exceptions skips test_recursion_normalizing_with_no_memory() and test_memory_error_in_PyErr_PrintEx() if Python is built with Py_TRACE_REFS. * test_repl skips test_no_memory(). * test_capi skisp test_set_nomemory().
This commit is contained in:
parent
013a99a47b
commit
13a00078b8
|
@ -528,28 +528,6 @@ type objects) *must* have the :c:member:`~PyVarObject.ob_size` field.
|
||||||
This field is inherited by subtypes.
|
This field is inherited by subtypes.
|
||||||
|
|
||||||
|
|
||||||
.. c:member:: PyObject* PyObject._ob_next
|
|
||||||
PyObject* PyObject._ob_prev
|
|
||||||
|
|
||||||
These fields are only present when the macro ``Py_TRACE_REFS`` is defined
|
|
||||||
(see the :option:`configure --with-trace-refs option <--with-trace-refs>`).
|
|
||||||
|
|
||||||
Their initialization to ``NULL`` is taken care of by the
|
|
||||||
``PyObject_HEAD_INIT`` macro. For :ref:`statically allocated objects
|
|
||||||
<static-types>`, these fields always remain ``NULL``. For :ref:`dynamically
|
|
||||||
allocated objects <heap-types>`, these two fields are used to link the
|
|
||||||
object into a doubly linked list of *all* live objects on the heap.
|
|
||||||
|
|
||||||
This could be used for various debugging purposes; currently the only uses
|
|
||||||
are the :func:`sys.getobjects` function and to print the objects that are
|
|
||||||
still alive at the end of a run when the environment variable
|
|
||||||
:envvar:`PYTHONDUMPREFS` is set.
|
|
||||||
|
|
||||||
**Inheritance:**
|
|
||||||
|
|
||||||
These fields are not inherited by subtypes.
|
|
||||||
|
|
||||||
|
|
||||||
PyVarObject Slots
|
PyVarObject Slots
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
|
@ -425,8 +425,7 @@ See also the :ref:`Python Development Mode <devmode>` and the
|
||||||
.. versionchanged:: 3.8
|
.. versionchanged:: 3.8
|
||||||
Release builds and debug builds are now ABI compatible: defining the
|
Release builds and debug builds are now ABI compatible: defining the
|
||||||
``Py_DEBUG`` macro no longer implies the ``Py_TRACE_REFS`` macro (see the
|
``Py_DEBUG`` macro no longer implies the ``Py_TRACE_REFS`` macro (see the
|
||||||
:option:`--with-trace-refs` option), which introduces the only ABI
|
:option:`--with-trace-refs` option).
|
||||||
incompatibility.
|
|
||||||
|
|
||||||
|
|
||||||
Debug options
|
Debug options
|
||||||
|
@ -447,8 +446,14 @@ Debug options
|
||||||
* Add :func:`sys.getobjects` function.
|
* Add :func:`sys.getobjects` function.
|
||||||
* Add :envvar:`PYTHONDUMPREFS` environment variable.
|
* Add :envvar:`PYTHONDUMPREFS` environment variable.
|
||||||
|
|
||||||
This build is not ABI compatible with release build (default build) or debug
|
The :envvar:`PYTHONDUMPREFS` environment variable can be used to dump
|
||||||
build (``Py_DEBUG`` and ``Py_REF_DEBUG`` macros).
|
objects and reference counts still alive at Python exit.
|
||||||
|
|
||||||
|
:ref:`Statically allocated objects <static-types>` are not traced.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.13
|
||||||
|
This build is now ABI compatible with release build and :ref:`debug build
|
||||||
|
<debug-build>`.
|
||||||
|
|
||||||
.. versionadded:: 3.8
|
.. versionadded:: 3.8
|
||||||
|
|
||||||
|
|
|
@ -828,6 +828,11 @@ Build Changes
|
||||||
* SQLite 3.15.2 or newer is required to build the :mod:`sqlite3` extension module.
|
* SQLite 3.15.2 or newer is required to build the :mod:`sqlite3` extension module.
|
||||||
(Contributed by Erlend Aasland in :gh:`105875`.)
|
(Contributed by Erlend Aasland in :gh:`105875`.)
|
||||||
|
|
||||||
|
* Python built with :file:`configure` :option:`--with-trace-refs` (tracing
|
||||||
|
references) is now ABI compatible with Python release build and
|
||||||
|
:ref:`debug build <debug-build>`.
|
||||||
|
(Contributed by Victor Stinner in :gh:`108634`.)
|
||||||
|
|
||||||
|
|
||||||
C API Changes
|
C API Changes
|
||||||
=============
|
=============
|
||||||
|
@ -900,6 +905,10 @@ New Features
|
||||||
(with an underscore prefix).
|
(with an underscore prefix).
|
||||||
(Contributed by Victor Stinner in :gh:`108014`.)
|
(Contributed by Victor Stinner in :gh:`108014`.)
|
||||||
|
|
||||||
|
* Python built with :file:`configure` :option:`--with-trace-refs` (tracing
|
||||||
|
references) now supports the :ref:`Limited API <limited-c-api>`.
|
||||||
|
(Contributed by Victor Stinner in :gh:`108634`.)
|
||||||
|
|
||||||
Porting to Python 3.13
|
Porting to Python 3.13
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,6 @@ PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *);
|
||||||
backwards compatible solution */
|
backwards compatible solution */
|
||||||
#define _PyObject_HEAD_INIT(type) \
|
#define _PyObject_HEAD_INIT(type) \
|
||||||
{ \
|
{ \
|
||||||
_PyObject_EXTRA_INIT \
|
|
||||||
.ob_refcnt = _Py_IMMORTAL_REFCNT, \
|
.ob_refcnt = _Py_IMMORTAL_REFCNT, \
|
||||||
.ob_type = (type) \
|
.ob_type = (type) \
|
||||||
},
|
},
|
||||||
|
@ -184,6 +183,8 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
|
||||||
extern void _PyType_InitCache(PyInterpreterState *interp);
|
extern void _PyType_InitCache(PyInterpreterState *interp);
|
||||||
|
|
||||||
extern void _PyObject_InitState(PyInterpreterState *interp);
|
extern void _PyObject_InitState(PyInterpreterState *interp);
|
||||||
|
extern void _PyObject_FiniState(PyInterpreterState *interp);
|
||||||
|
extern bool _PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj);
|
||||||
|
|
||||||
/* Inline functions trading binary compatibility for speed:
|
/* Inline functions trading binary compatibility for speed:
|
||||||
_PyObject_Init() is the fast version of PyObject_Init(), and
|
_PyObject_Init() is the fast version of PyObject_Init(), and
|
||||||
|
@ -302,7 +303,7 @@ extern void _PyDebug_PrintTotalRefs(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
extern void _Py_AddToAllObjects(PyObject *op, int force);
|
extern void _Py_AddToAllObjects(PyObject *op);
|
||||||
extern void _Py_PrintReferences(PyInterpreterState *, FILE *);
|
extern void _Py_PrintReferences(PyInterpreterState *, FILE *);
|
||||||
extern void _Py_PrintReferenceAddresses(PyInterpreterState *, FILE *);
|
extern void _Py_PrintReferenceAddresses(PyInterpreterState *, FILE *);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,6 +8,8 @@ extern "C" {
|
||||||
# error "this header requires Py_BUILD_CORE define"
|
# error "this header requires Py_BUILD_CORE define"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "pycore_hashtable.h" // _Py_hashtable_t
|
||||||
|
|
||||||
struct _py_object_runtime_state {
|
struct _py_object_runtime_state {
|
||||||
#ifdef Py_REF_DEBUG
|
#ifdef Py_REF_DEBUG
|
||||||
Py_ssize_t interpreter_leaks;
|
Py_ssize_t interpreter_leaks;
|
||||||
|
@ -20,11 +22,10 @@ struct _py_object_state {
|
||||||
Py_ssize_t reftotal;
|
Py_ssize_t reftotal;
|
||||||
#endif
|
#endif
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
/* Head of circular doubly-linked list of all objects. These are linked
|
// Hash table storing all objects. The key is the object pointer
|
||||||
* together via the _ob_prev and _ob_next members of a PyObject, which
|
// (PyObject*) and the value is always the number 1 (as uintptr_t).
|
||||||
* exist only in a Py_TRACE_REFS build.
|
// See _PyRefchain_IsTraced() and _PyRefchain_Trace() functions.
|
||||||
*/
|
_Py_hashtable_t *refchain;
|
||||||
PyObject refchain;
|
|
||||||
#endif
|
#endif
|
||||||
int _not_used;
|
int _not_used;
|
||||||
};
|
};
|
||||||
|
|
|
@ -192,7 +192,7 @@ extern PyTypeObject _PyExc_MemoryError;
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
# define _py_object_state_INIT(INTERP) \
|
# define _py_object_state_INIT(INTERP) \
|
||||||
{ \
|
{ \
|
||||||
.refchain = {&INTERP.object_state.refchain, &INTERP.object_state.refchain}, \
|
.refchain = NULL, \
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
# define _py_object_state_INIT(INTERP) \
|
# define _py_object_state_INIT(INTERP) \
|
||||||
|
|
|
@ -111,14 +111,6 @@ PyAPI_FUNC(int) PyModule_ExecDef(PyObject *module, PyModuleDef *def);
|
||||||
#define PYTHON_ABI_VERSION 3
|
#define PYTHON_ABI_VERSION 3
|
||||||
#define PYTHON_ABI_STRING "3"
|
#define PYTHON_ABI_STRING "3"
|
||||||
|
|
||||||
#ifdef Py_TRACE_REFS
|
|
||||||
/* When we are tracing reference counts, rename module creation functions so
|
|
||||||
modules compiled with incompatible settings will generate a
|
|
||||||
link-time error. */
|
|
||||||
#define PyModule_Create2 PyModule_Create2TraceRefs
|
|
||||||
#define PyModule_FromDefAndSpec2 PyModule_FromDefAndSpec2TraceRefs
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) PyModule_Create2(PyModuleDef*, int apiver);
|
PyAPI_FUNC(PyObject *) PyModule_Create2(PyModuleDef*, int apiver);
|
||||||
|
|
||||||
#ifdef Py_LIMITED_API
|
#ifdef Py_LIMITED_API
|
||||||
|
|
|
@ -58,23 +58,6 @@ whose size is determined when the object is allocated.
|
||||||
# define Py_REF_DEBUG
|
# define Py_REF_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Py_LIMITED_API) && defined(Py_TRACE_REFS)
|
|
||||||
# error Py_LIMITED_API is incompatible with Py_TRACE_REFS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Py_TRACE_REFS
|
|
||||||
/* Define pointers to support a doubly-linked list of all live heap objects. */
|
|
||||||
#define _PyObject_HEAD_EXTRA \
|
|
||||||
PyObject *_ob_next; \
|
|
||||||
PyObject *_ob_prev;
|
|
||||||
|
|
||||||
#define _PyObject_EXTRA_INIT _Py_NULL, _Py_NULL,
|
|
||||||
|
|
||||||
#else
|
|
||||||
# define _PyObject_HEAD_EXTRA
|
|
||||||
# define _PyObject_EXTRA_INIT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* PyObject_HEAD defines the initial segment of every PyObject. */
|
/* PyObject_HEAD defines the initial segment of every PyObject. */
|
||||||
#define PyObject_HEAD PyObject ob_base;
|
#define PyObject_HEAD PyObject ob_base;
|
||||||
|
|
||||||
|
@ -130,14 +113,12 @@ check by comparing the reference count field to the immortality reference count.
|
||||||
#ifdef Py_BUILD_CORE
|
#ifdef Py_BUILD_CORE
|
||||||
#define PyObject_HEAD_INIT(type) \
|
#define PyObject_HEAD_INIT(type) \
|
||||||
{ \
|
{ \
|
||||||
_PyObject_EXTRA_INIT \
|
|
||||||
{ _Py_IMMORTAL_REFCNT }, \
|
{ _Py_IMMORTAL_REFCNT }, \
|
||||||
(type) \
|
(type) \
|
||||||
},
|
},
|
||||||
#else
|
#else
|
||||||
#define PyObject_HEAD_INIT(type) \
|
#define PyObject_HEAD_INIT(type) \
|
||||||
{ \
|
{ \
|
||||||
_PyObject_EXTRA_INIT \
|
|
||||||
{ 1 }, \
|
{ 1 }, \
|
||||||
(type) \
|
(type) \
|
||||||
},
|
},
|
||||||
|
@ -164,8 +145,6 @@ check by comparing the reference count field to the immortality reference count.
|
||||||
* in addition, be cast to PyVarObject*.
|
* in addition, be cast to PyVarObject*.
|
||||||
*/
|
*/
|
||||||
struct _object {
|
struct _object {
|
||||||
_PyObject_HEAD_EXTRA
|
|
||||||
|
|
||||||
#if (defined(__GNUC__) || defined(__clang__)) \
|
#if (defined(__GNUC__) || defined(__clang__)) \
|
||||||
&& !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L)
|
&& !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L)
|
||||||
// On C99 and older, anonymous union is a GCC and clang extension
|
// On C99 and older, anonymous union is a GCC and clang extension
|
||||||
|
|
|
@ -684,12 +684,6 @@ extern char * _getpty(int *, int, mode_t, int);
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check that ALT_SOABI is consistent with Py_TRACE_REFS:
|
|
||||||
./configure --with-trace-refs should must be used to define Py_TRACE_REFS */
|
|
||||||
#if defined(ALT_SOABI) && defined(Py_TRACE_REFS)
|
|
||||||
# error "Py_TRACE_REFS ABI is not compatible with release and debug ABI"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__ANDROID__) || defined(__VXWORKS__)
|
#if defined(__ANDROID__) || defined(__VXWORKS__)
|
||||||
// Use UTF-8 as the locale encoding, ignore the LC_CTYPE locale.
|
// Use UTF-8 as the locale encoding, ignore the LC_CTYPE locale.
|
||||||
// See _Py_GetLocaleEncoding(), PyUnicode_DecodeLocale()
|
// See _Py_GetLocaleEncoding(), PyUnicode_DecodeLocale()
|
||||||
|
|
|
@ -779,9 +779,6 @@ def python_is_optimized():
|
||||||
|
|
||||||
_header = 'nP'
|
_header = 'nP'
|
||||||
_align = '0n'
|
_align = '0n'
|
||||||
if hasattr(sys, "getobjects"):
|
|
||||||
_header = '2P' + _header
|
|
||||||
_align = '0P'
|
|
||||||
_vheader = _header + 'n'
|
_vheader = _header + 'n'
|
||||||
|
|
||||||
def calcobjsize(fmt):
|
def calcobjsize(fmt):
|
||||||
|
@ -2469,3 +2466,5 @@ C_RECURSION_LIMIT = 1500
|
||||||
#Windows doesn't have os.uname() but it doesn't support s390x.
|
#Windows doesn't have os.uname() but it doesn't support s390x.
|
||||||
skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x',
|
skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x',
|
||||||
'skipped on s390x')
|
'skipped on s390x')
|
||||||
|
|
||||||
|
Py_TRACE_REFS = hasattr(sys, 'getobjects')
|
||||||
|
|
|
@ -112,6 +112,9 @@ class PyMemDebugTests(unittest.TestCase):
|
||||||
def test_pyobject_freed_is_freed(self):
|
def test_pyobject_freed_is_freed(self):
|
||||||
self.check_pyobject_is_freed('check_pyobject_freed_is_freed')
|
self.check_pyobject_is_freed('check_pyobject_freed_is_freed')
|
||||||
|
|
||||||
|
# Python built with Py_TRACE_REFS fail with a fatal error in
|
||||||
|
# _PyRefchain_Trace() on memory allocation error.
|
||||||
|
@unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build')
|
||||||
def test_set_nomemory(self):
|
def test_set_nomemory(self):
|
||||||
code = """if 1:
|
code = """if 1:
|
||||||
import _testcapi
|
import _testcapi
|
||||||
|
|
|
@ -1484,6 +1484,9 @@ class ExceptionTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
@cpython_only
|
@cpython_only
|
||||||
|
# Python built with Py_TRACE_REFS fail with a fatal error in
|
||||||
|
# _PyRefchain_Trace() on memory allocation error.
|
||||||
|
@unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build')
|
||||||
def test_recursion_normalizing_with_no_memory(self):
|
def test_recursion_normalizing_with_no_memory(self):
|
||||||
# Issue #30697. Test that in the abort that occurs when there is no
|
# Issue #30697. Test that in the abort that occurs when there is no
|
||||||
# memory left and the size of the Python frames stack is greater than
|
# memory left and the size of the Python frames stack is greater than
|
||||||
|
@ -1652,6 +1655,9 @@ class ExceptionTests(unittest.TestCase):
|
||||||
self.assertTrue(report.endswith("\n"))
|
self.assertTrue(report.endswith("\n"))
|
||||||
|
|
||||||
@cpython_only
|
@cpython_only
|
||||||
|
# Python built with Py_TRACE_REFS fail with a fatal error in
|
||||||
|
# _PyRefchain_Trace() on memory allocation error.
|
||||||
|
@unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build')
|
||||||
def test_memory_error_in_PyErr_PrintEx(self):
|
def test_memory_error_in_PyErr_PrintEx(self):
|
||||||
code = """if 1:
|
code = """if 1:
|
||||||
import _testcapi
|
import _testcapi
|
||||||
|
|
|
@ -28,7 +28,7 @@ import _imp
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
from test.support import (
|
from test.support import (
|
||||||
STDLIB_DIR, swap_attr, swap_item, cpython_only, is_emscripten,
|
STDLIB_DIR, swap_attr, swap_item, cpython_only, is_emscripten,
|
||||||
is_wasi, run_in_subinterp, run_in_subinterp_with_config)
|
is_wasi, run_in_subinterp, run_in_subinterp_with_config, Py_TRACE_REFS)
|
||||||
from test.support.import_helper import (
|
from test.support.import_helper import (
|
||||||
forget, make_legacy_pyc, unlink, unload, DirsOnSysPath, CleanImport)
|
forget, make_legacy_pyc, unlink, unload, DirsOnSysPath, CleanImport)
|
||||||
from test.support.os_helper import (
|
from test.support.os_helper import (
|
||||||
|
@ -2555,7 +2555,7 @@ class SinglephaseInitTests(unittest.TestCase):
|
||||||
def test_basic_multiple_interpreters_deleted_no_reset(self):
|
def test_basic_multiple_interpreters_deleted_no_reset(self):
|
||||||
# without resetting; already loaded in a deleted interpreter
|
# without resetting; already loaded in a deleted interpreter
|
||||||
|
|
||||||
if hasattr(sys, 'getobjects'):
|
if Py_TRACE_REFS:
|
||||||
# It's a Py_TRACE_REFS build.
|
# It's a Py_TRACE_REFS build.
|
||||||
# This test breaks interpreter isolation a little,
|
# This test breaks interpreter isolation a little,
|
||||||
# which causes problems on Py_TRACE_REF builds.
|
# which causes problems on Py_TRACE_REF builds.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import os
|
||||||
import unittest
|
import unittest
|
||||||
import subprocess
|
import subprocess
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
from test import support
|
||||||
from test.support import cpython_only, has_subprocess_support, SuppressCrashReport
|
from test.support import cpython_only, has_subprocess_support, SuppressCrashReport
|
||||||
from test.support.script_helper import kill_python
|
from test.support.script_helper import kill_python
|
||||||
|
|
||||||
|
@ -59,6 +60,9 @@ def run_on_interactive_mode(source):
|
||||||
class TestInteractiveInterpreter(unittest.TestCase):
|
class TestInteractiveInterpreter(unittest.TestCase):
|
||||||
|
|
||||||
@cpython_only
|
@cpython_only
|
||||||
|
# Python built with Py_TRACE_REFS fail with a fatal error in
|
||||||
|
# _PyRefchain_Trace() on memory allocation error.
|
||||||
|
@unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build')
|
||||||
def test_no_memory(self):
|
def test_no_memory(self):
|
||||||
# Issue #30696: Fix the interactive interpreter looping endlessly when
|
# Issue #30696: Fix the interactive interpreter looping endlessly when
|
||||||
# no memory. Check also that the fix does not break the interactive
|
# no memory. Check also that the fix does not break the interactive
|
||||||
|
|
|
@ -1174,6 +1174,27 @@ class SysModuleTest(unittest.TestCase):
|
||||||
self.assertEqual(os.path.normpath(sys._stdlib_dir),
|
self.assertEqual(os.path.normpath(sys._stdlib_dir),
|
||||||
os.path.normpath(expected))
|
os.path.normpath(expected))
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(sys, 'getobjects'), 'need sys.getobjects()')
|
||||||
|
def test_getobjects(self):
|
||||||
|
# sys.getobjects(0)
|
||||||
|
all_objects = sys.getobjects(0)
|
||||||
|
self.assertIsInstance(all_objects, list)
|
||||||
|
self.assertGreater(len(all_objects), 0)
|
||||||
|
|
||||||
|
# sys.getobjects(0, MyType)
|
||||||
|
class MyType:
|
||||||
|
pass
|
||||||
|
size = 100
|
||||||
|
my_objects = [MyType() for _ in range(size)]
|
||||||
|
get_objects = sys.getobjects(0, MyType)
|
||||||
|
self.assertEqual(len(get_objects), size)
|
||||||
|
for obj in get_objects:
|
||||||
|
self.assertIsInstance(obj, MyType)
|
||||||
|
|
||||||
|
# sys.getobjects(3, MyType)
|
||||||
|
get_objects = sys.getobjects(3, MyType)
|
||||||
|
self.assertEqual(len(get_objects), 3)
|
||||||
|
|
||||||
|
|
||||||
@test.support.cpython_only
|
@test.support.cpython_only
|
||||||
class UnraisableHookTest(unittest.TestCase):
|
class UnraisableHookTest(unittest.TestCase):
|
||||||
|
|
|
@ -173,9 +173,11 @@ class TestTracemallocEnabled(unittest.TestCase):
|
||||||
self.assertEqual(len(traceback), 1)
|
self.assertEqual(len(traceback), 1)
|
||||||
self.assertEqual(traceback, obj_traceback)
|
self.assertEqual(traceback, obj_traceback)
|
||||||
|
|
||||||
def find_trace(self, traces, traceback):
|
def find_trace(self, traces, traceback, size):
|
||||||
|
# filter also by size to ignore the memory allocated by
|
||||||
|
# _PyRefchain_Trace() if Python is built with Py_TRACE_REFS.
|
||||||
for trace in traces:
|
for trace in traces:
|
||||||
if trace[2] == traceback._frames:
|
if trace[2] == traceback._frames and trace[1] == size:
|
||||||
return trace
|
return trace
|
||||||
|
|
||||||
self.fail("trace not found")
|
self.fail("trace not found")
|
||||||
|
@ -186,11 +188,10 @@ class TestTracemallocEnabled(unittest.TestCase):
|
||||||
obj, obj_traceback = allocate_bytes(obj_size)
|
obj, obj_traceback = allocate_bytes(obj_size)
|
||||||
|
|
||||||
traces = tracemalloc._get_traces()
|
traces = tracemalloc._get_traces()
|
||||||
trace = self.find_trace(traces, obj_traceback)
|
trace = self.find_trace(traces, obj_traceback, obj_size)
|
||||||
|
|
||||||
self.assertIsInstance(trace, tuple)
|
self.assertIsInstance(trace, tuple)
|
||||||
domain, size, traceback, length = trace
|
domain, size, traceback, length = trace
|
||||||
self.assertEqual(size, obj_size)
|
|
||||||
self.assertEqual(traceback, obj_traceback._frames)
|
self.assertEqual(traceback, obj_traceback._frames)
|
||||||
|
|
||||||
tracemalloc.stop()
|
tracemalloc.stop()
|
||||||
|
@ -208,17 +209,18 @@ class TestTracemallocEnabled(unittest.TestCase):
|
||||||
# Ensure that two identical tracebacks are not duplicated
|
# Ensure that two identical tracebacks are not duplicated
|
||||||
tracemalloc.stop()
|
tracemalloc.stop()
|
||||||
tracemalloc.start(4)
|
tracemalloc.start(4)
|
||||||
obj_size = 123
|
obj1_size = 123
|
||||||
obj1, obj1_traceback = allocate_bytes4(obj_size)
|
obj2_size = 125
|
||||||
obj2, obj2_traceback = allocate_bytes4(obj_size)
|
obj1, obj1_traceback = allocate_bytes4(obj1_size)
|
||||||
|
obj2, obj2_traceback = allocate_bytes4(obj2_size)
|
||||||
|
|
||||||
traces = tracemalloc._get_traces()
|
traces = tracemalloc._get_traces()
|
||||||
|
|
||||||
obj1_traceback._frames = tuple(reversed(obj1_traceback._frames))
|
obj1_traceback._frames = tuple(reversed(obj1_traceback._frames))
|
||||||
obj2_traceback._frames = tuple(reversed(obj2_traceback._frames))
|
obj2_traceback._frames = tuple(reversed(obj2_traceback._frames))
|
||||||
|
|
||||||
trace1 = self.find_trace(traces, obj1_traceback)
|
trace1 = self.find_trace(traces, obj1_traceback, obj1_size)
|
||||||
trace2 = self.find_trace(traces, obj2_traceback)
|
trace2 = self.find_trace(traces, obj2_traceback, obj2_size)
|
||||||
domain1, size1, traceback1, length1 = trace1
|
domain1, size1, traceback1, length1 = trace1
|
||||||
domain2, size2, traceback2, length2 = trace2
|
domain2, size2, traceback2, length2 = trace2
|
||||||
self.assertIs(traceback2, traceback1)
|
self.assertIs(traceback2, traceback1)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Python built with :file:`configure` :option:`--with-trace-refs` (tracing
|
||||||
|
references) is now ABI compatible with Python release build and :ref:`debug
|
||||||
|
build <debug-build>`. Patch by Victor Stinner.
|
|
@ -0,0 +1,3 @@
|
||||||
|
Python built with :file:`configure` :option:`--with-trace-refs` (tracing
|
||||||
|
references) now supports the :ref:`Limited API <limited-c-api>`. Patch by
|
||||||
|
Victor Stinner.
|
|
@ -43,13 +43,9 @@ Py_TRACE_REFS
|
||||||
|
|
||||||
Build option: ``./configure --with-trace-refs``.
|
Build option: ``./configure --with-trace-refs``.
|
||||||
|
|
||||||
Turn on heavy reference debugging. This is major surgery. Every PyObject grows
|
Turn on heavy reference debugging. This is major surgery. All live
|
||||||
two more pointers, to maintain a doubly-linked list of all live heap-allocated
|
heap-allocated objects are traced in a hash table. Most built-in type objects
|
||||||
objects. Most built-in type objects are not in this list, as they're statically
|
are not in this list, as they're statically allocated.
|
||||||
allocated.
|
|
||||||
|
|
||||||
Note that because the fundamental PyObject layout changes, Python modules
|
|
||||||
compiled with Py_TRACE_REFS are incompatible with modules compiled without it.
|
|
||||||
|
|
||||||
Special gimmicks:
|
Special gimmicks:
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,9 @@
|
||||||
#ifndef Py_TESTCAPI_PARTS_H
|
#ifndef Py_TESTCAPI_PARTS_H
|
||||||
#define Py_TESTCAPI_PARTS_H
|
#define Py_TESTCAPI_PARTS_H
|
||||||
|
|
||||||
#include "pyconfig.h" // for Py_TRACE_REFS
|
|
||||||
|
|
||||||
// Figure out if Limited API is available for this build. If it isn't we won't
|
|
||||||
// build tests for it.
|
|
||||||
// Currently, only Py_TRACE_REFS disables Limited API.
|
|
||||||
#ifdef Py_TRACE_REFS
|
|
||||||
#undef LIMITED_API_AVAILABLE
|
|
||||||
#else
|
|
||||||
#define LIMITED_API_AVAILABLE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Always enable assertions
|
// Always enable assertions
|
||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
|
|
||||||
#if !defined(LIMITED_API_AVAILABLE) && defined(Py_LIMITED_API)
|
|
||||||
// Limited API being unavailable means that with Py_LIMITED_API defined
|
|
||||||
// we can't even include Python.h.
|
|
||||||
// Do nothing; the .c file that defined Py_LIMITED_API should also do nothing.
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
|
||||||
int _PyTestCapi_Init_Vectorcall(PyObject *module);
|
int _PyTestCapi_Init_Vectorcall(PyObject *module);
|
||||||
|
@ -44,10 +26,7 @@ int _PyTestCapi_Init_PyOS(PyObject *module);
|
||||||
int _PyTestCapi_Init_Immortal(PyObject *module);
|
int _PyTestCapi_Init_Immortal(PyObject *module);
|
||||||
int _PyTestCapi_Init_GC(PyObject *mod);
|
int _PyTestCapi_Init_GC(PyObject *mod);
|
||||||
|
|
||||||
#ifdef LIMITED_API_AVAILABLE
|
|
||||||
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
|
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
|
||||||
int _PyTestCapi_Init_HeaptypeRelative(PyObject *module);
|
int _PyTestCapi_Init_HeaptypeRelative(PyObject *module);
|
||||||
#endif // LIMITED_API_AVAILABLE
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif // Py_TESTCAPI_PARTS_H
|
#endif // Py_TESTCAPI_PARTS_H
|
||||||
|
|
272
Objects/object.c
272
Objects/object.c
|
@ -9,6 +9,7 @@
|
||||||
#include "pycore_dict.h" // _PyObject_MakeDictFromInstanceAttributes()
|
#include "pycore_dict.h" // _PyObject_MakeDictFromInstanceAttributes()
|
||||||
#include "pycore_floatobject.h" // _PyFloat_DebugMallocStats()
|
#include "pycore_floatobject.h" // _PyFloat_DebugMallocStats()
|
||||||
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
|
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
|
||||||
|
#include "pycore_hashtable.h" // _Py_hashtable_new()
|
||||||
#include "pycore_memoryobject.h" // _PyManagedBuffer_Type
|
#include "pycore_memoryobject.h" // _PyManagedBuffer_Type
|
||||||
#include "pycore_namespace.h" // _PyNamespace_Type
|
#include "pycore_namespace.h" // _PyNamespace_Type
|
||||||
#include "pycore_object.h" // PyAPI_DATA() _Py_SwappedOp definition
|
#include "pycore_object.h" // PyAPI_DATA() _Py_SwappedOp definition
|
||||||
|
@ -162,44 +163,51 @@ _PyDebug_PrintTotalRefs(void) {
|
||||||
|
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
|
|
||||||
#define REFCHAIN(interp) &interp->object_state.refchain
|
#define REFCHAIN(interp) interp->object_state.refchain
|
||||||
|
#define REFCHAIN_VALUE ((void*)(uintptr_t)1)
|
||||||
|
|
||||||
static inline void
|
bool
|
||||||
init_refchain(PyInterpreterState *interp)
|
_PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj)
|
||||||
{
|
{
|
||||||
PyObject *refchain = REFCHAIN(interp);
|
return (_Py_hashtable_get(REFCHAIN(interp), obj) == REFCHAIN_VALUE);
|
||||||
refchain->_ob_prev = refchain;
|
|
||||||
refchain->_ob_next = refchain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Insert op at the front of the list of all objects. If force is true,
|
|
||||||
* op is added even if _ob_prev and _ob_next are non-NULL already. If
|
static void
|
||||||
* force is false amd _ob_prev or _ob_next are non-NULL, do nothing.
|
_PyRefchain_Trace(PyInterpreterState *interp, PyObject *obj)
|
||||||
* force should be true if and only if op points to freshly allocated,
|
|
||||||
* uninitialized memory, or you've unlinked op from the list and are
|
|
||||||
* relinking it into the front.
|
|
||||||
* Note that objects are normally added to the list via _Py_NewReference,
|
|
||||||
* which is called by PyObject_Init. Not all objects are initialized that
|
|
||||||
* way, though; exceptions include statically allocated type objects, and
|
|
||||||
* statically allocated singletons (like Py_True and Py_None).
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
_Py_AddToAllObjects(PyObject *op, int force)
|
|
||||||
{
|
{
|
||||||
#ifdef Py_DEBUG
|
if (_Py_hashtable_set(REFCHAIN(interp), obj, REFCHAIN_VALUE) < 0) {
|
||||||
if (!force) {
|
// Use a fatal error because _Py_NewReference() cannot report
|
||||||
/* If it's initialized memory, op must be in or out of
|
// the error to the caller.
|
||||||
* the list unambiguously.
|
Py_FatalError("_Py_hashtable_set() memory allocation failed");
|
||||||
*/
|
|
||||||
_PyObject_ASSERT(op, (op->_ob_prev == NULL) == (op->_ob_next == NULL));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
_PyRefchain_Remove(PyInterpreterState *interp, PyObject *obj)
|
||||||
|
{
|
||||||
|
void *value = _Py_hashtable_steal(REFCHAIN(interp), obj);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(value == REFCHAIN_VALUE);
|
||||||
|
#else
|
||||||
|
(void)value;
|
||||||
#endif
|
#endif
|
||||||
if (force || op->_ob_prev == NULL) {
|
}
|
||||||
PyObject *refchain = REFCHAIN(_PyInterpreterState_GET());
|
|
||||||
op->_ob_next = refchain->_ob_next;
|
|
||||||
op->_ob_prev = refchain;
|
/* Add an object to the refchain hash table.
|
||||||
refchain->_ob_next->_ob_prev = op;
|
*
|
||||||
refchain->_ob_next = op;
|
* Note that objects are normally added to the list by PyObject_Init()
|
||||||
|
* indirectly. Not all objects are initialized that way, though; exceptions
|
||||||
|
* include statically allocated type objects, and statically allocated
|
||||||
|
* singletons (like Py_True and Py_None). */
|
||||||
|
void
|
||||||
|
_Py_AddToAllObjects(PyObject *op)
|
||||||
|
{
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
if (!_PyRefchain_IsTraced(interp, op)) {
|
||||||
|
_PyRefchain_Trace(interp, op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* Py_TRACE_REFS */
|
#endif /* Py_TRACE_REFS */
|
||||||
|
@ -471,16 +479,6 @@ _PyObject_IsFreed(PyObject *op)
|
||||||
if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
|
if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* ignore op->ob_ref: its value can have be modified
|
|
||||||
by Py_INCREF() and Py_DECREF(). */
|
|
||||||
#ifdef Py_TRACE_REFS
|
|
||||||
if (op->_ob_next != NULL && _PyMem_IsPtrFreed(op->_ob_next)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (op->_ob_prev != NULL && _PyMem_IsPtrFreed(op->_ob_prev)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1929,7 +1927,6 @@ PyTypeObject _PyNone_Type = {
|
||||||
};
|
};
|
||||||
|
|
||||||
PyObject _Py_NoneStruct = {
|
PyObject _Py_NoneStruct = {
|
||||||
_PyObject_EXTRA_INIT
|
|
||||||
{ _Py_IMMORTAL_REFCNT },
|
{ _Py_IMMORTAL_REFCNT },
|
||||||
&_PyNone_Type
|
&_PyNone_Type
|
||||||
};
|
};
|
||||||
|
@ -2032,7 +2029,6 @@ PyTypeObject _PyNotImplemented_Type = {
|
||||||
};
|
};
|
||||||
|
|
||||||
PyObject _Py_NotImplementedStruct = {
|
PyObject _Py_NotImplementedStruct = {
|
||||||
_PyObject_EXTRA_INIT
|
|
||||||
{ _Py_IMMORTAL_REFCNT },
|
{ _Py_IMMORTAL_REFCNT },
|
||||||
&_PyNotImplemented_Type
|
&_PyNotImplemented_Type
|
||||||
};
|
};
|
||||||
|
@ -2042,12 +2038,30 @@ void
|
||||||
_PyObject_InitState(PyInterpreterState *interp)
|
_PyObject_InitState(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
if (!_Py_IsMainInterpreter(interp)) {
|
_Py_hashtable_allocator_t alloc = {
|
||||||
init_refchain(interp);
|
// Don't use default PyMem_Malloc() and PyMem_Free() which
|
||||||
|
// require the caller to hold the GIL.
|
||||||
|
.malloc = PyMem_RawMalloc,
|
||||||
|
.free = PyMem_RawFree,
|
||||||
|
};
|
||||||
|
REFCHAIN(interp) = _Py_hashtable_new_full(
|
||||||
|
_Py_hashtable_hash_ptr, _Py_hashtable_compare_direct,
|
||||||
|
NULL, NULL, &alloc);
|
||||||
|
if (REFCHAIN(interp) == NULL) {
|
||||||
|
Py_FatalError("_PyObject_InitState() memory allocation failure");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyObject_FiniState(PyInterpreterState *interp)
|
||||||
|
{
|
||||||
|
#ifdef Py_TRACE_REFS
|
||||||
|
_Py_hashtable_destroy(REFCHAIN(interp));
|
||||||
|
REFCHAIN(interp) = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extern PyTypeObject _PyAnextAwaitable_Type;
|
extern PyTypeObject _PyAnextAwaitable_Type;
|
||||||
extern PyTypeObject _PyLegacyEventHandler_Type;
|
extern PyTypeObject _PyLegacyEventHandler_Type;
|
||||||
|
@ -2230,7 +2244,7 @@ new_reference(PyObject *op)
|
||||||
// Skip the immortal object check in Py_SET_REFCNT; always set refcnt to 1
|
// Skip the immortal object check in Py_SET_REFCNT; always set refcnt to 1
|
||||||
op->ob_refcnt = 1;
|
op->ob_refcnt = 1;
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
_Py_AddToAllObjects(op, 1);
|
_Py_AddToAllObjects(op);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2258,53 +2272,62 @@ _Py_ForgetReference(PyObject *op)
|
||||||
_PyObject_ASSERT_FAILED_MSG(op, "negative refcnt");
|
_PyObject_ASSERT_FAILED_MSG(op, "negative refcnt");
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *refchain = REFCHAIN(_PyInterpreterState_GET());
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
if (op == refchain ||
|
|
||||||
op->_ob_prev->_ob_next != op || op->_ob_next->_ob_prev != op)
|
|
||||||
{
|
|
||||||
_PyObject_ASSERT_FAILED_MSG(op, "invalid object chain");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SLOW_UNREF_CHECK
|
#ifdef SLOW_UNREF_CHECK
|
||||||
PyObject *p;
|
if (!_PyRefchain_Get(interp, op)) {
|
||||||
for (p = refchain->_ob_next; p != refchain; p = p->_ob_next) {
|
|
||||||
if (p == op) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (p == refchain) {
|
|
||||||
/* Not found */
|
/* Not found */
|
||||||
_PyObject_ASSERT_FAILED_MSG(op,
|
_PyObject_ASSERT_FAILED_MSG(op,
|
||||||
"object not found in the objects list");
|
"object not found in the objects list");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
op->_ob_next->_ob_prev = op->_ob_prev;
|
_PyRefchain_Remove(interp, op);
|
||||||
op->_ob_prev->_ob_next = op->_ob_next;
|
|
||||||
op->_ob_next = op->_ob_prev = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_Py_PrintReference(_Py_hashtable_t *ht,
|
||||||
|
const void *key, const void *value,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
PyObject *op = (PyObject*)key;
|
||||||
|
FILE *fp = (FILE *)user_data;
|
||||||
|
fprintf(fp, "%p [%zd] ", (void *)op, Py_REFCNT(op));
|
||||||
|
if (PyObject_Print(op, fp, 0) != 0) {
|
||||||
|
PyErr_Clear();
|
||||||
|
}
|
||||||
|
putc('\n', fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Print all live objects. Because PyObject_Print is called, the
|
/* Print all live objects. Because PyObject_Print is called, the
|
||||||
* interpreter must be in a healthy state.
|
* interpreter must be in a healthy state.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
_Py_PrintReferences(PyInterpreterState *interp, FILE *fp)
|
_Py_PrintReferences(PyInterpreterState *interp, FILE *fp)
|
||||||
{
|
{
|
||||||
PyObject *op;
|
|
||||||
if (interp == NULL) {
|
if (interp == NULL) {
|
||||||
interp = _PyInterpreterState_Main();
|
interp = _PyInterpreterState_Main();
|
||||||
}
|
}
|
||||||
fprintf(fp, "Remaining objects:\n");
|
fprintf(fp, "Remaining objects:\n");
|
||||||
PyObject *refchain = REFCHAIN(interp);
|
_Py_hashtable_foreach(REFCHAIN(interp), _Py_PrintReference, fp);
|
||||||
for (op = refchain->_ob_next; op != refchain; op = op->_ob_next) {
|
|
||||||
fprintf(fp, "%p [%zd] ", (void *)op, Py_REFCNT(op));
|
|
||||||
if (PyObject_Print(op, fp, 0) != 0) {
|
|
||||||
PyErr_Clear();
|
|
||||||
}
|
|
||||||
putc('\n', fp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
_Py_PrintReferenceAddress(_Py_hashtable_t *ht,
|
||||||
|
const void *key, const void *value,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
PyObject *op = (PyObject*)key;
|
||||||
|
FILE *fp = (FILE *)user_data;
|
||||||
|
fprintf(fp, "%p [%zd] %s\n",
|
||||||
|
(void *)op, Py_REFCNT(op), Py_TYPE(op)->tp_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Print the addresses of all live objects. Unlike _Py_PrintReferences, this
|
/* Print the addresses of all live objects. Unlike _Py_PrintReferences, this
|
||||||
* doesn't make any calls to the Python C API, so is always safe to call.
|
* doesn't make any calls to the Python C API, so is always safe to call.
|
||||||
*/
|
*/
|
||||||
|
@ -2315,47 +2338,96 @@ _Py_PrintReferences(PyInterpreterState *interp, FILE *fp)
|
||||||
void
|
void
|
||||||
_Py_PrintReferenceAddresses(PyInterpreterState *interp, FILE *fp)
|
_Py_PrintReferenceAddresses(PyInterpreterState *interp, FILE *fp)
|
||||||
{
|
{
|
||||||
PyObject *op;
|
|
||||||
PyObject *refchain = REFCHAIN(interp);
|
|
||||||
fprintf(fp, "Remaining object addresses:\n");
|
fprintf(fp, "Remaining object addresses:\n");
|
||||||
for (op = refchain->_ob_next; op != refchain; op = op->_ob_next)
|
_Py_hashtable_foreach(REFCHAIN(interp), _Py_PrintReferenceAddress, fp);
|
||||||
fprintf(fp, "%p [%zd] %s\n", (void *)op,
|
|
||||||
Py_REFCNT(op), Py_TYPE(op)->tp_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject *self;
|
||||||
|
PyObject *args;
|
||||||
|
PyObject *list;
|
||||||
|
PyObject *type;
|
||||||
|
Py_ssize_t limit;
|
||||||
|
} _Py_GetObjectsData;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
_PY_GETOBJECTS_IGNORE = 0,
|
||||||
|
_PY_GETOBJECTS_ERROR = 1,
|
||||||
|
_PY_GETOBJECTS_STOP = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
_Py_GetObject(_Py_hashtable_t *ht,
|
||||||
|
const void *key, const void *value,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
PyObject *op = (PyObject *)key;
|
||||||
|
_Py_GetObjectsData *data = user_data;
|
||||||
|
if (data->limit > 0) {
|
||||||
|
if (PyList_GET_SIZE(data->list) >= data->limit) {
|
||||||
|
return _PY_GETOBJECTS_STOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == data->self) {
|
||||||
|
return _PY_GETOBJECTS_IGNORE;
|
||||||
|
}
|
||||||
|
if (op == data->args) {
|
||||||
|
return _PY_GETOBJECTS_IGNORE;
|
||||||
|
}
|
||||||
|
if (op == data->list) {
|
||||||
|
return _PY_GETOBJECTS_IGNORE;
|
||||||
|
}
|
||||||
|
if (data->type != NULL) {
|
||||||
|
if (op == data->type) {
|
||||||
|
return _PY_GETOBJECTS_IGNORE;
|
||||||
|
}
|
||||||
|
if (!Py_IS_TYPE(op, (PyTypeObject *)data->type)) {
|
||||||
|
return _PY_GETOBJECTS_IGNORE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyList_Append(data->list, op) < 0) {
|
||||||
|
return _PY_GETOBJECTS_ERROR;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* The implementation of sys.getobjects(). */
|
/* The implementation of sys.getobjects(). */
|
||||||
PyObject *
|
PyObject *
|
||||||
_Py_GetObjects(PyObject *self, PyObject *args)
|
_Py_GetObjects(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
int i, n;
|
Py_ssize_t limit;
|
||||||
PyObject *t = NULL;
|
PyObject *type = NULL;
|
||||||
PyObject *res, *op;
|
if (!PyArg_ParseTuple(args, "n|O", &limit, &type)) {
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "i|O", &n, &t))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
PyObject *refchain = REFCHAIN(interp);
|
|
||||||
op = refchain->_ob_next;
|
|
||||||
res = PyList_New(0);
|
|
||||||
if (res == NULL)
|
|
||||||
return NULL;
|
|
||||||
for (i = 0; (n == 0 || i < n) && op != refchain; i++) {
|
|
||||||
while (op == self || op == args || op == res || op == t ||
|
|
||||||
(t != NULL && !Py_IS_TYPE(op, (PyTypeObject *) t))) {
|
|
||||||
op = op->_ob_next;
|
|
||||||
if (op == refchain)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
if (PyList_Append(res, op) < 0) {
|
|
||||||
Py_DECREF(res);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
op = op->_ob_next;
|
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
|
PyObject *list = PyList_New(0);
|
||||||
|
if (list == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Py_GetObjectsData data = {
|
||||||
|
.self = self,
|
||||||
|
.args = args,
|
||||||
|
.list = list,
|
||||||
|
.type = type,
|
||||||
|
.limit = limit,
|
||||||
|
};
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
int res = _Py_hashtable_foreach(REFCHAIN(interp), _Py_GetObject, &data);
|
||||||
|
if (res == _PY_GETOBJECTS_ERROR) {
|
||||||
|
Py_DECREF(list);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef REFCHAIN
|
#undef REFCHAIN
|
||||||
|
#undef REFCHAIN_VALUE
|
||||||
|
|
||||||
#endif /* Py_TRACE_REFS */
|
#endif /* Py_TRACE_REFS */
|
||||||
|
|
||||||
|
|
|
@ -2548,7 +2548,6 @@ static PyTypeObject _PySetDummy_Type = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyObject _dummy_struct = {
|
static PyObject _dummy_struct = {
|
||||||
_PyObject_EXTRA_INIT
|
|
||||||
{ _Py_IMMORTAL_REFCNT },
|
{ _Py_IMMORTAL_REFCNT },
|
||||||
&_PySetDummy_Type
|
&_PySetDummy_Type
|
||||||
};
|
};
|
||||||
|
|
|
@ -98,7 +98,6 @@ PyTypeObject PyEllipsis_Type = {
|
||||||
};
|
};
|
||||||
|
|
||||||
PyObject _Py_EllipsisObject = {
|
PyObject _Py_EllipsisObject = {
|
||||||
_PyObject_EXTRA_INIT
|
|
||||||
{ _Py_IMMORTAL_REFCNT },
|
{ _Py_IMMORTAL_REFCNT },
|
||||||
&PyEllipsis_Type
|
&PyEllipsis_Type
|
||||||
};
|
};
|
||||||
|
|
|
@ -573,9 +573,10 @@ PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
|
||||||
Py_ssize_t n_members, n_unnamed_members;
|
Py_ssize_t n_members, n_unnamed_members;
|
||||||
|
|
||||||
#ifdef Py_TRACE_REFS
|
#ifdef Py_TRACE_REFS
|
||||||
/* if the type object was chained, unchain it first
|
/* if the type object was traced, remove it first
|
||||||
before overwriting its storage */
|
before overwriting its storage */
|
||||||
if (type->ob_base.ob_base._ob_next) {
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
if (_PyRefchain_IsTraced(interp, (PyObject *)type)) {
|
||||||
_Py_ForgetReference((PyObject *)type);
|
_Py_ForgetReference((PyObject *)type);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7508,7 +7508,7 @@ type_ready(PyTypeObject *type, int rerunbuiltin)
|
||||||
* to get type objects into the doubly-linked list of all objects.
|
* to get type objects into the doubly-linked list of all objects.
|
||||||
* Still, not all type objects go through PyType_Ready.
|
* Still, not all type objects go through PyType_Ready.
|
||||||
*/
|
*/
|
||||||
_Py_AddToAllObjects((PyObject *)type, 0);
|
_Py_AddToAllObjects((PyObject *)type);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Initialize tp_dict: _PyType_IsReady() tests if tp_dict != NULL */
|
/* Initialize tp_dict: _PyType_IsReady() tests if tp_dict != NULL */
|
||||||
|
|
|
@ -3110,7 +3110,7 @@ _PyBuiltin_Init(PyInterpreterState *interp)
|
||||||
* result, programs leaking references to None and False (etc)
|
* result, programs leaking references to None and False (etc)
|
||||||
* couldn't be diagnosed by examining sys.getobjects(0).
|
* couldn't be diagnosed by examining sys.getobjects(0).
|
||||||
*/
|
*/
|
||||||
#define ADD_TO_ALL(OBJECT) _Py_AddToAllObjects((PyObject *)(OBJECT), 0)
|
#define ADD_TO_ALL(OBJECT) _Py_AddToAllObjects((PyObject *)(OBJECT))
|
||||||
#else
|
#else
|
||||||
#define ADD_TO_ALL(OBJECT) (void)0
|
#define ADD_TO_ALL(OBJECT) (void)0
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -226,7 +226,6 @@ _Py_hashtable_set(_Py_hashtable_t *ht, const void *key, void *value)
|
||||||
assert(entry == NULL);
|
assert(entry == NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
entry = ht->alloc.malloc(sizeof(_Py_hashtable_entry_t));
|
entry = ht->alloc.malloc(sizeof(_Py_hashtable_entry_t));
|
||||||
if (entry == NULL) {
|
if (entry == NULL) {
|
||||||
/* memory allocation failed */
|
/* memory allocation failed */
|
||||||
|
|
|
@ -1956,6 +1956,20 @@ Py_FinalizeEx(void)
|
||||||
// XXX Ensure finalizer errors are handled properly.
|
// XXX Ensure finalizer errors are handled properly.
|
||||||
|
|
||||||
finalize_interp_clear(tstate);
|
finalize_interp_clear(tstate);
|
||||||
|
|
||||||
|
#ifdef Py_TRACE_REFS
|
||||||
|
/* Display addresses (& refcnts) of all objects still alive.
|
||||||
|
* An address can be used to find the repr of the object, printed
|
||||||
|
* above by _Py_PrintReferences. */
|
||||||
|
if (dump_refs) {
|
||||||
|
_Py_PrintReferenceAddresses(tstate->interp, stderr);
|
||||||
|
}
|
||||||
|
if (dump_refs_fp != NULL) {
|
||||||
|
_Py_PrintReferenceAddresses(tstate->interp, dump_refs_fp);
|
||||||
|
fclose(dump_refs_fp);
|
||||||
|
}
|
||||||
|
#endif /* Py_TRACE_REFS */
|
||||||
|
|
||||||
finalize_interp_delete(tstate->interp);
|
finalize_interp_delete(tstate->interp);
|
||||||
|
|
||||||
#ifdef Py_REF_DEBUG
|
#ifdef Py_REF_DEBUG
|
||||||
|
@ -1966,21 +1980,6 @@ Py_FinalizeEx(void)
|
||||||
#endif
|
#endif
|
||||||
_Py_FinalizeAllocatedBlocks(runtime);
|
_Py_FinalizeAllocatedBlocks(runtime);
|
||||||
|
|
||||||
#ifdef Py_TRACE_REFS
|
|
||||||
/* Display addresses (& refcnts) of all objects still alive.
|
|
||||||
* An address can be used to find the repr of the object, printed
|
|
||||||
* above by _Py_PrintReferences.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (dump_refs) {
|
|
||||||
_Py_PrintReferenceAddresses(tstate->interp, stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dump_refs_fp != NULL) {
|
|
||||||
_Py_PrintReferenceAddresses(tstate->interp, dump_refs_fp);
|
|
||||||
fclose(dump_refs_fp);
|
|
||||||
}
|
|
||||||
#endif /* Py_TRACE_REFS */
|
|
||||||
#ifdef WITH_PYMALLOC
|
#ifdef WITH_PYMALLOC
|
||||||
if (malloc_stats) {
|
if (malloc_stats) {
|
||||||
_PyObject_DebugMallocStats(stderr);
|
_PyObject_DebugMallocStats(stderr);
|
||||||
|
|
|
@ -674,6 +674,7 @@ init_interpreter(PyInterpreterState *interp,
|
||||||
_obmalloc_pools_INIT(interp->obmalloc.pools);
|
_obmalloc_pools_INIT(interp->obmalloc.pools);
|
||||||
memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp));
|
memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp));
|
||||||
}
|
}
|
||||||
|
|
||||||
_PyObject_InitState(interp);
|
_PyObject_InitState(interp);
|
||||||
|
|
||||||
_PyEval_InitState(interp, pending_lock);
|
_PyEval_InitState(interp, pending_lock);
|
||||||
|
@ -1001,6 +1002,9 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
|
||||||
if (interp->id_mutex != NULL) {
|
if (interp->id_mutex != NULL) {
|
||||||
PyThread_free_lock(interp->id_mutex);
|
PyThread_free_lock(interp->id_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_PyObject_FiniState(interp);
|
||||||
|
|
||||||
free_interpreter(interp);
|
free_interpreter(interp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23571,8 +23571,9 @@ SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${PLATFORM_TRIPLET:+-$PLATFO
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5
|
||||||
printf "%s\n" "$SOABI" >&6; }
|
printf "%s\n" "$SOABI" >&6; }
|
||||||
|
|
||||||
# Release and debug (Py_DEBUG) ABI are compatible, but not Py_TRACE_REFS ABI
|
# Release build, debug build (Py_DEBUG), and trace refs build (Py_TRACE_REFS)
|
||||||
if test "$Py_DEBUG" = 'true' -a "$with_trace_refs" != "yes"; then
|
# are ABI compatible
|
||||||
|
if test "$Py_DEBUG" = 'true'; then
|
||||||
# Similar to SOABI but remove "d" flag from ABIFLAGS
|
# Similar to SOABI but remove "d" flag from ABIFLAGS
|
||||||
|
|
||||||
ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET}
|
ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET}
|
||||||
|
@ -29962,7 +29963,7 @@ printf %s "checking for stdlib extension module _testclinic_limited... " >&6; }
|
||||||
if test "$py_cv_module__testclinic_limited" != "n/a"
|
if test "$py_cv_module__testclinic_limited" != "n/a"
|
||||||
then :
|
then :
|
||||||
|
|
||||||
if test "$TEST_MODULES" = yes -a "$with_trace_refs" = "no"
|
if test "$TEST_MODULES" = yes
|
||||||
then :
|
then :
|
||||||
if true
|
if true
|
||||||
then :
|
then :
|
||||||
|
@ -30267,7 +30268,7 @@ printf %s "checking for stdlib extension module xxlimited... " >&6; }
|
||||||
if test "$py_cv_module_xxlimited" != "n/a"
|
if test "$py_cv_module_xxlimited" != "n/a"
|
||||||
then :
|
then :
|
||||||
|
|
||||||
if test "$with_trace_refs" = "no"
|
if true
|
||||||
then :
|
then :
|
||||||
if test "$ac_cv_func_dlopen" = yes
|
if test "$ac_cv_func_dlopen" = yes
|
||||||
then :
|
then :
|
||||||
|
@ -30305,7 +30306,7 @@ printf %s "checking for stdlib extension module xxlimited_35... " >&6; }
|
||||||
if test "$py_cv_module_xxlimited_35" != "n/a"
|
if test "$py_cv_module_xxlimited_35" != "n/a"
|
||||||
then :
|
then :
|
||||||
|
|
||||||
if test "$with_trace_refs" = "no"
|
if true
|
||||||
then :
|
then :
|
||||||
if test "$ac_cv_func_dlopen" = yes
|
if test "$ac_cv_func_dlopen" = yes
|
||||||
then :
|
then :
|
||||||
|
|
12
configure.ac
12
configure.ac
|
@ -5684,8 +5684,9 @@ AC_MSG_CHECKING([SOABI])
|
||||||
SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET}
|
SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET}
|
||||||
AC_MSG_RESULT([$SOABI])
|
AC_MSG_RESULT([$SOABI])
|
||||||
|
|
||||||
# Release and debug (Py_DEBUG) ABI are compatible, but not Py_TRACE_REFS ABI
|
# Release build, debug build (Py_DEBUG), and trace refs build (Py_TRACE_REFS)
|
||||||
if test "$Py_DEBUG" = 'true' -a "$with_trace_refs" != "yes"; then
|
# are ABI compatible
|
||||||
|
if test "$Py_DEBUG" = 'true'; then
|
||||||
# Similar to SOABI but remove "d" flag from ABIFLAGS
|
# Similar to SOABI but remove "d" flag from ABIFLAGS
|
||||||
AC_SUBST([ALT_SOABI])
|
AC_SUBST([ALT_SOABI])
|
||||||
ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET}
|
ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET}
|
||||||
|
@ -7229,7 +7230,7 @@ PY_STDLIB_MOD([_hashlib], [], [test "$ac_cv_working_openssl_hashlib" = yes],
|
||||||
dnl test modules
|
dnl test modules
|
||||||
PY_STDLIB_MOD([_testcapi], [test "$TEST_MODULES" = yes])
|
PY_STDLIB_MOD([_testcapi], [test "$TEST_MODULES" = yes])
|
||||||
PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes])
|
PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes])
|
||||||
PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes -a "$with_trace_refs" = "no"])
|
PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes])
|
||||||
PY_STDLIB_MOD([_testinternalcapi], [test "$TEST_MODULES" = yes])
|
PY_STDLIB_MOD([_testinternalcapi], [test "$TEST_MODULES" = yes])
|
||||||
PY_STDLIB_MOD([_testbuffer], [test "$TEST_MODULES" = yes])
|
PY_STDLIB_MOD([_testbuffer], [test "$TEST_MODULES" = yes])
|
||||||
PY_STDLIB_MOD([_testimportmultiple], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes])
|
PY_STDLIB_MOD([_testimportmultiple], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes])
|
||||||
|
@ -7241,10 +7242,9 @@ PY_STDLIB_MOD([_ctypes_test],
|
||||||
[], [$LIBM])
|
[], [$LIBM])
|
||||||
|
|
||||||
dnl Limited API template modules.
|
dnl Limited API template modules.
|
||||||
dnl The limited C API is not compatible with the Py_TRACE_REFS macro.
|
|
||||||
dnl Emscripten does not support shared libraries yet.
|
dnl Emscripten does not support shared libraries yet.
|
||||||
PY_STDLIB_MOD([xxlimited], [test "$with_trace_refs" = "no"], [test "$ac_cv_func_dlopen" = yes])
|
PY_STDLIB_MOD([xxlimited], [], [test "$ac_cv_func_dlopen" = yes])
|
||||||
PY_STDLIB_MOD([xxlimited_35], [test "$with_trace_refs" = "no"], [test "$ac_cv_func_dlopen" = yes])
|
PY_STDLIB_MOD([xxlimited_35], [], [test "$ac_cv_func_dlopen" = yes])
|
||||||
|
|
||||||
# substitute multiline block, must come after last PY_STDLIB_MOD()
|
# substitute multiline block, must come after last PY_STDLIB_MOD()
|
||||||
AC_SUBST([MODULE_BLOCK])
|
AC_SUBST([MODULE_BLOCK])
|
||||||
|
|
Loading…
Reference in New Issue