mirror of
https://github.com/python/cpython.git
synced 2025-08-31 05:58:33 +00:00
bpo-32030: Rework memory allocators (#4625)
* Fix _PyMem_SetupAllocators("debug"): always restore allocators to the defaults, rather than only caling _PyMem_SetupDebugHooks(). * Add _PyMem_SetDefaultAllocator() helper to set the "default" allocator. * Add _PyMem_GetAllocatorsName(): get the name of the allocators * main() now uses debug hooks on memory allocators if Py_DEBUG is defined, rather than calling directly malloc() * Document default memory allocators in C API documentation * _Py_InitializeCore() now fails with a fatal user error if PYTHONMALLOC value is an unknown memory allocator, instead of failing with a fatal internal error. * Add new tests on the PYTHONMALLOC environment variable * Add support.with_pymalloc() * Add the _testcapi.WITH_PYMALLOC constant and expose it as support.with_pymalloc(). * sysconfig.get_config_var('WITH_PYMALLOC') doesn't work on Windows, so replace it with support.with_pymalloc(). * pythoninfo: add _testcapi collector for pymem
This commit is contained in:
parent
c15bb49d71
commit
5d39e04290
14 changed files with 405 additions and 171 deletions
|
@ -100,9 +100,10 @@ The following function sets are wrappers to the system allocator. These
|
||||||
functions are thread-safe, the :term:`GIL <global interpreter lock>` does not
|
functions are thread-safe, the :term:`GIL <global interpreter lock>` does not
|
||||||
need to be held.
|
need to be held.
|
||||||
|
|
||||||
The default raw memory block allocator uses the following functions:
|
The :ref:`default raw memory allocator <default-memory-allocators>` uses
|
||||||
:c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc` and :c:func:`free`; call
|
the following functions: :c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc`
|
||||||
``malloc(1)`` (or ``calloc(1, 1)``) when requesting zero bytes.
|
and :c:func:`free`; call ``malloc(1)`` (or ``calloc(1, 1)``) when requesting
|
||||||
|
zero bytes.
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
@ -165,7 +166,8 @@ The following function sets, modeled after the ANSI C standard, but specifying
|
||||||
behavior when requesting zero bytes, are available for allocating and releasing
|
behavior when requesting zero bytes, are available for allocating and releasing
|
||||||
memory from the Python heap.
|
memory from the Python heap.
|
||||||
|
|
||||||
By default, these functions use :ref:`pymalloc memory allocator <pymalloc>`.
|
The :ref:`default memory allocator <default-memory-allocators>` uses the
|
||||||
|
:ref:`pymalloc memory allocator <pymalloc>`.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
@ -270,7 +272,8 @@ The following function sets, modeled after the ANSI C standard, but specifying
|
||||||
behavior when requesting zero bytes, are available for allocating and releasing
|
behavior when requesting zero bytes, are available for allocating and releasing
|
||||||
memory from the Python heap.
|
memory from the Python heap.
|
||||||
|
|
||||||
By default, these functions use :ref:`pymalloc memory allocator <pymalloc>`.
|
The :ref:`default object allocator <default-memory-allocators>` uses the
|
||||||
|
:ref:`pymalloc memory allocator <pymalloc>`.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
@ -326,6 +329,31 @@ By default, these functions use :ref:`pymalloc memory allocator <pymalloc>`.
|
||||||
If *p* is *NULL*, no operation is performed.
|
If *p* is *NULL*, no operation is performed.
|
||||||
|
|
||||||
|
|
||||||
|
.. _default-memory-allocators:
|
||||||
|
|
||||||
|
Default Memory Allocators
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Default memory allocators:
|
||||||
|
|
||||||
|
=============================== ==================== ================== ===================== ====================
|
||||||
|
Configuration Name PyMem_RawMalloc PyMem_Malloc PyObject_Malloc
|
||||||
|
=============================== ==================== ================== ===================== ====================
|
||||||
|
Release build ``"pymalloc"`` ``malloc`` ``pymalloc`` ``pymalloc``
|
||||||
|
Debug build ``"pymalloc_debug"`` ``malloc`` + debug ``pymalloc`` + debug ``pymalloc`` + debug
|
||||||
|
Release build, without pymalloc ``"malloc"`` ``malloc`` ``malloc`` ``malloc``
|
||||||
|
Release build, without pymalloc ``"malloc_debug"`` ``malloc`` + debug ``malloc`` + debug ``malloc`` + debug
|
||||||
|
=============================== ==================== ================== ===================== ====================
|
||||||
|
|
||||||
|
Legend:
|
||||||
|
|
||||||
|
* Name: value for :envvar:`PYTHONMALLOC` environment variable
|
||||||
|
* ``malloc``: system allocators from the standard C library, C functions:
|
||||||
|
:c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc` and :c:func:`free`
|
||||||
|
* ``pymalloc``: :ref:`pymalloc memory allocator <pymalloc>`
|
||||||
|
* "+ debug": with debug hooks installed by :c:func:`PyMem_SetupDebugHooks`
|
||||||
|
|
||||||
|
|
||||||
Customize Memory Allocators
|
Customize Memory Allocators
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
@ -431,7 +459,8 @@ Customize Memory Allocators
|
||||||
displayed if :mod:`tracemalloc` is tracing Python memory allocations and the
|
displayed if :mod:`tracemalloc` is tracing Python memory allocations and the
|
||||||
memory block was traced.
|
memory block was traced.
|
||||||
|
|
||||||
These hooks are installed by default if Python is compiled in debug
|
These hooks are :ref:`installed by default <default-memory-allocators>` if
|
||||||
|
Python is compiled in debug
|
||||||
mode. The :envvar:`PYTHONMALLOC` environment variable can be used to install
|
mode. The :envvar:`PYTHONMALLOC` environment variable can be used to install
|
||||||
debug hooks on a Python compiled in release mode.
|
debug hooks on a Python compiled in release mode.
|
||||||
|
|
||||||
|
@ -453,9 +482,9 @@ to 512 bytes) with a short lifetime. It uses memory mappings called "arenas"
|
||||||
with a fixed size of 256 KiB. It falls back to :c:func:`PyMem_RawMalloc` and
|
with a fixed size of 256 KiB. It falls back to :c:func:`PyMem_RawMalloc` and
|
||||||
:c:func:`PyMem_RawRealloc` for allocations larger than 512 bytes.
|
:c:func:`PyMem_RawRealloc` for allocations larger than 512 bytes.
|
||||||
|
|
||||||
*pymalloc* is the default allocator of the :c:data:`PYMEM_DOMAIN_MEM` (ex:
|
*pymalloc* is the :ref:`default allocator <default-memory-allocators>` of the
|
||||||
:c:func:`PyMem_Malloc`) and :c:data:`PYMEM_DOMAIN_OBJ` (ex:
|
:c:data:`PYMEM_DOMAIN_MEM` (ex: :c:func:`PyMem_Malloc`) and
|
||||||
:c:func:`PyObject_Malloc`) domains.
|
:c:data:`PYMEM_DOMAIN_OBJ` (ex: :c:func:`PyObject_Malloc`) domains.
|
||||||
|
|
||||||
The arena allocator uses the following functions:
|
The arena allocator uses the following functions:
|
||||||
|
|
||||||
|
|
|
@ -687,6 +687,8 @@ conflict.
|
||||||
|
|
||||||
Set the family of memory allocators used by Python:
|
Set the family of memory allocators used by Python:
|
||||||
|
|
||||||
|
* ``default``: use the :ref:`default memory allocators
|
||||||
|
<default-memory-allocators>`.
|
||||||
* ``malloc``: use the :c:func:`malloc` function of the C library
|
* ``malloc``: use the :c:func:`malloc` function of the C library
|
||||||
for all domains (:c:data:`PYMEM_DOMAIN_RAW`, :c:data:`PYMEM_DOMAIN_MEM`,
|
for all domains (:c:data:`PYMEM_DOMAIN_RAW`, :c:data:`PYMEM_DOMAIN_MEM`,
|
||||||
:c:data:`PYMEM_DOMAIN_OBJ`).
|
:c:data:`PYMEM_DOMAIN_OBJ`).
|
||||||
|
@ -696,20 +698,17 @@ conflict.
|
||||||
|
|
||||||
Install debug hooks:
|
Install debug hooks:
|
||||||
|
|
||||||
* ``debug``: install debug hooks on top of the default memory allocator
|
* ``debug``: install debug hooks on top of the :ref:`default memory
|
||||||
|
allocators <default-memory-allocators>`.
|
||||||
* ``malloc_debug``: same as ``malloc`` but also install debug hooks
|
* ``malloc_debug``: same as ``malloc`` but also install debug hooks
|
||||||
* ``pymalloc_debug``: same as ``pymalloc`` but also install debug hooks
|
* ``pymalloc_debug``: same as ``pymalloc`` but also install debug hooks
|
||||||
|
|
||||||
When Python is compiled in release mode, the default is ``pymalloc``. When
|
See the :ref:`default memory allocators <default-memory-allocators>` and the
|
||||||
compiled in debug mode, the default is ``pymalloc_debug`` and the debug hooks
|
:c:func:`PyMem_SetupDebugHooks` function (install debug hooks on Python
|
||||||
are used automatically.
|
memory allocators).
|
||||||
|
|
||||||
If Python is configured without ``pymalloc`` support, ``pymalloc`` and
|
.. versionchanged:: 3.7
|
||||||
``pymalloc_debug`` are not available, the default is ``malloc`` in release
|
Added the ``"default"`` allocator.
|
||||||
mode and ``malloc_debug`` in debug mode.
|
|
||||||
|
|
||||||
See the :c:func:`PyMem_SetupDebugHooks` function for debug hooks on Python
|
|
||||||
memory allocators.
|
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ PyAPI_FUNC(void) PyMem_RawFree(void *ptr);
|
||||||
allocators. */
|
allocators. */
|
||||||
PyAPI_FUNC(int) _PyMem_SetupAllocators(const char *opt);
|
PyAPI_FUNC(int) _PyMem_SetupAllocators(const char *opt);
|
||||||
|
|
||||||
|
/* Try to get the allocators name set by _PyMem_SetupAllocators(). */
|
||||||
|
PyAPI_FUNC(const char*) _PyMem_GetAllocatorsName(void);
|
||||||
|
|
||||||
#ifdef WITH_PYMALLOC
|
#ifdef WITH_PYMALLOC
|
||||||
PyAPI_FUNC(int) _PyMem_PymallocEnabled(void);
|
PyAPI_FUNC(int) _PyMem_PymallocEnabled(void);
|
||||||
#endif
|
#endif
|
||||||
|
@ -230,7 +233,12 @@ PyAPI_FUNC(void) PyMem_SetupDebugHooks(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Py_BUILD_CORE
|
#ifdef Py_BUILD_CORE
|
||||||
PyAPI_FUNC(void) _PyMem_GetDefaultRawAllocator(PyMemAllocatorEx *alloc);
|
/* Set the memory allocator of the specified domain to the default.
|
||||||
|
Save the old allocator into *old_alloc if it's non-NULL.
|
||||||
|
Return on success, or return -1 if the domain is unknown. */
|
||||||
|
PyAPI_FUNC(int) _PyMem_SetDefaultAllocator(
|
||||||
|
PyMemAllocatorDomain domain,
|
||||||
|
PyMemAllocatorEx *old_alloc);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -56,6 +56,14 @@ def copy_attributes(info_add, obj, name_fmt, attributes, *, formatter=None):
|
||||||
info_add(name, value)
|
info_add(name, value)
|
||||||
|
|
||||||
|
|
||||||
|
def copy_attr(info_add, name, mod, attr_name):
|
||||||
|
try:
|
||||||
|
value = getattr(mod, attr_name)
|
||||||
|
except AttributeError:
|
||||||
|
return
|
||||||
|
info_add(name, value)
|
||||||
|
|
||||||
|
|
||||||
def call_func(info_add, name, mod, func_name, *, formatter=None):
|
def call_func(info_add, name, mod, func_name, *, formatter=None):
|
||||||
try:
|
try:
|
||||||
func = getattr(mod, func_name)
|
func = getattr(mod, func_name)
|
||||||
|
@ -168,11 +176,10 @@ def collect_os(info_add):
|
||||||
call_func(info_add, 'os.gid', os, 'getgid')
|
call_func(info_add, 'os.gid', os, 'getgid')
|
||||||
call_func(info_add, 'os.uname', os, 'uname')
|
call_func(info_add, 'os.uname', os, 'uname')
|
||||||
|
|
||||||
if hasattr(os, 'getgroups'):
|
def format_groups(groups):
|
||||||
groups = os.getgroups()
|
return ', '.join(map(str, groups))
|
||||||
groups = map(str, groups)
|
|
||||||
groups = ', '.join(groups)
|
call_func(info_add, 'os.groups', os, 'getgroups', formatter=format_groups)
|
||||||
info_add("os.groups", groups)
|
|
||||||
|
|
||||||
if hasattr(os, 'getlogin'):
|
if hasattr(os, 'getlogin'):
|
||||||
try:
|
try:
|
||||||
|
@ -184,11 +191,7 @@ def collect_os(info_add):
|
||||||
else:
|
else:
|
||||||
info_add("os.login", login)
|
info_add("os.login", login)
|
||||||
|
|
||||||
if hasattr(os, 'cpu_count'):
|
call_func(info_add, 'os.cpu_count', os, 'cpu_count')
|
||||||
cpu_count = os.cpu_count()
|
|
||||||
if cpu_count:
|
|
||||||
info_add('os.cpu_count', cpu_count)
|
|
||||||
|
|
||||||
call_func(info_add, 'os.loadavg', os, 'getloadavg')
|
call_func(info_add, 'os.loadavg', os, 'getloadavg')
|
||||||
|
|
||||||
# Get environment variables: filter to list
|
# Get environment variables: filter to list
|
||||||
|
@ -219,7 +222,9 @@ def collect_os(info_add):
|
||||||
)
|
)
|
||||||
for name, value in os.environ.items():
|
for name, value in os.environ.items():
|
||||||
uname = name.upper()
|
uname = name.upper()
|
||||||
if (uname in ENV_VARS or uname.startswith(("PYTHON", "LC_"))
|
if (uname in ENV_VARS
|
||||||
|
# Copy PYTHON* and LC_* variables
|
||||||
|
or uname.startswith(("PYTHON", "LC_"))
|
||||||
# Visual Studio: VS140COMNTOOLS
|
# Visual Studio: VS140COMNTOOLS
|
||||||
or (uname.startswith("VS") and uname.endswith("COMNTOOLS"))):
|
or (uname.startswith("VS") and uname.endswith("COMNTOOLS"))):
|
||||||
info_add('os.environ[%s]' % name, value)
|
info_add('os.environ[%s]' % name, value)
|
||||||
|
@ -313,12 +318,10 @@ def collect_time(info_add):
|
||||||
)
|
)
|
||||||
copy_attributes(info_add, time, 'time.%s', attributes)
|
copy_attributes(info_add, time, 'time.%s', attributes)
|
||||||
|
|
||||||
if not hasattr(time, 'get_clock_info'):
|
if hasattr(time, 'get_clock_info'):
|
||||||
return
|
for clock in ('time', 'perf_counter'):
|
||||||
|
tinfo = time.get_clock_info(clock)
|
||||||
for clock in ('time', 'perf_counter'):
|
info_add('time.%s' % clock, tinfo)
|
||||||
tinfo = time.get_clock_info(clock)
|
|
||||||
info_add('time.%s' % clock, tinfo)
|
|
||||||
|
|
||||||
|
|
||||||
def collect_sysconfig(info_add):
|
def collect_sysconfig(info_add):
|
||||||
|
@ -331,7 +334,6 @@ def collect_sysconfig(info_add):
|
||||||
'CCSHARED',
|
'CCSHARED',
|
||||||
'CFLAGS',
|
'CFLAGS',
|
||||||
'CFLAGSFORSHARED',
|
'CFLAGSFORSHARED',
|
||||||
'PY_LDFLAGS',
|
|
||||||
'CONFIG_ARGS',
|
'CONFIG_ARGS',
|
||||||
'HOST_GNU_TYPE',
|
'HOST_GNU_TYPE',
|
||||||
'MACHDEP',
|
'MACHDEP',
|
||||||
|
@ -339,6 +341,7 @@ def collect_sysconfig(info_add):
|
||||||
'OPT',
|
'OPT',
|
||||||
'PY_CFLAGS',
|
'PY_CFLAGS',
|
||||||
'PY_CFLAGS_NODIST',
|
'PY_CFLAGS_NODIST',
|
||||||
|
'PY_LDFLAGS',
|
||||||
'Py_DEBUG',
|
'Py_DEBUG',
|
||||||
'Py_ENABLE_SHARED',
|
'Py_ENABLE_SHARED',
|
||||||
'SHELL',
|
'SHELL',
|
||||||
|
@ -422,6 +425,16 @@ def collect_decimal(info_add):
|
||||||
copy_attributes(info_add, _decimal, '_decimal.%s', attributes)
|
copy_attributes(info_add, _decimal, '_decimal.%s', attributes)
|
||||||
|
|
||||||
|
|
||||||
|
def collect_testcapi(info_add):
|
||||||
|
try:
|
||||||
|
import _testcapi
|
||||||
|
except ImportError:
|
||||||
|
return
|
||||||
|
|
||||||
|
call_func(info_add, 'pymem.allocator', _testcapi, 'pymem_getallocatorsname')
|
||||||
|
copy_attr(info_add, 'pymem.with_pymalloc', _testcapi, 'WITH_PYMALLOC')
|
||||||
|
|
||||||
|
|
||||||
def collect_info(info):
|
def collect_info(info):
|
||||||
error = False
|
error = False
|
||||||
info_add = info.add
|
info_add = info.add
|
||||||
|
@ -444,6 +457,7 @@ def collect_info(info):
|
||||||
collect_zlib,
|
collect_zlib,
|
||||||
collect_expat,
|
collect_expat,
|
||||||
collect_decimal,
|
collect_decimal,
|
||||||
|
collect_testcapi,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
collect_func(info_add)
|
collect_func(info_add)
|
||||||
|
|
|
@ -2848,3 +2848,8 @@ class SaveSignals:
|
||||||
def restore(self):
|
def restore(self):
|
||||||
for signum, handler in self.handlers.items():
|
for signum, handler in self.handlers.items():
|
||||||
self.signal.signal(signum, handler)
|
self.signal.signal(signum, handler)
|
||||||
|
|
||||||
|
|
||||||
|
def with_pymalloc():
|
||||||
|
import _testcapi
|
||||||
|
return _testcapi.WITH_PYMALLOC
|
||||||
|
|
|
@ -654,8 +654,7 @@ class PyMemMallocDebugTests(PyMemDebugTests):
|
||||||
PYTHONMALLOC = 'malloc_debug'
|
PYTHONMALLOC = 'malloc_debug'
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(sysconfig.get_config_var('WITH_PYMALLOC') == 1,
|
@unittest.skipUnless(support.with_pymalloc(), 'need pymalloc')
|
||||||
'need pymalloc')
|
|
||||||
class PyMemPymallocDebugTests(PyMemDebugTests):
|
class PyMemPymallocDebugTests(PyMemDebugTests):
|
||||||
PYTHONMALLOC = 'pymalloc_debug'
|
PYTHONMALLOC = 'pymalloc_debug'
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import sysconfig
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from test import support
|
from test import support
|
||||||
|
@ -559,10 +560,14 @@ class CmdLineTest(unittest.TestCase):
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
code = "import _testcapi; _testcapi.pymem_api_misuse()"
|
code = "import _testcapi; print(_testcapi.pymem_getallocatorsname())"
|
||||||
with support.SuppressCrashReport():
|
with support.SuppressCrashReport():
|
||||||
out = self.run_xdev("-c", code, check_exitcode=False)
|
out = self.run_xdev("-c", code, check_exitcode=False)
|
||||||
self.assertIn("Debug memory block at address p=", out)
|
if support.with_pymalloc():
|
||||||
|
alloc_name = "pymalloc_debug"
|
||||||
|
else:
|
||||||
|
alloc_name = "malloc_debug"
|
||||||
|
self.assertEqual(out, alloc_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import faulthandler
|
import faulthandler
|
||||||
|
@ -573,6 +578,49 @@ class CmdLineTest(unittest.TestCase):
|
||||||
out = self.run_xdev("-c", code)
|
out = self.run_xdev("-c", code)
|
||||||
self.assertEqual(out, "True")
|
self.assertEqual(out, "True")
|
||||||
|
|
||||||
|
def check_pythonmalloc(self, env_var, name):
|
||||||
|
code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())'
|
||||||
|
env = dict(os.environ)
|
||||||
|
if env_var is not None:
|
||||||
|
env['PYTHONMALLOC'] = env_var
|
||||||
|
else:
|
||||||
|
env.pop('PYTHONMALLOC', None)
|
||||||
|
args = (sys.executable, '-c', code)
|
||||||
|
proc = subprocess.run(args,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True,
|
||||||
|
env=env)
|
||||||
|
self.assertEqual(proc.stdout.rstrip(), name)
|
||||||
|
self.assertEqual(proc.returncode, 0)
|
||||||
|
|
||||||
|
def test_pythonmalloc(self):
|
||||||
|
# Test the PYTHONMALLOC environment variable
|
||||||
|
pydebug = hasattr(sys, "gettotalrefcount")
|
||||||
|
pymalloc = support.with_pymalloc()
|
||||||
|
if pymalloc:
|
||||||
|
default_name = 'pymalloc_debug' if pydebug else 'pymalloc'
|
||||||
|
default_name_debug = 'pymalloc_debug'
|
||||||
|
else:
|
||||||
|
default_name = 'malloc_debug' if pydebug else 'malloc'
|
||||||
|
default_name_debug = 'malloc_debug'
|
||||||
|
|
||||||
|
tests = [
|
||||||
|
(None, default_name),
|
||||||
|
('debug', default_name_debug),
|
||||||
|
('malloc', 'malloc'),
|
||||||
|
('malloc_debug', 'malloc_debug'),
|
||||||
|
]
|
||||||
|
if pymalloc:
|
||||||
|
tests.extend((
|
||||||
|
('pymalloc', 'pymalloc'),
|
||||||
|
('pymalloc_debug', 'pymalloc_debug'),
|
||||||
|
))
|
||||||
|
|
||||||
|
for env_var, name in tests:
|
||||||
|
with self.subTest(env_var=env_var, name=name):
|
||||||
|
self.check_pythonmalloc(env_var, name)
|
||||||
|
|
||||||
|
|
||||||
class IgnoreEnvironmentTest(unittest.TestCase):
|
class IgnoreEnvironmentTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -753,8 +753,15 @@ class SysModuleTest(unittest.TestCase):
|
||||||
@unittest.skipUnless(hasattr(sys, "getallocatedblocks"),
|
@unittest.skipUnless(hasattr(sys, "getallocatedblocks"),
|
||||||
"sys.getallocatedblocks unavailable on this build")
|
"sys.getallocatedblocks unavailable on this build")
|
||||||
def test_getallocatedblocks(self):
|
def test_getallocatedblocks(self):
|
||||||
|
try:
|
||||||
|
import _testcapi
|
||||||
|
except ImportError:
|
||||||
|
with_pymalloc = support.with_pymalloc()
|
||||||
|
else:
|
||||||
|
alloc_name = _testcapi.pymem_getallocatorsname()
|
||||||
|
with_pymalloc = (alloc_name in ('pymalloc', 'pymalloc_debug'))
|
||||||
|
|
||||||
# Some sanity checks
|
# Some sanity checks
|
||||||
with_pymalloc = sysconfig.get_config_var('WITH_PYMALLOC')
|
|
||||||
a = sys.getallocatedblocks()
|
a = sys.getallocatedblocks()
|
||||||
self.assertIs(type(a), int)
|
self.assertIs(type(a), int)
|
||||||
if with_pymalloc:
|
if with_pymalloc:
|
||||||
|
|
|
@ -4104,6 +4104,19 @@ pymem_malloc_without_gil(PyObject *self, PyObject *args)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
test_pymem_getallocatorsname(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
const char *name = _PyMem_GetAllocatorsName();
|
||||||
|
if (name == NULL) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "cannot get allocators name");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyUnicode_FromString(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
pyobject_malloc_without_gil(PyObject *self, PyObject *args)
|
pyobject_malloc_without_gil(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
|
@ -4624,6 +4637,7 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS},
|
{"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS},
|
||||||
{"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
|
{"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
|
||||||
{"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS},
|
{"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS},
|
||||||
|
{"pymem_getallocatorsname", test_pymem_getallocatorsname, METH_NOARGS},
|
||||||
{"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS},
|
{"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS},
|
||||||
{"tracemalloc_track", tracemalloc_track, METH_VARARGS},
|
{"tracemalloc_track", tracemalloc_track, METH_VARARGS},
|
||||||
{"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS},
|
{"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS},
|
||||||
|
@ -5115,6 +5129,11 @@ PyInit__testcapi(void)
|
||||||
PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type);
|
PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type);
|
||||||
|
|
||||||
PyModule_AddIntConstant(m, "the_number_three", 3);
|
PyModule_AddIntConstant(m, "the_number_three", 3);
|
||||||
|
#ifdef WITH_PYMALLOC
|
||||||
|
PyModule_AddObject(m, "WITH_PYMALLOC", Py_True);
|
||||||
|
#else
|
||||||
|
PyModule_AddObject(m, "WITH_PYMALLOC", Py_False);
|
||||||
|
#endif
|
||||||
|
|
||||||
TestError = PyErr_NewException("_testcapi.error", NULL, NULL);
|
TestError = PyErr_NewException("_testcapi.error", NULL, NULL);
|
||||||
Py_INCREF(TestError);
|
Py_INCREF(TestError);
|
||||||
|
|
|
@ -475,11 +475,9 @@ pymain_free_impl(_PyMain *pymain)
|
||||||
static void
|
static void
|
||||||
pymain_free(_PyMain *pymain)
|
pymain_free(_PyMain *pymain)
|
||||||
{
|
{
|
||||||
/* Force malloc() memory allocator */
|
/* Force the allocator used by pymain_parse_cmdline_envvars() */
|
||||||
PyMemAllocatorEx old_alloc, raw_alloc;
|
PyMemAllocatorEx old_alloc;
|
||||||
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||||
_PyMem_GetDefaultRawAllocator(&raw_alloc);
|
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc);
|
|
||||||
|
|
||||||
pymain_free_impl(pymain);
|
pymain_free_impl(pymain);
|
||||||
|
|
||||||
|
@ -1561,17 +1559,14 @@ pymain_parse_cmdline_envvars_impl(_PyMain *pymain)
|
||||||
static int
|
static int
|
||||||
pymain_parse_cmdline_envvars(_PyMain *pymain)
|
pymain_parse_cmdline_envvars(_PyMain *pymain)
|
||||||
{
|
{
|
||||||
/* Force malloc() memory allocator */
|
/* Force default allocator, since pymain_free() must use the same allocator
|
||||||
PyMemAllocatorEx old_alloc, raw_alloc;
|
than this function. */
|
||||||
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
PyMemAllocatorEx old_alloc;
|
||||||
_PyMem_GetDefaultRawAllocator(&raw_alloc);
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc);
|
|
||||||
|
|
||||||
int res = pymain_parse_cmdline_envvars_impl(pymain);
|
int res = pymain_parse_cmdline_envvars_impl(pymain);
|
||||||
|
|
||||||
/* Restore the old memory allocator */
|
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@ static void _PyMem_DebugFree(void *ctx, void *p);
|
||||||
static void _PyObject_DebugDumpAddress(const void *p);
|
static void _PyObject_DebugDumpAddress(const void *p);
|
||||||
static void _PyMem_DebugCheckAddress(char api_id, const void *p);
|
static void _PyMem_DebugCheckAddress(char api_id, const void *p);
|
||||||
|
|
||||||
|
static void _PyMem_SetupDebugHooksDomain(PyMemAllocatorDomain domain);
|
||||||
|
|
||||||
#if defined(__has_feature) /* Clang */
|
#if defined(__has_feature) /* Clang */
|
||||||
#if __has_feature(address_sanitizer) /* is ASAN enabled? */
|
#if __has_feature(address_sanitizer) /* is ASAN enabled? */
|
||||||
#define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
|
#define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
|
||||||
|
@ -149,14 +151,18 @@ _PyObject_ArenaFree(void *ctx, void *ptr, size_t size)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MALLOC_ALLOC {NULL, _PyMem_RawMalloc, _PyMem_RawCalloc, _PyMem_RawRealloc, _PyMem_RawFree}
|
||||||
#define PYRAW_FUNCS _PyMem_RawMalloc, _PyMem_RawCalloc, _PyMem_RawRealloc, _PyMem_RawFree
|
|
||||||
#ifdef WITH_PYMALLOC
|
#ifdef WITH_PYMALLOC
|
||||||
# define PYOBJ_FUNCS _PyObject_Malloc, _PyObject_Calloc, _PyObject_Realloc, _PyObject_Free
|
# define PYMALLOC_ALLOC {NULL, _PyObject_Malloc, _PyObject_Calloc, _PyObject_Realloc, _PyObject_Free}
|
||||||
#else
|
|
||||||
# define PYOBJ_FUNCS PYRAW_FUNCS
|
|
||||||
#endif
|
#endif
|
||||||
#define PYMEM_FUNCS PYOBJ_FUNCS
|
|
||||||
|
#define PYRAW_ALLOC MALLOC_ALLOC
|
||||||
|
#ifdef WITH_PYMALLOC
|
||||||
|
# define PYOBJ_ALLOC PYMALLOC_ALLOC
|
||||||
|
#else
|
||||||
|
# define PYOBJ_ALLOC MALLOC_ALLOC
|
||||||
|
#endif
|
||||||
|
#define PYMEM_ALLOC PYOBJ_ALLOC
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* We tag each block with an API ID in order to tag API violations */
|
/* We tag each block with an API ID in order to tag API violations */
|
||||||
|
@ -168,103 +174,118 @@ static struct {
|
||||||
debug_alloc_api_t mem;
|
debug_alloc_api_t mem;
|
||||||
debug_alloc_api_t obj;
|
debug_alloc_api_t obj;
|
||||||
} _PyMem_Debug = {
|
} _PyMem_Debug = {
|
||||||
{'r', {NULL, PYRAW_FUNCS}},
|
{'r', PYRAW_ALLOC},
|
||||||
{'m', {NULL, PYMEM_FUNCS}},
|
{'m', PYMEM_ALLOC},
|
||||||
{'o', {NULL, PYOBJ_FUNCS}}
|
{'o', PYOBJ_ALLOC}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PYRAWDBG_FUNCS \
|
#define PYDBGRAW_ALLOC \
|
||||||
_PyMem_DebugRawMalloc, _PyMem_DebugRawCalloc, _PyMem_DebugRawRealloc, _PyMem_DebugRawFree
|
{&_PyMem_Debug.raw, _PyMem_DebugRawMalloc, _PyMem_DebugRawCalloc, _PyMem_DebugRawRealloc, _PyMem_DebugRawFree}
|
||||||
#define PYDBG_FUNCS \
|
#define PYDBGMEM_ALLOC \
|
||||||
_PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree
|
{&_PyMem_Debug.mem, _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree}
|
||||||
|
#define PYDBGOBJ_ALLOC \
|
||||||
|
{&_PyMem_Debug.obj, _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree}
|
||||||
|
|
||||||
static PyMemAllocatorEx _PyMem_Raw = {
|
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
&_PyMem_Debug.raw, PYRAWDBG_FUNCS
|
static PyMemAllocatorEx _PyMem_Raw = PYDBGRAW_ALLOC;
|
||||||
|
static PyMemAllocatorEx _PyMem = PYDBGMEM_ALLOC;
|
||||||
|
static PyMemAllocatorEx _PyObject = PYDBGOBJ_ALLOC;
|
||||||
#else
|
#else
|
||||||
NULL, PYRAW_FUNCS
|
static PyMemAllocatorEx _PyMem_Raw = PYRAW_ALLOC;
|
||||||
|
static PyMemAllocatorEx _PyMem = PYMEM_ALLOC;
|
||||||
|
static PyMemAllocatorEx _PyObject = PYOBJ_ALLOC;
|
||||||
#endif
|
#endif
|
||||||
};
|
|
||||||
|
|
||||||
static PyMemAllocatorEx _PyMem = {
|
|
||||||
#ifdef Py_DEBUG
|
|
||||||
&_PyMem_Debug.mem, PYDBG_FUNCS
|
|
||||||
#else
|
|
||||||
NULL, PYMEM_FUNCS
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static PyMemAllocatorEx _PyObject = {
|
static int
|
||||||
#ifdef Py_DEBUG
|
pymem_set_default_allocator(PyMemAllocatorDomain domain, int debug,
|
||||||
&_PyMem_Debug.obj, PYDBG_FUNCS
|
PyMemAllocatorEx *old_alloc)
|
||||||
#else
|
{
|
||||||
NULL, PYOBJ_FUNCS
|
if (old_alloc != NULL) {
|
||||||
#endif
|
PyMem_GetAllocator(domain, old_alloc);
|
||||||
};
|
}
|
||||||
|
|
||||||
void
|
|
||||||
_PyMem_GetDefaultRawAllocator(PyMemAllocatorEx *alloc_p)
|
PyMemAllocatorEx new_alloc;
|
||||||
|
switch(domain)
|
||||||
|
{
|
||||||
|
case PYMEM_DOMAIN_RAW:
|
||||||
|
new_alloc = (PyMemAllocatorEx)PYRAW_ALLOC;
|
||||||
|
break;
|
||||||
|
case PYMEM_DOMAIN_MEM:
|
||||||
|
new_alloc = (PyMemAllocatorEx)PYMEM_ALLOC;
|
||||||
|
break;
|
||||||
|
case PYMEM_DOMAIN_OBJ:
|
||||||
|
new_alloc = (PyMemAllocatorEx)PYOBJ_ALLOC;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unknown domain */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
PyMem_SetAllocator(domain, &new_alloc);
|
||||||
|
if (debug) {
|
||||||
|
_PyMem_SetupDebugHooksDomain(domain);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyMem_SetDefaultAllocator(PyMemAllocatorDomain domain,
|
||||||
|
PyMemAllocatorEx *old_alloc)
|
||||||
{
|
{
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
PyMemAllocatorEx alloc = {&_PyMem_Debug.raw, PYDBG_FUNCS};
|
const int debug = 1;
|
||||||
#else
|
#else
|
||||||
PyMemAllocatorEx alloc = {NULL, PYRAW_FUNCS};
|
const int debug = 0;
|
||||||
#endif
|
#endif
|
||||||
*alloc_p = alloc;
|
return pymem_set_default_allocator(domain, debug, old_alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyMem_SetupAllocators(const char *opt)
|
_PyMem_SetupAllocators(const char *opt)
|
||||||
{
|
{
|
||||||
if (opt == NULL || *opt == '\0') {
|
if (opt == NULL || *opt == '\0') {
|
||||||
/* PYTHONMALLOC is empty or is not set or ignored (-E/-I command line
|
/* PYTHONMALLOC is empty or is not set or ignored (-E/-I command line
|
||||||
options): use default allocators */
|
options): use default memory allocators */
|
||||||
#ifdef Py_DEBUG
|
opt = "default";
|
||||||
# ifdef WITH_PYMALLOC
|
|
||||||
opt = "pymalloc_debug";
|
|
||||||
# else
|
|
||||||
opt = "malloc_debug";
|
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
/* !Py_DEBUG */
|
|
||||||
# ifdef WITH_PYMALLOC
|
|
||||||
opt = "pymalloc";
|
|
||||||
# else
|
|
||||||
opt = "malloc";
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(opt, "debug") == 0) {
|
if (strcmp(opt, "default") == 0) {
|
||||||
PyMem_SetupDebugHooks();
|
(void)_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, NULL);
|
||||||
|
(void)_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_MEM, NULL);
|
||||||
|
(void)_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_OBJ, NULL);
|
||||||
}
|
}
|
||||||
else if (strcmp(opt, "malloc") == 0 || strcmp(opt, "malloc_debug") == 0)
|
else if (strcmp(opt, "debug") == 0) {
|
||||||
{
|
(void)pymem_set_default_allocator(PYMEM_DOMAIN_RAW, 1, NULL);
|
||||||
PyMemAllocatorEx alloc = {NULL, PYRAW_FUNCS};
|
(void)pymem_set_default_allocator(PYMEM_DOMAIN_MEM, 1, NULL);
|
||||||
|
(void)pymem_set_default_allocator(PYMEM_DOMAIN_OBJ, 1, NULL);
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
|
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
|
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
|
|
||||||
|
|
||||||
if (strcmp(opt, "malloc_debug") == 0)
|
|
||||||
PyMem_SetupDebugHooks();
|
|
||||||
}
|
}
|
||||||
#ifdef WITH_PYMALLOC
|
#ifdef WITH_PYMALLOC
|
||||||
else if (strcmp(opt, "pymalloc") == 0
|
else if (strcmp(opt, "pymalloc") == 0 || strcmp(opt, "pymalloc_debug") == 0) {
|
||||||
|| strcmp(opt, "pymalloc_debug") == 0)
|
PyMemAllocatorEx malloc_alloc = MALLOC_ALLOC;
|
||||||
{
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &malloc_alloc);
|
||||||
PyMemAllocatorEx raw_alloc = {NULL, PYRAW_FUNCS};
|
|
||||||
PyMemAllocatorEx mem_alloc = {NULL, PYMEM_FUNCS};
|
|
||||||
PyMemAllocatorEx obj_alloc = {NULL, PYOBJ_FUNCS};
|
|
||||||
|
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc);
|
PyMemAllocatorEx pymalloc = PYMALLOC_ALLOC;
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &mem_alloc);
|
PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &pymalloc);
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &obj_alloc);
|
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &pymalloc);
|
||||||
|
|
||||||
if (strcmp(opt, "pymalloc_debug") == 0)
|
if (strcmp(opt, "pymalloc_debug") == 0) {
|
||||||
PyMem_SetupDebugHooks();
|
PyMem_SetupDebugHooks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
else if (strcmp(opt, "malloc") == 0 || strcmp(opt, "malloc_debug") == 0) {
|
||||||
|
PyMemAllocatorEx malloc_alloc = MALLOC_ALLOC;
|
||||||
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &malloc_alloc);
|
||||||
|
PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &malloc_alloc);
|
||||||
|
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &malloc_alloc);
|
||||||
|
|
||||||
|
if (strcmp(opt, "malloc_debug") == 0) {
|
||||||
|
PyMem_SetupDebugHooks();
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
/* unknown allocator */
|
/* unknown allocator */
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -272,11 +293,74 @@ _PyMem_SetupAllocators(const char *opt)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef PYRAW_FUNCS
|
|
||||||
#undef PYMEM_FUNCS
|
static int
|
||||||
#undef PYOBJ_FUNCS
|
pymemallocator_eq(PyMemAllocatorEx *a, PyMemAllocatorEx *b)
|
||||||
#undef PYRAWDBG_FUNCS
|
{
|
||||||
#undef PYDBG_FUNCS
|
return (memcmp(a, b, sizeof(PyMemAllocatorEx)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
_PyMem_GetAllocatorsName(void)
|
||||||
|
{
|
||||||
|
PyMemAllocatorEx malloc_alloc = MALLOC_ALLOC;
|
||||||
|
#ifdef WITH_PYMALLOC
|
||||||
|
PyMemAllocatorEx pymalloc = PYMALLOC_ALLOC;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (pymemallocator_eq(&_PyMem_Raw, &malloc_alloc) &&
|
||||||
|
pymemallocator_eq(&_PyMem, &malloc_alloc) &&
|
||||||
|
pymemallocator_eq(&_PyObject, &malloc_alloc))
|
||||||
|
{
|
||||||
|
return "malloc";
|
||||||
|
}
|
||||||
|
#ifdef WITH_PYMALLOC
|
||||||
|
if (pymemallocator_eq(&_PyMem_Raw, &malloc_alloc) &&
|
||||||
|
pymemallocator_eq(&_PyMem, &pymalloc) &&
|
||||||
|
pymemallocator_eq(&_PyObject, &pymalloc))
|
||||||
|
{
|
||||||
|
return "pymalloc";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PyMemAllocatorEx dbg_raw = PYDBGRAW_ALLOC;
|
||||||
|
PyMemAllocatorEx dbg_mem = PYDBGMEM_ALLOC;
|
||||||
|
PyMemAllocatorEx dbg_obj = PYDBGOBJ_ALLOC;
|
||||||
|
|
||||||
|
if (pymemallocator_eq(&_PyMem_Raw, &dbg_raw) &&
|
||||||
|
pymemallocator_eq(&_PyMem, &dbg_mem) &&
|
||||||
|
pymemallocator_eq(&_PyObject, &dbg_obj))
|
||||||
|
{
|
||||||
|
/* Debug hooks installed */
|
||||||
|
if (pymemallocator_eq(&_PyMem_Debug.raw.alloc, &malloc_alloc) &&
|
||||||
|
pymemallocator_eq(&_PyMem_Debug.mem.alloc, &malloc_alloc) &&
|
||||||
|
pymemallocator_eq(&_PyMem_Debug.obj.alloc, &malloc_alloc))
|
||||||
|
{
|
||||||
|
return "malloc_debug";
|
||||||
|
}
|
||||||
|
#ifdef WITH_PYMALLOC
|
||||||
|
if (pymemallocator_eq(&_PyMem_Debug.raw.alloc, &malloc_alloc) &&
|
||||||
|
pymemallocator_eq(&_PyMem_Debug.mem.alloc, &pymalloc) &&
|
||||||
|
pymemallocator_eq(&_PyMem_Debug.obj.alloc, &pymalloc))
|
||||||
|
{
|
||||||
|
return "pymalloc_debug";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef MALLOC_ALLOC
|
||||||
|
#undef PYMALLOC_ALLOC
|
||||||
|
#undef PYRAW_ALLOC
|
||||||
|
#undef PYMEM_ALLOC
|
||||||
|
#undef PYOBJ_ALLOC
|
||||||
|
#undef PYDBGRAW_ALLOC
|
||||||
|
#undef PYDBGMEM_ALLOC
|
||||||
|
#undef PYDBGOBJ_ALLOC
|
||||||
|
|
||||||
|
|
||||||
static PyObjectArenaAllocator _PyObject_Arena = {NULL,
|
static PyObjectArenaAllocator _PyObject_Arena = {NULL,
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
|
@ -307,40 +391,62 @@ _PyMem_PymallocEnabled(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
|
||||||
PyMem_SetupDebugHooks(void)
|
static void
|
||||||
|
_PyMem_SetupDebugHooksDomain(PyMemAllocatorDomain domain)
|
||||||
{
|
{
|
||||||
PyMemAllocatorEx alloc;
|
PyMemAllocatorEx alloc;
|
||||||
|
|
||||||
alloc.malloc = _PyMem_DebugRawMalloc;
|
if (domain == PYMEM_DOMAIN_RAW) {
|
||||||
alloc.calloc = _PyMem_DebugRawCalloc;
|
if (_PyMem_Raw.malloc == _PyMem_DebugRawMalloc) {
|
||||||
alloc.realloc = _PyMem_DebugRawRealloc;
|
return;
|
||||||
alloc.free = _PyMem_DebugRawFree;
|
}
|
||||||
|
|
||||||
if (_PyMem_Raw.malloc != _PyMem_DebugRawMalloc) {
|
|
||||||
alloc.ctx = &_PyMem_Debug.raw;
|
|
||||||
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &_PyMem_Debug.raw.alloc);
|
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &_PyMem_Debug.raw.alloc);
|
||||||
|
alloc.ctx = &_PyMem_Debug.raw;
|
||||||
|
alloc.malloc = _PyMem_DebugRawMalloc;
|
||||||
|
alloc.calloc = _PyMem_DebugRawCalloc;
|
||||||
|
alloc.realloc = _PyMem_DebugRawRealloc;
|
||||||
|
alloc.free = _PyMem_DebugRawFree;
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
|
||||||
}
|
}
|
||||||
|
else if (domain == PYMEM_DOMAIN_MEM) {
|
||||||
|
if (_PyMem.malloc == _PyMem_DebugMalloc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
alloc.malloc = _PyMem_DebugMalloc;
|
|
||||||
alloc.calloc = _PyMem_DebugCalloc;
|
|
||||||
alloc.realloc = _PyMem_DebugRealloc;
|
|
||||||
alloc.free = _PyMem_DebugFree;
|
|
||||||
|
|
||||||
if (_PyMem.malloc != _PyMem_DebugMalloc) {
|
|
||||||
alloc.ctx = &_PyMem_Debug.mem;
|
|
||||||
PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &_PyMem_Debug.mem.alloc);
|
PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &_PyMem_Debug.mem.alloc);
|
||||||
|
alloc.ctx = &_PyMem_Debug.mem;
|
||||||
|
alloc.malloc = _PyMem_DebugMalloc;
|
||||||
|
alloc.calloc = _PyMem_DebugCalloc;
|
||||||
|
alloc.realloc = _PyMem_DebugRealloc;
|
||||||
|
alloc.free = _PyMem_DebugFree;
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
|
PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
|
||||||
}
|
}
|
||||||
|
else if (domain == PYMEM_DOMAIN_OBJ) {
|
||||||
|
if (_PyObject.malloc == _PyMem_DebugMalloc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_PyObject.malloc != _PyMem_DebugMalloc) {
|
|
||||||
alloc.ctx = &_PyMem_Debug.obj;
|
|
||||||
PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc);
|
PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc);
|
||||||
|
alloc.ctx = &_PyMem_Debug.obj;
|
||||||
|
alloc.malloc = _PyMem_DebugMalloc;
|
||||||
|
alloc.calloc = _PyMem_DebugCalloc;
|
||||||
|
alloc.realloc = _PyMem_DebugRealloc;
|
||||||
|
alloc.free = _PyMem_DebugFree;
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
|
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PyMem_SetupDebugHooks(void)
|
||||||
|
{
|
||||||
|
_PyMem_SetupDebugHooksDomain(PYMEM_DOMAIN_RAW);
|
||||||
|
_PyMem_SetupDebugHooksDomain(PYMEM_DOMAIN_MEM);
|
||||||
|
_PyMem_SetupDebugHooksDomain(PYMEM_DOMAIN_OBJ);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
|
PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,12 +33,9 @@ main(int argc, char **argv)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Force malloc() allocator to bootstrap Python */
|
/* Force default allocator, to be able to release memory above
|
||||||
#ifdef Py_DEBUG
|
with a known allocator. */
|
||||||
(void)_PyMem_SetupAllocators("malloc_debug");
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, NULL);
|
||||||
# else
|
|
||||||
(void)_PyMem_SetupAllocators("malloc");
|
|
||||||
# endif
|
|
||||||
|
|
||||||
argv_copy = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1));
|
argv_copy = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1));
|
||||||
argv_copy2 = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1));
|
argv_copy2 = (wchar_t **)PyMem_RawMalloc(sizeof(wchar_t*) * (argc+1));
|
||||||
|
@ -98,13 +95,9 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
status = Py_Main(argc, argv_copy);
|
status = Py_Main(argc, argv_copy);
|
||||||
|
|
||||||
/* Force again malloc() allocator to release memory blocks allocated
|
/* Py_Main() can change PyMem_RawMalloc() allocator, so restore the default
|
||||||
before Py_Main() */
|
to release memory blocks allocated before Py_Main() */
|
||||||
#ifdef Py_DEBUG
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, NULL);
|
||||||
(void)_PyMem_SetupAllocators("malloc_debug");
|
|
||||||
# else
|
|
||||||
(void)_PyMem_SetupAllocators("malloc");
|
|
||||||
# endif
|
|
||||||
|
|
||||||
for (i = 0; i < argc; i++) {
|
for (i = 0; i < argc; i++) {
|
||||||
PyMem_RawFree(argv_copy2[i]);
|
PyMem_RawFree(argv_copy2[i]);
|
||||||
|
|
|
@ -630,7 +630,7 @@ _Py_InitializeCore(const _PyCoreConfig *config)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_PyMem_SetupAllocators(core_config.allocator) < 0) {
|
if (_PyMem_SetupAllocators(core_config.allocator) < 0) {
|
||||||
return _Py_INIT_ERR("Unknown PYTHONMALLOC allocator");
|
return _Py_INIT_USER_ERR("Unknown PYTHONMALLOC allocator");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_PyRuntime.initialized) {
|
if (_PyRuntime.initialized) {
|
||||||
|
|
|
@ -35,8 +35,8 @@ to avoid the expense of doing their own locking).
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_PyInitError
|
static _PyInitError
|
||||||
_PyRuntimeState_Init(_PyRuntimeState *runtime)
|
_PyRuntimeState_Init_impl(_PyRuntimeState *runtime)
|
||||||
{
|
{
|
||||||
memset(runtime, 0, sizeof(*runtime));
|
memset(runtime, 0, sizeof(*runtime));
|
||||||
|
|
||||||
|
@ -59,14 +59,26 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
||||||
return _Py_INIT_OK();
|
return _Py_INIT_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_PyInitError
|
||||||
|
_PyRuntimeState_Init(_PyRuntimeState *runtime)
|
||||||
|
{
|
||||||
|
/* Force default allocator, since _PyRuntimeState_Fini() must
|
||||||
|
use the same allocator than this function. */
|
||||||
|
PyMemAllocatorEx old_alloc;
|
||||||
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||||
|
|
||||||
|
_PyInitError err = _PyRuntimeState_Init_impl(runtime);
|
||||||
|
|
||||||
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
|
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
|
||||||
{
|
{
|
||||||
/* Use the same memory allocator than _PyRuntimeState_Init() */
|
/* Force the allocator used by _PyRuntimeState_Init(). */
|
||||||
PyMemAllocatorEx old_alloc, raw_alloc;
|
PyMemAllocatorEx old_alloc;
|
||||||
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||||
_PyMem_GetDefaultRawAllocator(&raw_alloc);
|
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc);
|
|
||||||
|
|
||||||
if (runtime->interpreters.mutex != NULL) {
|
if (runtime->interpreters.mutex != NULL) {
|
||||||
PyThread_free_lock(runtime->interpreters.mutex);
|
PyThread_free_lock(runtime->interpreters.mutex);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue