diff -up Python-2.7.2/Include/dictobject.h.add-debug-malloc-stats Python-2.7.2/Include/dictobject.h --- Python-2.7.2/Include/dictobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/dictobject.h 2011-09-16 19:03:25.105821625 -0400 @@ -150,6 +150,8 @@ PyAPI_FUNC(PyObject *) PyDict_GetItemStr 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 -up Python-2.7.2/Include/floatobject.h.add-debug-malloc-stats Python-2.7.2/Include/floatobject.h --- Python-2.7.2/Include/floatobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/floatobject.h 2011-09-16 19:03:25.106821625 -0400 @@ -132,6 +132,7 @@ PyAPI_FUNC(PyObject *) _PyFloat_FormatAd 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 -up Python-2.7.2/Include/frameobject.h.add-debug-malloc-stats Python-2.7.2/Include/frameobject.h --- Python-2.7.2/Include/frameobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/frameobject.h 2011-09-16 19:03:25.107821625 -0400 @@ -80,6 +80,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(Py 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 -up Python-2.7.2/Include/intobject.h.add-debug-malloc-stats Python-2.7.2/Include/intobject.h --- Python-2.7.2/Include/intobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/intobject.h 2011-09-16 19:03:25.107821625 -0400 @@ -74,6 +74,8 @@ PyAPI_FUNC(PyObject *) _PyInt_FormatAdva char *format_spec, Py_ssize_t format_spec_len); +PyAPI_FUNC(void) _PyInt_DebugMallocStats(FILE *out); + #ifdef __cplusplus } #endif diff -up Python-2.7.2/Include/listobject.h.add-debug-malloc-stats Python-2.7.2/Include/listobject.h --- Python-2.7.2/Include/listobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/listobject.h 2011-09-16 19:03:25.107821625 -0400 @@ -62,6 +62,8 @@ PyAPI_FUNC(PyObject *) _PyList_Extend(Py #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 -up Python-2.7.2/Include/methodobject.h.add-debug-malloc-stats Python-2.7.2/Include/methodobject.h --- Python-2.7.2/Include/methodobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/methodobject.h 2011-09-16 19:03:25.108821625 -0400 @@ -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 -up Python-2.7.2/Include/object.h.add-debug-malloc-stats Python-2.7.2/Include/object.h --- Python-2.7.2/Include/object.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/object.h 2011-09-16 19:03:25.108821625 -0400 @@ -980,6 +980,13 @@ PyAPI_DATA(PyObject *) _PyTrash_delete_l _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 -up Python-2.7.2/Include/objimpl.h.add-debug-malloc-stats Python-2.7.2/Include/objimpl.h --- Python-2.7.2/Include/objimpl.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/objimpl.h 2011-09-16 19:03:25.108821625 -0400 @@ -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 -up Python-2.7.2/Include/stringobject.h.add-debug-malloc-stats Python-2.7.2/Include/stringobject.h --- Python-2.7.2/Include/stringobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/stringobject.h 2011-09-16 19:03:25.109821625 -0400 @@ -204,6 +204,8 @@ PyAPI_FUNC(PyObject *) _PyBytes_FormatAd char *format_spec, Py_ssize_t format_spec_len); +PyAPI_FUNC(void) _PyString_DebugMallocStats(FILE *out); + #ifdef __cplusplus } #endif diff -up Python-2.7.2/Include/unicodeobject.h.add-debug-malloc-stats Python-2.7.2/Include/unicodeobject.h --- Python-2.7.2/Include/unicodeobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 +++ Python-2.7.2/Include/unicodeobject.h 2011-09-16 19:03:25.109821625 -0400 @@ -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 -up Python-2.7.2/Lib/test/test_sys.py.add-debug-malloc-stats Python-2.7.2/Lib/test/test_sys.py --- Python-2.7.2/Lib/test/test_sys.py.add-debug-malloc-stats 2011-09-16 19:03:25.048821626 -0400 +++ Python-2.7.2/Lib/test/test_sys.py 2011-09-16 19:03:25.110821625 -0400 @@ -473,6 +473,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) + + @test.test_support.cpython_only class SizeofTest(unittest.TestCase): diff -up Python-2.7.2/Objects/classobject.c.add-debug-malloc-stats Python-2.7.2/Objects/classobject.c --- Python-2.7.2/Objects/classobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/classobject.c 2011-09-16 19:03:25.110821625 -0400 @@ -2670,3 +2670,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 -up Python-2.7.2/Objects/dictobject.c.add-debug-malloc-stats Python-2.7.2/Objects/dictobject.c --- Python-2.7.2/Objects/dictobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/dictobject.c 2011-09-16 19:03:25.111821625 -0400 @@ -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 -up Python-2.7.2/Objects/floatobject.c.add-debug-malloc-stats Python-2.7.2/Objects/floatobject.c --- Python-2.7.2/Objects/floatobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/floatobject.c 2011-09-16 19:03:25.111821625 -0400 @@ -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 -up Python-2.7.2/Objects/frameobject.c.add-debug-malloc-stats Python-2.7.2/Objects/frameobject.c --- Python-2.7.2/Objects/frameobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/frameobject.c 2011-09-16 19:03:25.112821625 -0400 @@ -980,3 +980,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 -up Python-2.7.2/Objects/intobject.c.add-debug-malloc-stats Python-2.7.2/Objects/intobject.c --- Python-2.7.2/Objects/intobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/intobject.c 2011-09-16 19:03:25.112821625 -0400 @@ -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 -up Python-2.7.2/Objects/listobject.c.add-debug-malloc-stats Python-2.7.2/Objects/listobject.c --- Python-2.7.2/Objects/listobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/listobject.c 2011-09-16 19:03:25.113821625 -0400 @@ -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 -up Python-2.7.2/Objects/methodobject.c.add-debug-malloc-stats Python-2.7.2/Objects/methodobject.c --- Python-2.7.2/Objects/methodobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/methodobject.c 2011-09-16 19:03:25.113821625 -0400 @@ -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 -up Python-2.7.2/Objects/object.c.add-debug-malloc-stats Python-2.7.2/Objects/object.c --- Python-2.7.2/Objects/object.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/object.c 2011-09-16 19:04:46.463820849 -0400 @@ -2334,6 +2334,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 -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Objects/obmalloc.c --- Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/obmalloc.c 2011-09-16 19:03:25.114821625 -0400 @@ -508,12 +508,10 @@ static struct arena_object* usable_arena /* 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) arenaobj->address = (uptr)address; ++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 -up Python-2.7.2/Objects/setobject.c.add-debug-malloc-stats Python-2.7.2/Objects/setobject.c --- Python-2.7.2/Objects/setobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/setobject.c 2011-09-16 19:03:25.115821625 -0400 @@ -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 -up Python-2.7.2/Objects/stringobject.c.add-debug-malloc-stats Python-2.7.2/Objects/stringobject.c --- Python-2.7.2/Objects/stringobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/stringobject.c 2011-09-16 19:03:25.116821625 -0400 @@ -4822,3 +4822,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 -up Python-2.7.2/Objects/tupleobject.c.add-debug-malloc-stats Python-2.7.2/Objects/tupleobject.c --- Python-2.7.2/Objects/tupleobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/tupleobject.c 2011-09-16 19:03:25.116821625 -0400 @@ -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 -up Python-2.7.2/Objects/unicodeobject.c.add-debug-malloc-stats Python-2.7.2/Objects/unicodeobject.c --- Python-2.7.2/Objects/unicodeobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 +++ Python-2.7.2/Objects/unicodeobject.c 2011-09-16 19:03:25.118821625 -0400 @@ -8883,6 +8883,12 @@ _PyUnicode_Fini(void) (void)PyUnicode_ClearFreeList(); } +void _PyUnicode_DebugMallocStats(FILE *out) +{ + _PyDebugAllocatorStats(out, "free PyUnicodeObject", numfree, + sizeof(PyUnicodeObject)); +} + #ifdef __cplusplus } #endif diff -up Python-2.7.2/Python/pythonrun.c.add-debug-malloc-stats Python-2.7.2/Python/pythonrun.c --- Python-2.7.2/Python/pythonrun.c.add-debug-malloc-stats 2011-09-16 19:03:25.025821626 -0400 +++ Python-2.7.2/Python/pythonrun.c 2011-09-16 19:03:25.118821625 -0400 @@ -549,7 +549,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 -up Python-2.7.2/Python/sysmodule.c.add-debug-malloc-stats Python-2.7.2/Python/sysmodule.c --- Python-2.7.2/Python/sysmodule.c.add-debug-malloc-stats 2011-09-16 19:03:25.007821626 -0400 +++ Python-2.7.2/Python/sysmodule.c 2011-09-16 19:03:25.119821625 -0400 @@ -872,6 +872,57 @@ a 11-tuple where the entries in the tupl 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 *); @@ -970,6 +1021,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 */ };