mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	Issue #14785: Add sys._debugmallocstats() to help debug low-level memory allocation issues
This commit is contained in:
		
							parent
							
								
									69cf913ba1
								
							
						
					
					
						commit
						49526f48fc
					
				
					 24 changed files with 217 additions and 35 deletions
				
			
		|  | @ -106,6 +106,22 @@ always available. | ||||||
|    This function should be used for internal and specialized purposes only. |    This function should be used for internal and specialized purposes only. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | .. function:: _debugmallocstats() | ||||||
|  | 
 | ||||||
|  |    Print low-level information to stderr about the state of CPython's memory | ||||||
|  |    allocator. | ||||||
|  | 
 | ||||||
|  |    If Python is configured --with-pydebug, it also performs some expensive | ||||||
|  |    internal consistency checks. | ||||||
|  | 
 | ||||||
|  |    .. versionadded:: 3.3 | ||||||
|  | 
 | ||||||
|  |    .. impl-detail:: | ||||||
|  | 
 | ||||||
|  |       This function is specific to CPython.  The exact output format is not | ||||||
|  |       defined here, and may change. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| .. data:: dllhandle | .. data:: dllhandle | ||||||
| 
 | 
 | ||||||
|    Integer specifying the handle of the Python DLL. Availability: Windows. |    Integer specifying the handle of the Python DLL. Availability: Windows. | ||||||
|  |  | ||||||
|  | @ -111,6 +111,7 @@ PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key); | ||||||
| #ifndef Py_LIMITED_API | #ifndef Py_LIMITED_API | ||||||
| int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value); | int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value); | ||||||
| PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *); | PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *); | ||||||
|  | PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  |  | ||||||
|  | @ -110,6 +110,8 @@ PyAPI_FUNC(double) _PyFloat_Unpack8(const unsigned char *p, int le); | ||||||
| /* free list api */ | /* free list api */ | ||||||
| PyAPI_FUNC(int) PyFloat_ClearFreeList(void); | PyAPI_FUNC(int) PyFloat_ClearFreeList(void); | ||||||
| 
 | 
 | ||||||
|  | PyAPI_FUNC(void) _PyFloat_DebugMallocStats(FILE* out); | ||||||
|  | 
 | ||||||
| /* Format the object based on the format_spec, as defined in PEP 3101
 | /* Format the object based on the format_spec, as defined in PEP 3101
 | ||||||
|    (Advanced String Formatting). */ |    (Advanced String Formatting). */ | ||||||
| PyAPI_FUNC(int) _PyFloat_FormatAdvancedWriter( | PyAPI_FUNC(int) _PyFloat_FormatAdvancedWriter( | ||||||
|  |  | ||||||
|  | @ -79,6 +79,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); | ||||||
| 
 | 
 | ||||||
| PyAPI_FUNC(int) PyFrame_ClearFreeList(void); | PyAPI_FUNC(int) PyFrame_ClearFreeList(void); | ||||||
| 
 | 
 | ||||||
|  | PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); | ||||||
|  | 
 | ||||||
| /* Return the line of code the frame is currently executing. */ | /* Return the line of code the frame is currently executing. */ | ||||||
| PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *); | PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -64,6 +64,7 @@ PyAPI_FUNC(PyObject *) PyList_AsTuple(PyObject *); | ||||||
| PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *); | PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *); | ||||||
| 
 | 
 | ||||||
