bpo-39489: Remove COUNT_ALLOCS special build (GH-18259)

Remove:

* COUNT_ALLOCS macro
* sys.getcounts() function
* SHOW_ALLOC_COUNT code in listobject.c
* SHOW_TRACK_COUNT code in tupleobject.c
* PyConfig.show_alloc_count field
* -X showalloccount command line option
* @test.support.requires_type_collecting decorator
This commit is contained in:
Victor Stinner 2020-02-03 15:17:15 +01:00 committed by GitHub
parent 869c0c99b9
commit c6e5c1123b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 24 additions and 469 deletions

View File

@ -627,14 +627,6 @@ PyConfig
``python3 -m MODULE`` argument. Used by :c:func:`Py_RunMain`.
.. c:member:: int show_alloc_count
Show allocation counts at exit?
Set to 1 by :option:`-X showalloccount <-X>` command line option.
Need a special Python build with ``COUNT_ALLOCS`` macro defined.
.. c:member:: int show_ref_count
Show total reference count at exit?
@ -702,6 +694,10 @@ arguments are stripped from ``argv``: see :ref:`Command Line Arguments
The ``xoptions`` options are parsed to set other options: see :option:`-X`
option.
.. versionchanged:: 3.9
The ``show_alloc_count`` field has been removed.
Initialization with PyConfig
----------------------------

View File

@ -148,15 +148,6 @@ Quick Reference
| :c:member:`~PyTypeObject.tp_vectorcall` | :c:type:`vectorcallfunc` | | | | | |
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
If :const:`COUNT_ALLOCS` is defined then the following (internal-only)
fields exist as well:
* :c:member:`~PyTypeObject.tp_allocs`
* :c:member:`~PyTypeObject.tp_frees`
* :c:member:`~PyTypeObject.tp_maxalloc`
* :c:member:`~PyTypeObject.tp_prev`
* :c:member:`~PyTypeObject.tp_next`
.. [#slots]
A slot name in parentheses indicates it is (effectively) deprecated.
Names in angle brackets should be treated as read-only.
@ -1904,31 +1895,6 @@ and :c:type:`PyType_Type` effectively act as defaults.)
.. versionadded:: 3.9 (the field exists since 3.8 but it's only used since 3.9)
The remaining fields are only defined if the feature test macro
:const:`COUNT_ALLOCS` is defined, and are for internal use only. They are
documented here for completeness. None of these fields are inherited by
subtypes.
.. c:member:: Py_ssize_t PyTypeObject.tp_allocs
Number of allocations.
.. c:member:: Py_ssize_t PyTypeObject.tp_frees
Number of frees.
.. c:member:: Py_ssize_t PyTypeObject.tp_maxalloc
Maximum simultaneously allocated objects.
.. c:member:: PyTypeObject* PyTypeObject.tp_prev
Pointer to the previous type object with a non-zero :c:member:`~PyTypeObject.tp_allocs` field.
.. c:member:: PyTypeObject* PyTypeObject.tp_next
Pointer to the next type object with a non-zero :c:member:`~PyTypeObject.tp_allocs` field.
Also, note that, in a garbage collected Python, :c:member:`~PyTypeObject.tp_dealloc` may be called from
any Python thread, not just the thread which created the object (if the object
becomes part of a refcount cycle, that cycle might be collected by a garbage

View File

@ -434,9 +434,6 @@ Miscellaneous options
stored in a traceback of a trace. Use ``-X tracemalloc=NFRAME`` to start
tracing with a traceback limit of *NFRAME* frames. See the
:func:`tracemalloc.start` for more information.
* ``-X showalloccount`` to output the total count of allocated objects for
each type when the program finishes. This only works when Python was built with
``COUNT_ALLOCS`` defined.
* ``-X importtime`` to show how long each import takes. It shows module
name, cumulative time (including nested imports) and self time (excluding
nested imports). Note that its output may be broken in multi-threaded
@ -479,6 +476,8 @@ Miscellaneous options
Using ``-X dev`` option, check *encoding* and *errors* arguments on
string encoding and decoding operations.
The ``-X showalloccount`` option has been removed.
Options you shouldn't use
~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -348,6 +348,9 @@ Build and C API Changes
functions are now required to build Python.
(Contributed by Victor Stinner in :issue:`39395`.)
* The ``COUNT_ALLOCS`` special build macro has been removed.
(Contributed by Victor Stinner in :issue:`39489`.)
Deprecated
==========
@ -484,6 +487,12 @@ Removed
``asyncio.Condition`` and ``asyncio.Semaphore``.
(Contributed by Andrew Svetlov in :issue:`34793`.)
* The :func:`sys.getcounts` function, the ``-X showalloccount`` command line
option and the ``show_alloc_count`` field of the C structure
:c:type:`PyConfig` have been removed. They required a special Python build by
defining ``COUNT_ALLOCS`` macro.
(Contributed by Victor Stinner in :issue:`39489`.)
Porting to Python 3.9
=====================

View File

@ -153,7 +153,6 @@ typedef struct {
int import_time; /* PYTHONPROFILEIMPORTTIME, -X importtime */
int show_ref_count; /* -X showrefcount */
int show_alloc_count; /* -X showalloccount */
int dump_refs; /* PYTHONDUMPREFS */
int malloc_stats; /* PYTHONMALLOCSTATS */

View File

@ -255,15 +255,6 @@ typedef struct _typeobject {
destructor tp_finalize;
vectorcallfunc tp_vectorcall;
#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs;
Py_ssize_t tp_frees;
Py_ssize_t tp_maxalloc;
struct _typeobject *tp_prev;
struct _typeobject *tp_next;
#endif
} PyTypeObject;
/* The *real* layout of a type object when allocated on the heap */
@ -341,8 +332,6 @@ static inline void _Py_Dealloc_inline(PyObject *op)
destructor dealloc = Py_TYPE(op)->tp_dealloc;
#ifdef Py_TRACE_REFS
_Py_ForgetReference(op);
#else
_Py_INC_TPFREES(op);
#endif
(*dealloc)(op);
}

View File

@ -405,20 +405,6 @@ PyAPI_FUNC(void) _PyDebug_PrintTotalRefs(void);
#define _Py_DEC_REFTOTAL
#endif /* Py_REF_DEBUG */
#ifdef COUNT_ALLOCS
PyAPI_FUNC(void) _Py_inc_count(struct _typeobject *);
PyAPI_FUNC(void) _Py_dec_count(struct _typeobject *);
#define _Py_INC_TPALLOCS(OP) _Py_inc_count(Py_TYPE(OP))
#define _Py_INC_TPFREES(OP) _Py_dec_count(Py_TYPE(OP))
#define _Py_DEC_TPFREES(OP) Py_TYPE(OP)->tp_frees--
#define _Py_COUNT_ALLOCS_COMMA ,
#else
#define _Py_INC_TPALLOCS(OP)
#define _Py_INC_TPFREES(OP)
#define _Py_DEC_TPFREES(OP)
#define _Py_COUNT_ALLOCS_COMMA
#endif /* COUNT_ALLOCS */
/* Update the Python traceback of an object. This function must be called
when a memory block is reused from a free list. */
PyAPI_FUNC(int) _PyTraceMalloc_NewReference(PyObject *op);
@ -438,15 +424,13 @@ static inline void _Py_NewReference(PyObject *op)
if (_Py_tracemalloc_config.tracing) {
_PyTraceMalloc_NewReference(op);
}
_Py_INC_TPALLOCS(op);
_Py_INC_REFTOTAL;
Py_REFCNT(op) = 1;
}
static inline void _Py_ForgetReference(PyObject *op)
static inline void _Py_ForgetReference(PyObject *Py_UNUSED(op))
{
(void)op; /* may be unused, shut up -Wunused-parameter */
_Py_INC_TPFREES(op);
/* nothing to do */
}
#endif /* !Py_TRACE_REFS */

View File

@ -325,7 +325,7 @@ def _args_from_interpreter_flags():
if dev_mode:
args.extend(('-X', 'dev'))
for opt in ('faulthandler', 'tracemalloc', 'importtime',
'showalloccount', 'showrefcount', 'utf8'):
'showrefcount', 'utf8'):
if opt in xoptions:
value = xoptions[opt]
if value is True:

View File

@ -2512,9 +2512,6 @@ def swap_item(obj, item, new_val):
if item in obj:
del obj[item]
requires_type_collecting = unittest.skipIf(hasattr(sys, 'getcounts'),
'types are immortal if COUNT_ALLOCS is defined')
def args_from_interpreter_flags():
"""Return a list of command-line arguments reproducing the current
settings in sys.flags and sys.warnoptions."""

View File

@ -356,7 +356,6 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'tracemalloc': 0,
'import_time': 0,
'show_ref_count': 0,
'show_alloc_count': 0,
'dump_refs': 0,
'malloc_stats': 0,
@ -729,7 +728,6 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'tracemalloc': 2,
'import_time': 1,
'show_ref_count': 1,
'show_alloc_count': 1,
'malloc_stats': 1,
'stdio_encoding': 'iso8859-1',

View File

@ -2,7 +2,7 @@ import unittest
import unittest.mock
from test.support import (verbose, refcount_test, run_unittest,
cpython_only, start_threads,
temp_dir, requires_type_collecting, TESTFN, unlink,
temp_dir, TESTFN, unlink,
import_module)
from test.support.script_helper import assert_python_ok, make_script
@ -131,7 +131,6 @@ class GCTests(unittest.TestCase):
del a
self.assertNotEqual(gc.collect(), 0)
@requires_type_collecting
def test_newinstance(self):
class A(object):
pass
@ -709,7 +708,6 @@ class GCTests(unittest.TestCase):
stderr = run_command(code % "gc.DEBUG_SAVEALL")
self.assertNotIn(b"uncollectable objects at shutdown", stderr)
@requires_type_collecting
def test_gc_main_module_at_shutdown(self):
# Create a reference cycle through the __main__ module and check
# it gets collected at interpreter shutdown.
@ -723,7 +721,6 @@ class GCTests(unittest.TestCase):
rc, out, err = assert_python_ok('-c', code)
self.assertEqual(out.strip(), b'__del__ called')
@requires_type_collecting
def test_gc_ordinary_module_at_shutdown(self):
# Same as above, but with a non-__main__ module.
with temp_dir() as script_dir:
@ -743,7 +740,6 @@ class GCTests(unittest.TestCase):
rc, out, err = assert_python_ok('-c', code)
self.assertEqual(out.strip(), b'__del__ called')
@requires_type_collecting
def test_global_del_SystemExit(self):
code = """if 1:
class ClassWithDel:

View File

@ -3492,7 +3492,6 @@ class TextIOWrapperTest(unittest.TestCase):
""".format(iomod=iomod, kwargs=kwargs)
return assert_python_ok("-c", code)
@support.requires_type_collecting
def test_create_at_shutdown_without_encoding(self):
rc, out, err = self._check_create_at_shutdown()
if err:
@ -3502,7 +3501,6 @@ class TextIOWrapperTest(unittest.TestCase):
else:
self.assertEqual("ok", out.decode().strip())
@support.requires_type_collecting
def test_create_at_shutdown_with_encoding(self):
rc, out, err = self._check_create_at_shutdown(encoding='utf-8',
errors='strict')

View File

@ -4252,7 +4252,6 @@ class ModuleLevelMiscTest(BaseTest):
h.close()
logging.setLoggerClass(logging.Logger)
@support.requires_type_collecting
def test_logging_at_shutdown(self):
# Issue #20037
code = """if 1:

View File

@ -1,7 +1,7 @@
# Test the module type
import unittest
import weakref
from test.support import gc_collect, requires_type_collecting
from test.support import gc_collect
from test.support.script_helper import assert_python_ok
import sys
@ -101,7 +101,6 @@ class ModuleTests(unittest.TestCase):
gc_collect()
self.assertEqual(f().__dict__["bar"], 4)
@requires_type_collecting
def test_clear_dict_in_ref_cycle(self):
destroyed = []
m = ModuleType("foo")
@ -266,7 +265,6 @@ a = A(destroyed)"""
self.assertEqual(r[-len(ends_with):], ends_with,
'{!r} does not end with {!r}'.format(r, ends_with))
@requires_type_collecting
def test_module_finalization_at_shutdown(self):
# Module globals and builtins should still be available during shutdown
rc, out, err = assert_python_ok("-c", "from test import final_a")

View File

@ -493,7 +493,6 @@ class TestSupport(unittest.TestCase):
['-Wignore', '-X', 'dev'],
['-X', 'faulthandler'],
['-X', 'importtime'],
['-X', 'showalloccount'],
['-X', 'showrefcount'],
['-X', 'tracemalloc'],
['-X', 'tracemalloc=3'],

View File

@ -819,7 +819,6 @@ class SysModuleTest(unittest.TestCase):
c = sys.getallocatedblocks()
self.assertIn(c, range(b - 50, b + 50))
@test.support.requires_type_collecting
def test_is_finalizing(self):
self.assertIs(sys.is_finalizing(), False)
# Don't use the atexit module because _Py_Finalizing is only set
@ -841,7 +840,6 @@ class SysModuleTest(unittest.TestCase):
rc, stdout, stderr = assert_python_ok('-c', code)
self.assertEqual(stdout.rstrip(), b'True')
@test.support.requires_type_collecting
def test_issue20602(self):
# sys.flags and sys.float_info were wiped during shutdown.
code = """if 1:
@ -1295,8 +1293,6 @@ class SizeofTest(unittest.TestCase):
# type
# static type: PyTypeObject
fmt = 'P2nPI13Pl4Pn9Pn11PIPP'
if hasattr(sys, 'getcounts'):
fmt += '3n2P'
s = vsize(fmt)
check(int, s)
# class

View File

@ -3,8 +3,7 @@ Tests for the threading module.
"""
import test.support
from test.support import (verbose, import_module, cpython_only,
requires_type_collecting)
from test.support import verbose, import_module, cpython_only
from test.support.script_helper import assert_python_ok, assert_python_failure
import random
@ -552,7 +551,6 @@ class ThreadTests(BaseTestCase):
self.assertEqual(err, b"")
self.assertEqual(data, "Thread-1\nTrue\nTrue\n")
@requires_type_collecting
def test_main_thread_during_shutdown(self):
# bpo-31516: current_thread() should still point to the main thread
# at shutdown
@ -1113,7 +1111,6 @@ class ThreadingExceptionTests(BaseTestCase):
self.assertIn("ZeroDivisionError", err)
self.assertNotIn("Unhandled exception", err)
@requires_type_collecting
def test_print_exception_stderr_is_none_1(self):
script = r"""if True:
import sys

View File

@ -174,7 +174,6 @@ class TracebackCases(unittest.TestCase):
# Issue #18960: coding spec should have no effect
do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
@support.requires_type_collecting
def test_print_traceback_at_exit(self):
# Issue #22599: Ensure that it is possible to use the traceback module
# to display an exception at Python exit

View File

@ -1227,7 +1227,6 @@ class BootstrapTest(unittest.TestCase):
class FinalizationTest(unittest.TestCase):
@support.requires_type_collecting
def test_finalization(self):
# Issue #19421: warnings.warn() should not crash
# during Python finalization

View File

@ -649,7 +649,6 @@ class ReferencesTestCase(TestBase):
del c1, c2, C, D
gc.collect()
@support.requires_type_collecting
def test_callback_in_cycle_resurrection(self):
import gc

View File

@ -0,0 +1 @@
Remove ``COUNT_ALLOCS`` special build.

View File

@ -46,9 +46,7 @@ Build option: ``./configure --with-trace-refs``.
Turn on heavy reference debugging. This is major surgery. Every PyObject grows
two more pointers, to maintain a doubly-linked list of all live heap-allocated
objects. Most built-in type objects are not in this list, as they're statically
allocated. Starting in Python 2.3, if COUNT_ALLOCS (see below) is also defined,
a static type object T does appear in this list if at least one object of type T
has been created.
allocated.
Note that because the fundamental PyObject layout changes, Python modules
compiled with Py_TRACE_REFS are incompatible with modules compiled without it.
@ -165,55 +163,6 @@ by not defining NDEBUG), and some routines do additional sanity checks inside
"#ifdef Py_DEBUG" blocks.
COUNT_ALLOCS
------------
Each type object grows three new members:
/* Number of times an object of this type was allocated. */
int tp_allocs;
/* Number of times an object of this type was deallocated. */
int tp_frees;
/* Highwater mark: the maximum value of tp_allocs - tp_frees so
* far; or, IOW, the largest number of objects of this type alive at
* the same time.
*/
int tp_maxalloc;
Allocation and deallocation code keeps these counts up to date. Py_FinalizeEx()
displays a summary of the info returned by sys.getcounts() (see below), along
with assorted other special allocation counts (like the number of tuple
allocations satisfied by a tuple free-list, the number of 1-character strings
allocated, etc).
Before Python 2.2, type objects were immortal, and the COUNT_ALLOCS
implementation relies on that. As of Python 2.2, heap-allocated type/ class
objects can go away. COUNT_ALLOCS can blow up in 2.2 and 2.2.1 because of this;
this was fixed in 2.2.2. Use of COUNT_ALLOCS makes all heap-allocated type
objects immortal, except for those for which no object of that type is ever
allocated.
Starting with Python 2.3, If Py_TRACE_REFS is also defined, COUNT_ALLOCS
arranges to ensure that the type object for each allocated object appears in the
doubly-linked list of all objects maintained by Py_TRACE_REFS.
Special gimmicks:
sys.getcounts()
Return a list of 4-tuples, one entry for each type object for which at least
one object of that type was allocated. Each tuple is of the form:
(tp_name, tp_allocs, tp_frees, tp_maxalloc)
Each distinct type object gets a distinct entry in this list, even if two or
more type objects have the same tp_name (in which case there's no way to
distinguish them by looking at this list). The list is ordered by time of
first object allocation: the type object for which the first allocation of
an object of that type occurred most recently is at the front of the list.
LLTRACE
-------

View File

@ -286,10 +286,6 @@ Set implementation specific option. The following options are available:
traceback of a trace. Use -X tracemalloc=NFRAME to start tracing with a
traceback limit of NFRAME frames
-X showalloccount: output the total count of allocated objects for each
type when the program finishes. This only works when Python was built with
COUNT_ALLOCS defined
-X importtime: show how long each import takes. It shows module name,
cumulative time (including nested imports) and self time (excluding
nested imports). Note that its output may be broken in multi-threaded

View File

@ -3589,16 +3589,6 @@ slot_tp_del(PyObject *self)
/* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so
* we need to undo that. */
_Py_DEC_REFTOTAL;
/* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
* chain, so no more to do there.
* If COUNT_ALLOCS, the original decref bumped tp_frees, and
* _Py_NewReference bumped tp_allocs: both of those need to be
* undone.
*/
#ifdef COUNT_ALLOCS
--Py_TYPE(self)->tp_frees;
--Py_TYPE(self)->tp_allocs;
#endif
}
static PyObject *

View File

@ -18,10 +18,6 @@ class bytes "PyBytesObject *" "&PyBytes_Type"
#include "clinic/bytesobject.c.h"
#ifdef COUNT_ALLOCS
Py_ssize_t _Py_null_strings, _Py_one_strings;
#endif
static PyBytesObject *characters[UCHAR_MAX + 1];
static PyBytesObject *nullstring;
@ -68,9 +64,6 @@ _PyBytes_FromSize(Py_ssize_t size, int use_calloc)
assert(size >= 0);
if (size == 0 && (op = nullstring) != NULL) {
#ifdef COUNT_ALLOCS
_Py_null_strings++;
#endif
Py_INCREF(op);
return (PyObject *)op;
}
@ -112,9 +105,6 @@ PyBytes_FromStringAndSize(const char *str, Py_ssize_t size)
if (size == 1 && str != NULL &&
(op = characters[*str & UCHAR_MAX]) != NULL)
{
#ifdef COUNT_ALLOCS
_Py_one_strings++;
#endif
Py_INCREF(op);
return (PyObject *)op;
}
@ -148,16 +138,10 @@ PyBytes_FromString(const char *str)
return NULL;
}
if (size == 0 && (op = nullstring) != NULL) {
#ifdef COUNT_ALLOCS
_Py_null_strings++;
#endif
Py_INCREF(op);
return (PyObject *)op;
}
if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
#ifdef COUNT_ALLOCS
_Py_one_strings++;
#endif
Py_INCREF(op);
return (PyObject *)op;
}

View File

@ -94,29 +94,6 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size)
return 0;
}
/* Debug statistic to compare allocations with reuse through the free list */
#undef SHOW_ALLOC_COUNT
#ifdef SHOW_ALLOC_COUNT
static size_t count_alloc = 0;
static size_t count_reuse = 0;
static void
show_alloc(void)
{
PyInterpreterState *interp = _PyInterpreterState_Get();
if (!interp->config.show_alloc_count) {
return;
}
fprintf(stderr, "List allocations: %" PY_FORMAT_SIZE_T "d\n",
count_alloc);
fprintf(stderr, "List reuse through freelist: %" PY_FORMAT_SIZE_T
"d\n", count_reuse);
fprintf(stderr, "%.2f%% reuse rate\n\n",
(100.0*count_reuse/(count_alloc+count_reuse)));
}
#endif
/* Empty list reuse scheme to save calls to malloc and free */
#ifndef PyList_MAXFREELIST
#define PyList_MAXFREELIST 80
@ -156,13 +133,6 @@ PyObject *
PyList_New(Py_ssize_t size)
{
PyListObject *op;
#ifdef SHOW_ALLOC_COUNT
static int initialized = 0;
if (!initialized) {
Py_AtExit(show_alloc);
initialized = 1;
}
#endif
if (size < 0) {
PyErr_BadInternalCall();
@ -172,16 +142,10 @@ PyList_New(Py_ssize_t size)
numfree--;
op = free_list[numfree];
_Py_NewReference((PyObject *)op);
#ifdef SHOW_ALLOC_COUNT
count_reuse++;
#endif
} else {
op = PyObject_GC_New(PyListObject, &PyList_Type);
if (op == NULL)
return NULL;
#ifdef SHOW_ALLOC_COUNT
count_alloc++;
#endif
}
if (size <= 0)
op->ob_item = NULL;

View File

@ -35,10 +35,6 @@ PyObject *_PyLong_One = NULL;
#define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS)
#define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS)
#ifdef COUNT_ALLOCS
Py_ssize_t _Py_quick_int_allocs, _Py_quick_neg_int_allocs;
#endif
static PyObject *
get_small_int(sdigit ival)
{
@ -46,12 +42,6 @@ get_small_int(sdigit ival)
PyThreadState *tstate = _PyThreadState_GET();
PyObject *v = (PyObject*)tstate->interp->small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
#ifdef COUNT_ALLOCS
if (ival >= 0)
_Py_quick_int_allocs++;
else
_Py_quick_neg_int_allocs++;
#endif
return v;
}

View File

@ -113,120 +113,6 @@ _Py_AddToAllObjects(PyObject *op, int force)
}
#endif /* Py_TRACE_REFS */
#ifdef COUNT_ALLOCS
static PyTypeObject *type_list;
/* All types are added to type_list, at least when
they get one object created. That makes them
immortal, which unfortunately contributes to
garbage itself. If unlist_types_without_objects
is set, they will be removed from the type_list
once the last object is deallocated. */
static int unlist_types_without_objects;
extern Py_ssize_t _Py_tuple_zero_allocs, _Py_fast_tuple_allocs;
extern Py_ssize_t _Py_quick_int_allocs, _Py_quick_neg_int_allocs;
extern Py_ssize_t _Py_null_strings, _Py_one_strings;
void
_Py_dump_counts(FILE* f)
{
PyInterpreterState *interp = _PyInterpreterState_Get();
if (!interp->config.show_alloc_count) {
return;
}
PyTypeObject *tp;
for (tp = type_list; tp; tp = tp->tp_next)
fprintf(f, "%s alloc'd: %" PY_FORMAT_SIZE_T "d, "
"freed: %" PY_FORMAT_SIZE_T "d, "
"max in use: %" PY_FORMAT_SIZE_T "d\n",
tp->tp_name, tp->tp_allocs, tp->tp_frees,
tp->tp_maxalloc);
fprintf(f, "fast tuple allocs: %" PY_FORMAT_SIZE_T "d, "
"empty: %" PY_FORMAT_SIZE_T "d\n",
_Py_fast_tuple_allocs, _Py_tuple_zero_allocs);
fprintf(f, "fast int allocs: pos: %" PY_FORMAT_SIZE_T "d, "
"neg: %" PY_FORMAT_SIZE_T "d\n",
_Py_quick_int_allocs, _Py_quick_neg_int_allocs);
fprintf(f, "null strings: %" PY_FORMAT_SIZE_T "d, "
"1-strings: %" PY_FORMAT_SIZE_T "d\n",
_Py_null_strings, _Py_one_strings);
}
PyObject *
_Py_get_counts(void)
{
PyTypeObject *tp;
PyObject *result;
PyObject *v;
result = PyList_New(0);
if (result == NULL)
return NULL;
for (tp = type_list; tp; tp = tp->tp_next) {
v = Py_BuildValue("(snnn)", tp->tp_name, tp->tp_allocs,
tp->tp_frees, tp->tp_maxalloc);
if (v == NULL) {
Py_DECREF(result);
return NULL;
}
if (PyList_Append(result, v) < 0) {
Py_DECREF(v);
Py_DECREF(result);
return NULL;
}
Py_DECREF(v);
}
return result;
}
void
_Py_inc_count(PyTypeObject *tp)
{
if (tp->tp_next == NULL && tp->tp_prev == NULL) {
/* first time; insert in linked list */
if (type_list)
type_list->tp_prev = tp;
tp->tp_next = type_list;
/* Note that as of Python 2.2, heap-allocated type objects
* can go away, but this code requires that they stay alive
* until program exit. That's why we're careful with
* refcounts here. type_list gets a new reference to tp,
* while ownership of the reference type_list used to hold
* (if any) was transferred to tp->tp_next in the line above.
* tp is thus effectively immortal after this.
*/
Py_INCREF(tp);
type_list = tp;
#ifdef Py_TRACE_REFS
/* Also insert in the doubly-linked list of all objects,
* if not already there.
*/
_Py_AddToAllObjects((PyObject *)tp, 0);
#endif
}
tp->tp_allocs++;
if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc)
tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees;
}
void _Py_dec_count(PyTypeObject *tp)
{
tp->tp_frees++;
if (unlist_types_without_objects &&
tp->tp_allocs == tp->tp_frees) {
/* unlink the type from type_list */
if (tp->tp_prev)
tp->tp_prev->tp_next = tp->tp_next;
else
type_list = tp->tp_next;
if (tp->tp_next)
tp->tp_next->tp_prev = tp->tp_prev;
tp->tp_next = tp->tp_prev = NULL;
Py_DECREF(tp);
}
}
#endif
#ifdef Py_REF_DEBUG
/* Log a fatal error; doesn't return. */
void
@ -349,15 +235,6 @@ PyObject_CallFinalizerFromDealloc(PyObject *self)
/* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so
* we need to undo that. */
_Py_DEC_REFTOTAL;
/* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
* chain, so no more to do there.
* If COUNT_ALLOCS, the original decref bumped tp_frees, and
* _Py_NewReference bumped tp_allocs: both of those need to be
* undone. */
#ifdef COUNT_ALLOCS
--Py_TYPE(self)->tp_frees;
--Py_TYPE(self)->tp_allocs;
#endif
return -1;
}
@ -1970,7 +1847,6 @@ _Py_ForgetReference(PyObject *op)
op->_ob_next->_ob_prev = op->_ob_prev;
op->_ob_prev->_ob_next = op->_ob_next;
op->_ob_next = op->_ob_prev = NULL;
_Py_INC_TPFREES(op);
}
/* Print all live objects. Because PyObject_Print is called, the
@ -2289,8 +2165,6 @@ _Py_Dealloc(PyObject *op)
destructor dealloc = Py_TYPE(op)->tp_dealloc;
#ifdef Py_TRACE_REFS
_Py_ForgetReference(op);
#else
_Py_INC_TPFREES(op);
#endif
(*dealloc)(op);
}

View File

@ -28,43 +28,10 @@ class tuple "PyTupleObject *" "&PyTuple_Type"
static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
static int numfree[PyTuple_MAXSAVESIZE];
#endif
#ifdef COUNT_ALLOCS
Py_ssize_t _Py_fast_tuple_allocs;
Py_ssize_t _Py_tuple_zero_allocs;
#endif
/* Debug statistic to count GC tracking of tuples.
Please note that tuples are only untracked when considered by the GC, and
many of them will be dead before. Therefore, a tracking rate close to 100%
does not necessarily prove that the heuristic is inefficient.
*/
#ifdef SHOW_TRACK_COUNT
static Py_ssize_t count_untracked = 0;
static Py_ssize_t count_tracked = 0;
static void
show_track(void)
{
PyInterpreterState *interp = _PyInterpreterState_Get();
if (!interp->config.show_alloc_count) {
return;
}
fprintf(stderr, "Tuples created: %" PY_FORMAT_SIZE_T "d\n",
count_tracked + count_untracked);
fprintf(stderr, "Tuples tracked by the GC: %" PY_FORMAT_SIZE_T
"d\n", count_tracked);
fprintf(stderr, "%.2f%% tuple tracking rate\n\n",
(100.0*count_tracked/(count_untracked+count_tracked)));
}
#endif
static inline void
tuple_gc_track(PyTupleObject *op)
{
#ifdef SHOW_TRACK_COUNT
count_tracked++;
#endif
_PyObject_GC_TRACK(op);
}
@ -106,9 +73,6 @@ tuple_alloc(Py_ssize_t size)
assert(size != 0);
free_list[size] = (PyTupleObject *) op->ob_item[0];
numfree[size]--;
#ifdef COUNT_ALLOCS
_Py_fast_tuple_allocs++;
#endif
/* Inline PyObject_InitVar */
#ifdef Py_TRACE_REFS
Py_SIZE(op) = size;
@ -139,9 +103,6 @@ PyTuple_New(Py_ssize_t size)
if (size == 0 && free_list[0]) {
op = free_list[0];
Py_INCREF(op);
#ifdef COUNT_ALLOCS
_Py_tuple_zero_allocs++;
#endif
return (PyObject *) op;
}
#endif
@ -227,10 +188,6 @@ _PyTuple_MaybeUntrack(PyObject *op)
_PyObject_GC_MAY_BE_TRACKED(elt))
return;
}
#ifdef SHOW_TRACK_COUNT
count_tracked--;
count_untracked++;
#endif
_PyObject_GC_UNTRACK(op);
}
@ -1001,9 +958,6 @@ _PyTuple_Fini(void)
(void)PyTuple_ClearFreeList();
#endif
#ifdef SHOW_TRACK_COUNT
show_track();
#endif
}
/*********************** Tuple Iterator **************************/

View File

@ -506,7 +506,6 @@ static int test_init_from_config(void)
config.import_time = 1;
config.show_ref_count = 1;
config.show_alloc_count = 1;
/* FIXME: test dump_refs: bpo-34223 */
putenv("PYTHONMALLOCSTATS=0");

View File

@ -758,27 +758,6 @@ exit:
return return_value;
}
#if defined(COUNT_ALLOCS)
PyDoc_STRVAR(sys_getcounts__doc__,
"getcounts($module, /)\n"
"--\n"
"\n");
#define SYS_GETCOUNTS_METHODDEF \
{"getcounts", (PyCFunction)sys_getcounts, METH_NOARGS, sys_getcounts__doc__},
static PyObject *
sys_getcounts_impl(PyObject *module);
static PyObject *
sys_getcounts(PyObject *module, PyObject *Py_UNUSED(ignored))
{
return sys_getcounts_impl(module);
}
#endif /* defined(COUNT_ALLOCS) */
PyDoc_STRVAR(sys__getframe__doc__,
"_getframe($module, depth=0, /)\n"
"--\n"
@ -988,11 +967,7 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored))
#define SYS_GETTOTALREFCOUNT_METHODDEF
#endif /* !defined(SYS_GETTOTALREFCOUNT_METHODDEF) */
#ifndef SYS_GETCOUNTS_METHODDEF
#define SYS_GETCOUNTS_METHODDEF
#endif /* !defined(SYS_GETCOUNTS_METHODDEF) */
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
#define SYS_GETANDROIDAPILEVEL_METHODDEF
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
/*[clinic end generated code: output=decd687b7631de4b input=a9049054013a1b77]*/
/*[clinic end generated code: output=39eb34a01fb9a919 input=a9049054013a1b77]*/

View File

@ -73,9 +73,6 @@ static const char usage_3[] = "\
tracemalloc module. By default, only the most recent frame is stored in a\n\
traceback of a trace. Use -X tracemalloc=NFRAME to start tracing with a\n\
traceback limit of NFRAME frames\n\
-X showalloccount: output the total count of allocated objects for each\n\
type when the program finishes. This only works when Python was built with\n\
COUNT_ALLOCS defined\n\
-X importtime: show how long each import takes. It shows module name,\n\
cumulative time (including nested imports) and self time (excluding\n\
nested imports). Note that its output may be broken in multi-threaded\n\
@ -800,7 +797,6 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
COPY_ATTR(tracemalloc);
COPY_ATTR(import_time);
COPY_ATTR(show_ref_count);
COPY_ATTR(show_alloc_count);
COPY_ATTR(dump_refs);
COPY_ATTR(malloc_stats);
@ -903,7 +899,6 @@ config_as_dict(const PyConfig *config)
SET_ITEM_INT(tracemalloc);
SET_ITEM_INT(import_time);
SET_ITEM_INT(show_ref_count);
SET_ITEM_INT(show_alloc_count);
SET_ITEM_INT(dump_refs);
SET_ITEM_INT(malloc_stats);
SET_ITEM_WSTR(filesystem_encoding);
@ -1691,9 +1686,6 @@ config_read(PyConfig *config)
if (config_get_xoption(config, L"showrefcount")) {
config->show_ref_count = 1;
}
if (config_get_xoption(config, L"showalloccount")) {
config->show_alloc_count = 1;
}
status = config_read_complex_options(config);
if (_PyStatus_EXCEPTION(status)) {

View File

@ -1173,10 +1173,6 @@ Py_Initialize(void)
}
#ifdef COUNT_ALLOCS
extern void _Py_dump_counts(FILE*);
#endif
/* Flush stdout and stderr */
static int
@ -1393,13 +1389,6 @@ Py_FinalizeEx(void)
* XXX I haven't seen a real-life report of either of these.
*/
_PyGC_CollectIfEnabled();
#ifdef COUNT_ALLOCS
/* With COUNT_ALLOCS, it helps to run GC multiple times:
each collection might release some types from the type
list, so they become garbage. */
while (_PyGC_CollectIfEnabled() > 0)
/* nothing */;
#endif
/* Clear all loghooks */
/* We want minimal exposure of this function, so define the extern
@ -1451,10 +1440,6 @@ Py_FinalizeEx(void)
/* unload faulthandler module */
_PyFaulthandler_Fini();
/* Debugging stuff */
#ifdef COUNT_ALLOCS
_Py_dump_counts(stderr);
#endif
/* dump hash stats */
_PyHash_Fini();

View File

@ -1685,20 +1685,6 @@ sys_getallocatedblocks_impl(PyObject *module)
return _Py_GetAllocatedBlocks();
}
#ifdef COUNT_ALLOCS
/*[clinic input]
sys.getcounts
[clinic start generated code]*/
static PyObject *
sys_getcounts_impl(PyObject *module)
/*[clinic end generated code: output=20df00bc164f43cb input=ad2ec7bda5424953]*/
{
extern PyObject *_Py_get_counts(void);
return _Py_get_counts();
}
#endif
/*[clinic input]
sys._getframe
@ -1879,7 +1865,6 @@ static PyMethodDef sys_methods[] = {
SYS_GETDEFAULTENCODING_METHODDEF
SYS_GETDLOPENFLAGS_METHODDEF
SYS_GETALLOCATEDBLOCKS_METHODDEF
SYS_GETCOUNTS_METHODDEF
#ifdef DYNAMIC_EXECUTION_PROFILE
{"getdxp", _Py_GetDXProfile, METH_VARARGS},
#endif