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:
Victor Stinner 2023-08-31 18:33:34 +02:00 committed by GitHub
parent 013a99a47b
commit 13a00078b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 293 additions and 244 deletions

View file

@ -173,9 +173,11 @@ class TestTracemallocEnabled(unittest.TestCase):
self.assertEqual(len(traceback), 1)
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:
if trace[2] == traceback._frames:
if trace[2] == traceback._frames and trace[1] == size:
return trace
self.fail("trace not found")
@ -186,11 +188,10 @@ class TestTracemallocEnabled(unittest.TestCase):
obj, obj_traceback = allocate_bytes(obj_size)
traces = tracemalloc._get_traces()
trace = self.find_trace(traces, obj_traceback)
trace = self.find_trace(traces, obj_traceback, obj_size)
self.assertIsInstance(trace, tuple)
domain, size, traceback, length = trace
self.assertEqual(size, obj_size)
self.assertEqual(traceback, obj_traceback._frames)
tracemalloc.stop()
@ -208,17 +209,18 @@ class TestTracemallocEnabled(unittest.TestCase):
# Ensure that two identical tracebacks are not duplicated
tracemalloc.stop()
tracemalloc.start(4)
obj_size = 123
obj1, obj1_traceback = allocate_bytes4(obj_size)
obj2, obj2_traceback = allocate_bytes4(obj_size)
obj1_size = 123
obj2_size = 125
obj1, obj1_traceback = allocate_bytes4(obj1_size)
obj2, obj2_traceback = allocate_bytes4(obj2_size)
traces = tracemalloc._get_traces()
obj1_traceback._frames = tuple(reversed(obj1_traceback._frames))
obj2_traceback._frames = tuple(reversed(obj2_traceback._frames))
trace1 = self.find_trace(traces, obj1_traceback)
trace2 = self.find_trace(traces, obj2_traceback)
trace1 = self.find_trace(traces, obj1_traceback, obj1_size)
trace2 = self.find_trace(traces, obj2_traceback, obj2_size)
domain1, size1, traceback1, length1 = trace1
domain2, size2, traceback2, length2 = trace2
self.assertIs(traceback2, traceback1)