| PyAPI_FUNC(int) PyList_ClearFreeList(void); | PyAPI_FUNC(int) PyList_ClearFreeList(void); | ||||||
|  | PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Macro, trading safety for speed */ | /* Macro, trading safety for speed */ | ||||||
|  |  | ||||||
|  | @ -82,6 +82,11 @@ typedef struct { | ||||||
| 
 | 
 | ||||||
| PyAPI_FUNC(int) PyCFunction_ClearFreeList(void); | PyAPI_FUNC(int) PyCFunction_ClearFreeList(void); | ||||||
| 
 | 
 | ||||||
|  | #ifndef Py_LIMITED_API | ||||||
|  | PyAPI_FUNC(void) _PyCFunction_DebugMallocStats(FILE *out); | ||||||
|  | PyAPI_FUNC(void) _PyMethod_DebugMallocStats(FILE *out); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -977,6 +977,14 @@ PyAPI_DATA(PyObject *) _PyTrash_delete_later; | ||||||
|     else \ |     else \ | ||||||
|         _PyTrash_deposit_object((PyObject*)op); |         _PyTrash_deposit_object((PyObject*)op); | ||||||
| 
 | 
 | ||||||
|  | #ifndef Py_LIMITED_API | ||||||
|  | PyAPI_FUNC(void) | ||||||
|  | _PyDebugAllocatorStats(FILE *out, const char *block_name, int num_blocks, | ||||||
|  |                        size_t sizeof_block); | ||||||
|  | PyAPI_FUNC(void) | ||||||
|  | _PyObject_DebugTypeStats(FILE *out); | ||||||
|  | #endif /* ifndef Py_LIMITED_API */ | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -101,13 +101,15 @@ PyAPI_FUNC(void) PyObject_Free(void *); | ||||||
| 
 | 
 | ||||||
| /* Macros */ | /* Macros */ | ||||||
| #ifdef WITH_PYMALLOC | #ifdef WITH_PYMALLOC | ||||||
|  | #ifndef Py_LIMITED_API | ||||||
|  | PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out); | ||||||
|  | #endif /* #ifndef Py_LIMITED_API */ | ||||||
| #ifdef PYMALLOC_DEBUG   /* WITH_PYMALLOC && PYMALLOC_DEBUG */ | #ifdef PYMALLOC_DEBUG   /* WITH_PYMALLOC && PYMALLOC_DEBUG */ | ||||||
| PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes); | PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes); | ||||||
| PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes); | PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes); | ||||||
| PyAPI_FUNC(void) _PyObject_DebugFree(void *p); | PyAPI_FUNC(void) _PyObject_DebugFree(void *p); | ||||||
| PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p); | PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p); | ||||||
| PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p); | PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p); | ||||||
| PyAPI_FUNC(void) _PyObject_DebugMallocStats(void); |  | ||||||
| PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes); | PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes); | ||||||
| PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes); | PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes); | ||||||
| PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p); | PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p); | ||||||
|  |  | ||||||
|  | @ -101,6 +101,7 @@ PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); | ||||||
| PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); | PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); | ||||||
| 
 | 
 | ||||||
| PyAPI_FUNC(int) PySet_ClearFreeList(void); | PyAPI_FUNC(int) PySet_ClearFreeList(void); | ||||||
|  | PyAPI_FUNC(void) _PySet_DebugMallocStats(FILE *out); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  |  | ||||||
|  | @ -63,6 +63,9 @@ PyAPI_FUNC(void) _PyTuple_MaybeUntrack(PyObject *); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| PyAPI_FUNC(int) PyTuple_ClearFreeList(void); | PyAPI_FUNC(int) PyTuple_ClearFreeList(void); | ||||||
|  | #ifndef Py_LIMITED_API | ||||||
|  | PyAPI_FUNC(void) _PyTuple_DebugMallocStats(FILE *out); | ||||||
|  | #endif /* Py_LIMITED_API */ | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -603,6 +603,12 @@ def test_implementation(self): | ||||||
|         self.assertEqual(sys.implementation.name, |         self.assertEqual(sys.implementation.name, | ||||||
|                          sys.implementation.name.lower()) |                          sys.implementation.name.lower()) | ||||||
| 
 | 
 | ||||||
|  |     def test_debugmallocstats(self): | ||||||
|  |         # Test sys._debugmallocstats() | ||||||
|  |         from test.script_helper import assert_python_ok | ||||||
|  |         args = ['-c', 'import sys; sys._debugmallocstats()'] | ||||||
|  |         ret, out, err = assert_python_ok(*args) | ||||||
|  |         self.assertIn(b"free PyDictObjects", err) | ||||||
| 
 | 
 | ||||||
