Add PYTHONMALLOC env var
Issue #26516: * Add PYTHONMALLOC environment variable to set the Python memory allocators and/or install debug hooks. * PyMem_SetupDebugHooks() can now also be used on Python compiled in release mode. * The PYTHONMALLOCSTATS environment variable can now also be used on Python compiled in release mode. It now has no effect if set to an empty string. * In debug mode, debug hooks are now also installed on Python memory allocators when Python is configured without pymalloc.
This commit is contained in:
parent
c877658d1f
commit
34be807ca4
|
@ -85,9 +85,12 @@ for the I/O buffer escapes completely the Python memory manager.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
|
The :envvar:`PYTHONMALLOC` environment variable can be used to configure
|
||||||
|
the memory allocators used by Python.
|
||||||
|
|
||||||
The :envvar:`PYTHONMALLOCSTATS` environment variable can be used to print
|
The :envvar:`PYTHONMALLOCSTATS` environment variable can be used to print
|
||||||
memory allocation statistics every time a new object arena is created, and
|
statistics of the :ref:`pymalloc memory allocator <pymalloc>` every time a
|
||||||
on shutdown.
|
new pymalloc object arena is created, and on shutdown.
|
||||||
|
|
||||||
|
|
||||||
Raw Memory Interface
|
Raw Memory Interface
|
||||||
|
@ -343,25 +346,36 @@ Customize Memory Allocators
|
||||||
- detect write before the start of the buffer (buffer underflow)
|
- detect write before the start of the buffer (buffer underflow)
|
||||||
- detect write after the end of the buffer (buffer overflow)
|
- detect write after the end of the buffer (buffer overflow)
|
||||||
|
|
||||||
The function does nothing if Python is not compiled is debug mode.
|
These hooks are installed by default if Python is compiled in debug
|
||||||
|
mode. The :envvar:`PYTHONMALLOC` environment variable can be used to install
|
||||||
|
debug hooks on a Python compiled in release mode.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.6
|
||||||
|
This function now also works on Python compiled in release mode.
|
||||||
|
|
||||||
|
|
||||||
Customize PyObject Arena Allocator
|
.. _pymalloc:
|
||||||
==================================
|
|
||||||
|
|
||||||
Python has a *pymalloc* allocator for allocations smaller than 512 bytes. This
|
The pymalloc allocator
|
||||||
allocator is optimized for small objects with a short lifetime. It uses memory
|
======================
|
||||||
mappings called "arenas" with a fixed size of 256 KB. It falls back to
|
|
||||||
:c:func:`PyMem_RawMalloc` and :c:func:`PyMem_RawRealloc` for allocations larger
|
|
||||||
than 512 bytes. *pymalloc* is the default allocator used by
|
|
||||||
:c:func:`PyObject_Malloc`.
|
|
||||||
|
|
||||||
The default arena allocator uses the following functions:
|
Python has a *pymalloc* allocator optimized for small objects (smaller or equal
|
||||||
|
to 512 bytes) with a short lifetime. It uses memory mappings called "arenas"
|
||||||
|
with a fixed size of 256 KB. It falls back to :c:func:`PyMem_RawMalloc` and
|
||||||
|
:c:func:`PyMem_RawRealloc` for allocations larger than 512 bytes.
|
||||||
|
|
||||||
|
*pymalloc* is the default allocator of the :c:data:`PYMEM_DOMAIN_OBJ` domain
|
||||||
|
(:c:func:`PyObject_Malloc` & cie).
|
||||||
|
|
||||||
|
The arena allocator uses the following functions:
|
||||||
|
|
||||||
* :c:func:`VirtualAlloc` and :c:func:`VirtualFree` on Windows,
|
* :c:func:`VirtualAlloc` and :c:func:`VirtualFree` on Windows,
|
||||||
* :c:func:`mmap` and :c:func:`munmap` if available,
|
* :c:func:`mmap` and :c:func:`munmap` if available,
|
||||||
* :c:func:`malloc` and :c:func:`free` otherwise.
|
* :c:func:`malloc` and :c:func:`free` otherwise.
|
||||||
|
|
||||||
|
Customize pymalloc Arena Allocator
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
.. c:type:: PyObjectArenaAllocator
|
.. c:type:: PyObjectArenaAllocator
|
||||||
|
|
|
@ -621,6 +621,51 @@ conflict.
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
|
||||||
|
.. envvar:: PYTHONMALLOC
|
||||||
|
|
||||||
|
Set the Python memory allocators and/or install debug hooks.
|
||||||
|
|
||||||
|
Set the family of memory allocators used by Python:
|
||||||
|
|
||||||
|
* ``malloc``: use the :c:func:`malloc` function of the C library
|
||||||
|
for all Python memory allocators (:c:func:`PyMem_RawMalloc`,
|
||||||
|
:c:func:`PyMem_Malloc`, :c:func:`PyObject_Malloc` & cie).
|
||||||
|
* ``pymalloc``: :c:func:`PyObject_Malloc`, :c:func:`PyObject_Calloc` and
|
||||||
|
:c:func:`PyObject_Realloc` use the :ref:`pymalloc allocator <pymalloc>`.
|
||||||
|
Other Python memory allocators (:c:func:`PyMem_RawMalloc`,
|
||||||
|
:c:func:`PyMem_Malloc` & cie) use :c:func:`malloc`.
|
||||||
|
|
||||||
|
Install debug hooks:
|
||||||
|
|
||||||
|
* ``debug``: install debug hooks on top of the default memory allocator
|
||||||
|
* ``malloc_debug``: same than ``malloc`` but also install debug hooks
|
||||||
|
* ``pymalloc_debug``: same than ``malloc`` but also install debug hooks
|
||||||
|
|
||||||
|
See the :c:func:`PyMem_SetupDebugHooks` function for debug hooks on Python
|
||||||
|
memory allocators.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
``pymalloc`` and ``pymalloc_debug`` are not available if Python is
|
||||||
|
configured without ``pymalloc`` support.
|
||||||
|
|
||||||
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
|
|
||||||
|
.. envvar:: PYTHONMALLOCSTATS
|
||||||
|
|
||||||
|
If set to a non-empty string, Python will print statistics of the
|
||||||
|
:ref:`pymalloc memory allocator <pymalloc>` every time a new pymalloc object
|
||||||
|
arena is created, and on shutdown.
|
||||||
|
|
||||||
|
This variable is ignored if the :envvar:`PYTHONMALLOC` environment variable
|
||||||
|
is used to force the :c:func:`malloc` allocator of the C library, or if
|
||||||
|
Python is configured without ``pymalloc`` support.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.6
|
||||||
|
This variable can now also be used on Python compiled in release mode.
|
||||||
|
It now has no effect if set to an empty string.
|
||||||
|
|
||||||
|
|
||||||
Debug-mode variables
|
Debug-mode variables
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -636,9 +681,3 @@ if Python was configured with the ``--with-pydebug`` build option.
|
||||||
|
|
||||||
If set, Python will dump objects and reference counts still alive after
|
If set, Python will dump objects and reference counts still alive after
|
||||||
shutting down the interpreter.
|
shutting down the interpreter.
|
||||||
|
|
||||||
|
|
||||||
.. envvar:: PYTHONMALLOCSTATS
|
|
||||||
|
|
||||||
If set, Python will print memory allocation statistics every time a new
|
|
||||||
object arena is created, and on shutdown.
|
|
||||||
|
|
|
@ -80,6 +80,9 @@ Summary -- Release highlights
|
||||||
PEP written by Carl Meyer
|
PEP written by Carl Meyer
|
||||||
|
|
||||||
|
|
||||||
|
New Features
|
||||||
|
============
|
||||||
|
|
||||||
.. _whatsnew-fstrings:
|
.. _whatsnew-fstrings:
|
||||||
|
|
||||||
PEP 498: Formatted string literals
|
PEP 498: Formatted string literals
|
||||||
|
@ -98,6 +101,34 @@ evaluated at run time, and then formatted using the :func:`format` protocol.
|
||||||
See :pep:`498` and the main documentation at :ref:`f-strings`.
|
See :pep:`498` and the main documentation at :ref:`f-strings`.
|
||||||
|
|
||||||
|
|
||||||
|
PYTHONMALLOC environment variable
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
The new :envvar:`PYTHONMALLOC` environment variable allows to set the Python
|
||||||
|
memory allocators and/or install debug hooks.
|
||||||
|
|
||||||
|
It is now possible to install debug hooks on Python memory allocators on Python
|
||||||
|
compiled in release mode using ``PYTHONMALLOC=debug``. Effects of debug hooks:
|
||||||
|
|
||||||
|
* Newly allocated memory is filled with the byte ``0xCB``
|
||||||
|
* Freed memory is filled with the byte ``0xDB``
|
||||||
|
* Detect violations of Python memory allocator API. For example,
|
||||||
|
:c:func:`PyObject_Free` called on a memory block allocated by
|
||||||
|
:c:func:`PyMem_Malloc`.
|
||||||
|
* Detect write before the start of the buffer (buffer underflow)
|
||||||
|
* Detect write after the end of the buffer (buffer overflow)
|
||||||
|
|
||||||
|
See the :c:func:`PyMem_SetupDebugHooks` function for debug hooks on Python
|
||||||
|
memory allocators.
|
||||||
|
|
||||||
|
It is now also possible to force the usage of the :c:func:`malloc` allocator of
|
||||||
|
the C library for all Python memory allocations using ``PYTHONMALLOC=malloc``.
|
||||||
|
It helps to use external memory debuggers like Valgrind on a Python compiled in
|
||||||
|
release mode.
|
||||||
|
|
||||||
|
(Contributed by Victor Stinner in :issue:`26516`.)
|
||||||
|
|
||||||
|
|
||||||
Other Language Changes
|
Other Language Changes
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,17 @@ PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size);
|
||||||
PyAPI_FUNC(void *) PyMem_RawCalloc(size_t nelem, size_t elsize);
|
PyAPI_FUNC(void *) PyMem_RawCalloc(size_t nelem, size_t elsize);
|
||||||
PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size);
|
PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size);
|
||||||
PyAPI_FUNC(void) PyMem_RawFree(void *ptr);
|
PyAPI_FUNC(void) PyMem_RawFree(void *ptr);
|
||||||
|
|
||||||
|
/* Configure the Python memory allocators. Pass NULL to use default
|
||||||
|
allocators. */
|
||||||
|
PyAPI_FUNC(int) _PyMem_SetupAllocators(const char *opt);
|
||||||
|
|
||||||
|
#ifdef WITH_PYMALLOC
|
||||||
|
PyAPI_FUNC(int) _PyMem_PymallocEnabled(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif /* !Py_LIMITED_API */
|
||||||
|
|
||||||
|
|
||||||
/* BEWARE:
|
/* BEWARE:
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import pickle
|
||||||
import random
|
import random
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import sysconfig
|
||||||
import textwrap
|
import textwrap
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
@ -521,6 +522,7 @@ class SkipitemTest(unittest.TestCase):
|
||||||
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
|
self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords,
|
||||||
(), {}, b'', [42])
|
(), {}, b'', [42])
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(threading, 'Threading required for this test.')
|
@unittest.skipUnless(threading, 'Threading required for this test.')
|
||||||
class TestThreadState(unittest.TestCase):
|
class TestThreadState(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -545,6 +547,7 @@ class TestThreadState(unittest.TestCase):
|
||||||
t.start()
|
t.start()
|
||||||
t.join()
|
t.join()
|
||||||
|
|
||||||
|
|
||||||
class Test_testcapi(unittest.TestCase):
|
class Test_testcapi(unittest.TestCase):
|
||||||
def test__testcapi(self):
|
def test__testcapi(self):
|
||||||
for name in dir(_testcapi):
|
for name in dir(_testcapi):
|
||||||
|
@ -553,5 +556,61 @@ class Test_testcapi(unittest.TestCase):
|
||||||
test = getattr(_testcapi, name)
|
test = getattr(_testcapi, name)
|
||||||
test()
|
test()
|
||||||
|
|
||||||
|
|
||||||
|
class MallocTests(unittest.TestCase):
|
||||||
|
ENV = 'debug'
|
||||||
|
|
||||||
|
def check(self, code):
|
||||||
|
with support.SuppressCrashReport():
|
||||||
|
out = assert_python_failure('-c', code, PYTHONMALLOC=self.ENV)
|
||||||
|
stderr = out.err
|
||||||
|
return stderr.decode('ascii', 'replace')
|
||||||
|
|
||||||
|
def test_buffer_overflow(self):
|
||||||
|
out = self.check('import _testcapi; _testcapi.pymem_buffer_overflow()')
|
||||||
|
regex = (r"Debug memory block at address p=0x[0-9a-f]+: API 'm'\n"
|
||||||
|
r" 16 bytes originally requested\n"
|
||||||
|
r" The 7 pad bytes at p-7 are FORBIDDENBYTE, as expected.\n"
|
||||||
|
r" The 8 pad bytes at tail=0x[0-9a-f]+ are not all FORBIDDENBYTE \(0x[0-9a-f]{2}\):\n"
|
||||||
|
r" at tail\+0: 0x78 \*\*\* OUCH\n"
|
||||||
|
r" at tail\+1: 0xfb\n"
|
||||||
|
r" at tail\+2: 0xfb\n"
|
||||||
|
r" at tail\+3: 0xfb\n"
|
||||||
|
r" at tail\+4: 0xfb\n"
|
||||||
|
r" at tail\+5: 0xfb\n"
|
||||||
|
r" at tail\+6: 0xfb\n"
|
||||||
|
r" at tail\+7: 0xfb\n"
|
||||||
|
r" The block was made by call #[0-9]+ to debug malloc/realloc.\n"
|
||||||
|
r" Data at p: cb cb cb cb cb cb cb cb cb cb cb cb cb cb cb cb\n"
|
||||||
|
r"Fatal Python error: bad trailing pad byte")
|
||||||
|
self.assertRegex(out, regex)
|
||||||
|
|
||||||
|
def test_api_misuse(self):
|
||||||
|
out = self.check('import _testcapi; _testcapi.pymem_api_misuse()')
|
||||||
|
regex = (r"Debug memory block at address p=0x[0-9a-f]+: API 'm'\n"
|
||||||
|
r" 16 bytes originally requested\n"
|
||||||
|
r" The 7 pad bytes at p-7 are FORBIDDENBYTE, as expected.\n"
|
||||||
|
r" The 8 pad bytes at tail=0x[0-9a-f]+ are FORBIDDENBYTE, as expected.\n"
|
||||||
|
r" The block was made by call #[0-9]+ to debug malloc/realloc.\n"
|
||||||
|
r" Data at p: .*\n"
|
||||||
|
r"Fatal Python error: bad ID: Allocated using API 'm', verified using API 'r'\n")
|
||||||
|
self.assertRegex(out, regex)
|
||||||
|
|
||||||
|
|
||||||
|
class MallocDebugTests(MallocTests):
|
||||||
|
ENV = 'malloc_debug'
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(sysconfig.get_config_var('WITH_PYMALLOC') == 1,
|
||||||
|
'need pymalloc')
|
||||||
|
class PymallocDebugTests(MallocTests):
|
||||||
|
ENV = 'pymalloc_debug'
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(Py_DEBUG, 'need Py_DEBUG')
|
||||||
|
class DefaultMallocDebugTests(MallocTests):
|
||||||
|
ENV = ''
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
13
Misc/NEWS
13
Misc/NEWS
|
@ -10,6 +10,19 @@ Release date: tba
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #26516: Add :envvar`PYTHONMALLOC` environment variable to set the
|
||||||
|
Python memory allocators and/or install debug hooks.
|
||||||
|
|
||||||
|
- Issue #26516: The :c:func`PyMem_SetupDebugHooks` function can now also be
|
||||||
|
used on Python compiled in release mode.
|
||||||
|
|
||||||
|
- Issue #26516: The :envvar:`PYTHONMALLOCSTATS` environment variable can now
|
||||||
|
also be used on Python compiled in release mode. It now has no effect if
|
||||||
|
set to an empty string.
|
||||||
|
|
||||||
|
- Issue #26516: In debug mode, debug hooks are now also installed on Python
|
||||||
|
memory allocators when Python is configured without pymalloc.
|
||||||
|
|
||||||
- Issue #26464: Fix str.translate() when string is ASCII and first replacements
|
- Issue #26464: Fix str.translate() when string is ASCII and first replacements
|
||||||
removes character, but next replacement uses a non-ASCII character or a
|
removes character, but next replacement uses a non-ASCII character or a
|
||||||
string longer than 1 character. Regression introduced in Python 3.5.0.
|
string longer than 1 character. Regression introduced in Python 3.5.0.
|
||||||
|
|
|
@ -2,6 +2,9 @@ This document describes some caveats about the use of Valgrind with
|
||||||
Python. Valgrind is used periodically by Python developers to try
|
Python. Valgrind is used periodically by Python developers to try
|
||||||
to ensure there are no memory leaks or invalid memory reads/writes.
|
to ensure there are no memory leaks or invalid memory reads/writes.
|
||||||
|
|
||||||
|
UPDATE: Python 3.6 now supports PYTHONMALLOC=malloc environment variable which
|
||||||
|
can be used to force the usage of the malloc() allocator of the C library.
|
||||||
|
|
||||||
If you don't want to read about the details of using Valgrind, there
|
If you don't want to read about the details of using Valgrind, there
|
||||||
are still two things you must do to suppress the warnings. First,
|
are still two things you must do to suppress the warnings. First,
|
||||||
you must use a suppressions file. One is supplied in
|
you must use a suppressions file. One is supplied in
|
||||||
|
|
|
@ -3616,6 +3616,33 @@ get_recursion_depth(PyObject *self, PyObject *args)
|
||||||
return PyLong_FromLong(tstate->recursion_depth - 1);
|
return PyLong_FromLong(tstate->recursion_depth - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
pymem_buffer_overflow(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
char *buffer;
|
||||||
|
|
||||||
|
/* Deliberate buffer overflow to check that PyMem_Free() detects
|
||||||
|
the overflow when debug hooks are installed. */
|
||||||
|
buffer = PyMem_Malloc(16);
|
||||||
|
buffer[16] = 'x';
|
||||||
|
PyMem_Free(buffer);
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
pymem_api_misuse(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
char *buffer;
|
||||||
|
|
||||||
|
/* Deliberate misusage of Python allocators:
|
||||||
|
allococate with PyMem but release with PyMem_Raw. */
|
||||||
|
buffer = PyMem_Malloc(16);
|
||||||
|
PyMem_RawFree(buffer);
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef TestMethods[] = {
|
static PyMethodDef TestMethods[] = {
|
||||||
{"raise_exception", raise_exception, METH_VARARGS},
|
{"raise_exception", raise_exception, METH_VARARGS},
|
||||||
|
@ -3798,6 +3825,8 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"PyTime_AsMilliseconds", test_PyTime_AsMilliseconds, METH_VARARGS},
|
{"PyTime_AsMilliseconds", test_PyTime_AsMilliseconds, METH_VARARGS},
|
||||||
{"PyTime_AsMicroseconds", test_PyTime_AsMicroseconds, METH_VARARGS},
|
{"PyTime_AsMicroseconds", test_PyTime_AsMicroseconds, METH_VARARGS},
|
||||||
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
|
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
|
||||||
|
{"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS},
|
||||||
|
{"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -93,14 +93,15 @@ static const char usage_5[] =
|
||||||
" The default module search path uses %s.\n"
|
" The default module search path uses %s.\n"
|
||||||
"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n"
|
"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n"
|
||||||
"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n"
|
"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n"
|
||||||
"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n\
|
"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n";
|
||||||
";
|
static const char usage_6[] =
|
||||||
static const char usage_6[] = "\
|
"PYTHONHASHSEED: if this variable is set to 'random', a random value is used\n"
|
||||||
PYTHONHASHSEED: if this variable is set to 'random', a random value is used\n\
|
" to seed the hashes of str, bytes and datetime objects. It can also be\n"
|
||||||
to seed the hashes of str, bytes and datetime objects. It can also be\n\
|
" set to an integer in the range [0,4294967295] to get hash values with a\n"
|
||||||
set to an integer in the range [0,4294967295] to get hash values with a\n\
|
" predictable seed.\n"
|
||||||
predictable seed.\n\
|
"PYTHONMALLOC: set the Python memory allocators and/or install debug hooks\n"
|
||||||
";
|
" on Python memory allocators. Use PYTHONMALLOC=debug to install debug\n"
|
||||||
|
" hooks.\n";
|
||||||
|
|
||||||
static int
|
static int
|
||||||
usage(int exitcode, const wchar_t* program)
|
usage(int exitcode, const wchar_t* program)
|
||||||
|
@ -341,6 +342,7 @@ Py_Main(int argc, wchar_t **argv)
|
||||||
int help = 0;
|
int help = 0;
|
||||||
int version = 0;
|
int version = 0;
|
||||||
int saw_unbuffered_flag = 0;
|
int saw_unbuffered_flag = 0;
|
||||||
|
char *opt;
|
||||||
PyCompilerFlags cf;
|
PyCompilerFlags cf;
|
||||||
PyObject *warning_option = NULL;
|
PyObject *warning_option = NULL;
|
||||||
PyObject *warning_options = NULL;
|
PyObject *warning_options = NULL;
|
||||||
|
@ -365,6 +367,13 @@ Py_Main(int argc, wchar_t **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opt = Py_GETENV("PYTHONMALLOC");
|
||||||
|
if (_PyMem_SetupAllocators(opt) < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error in PYTHONMALLOC: unknown allocator \"%s\"!\n", opt);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
Py_HashRandomizationFlag = 1;
|
Py_HashRandomizationFlag = 1;
|
||||||
_PyRandom_Init();
|
_PyRandom_Init();
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,19 @@
|
||||||
|
|
||||||
/* Python's malloc wrappers (see pymem.h) */
|
/* Python's malloc wrappers (see pymem.h) */
|
||||||
|
|
||||||
#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */
|
/*
|
||||||
|
* Basic types
|
||||||
|
* I don't care if these are defined in <sys/types.h> or elsewhere. Axiom.
|
||||||
|
*/
|
||||||
|
#undef uchar
|
||||||
|
#define uchar unsigned char /* assuming == 8 bits */
|
||||||
|
|
||||||
|
#undef uint
|
||||||
|
#define uint unsigned int /* assuming >= 16 bits */
|
||||||
|
|
||||||
|
#undef uptr
|
||||||
|
#define uptr Py_uintptr_t
|
||||||
|
|
||||||
/* Forward declaration */
|
/* Forward declaration */
|
||||||
static void* _PyMem_DebugMalloc(void *ctx, size_t size);
|
static void* _PyMem_DebugMalloc(void *ctx, size_t size);
|
||||||
static void* _PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize);
|
static void* _PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize);
|
||||||
|
@ -11,7 +23,6 @@ static void* _PyMem_DebugRealloc(void *ctx, void *ptr, size_t size);
|
||||||
|
|
||||||
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);
|
||||||
#endif
|
|
||||||
|
|
||||||
#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? */
|
||||||
|
@ -147,7 +158,6 @@ _PyObject_ArenaFree(void *ctx, void *ptr, size_t size)
|
||||||
#endif
|
#endif
|
||||||
#define PYMEM_FUNCS PYRAW_FUNCS
|
#define PYMEM_FUNCS PYRAW_FUNCS
|
||||||
|
|
||||||
#ifdef PYMALLOC_DEBUG
|
|
||||||
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 */
|
||||||
char api_id;
|
char api_id;
|
||||||
|
@ -164,10 +174,9 @@ static struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PYDBG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree
|
#define PYDBG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree
|
||||||
#endif
|
|
||||||
|
|
||||||
static PyMemAllocatorEx _PyMem_Raw = {
|
static PyMemAllocatorEx _PyMem_Raw = {
|
||||||
#ifdef PYMALLOC_DEBUG
|
#ifdef Py_DEBUG
|
||||||
&_PyMem_Debug.raw, PYDBG_FUNCS
|
&_PyMem_Debug.raw, PYDBG_FUNCS
|
||||||
#else
|
#else
|
||||||
NULL, PYRAW_FUNCS
|
NULL, PYRAW_FUNCS
|
||||||
|
@ -175,7 +184,7 @@ static PyMemAllocatorEx _PyMem_Raw = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMemAllocatorEx _PyMem = {
|
static PyMemAllocatorEx _PyMem = {
|
||||||
#ifdef PYMALLOC_DEBUG
|
#ifdef Py_DEBUG
|
||||||
&_PyMem_Debug.mem, PYDBG_FUNCS
|
&_PyMem_Debug.mem, PYDBG_FUNCS
|
||||||
#else
|
#else
|
||||||
NULL, PYMEM_FUNCS
|
NULL, PYMEM_FUNCS
|
||||||
|
@ -183,13 +192,71 @@ static PyMemAllocatorEx _PyMem = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyMemAllocatorEx _PyObject = {
|
static PyMemAllocatorEx _PyObject = {
|
||||||
#ifdef PYMALLOC_DEBUG
|
#ifdef Py_DEBUG
|
||||||
&_PyMem_Debug.obj, PYDBG_FUNCS
|
&_PyMem_Debug.obj, PYDBG_FUNCS
|
||||||
#else
|
#else
|
||||||
NULL, PYOBJ_FUNCS
|
NULL, PYOBJ_FUNCS
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyMem_SetupAllocators(const char *opt)
|
||||||
|
{
|
||||||
|
if (opt == NULL || *opt == '\0') {
|
||||||
|
/* PYTHONMALLOC is empty or is not set or ignored (-E/-I command line
|
||||||
|
options): use default allocators */
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
# 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) {
|
||||||
|
PyMem_SetupDebugHooks();
|
||||||
|
}
|
||||||
|
else if (strcmp(opt, "malloc") == 0 || strcmp(opt, "malloc_debug") == 0)
|
||||||
|
{
|
||||||
|
PyMemAllocatorEx alloc = {NULL, PYRAW_FUNCS};
|
||||||
|
|
||||||
|
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
|
||||||
|
else if (strcmp(opt, "pymalloc") == 0
|
||||||
|
|| strcmp(opt, "pymalloc_debug") == 0)
|
||||||
|
{
|
||||||
|
PyMemAllocatorEx mem_alloc = {NULL, PYRAW_FUNCS};
|
||||||
|
PyMemAllocatorEx obj_alloc = {NULL, PYOBJ_FUNCS};
|
||||||
|
|
||||||
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &mem_alloc);
|
||||||
|
PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &mem_alloc);
|
||||||
|
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &obj_alloc);
|
||||||
|
|
||||||
|
if (strcmp(opt, "pymalloc_debug") == 0)
|
||||||
|
PyMem_SetupDebugHooks();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
|
/* unknown allocator */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#undef PYRAW_FUNCS
|
#undef PYRAW_FUNCS
|
||||||
#undef PYMEM_FUNCS
|
#undef PYMEM_FUNCS
|
||||||
#undef PYOBJ_FUNCS
|
#undef PYOBJ_FUNCS
|
||||||
|
@ -205,12 +272,34 @@ static PyObjectArenaAllocator _PyObject_Arena = {NULL,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
_PyMem_DebugEnabled(void)
|
||||||
|
{
|
||||||
|
return (_PyObject.malloc == _PyMem_DebugMalloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_PYMALLOC
|
||||||
|
int
|
||||||
|
_PyMem_PymallocEnabled(void)
|
||||||
|
{
|
||||||
|
if (_PyMem_DebugEnabled()) {
|
||||||
|
return (_PyMem_Debug.obj.alloc.malloc == _PyObject_Malloc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (_PyObject.malloc == _PyObject_Malloc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
PyMem_SetupDebugHooks(void)
|
PyMem_SetupDebugHooks(void)
|
||||||
{
|
{
|
||||||
#ifdef PYMALLOC_DEBUG
|
|
||||||
PyMemAllocatorEx alloc;
|
PyMemAllocatorEx alloc;
|
||||||
|
|
||||||
|
/* hooks already installed */
|
||||||
|
if (_PyMem_DebugEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
alloc.malloc = _PyMem_DebugMalloc;
|
alloc.malloc = _PyMem_DebugMalloc;
|
||||||
alloc.calloc = _PyMem_DebugCalloc;
|
alloc.calloc = _PyMem_DebugCalloc;
|
||||||
alloc.realloc = _PyMem_DebugRealloc;
|
alloc.realloc = _PyMem_DebugRealloc;
|
||||||
|
@ -233,7 +322,6 @@ PyMem_SetupDebugHooks(void)
|
||||||
PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc);
|
PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &_PyMem_Debug.obj.alloc);
|
||||||
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
|
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -264,7 +352,6 @@ PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
|
||||||
case PYMEM_DOMAIN_OBJ: _PyObject = *allocator; break;
|
case PYMEM_DOMAIN_OBJ: _PyObject = *allocator; break;
|
||||||
/* ignore unknown domain */
|
/* ignore unknown domain */
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -642,22 +729,6 @@ static int running_on_valgrind = -1;
|
||||||
#define SIMPLELOCK_LOCK(lock) /* acquire released lock */
|
#define SIMPLELOCK_LOCK(lock) /* acquire released lock */
|
||||||
#define SIMPLELOCK_UNLOCK(lock) /* release acquired lock */
|
#define SIMPLELOCK_UNLOCK(lock) /* release acquired lock */
|
||||||
|
|
||||||
/*
|
|
||||||
* Basic types
|
|
||||||
* I don't care if these are defined in <sys/types.h> or elsewhere. Axiom.
|
|
||||||
*/
|
|
||||||
#undef uchar
|
|
||||||
#define uchar unsigned char /* assuming == 8 bits */
|
|
||||||
|
|
||||||
#undef uint
|
|
||||||
#define uint unsigned int /* assuming >= 16 bits */
|
|
||||||
|
|
||||||
#undef ulong
|
|
||||||
#define ulong unsigned long /* assuming >= 32 bits */
|
|
||||||
|
|
||||||
#undef uptr
|
|
||||||
#define uptr Py_uintptr_t
|
|
||||||
|
|
||||||
/* When you say memory, my mind reasons in terms of (pointers to) blocks */
|
/* When you say memory, my mind reasons in terms of (pointers to) blocks */
|
||||||
typedef uchar block;
|
typedef uchar block;
|
||||||
|
|
||||||
|
@ -949,11 +1020,15 @@ new_arena(void)
|
||||||
struct arena_object* arenaobj;
|
struct arena_object* arenaobj;
|
||||||
uint excess; /* number of bytes above pool alignment */
|
uint excess; /* number of bytes above pool alignment */
|
||||||
void *address;
|
void *address;
|
||||||
|
static int debug_stats = -1;
|
||||||
|
|
||||||
#ifdef PYMALLOC_DEBUG
|
if (debug_stats == -1) {
|
||||||
if (Py_GETENV("PYTHONMALLOCSTATS"))
|
char *opt = Py_GETENV("PYTHONMALLOCSTATS");
|
||||||
|
debug_stats = (opt != NULL && *opt != '\0');
|
||||||
|
}
|
||||||
|
if (debug_stats)
|
||||||
_PyObject_DebugMallocStats(stderr);
|
_PyObject_DebugMallocStats(stderr);
|
||||||
#endif
|
|
||||||
if (unused_arena_objects == NULL) {
|
if (unused_arena_objects == NULL) {
|
||||||
uint i;
|
uint i;
|
||||||
uint numarenas;
|
uint numarenas;
|
||||||
|
@ -1709,7 +1784,7 @@ _Py_GetAllocatedBlocks(void)
|
||||||
|
|
||||||
#endif /* WITH_PYMALLOC */
|
#endif /* WITH_PYMALLOC */
|
||||||
|
|
||||||
#ifdef PYMALLOC_DEBUG
|
|
||||||
/*==========================================================================*/
|
/*==========================================================================*/
|
||||||
/* A x-platform debugging allocator. This doesn't manage memory directly,
|
/* A x-platform debugging allocator. This doesn't manage memory directly,
|
||||||
* it wraps a real allocator, adding extra debugging info to the memory blocks.
|
* it wraps a real allocator, adding extra debugging info to the memory blocks.
|
||||||
|
@ -1767,31 +1842,6 @@ write_size_t(void *p, size_t n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Py_DEBUG
|
|
||||||
/* Is target in the list? The list is traversed via the nextpool pointers.
|
|
||||||
* The list may be NULL-terminated, or circular. Return 1 if target is in
|
|
||||||
* list, else 0.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
pool_is_in_list(const poolp target, poolp list)
|
|
||||||
{
|
|
||||||
poolp origlist = list;
|
|
||||||
assert(target != NULL);
|
|
||||||
if (list == NULL)
|
|
||||||
return 0;
|
|
||||||
do {
|
|
||||||
if (target == list)
|
|
||||||
return 1;
|
|
||||||
list = list->nextpool;
|
|
||||||
} while (list != NULL && list != origlist);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define pool_is_in_list(X, Y) 1
|
|
||||||
|
|
||||||
#endif /* Py_DEBUG */
|
|
||||||
|
|
||||||
/* Let S = sizeof(size_t). The debug malloc asks for 4*S extra bytes and
|
/* Let S = sizeof(size_t). The debug malloc asks for 4*S extra bytes and
|
||||||
fills them with useful stuff, here calling the underlying malloc's result p:
|
fills them with useful stuff, here calling the underlying malloc's result p:
|
||||||
|
|
||||||
|
@ -2106,7 +2156,6 @@ _PyObject_DebugDumpAddress(const void *p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* PYMALLOC_DEBUG */
|
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
printone(FILE *out, const char* msg, size_t value)
|
printone(FILE *out, const char* msg, size_t value)
|
||||||
|
@ -2158,8 +2207,30 @@ _PyDebugAllocatorStats(FILE *out,
|
||||||
(void)printone(out, buf2, num_blocks * sizeof_block);
|
(void)printone(out, buf2, num_blocks * sizeof_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef WITH_PYMALLOC
|
#ifdef WITH_PYMALLOC
|
||||||
|
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
/* Is target in the list? The list is traversed via the nextpool pointers.
|
||||||
|
* The list may be NULL-terminated, or circular. Return 1 if target is in
|
||||||
|
* list, else 0.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
pool_is_in_list(const poolp target, poolp list)
|
||||||
|
{
|
||||||
|
poolp origlist = list;
|
||||||
|
assert(target != NULL);
|
||||||
|
if (list == NULL)
|
||||||
|
return 0;
|
||||||
|
do {
|
||||||
|
if (target == list)
|
||||||
|
return 1;
|
||||||
|
list = list->nextpool;
|
||||||
|
} while (list != NULL && list != origlist);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Print summary info to "out" about the state of pymalloc's structures.
|
/* Print summary info to "out" about the state of pymalloc's structures.
|
||||||
* In Py_DEBUG mode, also perform some expensive internal consistency
|
* In Py_DEBUG mode, also perform some expensive internal consistency
|
||||||
* checks.
|
* checks.
|
||||||
|
@ -2233,7 +2304,9 @@ _PyObject_DebugMallocStats(FILE *out)
|
||||||
|
|
||||||
if (p->ref.count == 0) {
|
if (p->ref.count == 0) {
|
||||||
/* currently unused */
|
/* currently unused */
|
||||||
|
#ifdef Py_DEBUG
|
||||||
assert(pool_is_in_list(p, arenas[i].freepools));
|
assert(pool_is_in_list(p, arenas[i].freepools));
|
||||||
|
#endif
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
++numpools[sz];
|
++numpools[sz];
|
||||||
|
@ -2273,9 +2346,8 @@ _PyObject_DebugMallocStats(FILE *out)
|
||||||
quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size);
|
quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size);
|
||||||
}
|
}
|
||||||
fputc('\n', out);
|
fputc('\n', out);
|
||||||
#ifdef PYMALLOC_DEBUG
|
if (_PyMem_DebugEnabled())
|
||||||
(void)printone(out, "# times object malloc called", serialno);
|
(void)printone(out, "# times object malloc called", serialno);
|
||||||
#endif
|
|
||||||
(void)printone(out, "# arenas allocated total", ntimes_arena_allocated);
|
(void)printone(out, "# arenas allocated total", ntimes_arena_allocated);
|
||||||
(void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas);
|
(void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas);
|
||||||
(void)printone(out, "# arenas highwater mark", narenas_highwater);
|
(void)printone(out, "# arenas highwater mark", narenas_highwater);
|
||||||
|
@ -2303,6 +2375,7 @@ _PyObject_DebugMallocStats(FILE *out)
|
||||||
|
|
||||||
#endif /* #ifdef WITH_PYMALLOC */
|
#endif /* #ifdef WITH_PYMALLOC */
|
||||||
|
|
||||||
|
|
||||||
#ifdef Py_USING_MEMORY_DEBUGGER
|
#ifdef Py_USING_MEMORY_DEBUGGER
|
||||||
/* Make this function last so gcc won't inline it since the definition is
|
/* Make this function last so gcc won't inline it since the definition is
|
||||||
* after the reference.
|
* after the reference.
|
||||||
|
|
|
@ -24,6 +24,9 @@ main(int argc, char **argv)
|
||||||
int i, res;
|
int i, res;
|
||||||
char *oldloc;
|
char *oldloc;
|
||||||
|
|
||||||
|
/* Force malloc() allocator to bootstrap Python */
|
||||||
|
(void)_PyMem_SetupAllocators("malloc");
|
||||||
|
|
||||||
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));
|
||||||
if (!argv_copy || !argv_copy2) {
|
if (!argv_copy || !argv_copy2) {
|
||||||
|
@ -62,7 +65,13 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
setlocale(LC_ALL, oldloc);
|
setlocale(LC_ALL, oldloc);
|
||||||
PyMem_RawFree(oldloc);
|
PyMem_RawFree(oldloc);
|
||||||
|
|
||||||
res = Py_Main(argc, argv_copy);
|
res = Py_Main(argc, argv_copy);
|
||||||
|
|
||||||
|
/* Force again malloc() allocator to release memory blocks allocated
|
||||||
|
before Py_Main() */
|
||||||
|
(void)_PyMem_SetupAllocators("malloc");
|
||||||
|
|
||||||
for (i = 0; i < argc; i++) {
|
for (i = 0; i < argc; i++) {
|
||||||
PyMem_RawFree(argv_copy2[i]);
|
PyMem_RawFree(argv_copy2[i]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -702,9 +702,12 @@ Py_FinalizeEx(void)
|
||||||
if (Py_GETENV("PYTHONDUMPREFS"))
|
if (Py_GETENV("PYTHONDUMPREFS"))
|
||||||
_Py_PrintReferenceAddresses(stderr);
|
_Py_PrintReferenceAddresses(stderr);
|
||||||
#endif /* Py_TRACE_REFS */
|
#endif /* Py_TRACE_REFS */
|
||||||
#ifdef PYMALLOC_DEBUG
|
#ifdef WITH_PYMALLOC
|
||||||
if (Py_GETENV("PYTHONMALLOCSTATS"))
|
if (_PyMem_PymallocEnabled()) {
|
||||||
_PyObject_DebugMallocStats(stderr);
|
char *opt = Py_GETENV("PYTHONMALLOCSTATS");
|
||||||
|
if (opt != NULL && *opt != '\0')
|
||||||
|
_PyObject_DebugMallocStats(stderr);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
call_ll_exitfuncs();
|
call_ll_exitfuncs();
|
||||||
|
|
|
@ -1151,8 +1151,10 @@ static PyObject *
|
||||||
sys_debugmallocstats(PyObject *self, PyObject *args)
|
sys_debugmallocstats(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
#ifdef WITH_PYMALLOC
|
#ifdef WITH_PYMALLOC
|
||||||
_PyObject_DebugMallocStats(stderr);
|
if (_PyMem_PymallocEnabled()) {
|
||||||
fputc('\n', stderr);
|
_PyObject_DebugMallocStats(stderr);
|
||||||
|
fputc('\n', stderr);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
_PyObject_DebugTypeStats(stderr);
|
_PyObject_DebugTypeStats(stderr);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue