You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
762 lines
24 KiB
762 lines
24 KiB
diff --git a/Include/dictobject.h b/Include/dictobject.h |
|
index ece01c6..acc1df0 100644 |
|
--- a/Include/dictobject.h |
|
+++ b/Include/dictobject.h |
|
@@ -150,6 +150,8 @@ PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key); |
|
PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item); |
|
PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key); |
|
|
|
+PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out); |
|
+ |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Include/floatobject.h b/Include/floatobject.h |
|
index 54e8825..33c6ac0 100644 |
|
--- a/Include/floatobject.h |
|
+++ b/Include/floatobject.h |
|
@@ -132,6 +132,7 @@ PyAPI_FUNC(PyObject *) _PyFloat_FormatAdvanced(PyObject *obj, |
|
failure. Used in builtin_round in bltinmodule.c. */ |
|
PyAPI_FUNC(PyObject *) _Py_double_round(double x, int ndigits); |
|
|
|
+PyAPI_FUNC(void) _PyFloat_DebugMallocStats(FILE* out); |
|
|
|
|
|
#ifdef __cplusplus |
|
diff --git a/Include/frameobject.h b/Include/frameobject.h |
|
index 17e7679..66d9d8b 100644 |
|
--- a/Include/frameobject.h |
|
+++ b/Include/frameobject.h |
|
@@ -80,6 +80,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); |
|
|
|
PyAPI_FUNC(int) PyFrame_ClearFreeList(void); |
|
|
|
+PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); |
|
+ |
|
/* Return the line of code the frame is currently executing. */ |
|
PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *); |
|
|
|
diff --git a/Include/intobject.h b/Include/intobject.h |
|
index 252eea9..4003736 100644 |
|
--- a/Include/intobject.h |
|
+++ b/Include/intobject.h |
|
@@ -75,6 +75,8 @@ PyAPI_FUNC(PyObject *) _PyInt_FormatAdvanced(PyObject *obj, |
|
char *format_spec, |
|
Py_ssize_t format_spec_len); |
|
|
|
+PyAPI_FUNC(void) _PyInt_DebugMallocStats(FILE *out); |
|
+ |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Include/listobject.h b/Include/listobject.h |
|
index c445873..04664d7 100644 |
|
--- a/Include/listobject.h |
|
+++ b/Include/listobject.h |
|
@@ -62,6 +62,8 @@ PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *); |
|
#define PyList_SET_ITEM(op, i, v) (((PyListObject *)(op))->ob_item[i] = (v)) |
|
#define PyList_GET_SIZE(op) Py_SIZE(op) |
|
|
|
+PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out); |
|
+ |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Include/methodobject.h b/Include/methodobject.h |
|
index 6e160b6..1944517 100644 |
|
--- a/Include/methodobject.h |
|
+++ b/Include/methodobject.h |
|
@@ -87,6 +87,10 @@ typedef struct { |
|
|
|
PyAPI_FUNC(int) PyCFunction_ClearFreeList(void); |
|
|
|
+PyAPI_FUNC(void) _PyCFunction_DebugMallocStats(FILE *out); |
|
+PyAPI_FUNC(void) _PyMethod_DebugMallocStats(FILE *out); |
|
+ |
|
+ |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Include/object.h b/Include/object.h |
|
index afbc68d..ce5febf 100644 |
|
--- a/Include/object.h |
|
+++ b/Include/object.h |
|
@@ -1005,6 +1005,13 @@ PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(void); |
|
_PyTrash_thread_deposit_object((PyObject*)op); \ |
|
} while (0); |
|
|
|
+PyAPI_FUNC(void) |
|
+_PyDebugAllocatorStats(FILE *out, const char *block_name, int num_blocks, |
|
+ size_t sizeof_block); |
|
+ |
|
+PyAPI_FUNC(void) |
|
+_PyObject_DebugTypeStats(FILE *out); |
|
+ |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Include/objimpl.h b/Include/objimpl.h |
|
index 55e83ec..331b456 100644 |
|
--- a/Include/objimpl.h |
|
+++ b/Include/objimpl.h |
|
@@ -101,13 +101,13 @@ PyAPI_FUNC(void) PyObject_Free(void *); |
|
|
|
/* Macros */ |
|
#ifdef WITH_PYMALLOC |
|
+PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out); |
|
#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ |
|
PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes); |
|
PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes); |
|
PyAPI_FUNC(void) _PyObject_DebugFree(void *p); |
|
PyAPI_FUNC(void) _PyObject_DebugDumpAddress(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_DebugReallocApi(char api, void *p, size_t nbytes); |
|
PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p); |
|
diff --git a/Include/setobject.h b/Include/setobject.h |
|
index 52b07d5..143b175 100644 |
|
--- a/Include/setobject.h |
|
+++ b/Include/setobject.h |
|
@@ -93,6 +93,7 @@ PyAPI_FUNC(int) _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, |
|
PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); |
|
PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); |
|
|
|
+PyAPI_FUNC(void) _PySet_DebugMallocStats(FILE *out); |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Include/stringobject.h b/Include/stringobject.h |
|
index 18b5b41..de78d76 100644 |
|
--- a/Include/stringobject.h |
|
+++ b/Include/stringobject.h |
|
@@ -204,6 +204,8 @@ PyAPI_FUNC(PyObject *) _PyBytes_FormatAdvanced(PyObject *obj, |
|
char *format_spec, |
|
Py_ssize_t format_spec_len); |
|
|
|
+PyAPI_FUNC(void) _PyString_DebugMallocStats(FILE *out); |
|
+ |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Include/tupleobject.h b/Include/tupleobject.h |
|
index a5ab733..e233f47 100644 |
|
--- a/Include/tupleobject.h |
|
+++ b/Include/tupleobject.h |
|
@@ -54,7 +54,7 @@ PyAPI_FUNC(void) _PyTuple_MaybeUntrack(PyObject *); |
|
#define PyTuple_SET_ITEM(op, i, v) (((PyTupleObject *)(op))->ob_item[i] = v) |
|
|
|
PyAPI_FUNC(int) PyTuple_ClearFreeList(void); |
|
- |
|
+PyAPI_FUNC(void) _PyTuple_DebugMallocStats(FILE *out); |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h |
|
index 9ab724a..b91250a 100644 |
|
--- a/Include/unicodeobject.h |
|
+++ b/Include/unicodeobject.h |
|
@@ -1406,6 +1406,8 @@ PyAPI_FUNC(int) _PyUnicode_IsAlpha( |
|
Py_UNICODE ch /* Unicode character */ |
|
); |
|
|
|
+PyAPI_FUNC(void) _PyUnicode_DebugMallocStats(FILE *out); |
|
+ |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py |
|
index 82243f3..8f1e1a0 100644 |
|
--- a/Lib/test/test_sys.py |
|
+++ b/Lib/test/test_sys.py |
|
@@ -488,6 +488,32 @@ class SysModuleTest(unittest.TestCase): |
|
p.wait() |
|
self.assertIn(executable, ["''", repr(sys.executable)]) |
|
|
|
+ def test_debugmallocstats(self): |
|
+ # Test sys._debugmallocstats() |
|
+ |
|
+ import subprocess |
|
+ |
|
+ # Verify the default of writing to stderr: |
|
+ p = subprocess.Popen([sys.executable, |
|
+ '-c', 'import sys; sys._debugmallocstats()'], |
|
+ stderr=subprocess.PIPE) |
|
+ out, err = p.communicate() |
|
+ p.wait() |
|
+ self.assertIn("arenas allocated current", err) |
|
+ |
|
+ # Verify that we can redirect the output to a file (not a file-like |
|
+ # object, though): |
|
+ with open('mallocstats.txt', 'w') as out: |
|
+ sys._debugmallocstats(out) |
|
+ result = open('mallocstats.txt').read() |
|
+ self.assertIn("arenas allocated current", result) |
|
+ os.unlink('mallocstats.txt') |
|
+ |
|
+ # Verify that the destination must be a file: |
|
+ with self.assertRaises(TypeError): |
|
+ sys._debugmallocstats(42) |
|
+ |
|
+ |
|
class SizeofTest(unittest.TestCase): |
|
|
|
def setUp(self): |
|
diff --git a/Objects/classobject.c b/Objects/classobject.c |
|
index 2c9c216..2ba7077 100644 |
|
--- a/Objects/classobject.c |
|
+++ b/Objects/classobject.c |
|
@@ -2694,3 +2694,12 @@ PyMethod_Fini(void) |
|
{ |
|
(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)); |
|
+} |
|
diff --git a/Objects/dictobject.c b/Objects/dictobject.c |
|
index ba36b18..b8a5c7f 100644 |
|
--- a/Objects/dictobject.c |
|
+++ b/Objects/dictobject.c |
|
@@ -225,6 +225,15 @@ show_track(void) |
|
static PyDictObject *free_list[PyDict_MAXFREELIST]; |
|
static int numfree = 0; |
|
|
|
+/* Print summary info about the state of the optimized allocator */ |
|
+void |
|
+_PyDict_DebugMallocStats(FILE *out) |
|
+{ |
|
+ _PyDebugAllocatorStats(out, |
|
+ "free PyDictObject", numfree, sizeof(PyDictObject)); |
|
+} |
|
+ |
|
+ |
|
void |
|
PyDict_Fini(void) |
|
{ |
|
diff --git a/Objects/floatobject.c b/Objects/floatobject.c |
|
index ba867ef..533511d 100644 |
|
--- a/Objects/floatobject.c |
|
+++ b/Objects/floatobject.c |
|
@@ -35,6 +35,22 @@ typedef struct _floatblock PyFloatBlock; |
|
static PyFloatBlock *block_list = NULL; |
|
static PyFloatObject *free_list = NULL; |
|
|
|
+/* Print summary info about the state of the optimized allocator */ |
|
+void |
|
+_PyFloat_DebugMallocStats(FILE *out) |
|
+{ |
|
+ int num_blocks = 0; |
|
+ PyFloatBlock *block; |
|
+ |
|
+ /* Walk the block list, counting */ |
|
+ for (block = block_list; block ; block = block->next) { |
|
+ num_blocks++; |
|
+ } |
|
+ |
|
+ _PyDebugAllocatorStats(out, |
|
+ "PyFloatBlock", num_blocks, sizeof(PyFloatBlock)); |
|
+} |
|
+ |
|
static PyFloatObject * |
|
fill_free_list(void) |
|
{ |
|
diff --git a/Objects/frameobject.c b/Objects/frameobject.c |
|
index f9e4a0e..337fc58 100644 |
|
--- a/Objects/frameobject.c |
|
+++ b/Objects/frameobject.c |
|
@@ -982,3 +982,13 @@ PyFrame_Fini(void) |
|
Py_XDECREF(builtin_object); |
|
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)); |
|
+} |
|
+ |
|
diff --git a/Objects/intobject.c b/Objects/intobject.c |
|
index 28182f9..f442ea0 100644 |
|
--- a/Objects/intobject.c |
|
+++ b/Objects/intobject.c |
|
@@ -44,6 +44,23 @@ typedef struct _intblock PyIntBlock; |
|
static PyIntBlock *block_list = NULL; |
|
static PyIntObject *free_list = NULL; |
|
|
|
+ |
|
+/* Print summary info about the state of the optimized allocator */ |
|
+void |
|
+_PyInt_DebugMallocStats(FILE *out) |
|
+{ |
|
+ int num_blocks = 0; |
|
+ PyIntBlock *block; |
|
+ |
|
+ /* Walk the block list, counting */ |
|
+ for (block = block_list; block ; block = block->next) { |
|
+ num_blocks++; |
|
+ } |
|
+ |
|
+ _PyDebugAllocatorStats(out, |
|
+ "PyIntBlock", num_blocks, sizeof(PyIntBlock)); |
|
+} |
|
+ |
|
static PyIntObject * |
|
fill_free_list(void) |
|
{ |
|
diff --git a/Objects/listobject.c b/Objects/listobject.c |
|
index f753643..e6fa17d 100644 |
|
--- a/Objects/listobject.c |
|
+++ b/Objects/listobject.c |
|
@@ -109,6 +109,15 @@ PyList_Fini(void) |
|
} |
|
} |
|
|
|
+/* Print summary info about the state of the optimized allocator */ |
|
+void |
|
+_PyList_DebugMallocStats(FILE *out) |
|
+{ |
|
+ _PyDebugAllocatorStats(out, |
|
+ "free PyListObject", |
|
+ numfree, sizeof(PyListObject)); |
|
+} |
|
+ |
|
PyObject * |
|
PyList_New(Py_ssize_t size) |
|
{ |
|
diff --git a/Objects/methodobject.c b/Objects/methodobject.c |
|
index 0b60ca3..3193135 100644 |
|
--- a/Objects/methodobject.c |
|
+++ b/Objects/methodobject.c |
|
@@ -412,6 +412,15 @@ PyCFunction_Fini(void) |
|
(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(), |
|
but it's part of the API so we need to keep a function around that |
|
existing C extensions can call. |
|
diff --git a/Objects/object.c b/Objects/object.c |
|
index 14f4e9f..68aedcd 100644 |
|
--- a/Objects/object.c |
|
+++ b/Objects/object.c |
|
@@ -2355,6 +2355,23 @@ PyMem_Free(void *p) |
|
PyMem_FREE(p); |
|
} |
|
|
|
+void |
|
+_PyObject_DebugTypeStats(FILE *out) |
|
+{ |
|
+ _PyString_DebugMallocStats(out); |
|
+ _PyCFunction_DebugMallocStats(out); |
|
+ _PyDict_DebugMallocStats(out); |
|
+ _PyFloat_DebugMallocStats(out); |
|
+ _PyFrame_DebugMallocStats(out); |
|
+ _PyInt_DebugMallocStats(out); |
|
+ _PyList_DebugMallocStats(out); |
|
+ _PyMethod_DebugMallocStats(out); |
|
+ _PySet_DebugMallocStats(out); |
|
+ _PyTuple_DebugMallocStats(out); |
|
+#if Py_USING_UNICODE |
|
+ _PyUnicode_DebugMallocStats(out); |
|
+#endif |
|
+} |
|
|
|
/* These methods are used to control infinite recursion in repr, str, print, |
|
etc. Container objects that may recursively contain themselves, |
|
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c |
|
index 38ebc37..2c05359 100644 |
|
--- a/Objects/obmalloc.c |
|
+++ b/Objects/obmalloc.c |
|
@@ -508,12 +508,10 @@ static struct arena_object* usable_arenas = NULL; |
|
/* Number of arenas allocated that haven't been free()'d. */ |
|
static size_t narenas_currently_allocated = 0; |
|
|
|
-#ifdef PYMALLOC_DEBUG |
|
/* Total number of times malloc() called to allocate an arena. */ |
|
static size_t ntimes_arena_allocated = 0; |
|
/* High water mark (max value ever seen) for narenas_currently_allocated. */ |
|
static size_t narenas_highwater = 0; |
|
-#endif |
|
|
|
/* 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 |
|
@@ -528,7 +526,7 @@ new_arena(void) |
|
|
|
#ifdef PYMALLOC_DEBUG |
|
if (Py_GETENV("PYTHONMALLOCSTATS")) |
|
- _PyObject_DebugMallocStats(); |
|
+ _PyObject_DebugMallocStats(stderr); |
|
#endif |
|
if (unused_arena_objects == NULL) { |
|
uint i; |
|
@@ -588,11 +586,9 @@ new_arena(void) |
|
} |
|
|
|
++narenas_currently_allocated; |
|
-#ifdef PYMALLOC_DEBUG |
|
++ntimes_arena_allocated; |
|
if (narenas_currently_allocated > narenas_highwater) |
|
narenas_highwater = narenas_currently_allocated; |
|
-#endif |
|
arenaobj->freepools = NULL; |
|
/* pool_address <- first pool-aligned address in the arena |
|
nfreepools <- number of whole pools that fit after alignment */ |
|
@@ -1694,17 +1690,19 @@ _PyObject_DebugDumpAddress(const void *p) |
|
} |
|
} |
|
|
|
+#endif /* PYMALLOC_DEBUG */ |
|
+ |
|
static size_t |
|
-printone(const char* msg, size_t value) |
|
+printone(FILE *out, const char* msg, size_t value) |
|
{ |
|
int i, k; |
|
char buf[100]; |
|
size_t origvalue = value; |
|
|
|
- fputs(msg, stderr); |
|
+ fputs(msg, out); |
|
for (i = (int)strlen(msg); i < 35; ++i) |
|
- fputc(' ', stderr); |
|
- fputc('=', stderr); |
|
+ fputc(' ', out); |
|
+ fputc('=', out); |
|
|
|
/* Write the value with commas. */ |
|
i = 22; |
|
@@ -1725,17 +1723,32 @@ printone(const char* msg, size_t value) |
|
|
|
while (i >= 0) |
|
buf[i--] = ' '; |
|
- fputs(buf, stderr); |
|
+ fputs(buf, out); |
|
|
|
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); |
|
+} |
|
+ |
|
+ |
|
+/* Print summary info to "out" about the state of pymalloc's structures. |
|
* In Py_DEBUG mode, also perform some expensive internal consistency |
|
* checks. |
|
*/ |
|
void |
|
-_PyObject_DebugMallocStats(void) |
|
+_PyObject_DebugMallocStats(FILE *out) |
|
{ |
|
uint i; |
|
const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT; |
|
@@ -1764,7 +1777,7 @@ _PyObject_DebugMallocStats(void) |
|
size_t total; |
|
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); |
|
|
|
for (i = 0; i < numclasses; ++i) |
|
@@ -1818,10 +1831,10 @@ _PyObject_DebugMallocStats(void) |
|
} |
|
assert(narenas == narenas_currently_allocated); |
|
|
|
- fputc('\n', stderr); |
|
+ fputc('\n', out); |
|
fputs("class size num pools blocks in use avail blocks\n" |
|
"----- ---- --------- ------------- ------------\n", |
|
- stderr); |
|
+ out); |
|
|
|
for (i = 0; i < numclasses; ++i) { |
|
size_t p = numpools[i]; |
|
@@ -1832,7 +1845,7 @@ _PyObject_DebugMallocStats(void) |
|
assert(b == 0 && f == 0); |
|
continue; |
|
} |
|
- fprintf(stderr, "%5u %6u " |
|
+ fprintf(out, "%5u %6u " |
|
"%11" PY_FORMAT_SIZE_T "u " |
|
"%15" PY_FORMAT_SIZE_T "u " |
|
"%13" PY_FORMAT_SIZE_T "u\n", |
|
@@ -1842,36 +1855,35 @@ _PyObject_DebugMallocStats(void) |
|
pool_header_bytes += p * POOL_OVERHEAD; |
|
quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size); |
|
} |
|
- fputc('\n', stderr); |
|
- (void)printone("# times object malloc called", serialno); |
|
- |
|
- (void)printone("# arenas allocated total", ntimes_arena_allocated); |
|
- (void)printone("# arenas reclaimed", ntimes_arena_allocated - narenas); |
|
- (void)printone("# arenas highwater mark", narenas_highwater); |
|
- (void)printone("# arenas allocated current", narenas); |
|
+ fputc('\n', out); |
|
+#ifdef PYMALLOC_DEBUG |
|
+ (void)printone(out, "# times object malloc called", serialno); |
|
+#endif |
|
+ (void)printone(out, "# arenas allocated total", ntimes_arena_allocated); |
|
+ (void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas); |
|
+ (void)printone(out, "# arenas highwater mark", narenas_highwater); |
|
+ (void)printone(out, "# arenas allocated current", narenas); |
|
|
|
PyOS_snprintf(buf, sizeof(buf), |
|
"%" PY_FORMAT_SIZE_T "u arenas * %d bytes/arena", |
|
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("# bytes in available blocks", available_bytes); |
|
+ total = printone(out, "# bytes in allocated blocks", allocated_bytes); |
|
+ total += printone(out, "# bytes in available blocks", available_bytes); |
|
|
|
PyOS_snprintf(buf, sizeof(buf), |
|
"%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("# bytes lost to quantization", quantization); |
|
- total += printone("# bytes lost to arena alignment", arena_alignment); |
|
- (void)printone("Total", total); |
|
+ total += printone(out, "# bytes lost to pool headers", pool_header_bytes); |
|
+ total += printone(out, "# bytes lost to quantization", quantization); |
|
+ total += printone(out, "# bytes lost to arena alignment", arena_alignment); |
|
+ (void)printone(out, "Total", total); |
|
} |
|
|
|
-#endif /* PYMALLOC_DEBUG */ |
|
- |
|
#ifdef Py_USING_MEMORY_DEBUGGER |
|
/* Make this function last so gcc won't inline it since the definition is |
|
* after the reference. |
|
diff --git a/Objects/setobject.c b/Objects/setobject.c |
|
index af1ce16..3439b7c 100644 |
|
--- a/Objects/setobject.c |
|
+++ b/Objects/setobject.c |
|
@@ -1088,6 +1088,16 @@ PySet_Fini(void) |
|
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 * |
|
set_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
|
{ |
|
diff --git a/Objects/stringobject.c b/Objects/stringobject.c |
|
index 1209197..b8646dd 100644 |
|
--- a/Objects/stringobject.c |
|
+++ b/Objects/stringobject.c |
|
@@ -4843,3 +4843,43 @@ void _Py_ReleaseInternedStrings(void) |
|
PyDict_Clear(interned); |
|
Py_CLEAR(interned); |
|
} |
|
+ |
|
+void _PyString_DebugMallocStats(FILE *out) |
|
+{ |
|
+ ssize_t i; |
|
+ int num_immortal = 0, num_mortal = 0; |
|
+ ssize_t immortal_size = 0, mortal_size = 0; |
|
+ |
|
+ if (interned == NULL || !PyDict_Check(interned)) |
|
+ return; |
|
+ |
|
+ for (i = 0; i <= ((PyDictObject*)interned)->ma_mask; i++) { |
|
+ PyDictEntry *ep = ((PyDictObject*)interned)->ma_table + i; |
|
+ PyObject *pvalue = ep->me_value; |
|
+ if (pvalue != NULL) { |
|
+ PyStringObject *s = (PyStringObject *)ep->me_key; |
|
+ |
|
+ switch (s->ob_sstate) { |
|
+ case SSTATE_NOT_INTERNED: |
|
+ /* XXX Shouldn't happen */ |
|
+ break; |
|
+ case SSTATE_INTERNED_IMMORTAL: |
|
+ num_immortal ++; |
|
+ immortal_size += s->ob_size; |
|
+ break; |
|
+ case SSTATE_INTERNED_MORTAL: |
|
+ num_mortal ++; |
|
+ mortal_size += s->ob_size; |
|
+ break; |
|
+ default: |
|
+ Py_FatalError("Inconsistent interned string state."); |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+ fprintf(out, "%d mortal interned strings\n", num_mortal); |
|
+ fprintf(out, "%d immortal interned strings\n", num_immortal); |
|
+ fprintf(out, "total size of all interned strings: " |
|
+ "%zi/%zi " |
|
+ "mortal/immortal\n", mortal_size, immortal_size); |
|
+} |
|
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c |
|
index 00f2e47..7682d81 100644 |
|
--- a/Objects/tupleobject.c |
|
+++ b/Objects/tupleobject.c |
|
@@ -44,6 +44,22 @@ show_track(void) |
|
} |
|
#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 * |
|
PyTuple_New(register Py_ssize_t size) |
|
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c |
|
index 6bea370..ced9acf 100644 |
|
--- a/Objects/unicodeobject.c |
|
+++ b/Objects/unicodeobject.c |
|
@@ -8920,6 +8920,12 @@ _PyUnicode_Fini(void) |
|
(void)PyUnicode_ClearFreeList(); |
|
} |
|
|
|
+void _PyUnicode_DebugMallocStats(FILE *out) |
|
+{ |
|
+ _PyDebugAllocatorStats(out, "free PyUnicodeObject", numfree, |
|
+ sizeof(PyUnicodeObject)); |
|
+} |
|
+ |
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
diff --git a/Python/pythonrun.c b/Python/pythonrun.c |
|
index f0fbd74..0b73f3a 100644 |
|
--- a/Python/pythonrun.c |
|
+++ b/Python/pythonrun.c |
|
@@ -557,7 +557,7 @@ Py_Finalize(void) |
|
#endif /* Py_TRACE_REFS */ |
|
#ifdef PYMALLOC_DEBUG |
|
if (Py_GETENV("PYTHONMALLOCSTATS")) |
|
- _PyObject_DebugMallocStats(); |
|
+ _PyObject_DebugMallocStats(stderr); |
|
#endif |
|
|
|
call_ll_exitfuncs(); |
|
diff --git a/Python/sysmodule.c b/Python/sysmodule.c |
|
index 2a7c207..fbb637b 100644 |
|
--- a/Python/sysmodule.c |
|
+++ b/Python/sysmodule.c |
|
@@ -873,6 +873,57 @@ a 11-tuple where the entries in the tuple are counts of:\n\ |
|
extern "C" { |
|
#endif |
|
|
|
+static PyObject * |
|
+sys_debugmallocstats(PyObject *self, PyObject *args) |
|
+{ |
|
+ PyObject *file = NULL; |
|
+ FILE *fp; |
|
+ |
|
+ if (!PyArg_ParseTuple(args, "|O!", |
|
+ &PyFile_Type, &file)) { |
|
+ return NULL; |
|
+ } |
|
+ if (!file) { |
|
+ /* Default to sys.stderr: */ |
|
+ file = PySys_GetObject("stderr"); |
|
+ if (!file) { |
|
+ PyErr_SetString(PyExc_ValueError, "sys.stderr not set"); |
|
+ return NULL; |
|
+ } |
|
+ if (!PyFile_Check(file)) { |
|
+ PyErr_SetString(PyExc_TypeError, "sys.stderr is not a file"); |
|
+ return NULL; |
|
+ } |
|
+ } |
|
+ |
|
+ Py_INCREF(file); |
|
+ /* OK, we now own a ref on non-NULL "file" */ |
|
+ |
|
+ fp = PyFile_AsFile(file); |
|
+ if (!fp) { |
|
+ PyErr_SetString(PyExc_ValueError, "file is closed"); |
|
+ Py_DECREF(file); |
|
+ return NULL; |
|
+ } |
|
+ |
|
+ _PyObject_DebugMallocStats(fp); |
|
+ fputc('\n', fp); |
|
+ _PyObject_DebugTypeStats(fp); |
|
+ |
|
+ Py_DECREF(file); |
|
+ |
|
+ Py_RETURN_NONE; |
|
+} |
|
+PyDoc_STRVAR(debugmallocstats_doc, |
|
+"_debugmallocstats([file])\n\ |
|
+\n\ |
|
+Print summary info to the given file (or sys.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 |
|
/* Defined in objects.c because it uses static globals if that file */ |
|
extern PyObject *_Py_GetObjects(PyObject *, PyObject *); |
|
@@ -971,6 +1022,8 @@ static PyMethodDef sys_methods[] = { |
|
{"settrace", sys_settrace, METH_O, settrace_doc}, |
|
{"gettrace", sys_gettrace, METH_NOARGS, gettrace_doc}, |
|
{"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, |
|
+ {"_debugmallocstats", sys_debugmallocstats, METH_VARARGS, |
|
+ debugmallocstats_doc}, |
|
{NULL, NULL} /* sentinel */ |
|
}; |
|
|
|
|