| class SizeofTest(unittest.TestCase): | class SizeofTest(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -147,6 +147,9 @@ Library | ||||||
| - Issue #14963: Convert contextlib.ExitStack.__exit__ to use an iterative | - Issue #14963: Convert contextlib.ExitStack.__exit__ to use an iterative | ||||||
|   algorithm (Patch by Alon Horev) |   algorithm (Patch by Alon Horev) | ||||||
| 
 | 
 | ||||||
|  | - Issue #14785: Add sys._debugmallocstats() to help debug low-level memory | ||||||
|  |   allocation issues | ||||||
|  | 
 | ||||||
| C-API | C-API | ||||||
| ----- | ----- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -400,6 +400,15 @@ PyMethod_Fini(void) | ||||||
|     (void)PyMethod_ClearFreeList(); |     (void)PyMethod_ClearFreeList(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Print summary info about the state of the optimized allocator */ | ||||||
|  | void | ||||||
|  | _PyMethod_DebugMallocStats(FILE *out) | ||||||
|  | { | ||||||
|  |     _PyDebugAllocatorStats(out, | ||||||
|  |                            "free PyMethodObject", | ||||||
|  |                            numfree, sizeof(PyMethodObject)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* ------------------------------------------------------------------------
 | /* ------------------------------------------------------------------------
 | ||||||
|  * instance method |  * instance method | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | @ -255,6 +255,15 @@ PyDict_ClearFreeList(void) | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Print summary info about the state of the optimized allocator */ | ||||||
|  | void | ||||||
|  | _PyDict_DebugMallocStats(FILE *out) | ||||||
|  | { | ||||||
|  |     _PyDebugAllocatorStats(out, | ||||||
|  |                            "free PyDictObject", numfree, sizeof(PyDictObject)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void | void | ||||||
| PyDict_Fini(void) | PyDict_Fini(void) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -1933,6 +1933,16 @@ PyFloat_Fini(void) | ||||||
|     (void)PyFloat_ClearFreeList(); |     (void)PyFloat_ClearFreeList(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Print summary info about the state of the optimized allocator */ | ||||||
|  | void | ||||||
|  | _PyFloat_DebugMallocStats(FILE *out) | ||||||
|  | { | ||||||
|  |     _PyDebugAllocatorStats(out, | ||||||
|  |                            "free PyFloatObject", | ||||||
|  |                            numfree, sizeof(PyFloatObject)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /*----------------------------------------------------------------------------
 | /*----------------------------------------------------------------------------
 | ||||||
|  * _PyFloat_{Pack,Unpack}{4,8}.  See floatobject.h. |  * _PyFloat_{Pack,Unpack}{4,8}.  See floatobject.h. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | @ -955,3 +955,13 @@ PyFrame_Fini(void) | ||||||
|     Py_XDECREF(builtin_object); |     Py_XDECREF(builtin_object); | ||||||
|     builtin_object = NULL; |     builtin_object = NULL; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /* Print summary info about the state of the optimized allocator */ | ||||||
|  | void | ||||||
|  | _PyFrame_DebugMallocStats(FILE *out) | ||||||
|  | { | ||||||
|  |     _PyDebugAllocatorStats(out, | ||||||
|  |                            "free PyFrameObject", | ||||||
|  |                            numfree, sizeof(PyFrameObject)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -117,6 +117,15 @@ PyList_Fini(void) | ||||||
|     PyList_ClearFreeList(); |     PyList_ClearFreeList(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Print summary info about the state of the optimized allocator */ | ||||||
|  | void | ||||||
|  | _PyList_DebugMallocStats(FILE *out) | ||||||
|  | { | ||||||
|  |     _PyDebugAllocatorStats(out, | ||||||
|  |                            "free PyListObject", | ||||||
|  |                            numfree, sizeof(PyListObject)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| PyObject * | PyObject * | ||||||
| PyList_New(Py_ssize_t size) | PyList_New(Py_ssize_t size) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -338,6 +338,15 @@ PyCFunction_Fini(void) | ||||||
|     (void)PyCFunction_ClearFreeList(); |     (void)PyCFunction_ClearFreeList(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Print summary info about the state of the optimized allocator */ | ||||||
|  | void | ||||||
|  | _PyCFunction_DebugMallocStats(FILE *out) | ||||||
|  | { | ||||||
|  |     _PyDebugAllocatorStats(out, | ||||||
|  |                            "free PyCFunction", | ||||||
|  |                            numfree, sizeof(PyCFunction)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* PyCFunction_New() is now just a macro that calls PyCFunction_NewEx(),
 | /* PyCFunction_New() is now just a macro that calls PyCFunction_NewEx(),
 | ||||||
|    but it's part of the API so we need to keep a function around that |    but it's part of the API so we need to keep a function around that | ||||||
|    existing C extensions can call. |    existing C extensions can call. | ||||||
|  |  | ||||||
|  | @ -1852,6 +1852,18 @@ PyMem_Free(void *p) | ||||||
|     PyMem_FREE(p); |     PyMem_FREE(p); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void | ||||||
|  | _PyObject_DebugTypeStats(FILE *out) | ||||||
|  | { | ||||||
|  |     _PyCFunction_DebugMallocStats(out); | ||||||
|  |     _PyDict_DebugMallocStats(out); | ||||||
|  |     _PyFloat_DebugMallocStats(out); | ||||||
|  |     _PyFrame_DebugMallocStats(out); | ||||||
|  |     _PyList_DebugMallocStats(out); | ||||||
|  |     _PyMethod_DebugMallocStats(out); | ||||||
|  |     _PySet_DebugMallocStats(out); | ||||||
|  |     _PyTuple_DebugMallocStats(out); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| /* These methods are used to control infinite recursion in repr, str, print,
 | /* These methods are used to control infinite recursion in repr, str, print,
 | ||||||
|    etc.  Container objects that may recursively contain themselves, |    etc.  Container objects that may recursively contain themselves, | ||||||
|  |  | ||||||
|  | @ -523,12 +523,10 @@ static struct arena_object* usable_arenas = NULL; | ||||||
| /* Number of arenas allocated that haven't been free()'d. */ | /* Number of arenas allocated that haven't been free()'d. */ | ||||||
| static size_t narenas_currently_allocated = 0; | static size_t narenas_currently_allocated = 0; | ||||||
| 
 | 
 | ||||||
| #ifdef PYMALLOC_DEBUG |  | ||||||
| /* Total number of times malloc() called to allocate an arena. */ | /* Total number of times malloc() called to allocate an arena. */ | ||||||
| static size_t ntimes_arena_allocated = 0; | static size_t ntimes_arena_allocated = 0; | ||||||
| /* High water mark (max value ever seen) for narenas_currently_allocated. */ | /* High water mark (max value ever seen) for narenas_currently_allocated. */ | ||||||
| static size_t narenas_highwater = 0; | static size_t narenas_highwater = 0; | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| /* Allocate a new arena.  If we run out of memory, return NULL.  Else
 | /* Allocate a new arena.  If we run out of memory, return NULL.  Else
 | ||||||
|  * allocate a new arena, and return the address of an arena_object |  * allocate a new arena, and return the address of an arena_object | ||||||
|  | @ -545,7 +543,7 @@ new_arena(void) | ||||||
| 
 | 
 | ||||||
| #ifdef PYMALLOC_DEBUG | #ifdef PYMALLOC_DEBUG | ||||||
|     if (Py_GETENV("PYTHONMALLOCSTATS")) |     if (Py_GETENV("PYTHONMALLOCSTATS")) | ||||||
|         _PyObject_DebugMallocStats(); |         _PyObject_DebugMallocStats(stderr); | ||||||
| #endif | #endif | ||||||
|     if (unused_arena_objects == NULL) { |     if (unused_arena_objects == NULL) { | ||||||
|         uint i; |         uint i; | ||||||
|  | @ -613,11 +611,9 @@ new_arena(void) | ||||||
|     arenaobj->address = (uptr)address; |     arenaobj->address = (uptr)address; | ||||||
| 
 | 
 | ||||||
|     ++narenas_currently_allocated; |     ++narenas_currently_allocated; | ||||||
| #ifdef PYMALLOC_DEBUG |  | ||||||
|     ++ntimes_arena_allocated; |     ++ntimes_arena_allocated; | ||||||
|     if (narenas_currently_allocated > narenas_highwater) |     if (narenas_currently_allocated > narenas_highwater) | ||||||
|         narenas_highwater = narenas_currently_allocated; |         narenas_highwater = narenas_currently_allocated; | ||||||
| #endif |  | ||||||
|     arenaobj->freepools = NULL; |     arenaobj->freepools = NULL; | ||||||
|     /* pool_address <- first pool-aligned address in the arena
 |     /* pool_address <- first pool-aligned address in the arena
 | ||||||
|        nfreepools <- number of whole pools that fit after alignment */ |        nfreepools <- number of whole pools that fit after alignment */ | ||||||
|  | @ -1723,17 +1719,19 @@ _PyObject_DebugDumpAddress(const void *p) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #endif  /* PYMALLOC_DEBUG */ | ||||||
|  | 
 | ||||||
| static size_t | static size_t | ||||||
| printone(const char* msg, size_t value) | printone(FILE *out, const char* msg, size_t value) | ||||||
| { | { | ||||||
|     int i, k; |     int i, k; | ||||||
|     char buf[100]; |     char buf[100]; | ||||||
|     size_t origvalue = value; |     size_t origvalue = value; | ||||||
| 
 | 
 | ||||||
|     fputs(msg, stderr); |     fputs(msg, out); | ||||||
|     for (i = (int)strlen(msg); i < 35; ++i) |     for (i = (int)strlen(msg); i < 35; ++i) | ||||||
|         fputc(' ', stderr); |         fputc(' ', out); | ||||||
|     fputc('=', stderr); |     fputc('=', out); | ||||||
| 
 | 
 | ||||||
|     /* Write the value with commas. */ |     /* Write the value with commas. */ | ||||||
|     i = 22; |     i = 22; | ||||||
|  | @ -1754,17 +1752,33 @@ printone(const char* msg, size_t value) | ||||||
| 
 | 
 | ||||||
|     while (i >= 0) |     while (i >= 0) | ||||||
|         buf[i--] = ' '; |         buf[i--] = ' '; | ||||||
|     fputs(buf, stderr); |     fputs(buf, out); | ||||||
| 
 | 
 | ||||||
|     return origvalue; |     return origvalue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Print summary info to stderr about the state of pymalloc's structures.
 | void | ||||||
|  | _PyDebugAllocatorStats(FILE *out, | ||||||
|  |                        const char *block_name, int num_blocks, size_t sizeof_block) | ||||||
|  | { | ||||||
|  |     char buf1[128]; | ||||||
|  |     char buf2[128]; | ||||||
|  |     PyOS_snprintf(buf1, sizeof(buf1), | ||||||
|  |                   "%d %ss * %zd bytes each", | ||||||
|  |                   num_blocks, block_name, sizeof_block); | ||||||
|  |     PyOS_snprintf(buf2, sizeof(buf2), | ||||||
|  |                   "%48s ", buf1); | ||||||
|  |     (void)printone(out, buf2, num_blocks * sizeof_block); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef WITH_PYMALLOC | ||||||
|  | 
 | ||||||
|  | /* 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. | ||||||
|  */ |  */ | ||||||
| void | void | ||||||
| _PyObject_DebugMallocStats(void) | _PyObject_DebugMallocStats(FILE *out) | ||||||
| { | { | ||||||
|     uint i; |     uint i; | ||||||
|     const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT; |     const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT; | ||||||
|  | @ -1793,7 +1807,7 @@ _PyObject_DebugMallocStats(void) | ||||||
|     size_t total; |     size_t total; | ||||||
|     char buf[128]; |     char buf[128]; | ||||||
| 
 | 
 | ||||||
|     fprintf(stderr, "Small block threshold = %d, in %u size classes.\n", |     fprintf(out, "Small block threshold = %d, in %u size classes.\n", | ||||||
|             SMALL_REQUEST_THRESHOLD, numclasses); |             SMALL_REQUEST_THRESHOLD, numclasses); | ||||||
| 
 | 
 | ||||||
|     for (i = 0; i < numclasses; ++i) |     for (i = 0; i < numclasses; ++i) | ||||||
|  | @ -1847,10 +1861,10 @@ _PyObject_DebugMallocStats(void) | ||||||
|     } |     } | ||||||
|     assert(narenas == narenas_currently_allocated); |     assert(narenas == narenas_currently_allocated); | ||||||
| 
 | 
 | ||||||
|     fputc('\n', stderr); |     fputc('\n', out); | ||||||
|     fputs("class   size   num pools   blocks in use  avail blocks\n" |     fputs("class   size   num pools   blocks in use  avail blocks\n" | ||||||
|           "-----   ----   ---------   -------------  ------------\n", |           "-----   ----   ---------   -------------  ------------\n", | ||||||
|           stderr); |           out); | ||||||
| 
 | 
 | ||||||
|     for (i = 0; i < numclasses; ++i) { |     for (i = 0; i < numclasses; ++i) { | ||||||
|         size_t p = numpools[i]; |         size_t p = numpools[i]; | ||||||
|  | @ -1861,7 +1875,7 @@ _PyObject_DebugMallocStats(void) | ||||||
|             assert(b == 0 && f == 0); |             assert(b == 0 && f == 0); | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         fprintf(stderr, "%5u %6u " |         fprintf(out, "%5u %6u " | ||||||
|                         "%11" PY_FORMAT_SIZE_T "u " |                         "%11" PY_FORMAT_SIZE_T "u " | ||||||
|                         "%15" PY_FORMAT_SIZE_T "u " |                         "%15" PY_FORMAT_SIZE_T "u " | ||||||
|                         "%13" PY_FORMAT_SIZE_T "u\n", |                         "%13" PY_FORMAT_SIZE_T "u\n", | ||||||
|  | @ -1871,35 +1885,36 @@ _PyObject_DebugMallocStats(void) | ||||||
|         pool_header_bytes += p * POOL_OVERHEAD; |         pool_header_bytes += p * POOL_OVERHEAD; | ||||||
|         quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size); |         quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size); | ||||||
|     } |     } | ||||||
|     fputc('\n', stderr); |     fputc('\n', out); | ||||||
|     (void)printone("# times object malloc called", serialno); | #ifdef PYMALLOC_DEBUG | ||||||
| 
 |     (void)printone(out, "# times object malloc called", serialno); | ||||||
|     (void)printone("# arenas allocated total", ntimes_arena_allocated); | #endif | ||||||
|     (void)printone("# arenas reclaimed", ntimes_arena_allocated - narenas); |     (void)printone(out, "# arenas allocated total", ntimes_arena_allocated); | ||||||
|     (void)printone("# arenas highwater mark", narenas_highwater); |     (void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas); | ||||||
|     (void)printone("# arenas allocated current", narenas); |     (void)printone(out, "# arenas highwater mark", narenas_highwater); | ||||||
|  |     (void)printone(out, "# arenas allocated current", narenas); | ||||||
| 
 | 
 | ||||||
|     PyOS_snprintf(buf, sizeof(buf), |     PyOS_snprintf(buf, sizeof(buf), | ||||||
|         "%" PY_FORMAT_SIZE_T "u arenas * %d bytes/arena", |         "%" PY_FORMAT_SIZE_T "u arenas * %d bytes/arena", | ||||||
|         narenas, ARENA_SIZE); |         narenas, ARENA_SIZE); | ||||||
|     (void)printone(buf, narenas * ARENA_SIZE); |     (void)printone(out, buf, narenas * ARENA_SIZE); | ||||||
| 
 | 
 | ||||||
|     fputc('\n', stderr); |     fputc('\n', out); | ||||||
| 
 | 
 | ||||||
|     total = printone("# bytes in allocated blocks", allocated_bytes); |     total = printone(out, "# bytes in allocated blocks", allocated_bytes); | ||||||
|     total += printone("# bytes in available blocks", available_bytes); |     total += printone(out, "# bytes in available blocks", available_bytes); | ||||||
| 
 | 
 | ||||||
|     PyOS_snprintf(buf, sizeof(buf), |     PyOS_snprintf(buf, sizeof(buf), | ||||||
|         "%u unused pools * %d bytes", numfreepools, POOL_SIZE); |         "%u unused pools * %d bytes", numfreepools, POOL_SIZE); | ||||||
|     total += printone(buf, (size_t)numfreepools * POOL_SIZE); |     total += printone(out, buf, (size_t)numfreepools * POOL_SIZE); | ||||||
| 
 | 
 | ||||||
|     total += printone("# bytes lost to pool headers", pool_header_bytes); |     total += printone(out, "# bytes lost to pool headers", pool_header_bytes); | ||||||
|     total += printone("# bytes lost to quantization", quantization); |     total += printone(out, "# bytes lost to quantization", quantization); | ||||||
|     total += printone("# bytes lost to arena alignment", arena_alignment); |     total += printone(out, "# bytes lost to arena alignment", arena_alignment); | ||||||
|     (void)printone("Total", total); |     (void)printone(out, "Total", total); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif  /* PYMALLOC_DEBUG */ | #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
 | ||||||
|  |  | ||||||
|  | @ -1133,6 +1133,16 @@ PySet_Fini(void) | ||||||
|     Py_CLEAR(emptyfrozenset); |     Py_CLEAR(emptyfrozenset); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Print summary info about the state of the optimized allocator */ | ||||||
|  | void | ||||||
|  | _PySet_DebugMallocStats(FILE *out) | ||||||
|  | { | ||||||
|  |     _PyDebugAllocatorStats(out, | ||||||
|  |                            "free PySetObject", | ||||||
|  |                            numfree, sizeof(PySetObject)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| set_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | set_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -45,6 +45,22 @@ show_track(void) | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /* Print summary info about the state of the optimized allocator */ | ||||||
|  | void | ||||||
|  | _PyTuple_DebugMallocStats(FILE *out) | ||||||
|  | { | ||||||
|  | #if PyTuple_MAXSAVESIZE > 0 | ||||||
|  |     int i; | ||||||
|  |     char buf[128]; | ||||||
|  |     for (i = 1; i < PyTuple_MAXSAVESIZE; i++) { | ||||||
|  |         PyOS_snprintf(buf, sizeof(buf), | ||||||
|  |                       "free %d-sized PyTupleObject", i); | ||||||
|  |         _PyDebugAllocatorStats(out, | ||||||
|  |                                buf, | ||||||
|  |                                numfree[i], _PyObject_VAR_SIZE(&PyTuple_Type, i)); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| PyObject * | PyObject * | ||||||
| PyTuple_New(register Py_ssize_t size) | PyTuple_New(register Py_ssize_t size) | ||||||
|  |  | ||||||
|  | @ -642,7 +642,7 @@ Py_Finalize(void) | ||||||
| #endif /* Py_TRACE_REFS */ | #endif /* Py_TRACE_REFS */ | ||||||
| #ifdef PYMALLOC_DEBUG | #ifdef PYMALLOC_DEBUG | ||||||
|     if (Py_GETENV("PYTHONMALLOCSTATS")) |     if (Py_GETENV("PYTHONMALLOCSTATS")) | ||||||
|         _PyObject_DebugMallocStats(); |         _PyObject_DebugMallocStats(stderr); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     call_ll_exitfuncs(); |     call_ll_exitfuncs(); | ||||||
|  |  | ||||||
|  | @ -997,6 +997,27 @@ a 11-tuple where the entries in the tuple are counts of:\n\ | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | static PyObject * | ||||||
|  | sys_debugmallocstats(PyObject *self, PyObject *args) | ||||||
|  | { | ||||||
|  | #ifdef WITH_PYMALLOC | ||||||
|  |     _PyObject_DebugMallocStats(stderr); | ||||||
|  |     fputc('\n', stderr); | ||||||
|  | #endif | ||||||
|  |     _PyObject_DebugTypeStats(stderr); | ||||||
|  | 
 | ||||||
|  |     Py_RETURN_NONE; | ||||||
|  | } | ||||||
|  | PyDoc_STRVAR(debugmallocstats_doc, | ||||||
|  | "_debugmallocstats()\n\
 | ||||||
|  | \n\ | ||||||
|  | Print summary info to stderr about the state of\n\ | ||||||
|  | pymalloc's structures.\n\ | ||||||
|  | \n\ | ||||||
|  | In Py_DEBUG mode, also perform some expensive internal consistency\n\ | ||||||
|  | checks.\n\ | ||||||
|  | "); | ||||||
|  | 
 | ||||||
| #ifdef Py_TRACE_REFS | #ifdef Py_TRACE_REFS | ||||||
| /* Defined in objects.c because it uses static globals if that file */ | /* Defined in objects.c because it uses static globals if that file */ | ||||||
| extern PyObject *_Py_GetObjects(PyObject *, PyObject *); | extern PyObject *_Py_GetObjects(PyObject *, PyObject *); | ||||||
|  | @ -1093,6 +1114,8 @@ static PyMethodDef sys_methods[] = { | ||||||
|     {"settrace",        sys_settrace, METH_O, settrace_doc}, |     {"settrace",        sys_settrace, METH_O, settrace_doc}, | ||||||
|     {"gettrace",        sys_gettrace, METH_NOARGS, gettrace_doc}, |     {"gettrace",        sys_gettrace, METH_NOARGS, gettrace_doc}, | ||||||
|     {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, |     {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, | ||||||
|  |     {"_debugmallocstats", sys_debugmallocstats, METH_VARARGS, | ||||||
|  |      debugmallocstats_doc}, | ||||||
|     {NULL,              NULL}           /* sentinel */ |     {NULL,              NULL}           /* sentinel */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 David Malcolm
						David Malcolm