[python] add a sys._debugmallocstats() function (patch 147)

dmalcolm dmalcolm at fedoraproject.org
Fri Sep 16 22:42:45 UTC 2011


commit a43d18ff331708af4d9cfd8358a4593f8d0bb418
Author: David Malcolm <dmalcolm at redhat.com>
Date:   Fri Sep 16 18:41:28 2011 -0400

    add a sys._debugmallocstats() function (patch 147)
    
    * Fri Sep 16 2011 David Malcolm <dmalcolm at redhat.com> - 2.7.2-15
    - add a sys._debugmallocstats() function (patch 147)

 00147-add-debug-malloc-stats.patch |  702 ++++++++++++++++++++++++++++++++++++
 python.spec                        |   11 +-
 2 files changed, 712 insertions(+), 1 deletions(-)
---
diff --git a/00147-add-debug-malloc-stats.patch b/00147-add-debug-malloc-stats.patch
new file mode 100644
index 0000000..ab0644d
--- /dev/null
+++ b/00147-add-debug-malloc-stats.patch
@@ -0,0 +1,702 @@
+diff -up Python-2.7.2/Include/classobject.h.add-debug-malloc-stats Python-2.7.2/Include/classobject.h
+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-09-16 17:35:22.304872006 -0400
++++ Python-2.7.2/Include/dictobject.h	2011-09-16 17:39:53.486869422 -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 17:13:54.589884287 -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-09-16 17:35:44.023871801 -0400
++++ Python-2.7.2/Include/frameobject.h	2011-09-16 17:40:10.230869260 -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 17:13:54.589884287 -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-09-16 17:35:56.608871678 -0400
++++ Python-2.7.2/Include/listobject.h	2011-09-16 17:40:25.526869113 -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-09-16 17:36:06.264871587 -0400
++++ Python-2.7.2/Include/methodobject.h	2011-09-16 17:40:50.949868868 -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 17:13:54.590884287 -0400
+@@ -980,6 +980,13 @@ PyAPI_DATA(PyObject *) _PyTrash_delete_l
+     else \
+         _PyTrash_deposit_object((PyObject*)op);
+ 
++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 17:13:54.590884287 -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/setobject.h.add-debug-malloc-stats Python-2.7.2/Include/setobject.h
+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 17:13:54.591884287 -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/tupleobject.h.add-debug-malloc-stats Python-2.7.2/Include/tupleobject.h
+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 17:13:54.591884287 -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 18:15:38.495848962 -0400
++++ Python-2.7.2/Lib/test/test_sys.py	2011-09-16 18:32:37.048839249 -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)
++        
++
+ class SizeofTest(unittest.TestCase):
+ 
+     TPFLAGS_HAVE_GC = 1<<14
+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-09-16 17:32:27.712873672 -0400
++++ Python-2.7.2/Objects/classobject.c	2011-09-16 18:01:44.375856916 -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-09-16 17:32:55.336873422 -0400
++++ Python-2.7.2/Objects/dictobject.c	2011-09-16 18:02:13.967856635 -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 17:43:43.695867228 -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-09-16 17:33:08.129873287 -0400
++++ Python-2.7.2/Objects/frameobject.c	2011-09-16 18:07:58.743853347 -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 17:44:27.776866803 -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-09-16 17:33:18.008873189 -0400
++++ Python-2.7.2/Objects/listobject.c	2011-09-16 18:08:54.167852819 -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-09-16 17:33:29.656873081 -0400
++++ Python-2.7.2/Objects/methodobject.c	2011-09-16 18:08:10.607853235 -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 17:56:21.031860008 -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 18:29:17.935841149 -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)
+     }
+ 
+     ++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-09-16 17:33:42.223872963 -0400
++++ Python-2.7.2/Objects/setobject.c	2011-09-16 18:09:12.870852640 -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 17:46:41.015865533 -0400
+@@ -4822,3 +4822,43 @@ void _Py_ReleaseInternedStrings(void)
+     Py_DECREF(interned);
+     interned = NULL;
+ }
++
++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-09-16 17:33:54.008872848 -0400
++++ Python-2.7.2/Objects/tupleobject.c	2011-09-16 18:07:05.575853855 -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 17:27:15.119876653 -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/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 17:13:54.475884288 -0400
++++ Python-2.7.2/Python/sysmodule.c	2011-09-16 17:47:22.648865136 -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 */
+ };
+ 
diff --git a/python.spec b/python.spec
index 3d08dff..e438863 100644
--- a/python.spec
+++ b/python.spec
@@ -108,7 +108,7 @@ Summary: An interpreted, interactive, object-oriented programming language
 Name: %{python}
 # Remember to also rebase python-docs when changing this:
 Version: 2.7.2
-Release: 14%{?dist}
+Release: 15%{?dist}
 License: Python
 Group: Development/Languages
 Requires: %{python}-libs%{?_isa} = %{version}-%{release}
@@ -592,6 +592,11 @@ Patch145: 00145-force-sys-platform-to-be-linux2.patch
 # (rhbz#563986)
 Patch146: 00146-hashlib-fips.patch
 
+# Add a sys._debugmallocstats() function
+# Based on patch 202 from RHEL 5's python.spec, with updates from rhbz#737198
+#  Not yet sent upstream
+Patch147: 00147-add-debug-malloc-stats.patch
+
 # (New patches go here ^^^)
 #
 # When adding new patches to "python" and "python3" in Fedora 17 onwards,
@@ -891,6 +896,7 @@ done
 %endif
 %patch145 -p1 -b .linux2
 %patch146 -p1
+%patch147 -p1
 
 # This shouldn't be necesarry, but is right now (2.2a3)
 find -name "*~" |xargs rm -f
@@ -1716,6 +1722,9 @@ rm -fr %{buildroot}
 # ======================================================
 
 %changelog
+* Fri Sep 16 2011 David Malcolm <dmalcolm at redhat.com> - 2.7.2-15
+- add a sys._debugmallocstats() function (patch 147)
+
 * Wed Sep 14 2011 David Malcolm <dmalcolm at redhat.com> - 2.7.2-14
 - support OpenSSL FIPS mode in _hashlib and hashlib; don't build the _md5 and
 _sha* modules, relying on _hashlib in hashlib, and thus within md5 etc


More information about the scm-commits mailing list