[Python-checkins] cpython (merge default -> default): Merge

antoine.pitrou python-checkins at python.org
Fri Jun 22 21:17:10 CEST 2012


http://hg.python.org/cpython/rev/34319fc28da6
changeset:   77580:34319fc28da6
parent:      77579:e193fe3d017e
parent:      77577:d63a80abfbec
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Fri Jun 22 21:13:34 2012 +0200
summary:
  Merge

files:
  Doc/library/sys.rst       |  16 +++++
  Include/dictobject.h      |   1 +
  Include/floatobject.h     |   2 +
  Include/frameobject.h     |   2 +
  Include/listobject.h      |   1 +
  Include/methodobject.h    |   5 +
  Include/object.h          |   8 ++
  Include/objimpl.h         |   4 +-
  Include/setobject.h       |   1 +
  Include/tupleobject.h     |   3 +
  Lib/test/test_sys.py      |   6 ++
  Misc/NEWS                 |   3 +
  Modules/_datetimemodule.c |   9 +-
  Modules/_threadmodule.c   |  38 +++++-------
  Objects/classobject.c     |   9 +++
  Objects/dictobject.c      |   9 +++
  Objects/floatobject.c     |  10 +++
  Objects/frameobject.c     |  10 +++
  Objects/listobject.c      |   9 +++
  Objects/methodobject.c    |   9 +++
  Objects/object.c          |  12 ++++
  Objects/obmalloc.c        |  81 ++++++++++++++++-----------
  Objects/setobject.c       |  10 +++
  Objects/tupleobject.c     |  16 +++++
  Python/pythonrun.c        |   2 +-
  Python/sysmodule.c        |  23 +++++++
  26 files changed, 239 insertions(+), 60 deletions(-)


diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -106,6 +106,22 @@
    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
 
    Integer specifying the handle of the Python DLL. Availability: Windows.
diff --git a/Include/dictobject.h b/Include/dictobject.h
--- a/Include/dictobject.h
+++ b/Include/dictobject.h
@@ -111,6 +111,7 @@
 #ifndef Py_LIMITED_API
 int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value);
 PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
+PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out);
 #endif
 
 #ifdef __cplusplus
diff --git a/Include/floatobject.h b/Include/floatobject.h
--- a/Include/floatobject.h
+++ b/Include/floatobject.h
@@ -110,6 +110,8 @@
 /* free list api */
 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
    (Advanced String Formatting). */
 PyAPI_FUNC(int) _PyFloat_FormatAdvancedWriter(
diff --git a/Include/frameobject.h b/Include/frameobject.h
--- a/Include/frameobject.h
+++ b/Include/frameobject.h
@@ -79,6 +79,8 @@
 
 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/listobject.h b/Include/listobject.h
--- a/Include/listobject.h
+++ b/Include/listobject.h
@@ -64,6 +64,7 @@
 PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *);
 
 PyAPI_FUNC(int) PyList_ClearFreeList(void);
+PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out);
 #endif
 
 /* Macro, trading safety for speed */
diff --git a/Include/methodobject.h b/Include/methodobject.h
--- a/Include/methodobject.h
+++ b/Include/methodobject.h
@@ -82,6 +82,11 @@
 
 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
 }
 #endif
diff --git a/Include/object.h b/Include/object.h
--- a/Include/object.h
+++ b/Include/object.h
@@ -977,6 +977,14 @@
     else \
         _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
 }
 #endif
diff --git a/Include/objimpl.h b/Include/objimpl.h
--- a/Include/objimpl.h
+++ b/Include/objimpl.h
@@ -101,13 +101,15 @@
 
 /* Macros */
 #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 */
 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
--- a/Include/setobject.h
+++ b/Include/setobject.h
@@ -101,6 +101,7 @@
 PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable);
 
 PyAPI_FUNC(int) PySet_ClearFreeList(void);
+PyAPI_FUNC(void) _PySet_DebugMallocStats(FILE *out);
 #endif
 
 #ifdef __cplusplus
diff --git a/Include/tupleobject.h b/Include/tupleobject.h
--- a/Include/tupleobject.h
+++ b/Include/tupleobject.h
@@ -63,6 +63,9 @@
 #endif
 
 PyAPI_FUNC(int) PyTuple_ClearFreeList(void);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void) _PyTuple_DebugMallocStats(FILE *out);
+#endif /* Py_LIMITED_API */
 
 #ifdef __cplusplus
 }
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -603,6 +603,12 @@
         self.assertEqual(sys.implementation.name,
                          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):
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -151,6 +151,9 @@
 - Issue #14963: Convert contextlib.ExitStack.__exit__ to use an iterative
   algorithm (Patch by Alon Horev)
 
+- Issue #14785: Add sys._debugmallocstats() to help debug low-level memory
+  allocation issues
+
 C-API
 -----
 
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -4754,7 +4754,7 @@
     return result;
 }
 
-static PyObject *
+static PyDateTime_DateTime *
 datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
 {
     PyDateTime_DateTime *result;
@@ -4777,7 +4777,7 @@
     /* Conversion to self's own time zone is a NOP. */
     if (self->tzinfo == tzinfo) {
         Py_INCREF(self);
-        return (PyObject *)self;
+        return self;
     }
 
     /* Convert self to UTC. */
@@ -4814,10 +4814,11 @@
     Py_DECREF(temp);
 
     temp = (PyObject *)result;
-    result = _PyObject_CallMethodId(tzinfo, &PyId_fromutc, "O", temp);
+    result = (PyDateTime_DateTime *)
+        _PyObject_CallMethodId(tzinfo, &PyId_fromutc, "O", temp);
     Py_DECREF(temp);
 
-    return (PyObject *)result;
+    return result;
 }
 
 static PyObject *
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -23,6 +23,7 @@
     PyObject_HEAD
     PyThread_type_lock lock_lock;
     PyObject *in_weakreflist;
+    char locked; /* for sanity checking */
 } lockobject;
 
 static void
@@ -32,9 +33,8 @@
         PyObject_ClearWeakRefs((PyObject *) self);
     if (self->lock_lock != NULL) {
         /* Unlock the lock so it's safe to free it */
-        PyThread_acquire_lock(self->lock_lock, 0);
-        PyThread_release_lock(self->lock_lock);
-
+        if (self->locked)
+            PyThread_release_lock(self->lock_lock);
         PyThread_free_lock(self->lock_lock);
     }
     PyObject_Del(self);
@@ -62,9 +62,13 @@
 
 
     do {
-        Py_BEGIN_ALLOW_THREADS
-        r = PyThread_acquire_lock_timed(lock, microseconds, 1);
-        Py_END_ALLOW_THREADS
+        /* first a simple non-blocking try without releasing the GIL */
+        r = PyThread_acquire_lock_timed(lock, 0, 0);
+        if (r == PY_LOCK_FAILURE && microseconds != 0) {
+            Py_BEGIN_ALLOW_THREADS
+            r = PyThread_acquire_lock_timed(lock, microseconds, 1);
+            Py_END_ALLOW_THREADS
+        } 
 
         if (r == PY_LOCK_INTR) {
             /* Run signal handlers if we were interrupted.  Propagate
@@ -135,6 +139,8 @@
         return NULL;
     }
 
+    if (r == PY_LOCK_ACQUIRED)
+        self->locked = 1;
     return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
 }
 
@@ -153,13 +159,13 @@
 lock_PyThread_release_lock(lockobject *self)
 {
     /* Sanity check: the lock must be locked */
-    if (PyThread_acquire_lock(self->lock_lock, 0)) {
-        PyThread_release_lock(self->lock_lock);
+    if (!self->locked) {
         PyErr_SetString(ThreadError, "release unlocked lock");
         return NULL;
     }
 
     PyThread_release_lock(self->lock_lock);
+    self->locked = 0;
     Py_INCREF(Py_None);
     return Py_None;
 }
@@ -175,11 +181,7 @@
 static PyObject *
 lock_locked_lock(lockobject *self)
 {
-    if (PyThread_acquire_lock(self->lock_lock, 0)) {
-        PyThread_release_lock(self->lock_lock);
-        return PyBool_FromLong(0L);
-    }
-    return PyBool_FromLong(1L);
+    return PyBool_FromLong((long)self->locked);
 }
 
 PyDoc_STRVAR(locked_doc,
@@ -313,14 +315,7 @@
         self->rlock_count = count;
         Py_RETURN_TRUE;
     }
-
-    if (self->rlock_count > 0 ||
-        !PyThread_acquire_lock(self->rlock_lock, 0)) {
-        if (microseconds == 0) {
-            Py_RETURN_FALSE;
-        }
-        r = acquire_timed(self->rlock_lock, microseconds);
-    }
+    r = acquire_timed(self->rlock_lock, microseconds);
     if (r == PY_LOCK_ACQUIRED) {
         assert(self->rlock_count == 0);
         self->rlock_owner = tid;
@@ -548,6 +543,7 @@
     if (self == NULL)
         return NULL;
     self->lock_lock = PyThread_allocate_lock();
+    self->locked = 0;
     self->in_weakreflist = NULL;
     if (self->lock_lock == NULL) {
         Py_DECREF(self);
diff --git a/Objects/classobject.c b/Objects/classobject.c
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -400,6 +400,15 @@
     (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
  */
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -255,6 +255,15 @@
     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
 PyDict_Fini(void)
 {
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -1933,6 +1933,16 @@
     (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.
  */
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -955,3 +955,13 @@
     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/listobject.c b/Objects/listobject.c
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -117,6 +117,15 @@
     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 *
 PyList_New(Py_ssize_t size)
 {
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -338,6 +338,15 @@
     (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
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1852,6 +1852,18 @@
     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,
    etc.  Container objects that may recursively contain themselves,
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -523,12 +523,10 @@
 /* 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
@@ -545,7 +543,7 @@
 
 #ifdef PYMALLOC_DEBUG
     if (Py_GETENV("PYTHONMALLOCSTATS"))
-        _PyObject_DebugMallocStats();
+        _PyObject_DebugMallocStats(stderr);
 #endif
     if (unused_arena_objects == NULL) {
         uint i;
@@ -613,11 +611,9 @@
     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 */
@@ -1723,17 +1719,19 @@
     }
 }
 
+#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;
@@ -1754,17 +1752,33 @@
 
     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);
+}
+
+#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
  * checks.
  */
 void
-_PyObject_DebugMallocStats(void)
+_PyObject_DebugMallocStats(FILE *out)
 {
     uint i;
     const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT;
@@ -1793,7 +1807,7 @@
     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)
@@ -1847,10 +1861,10 @@
     }
     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];
@@ -1861,7 +1875,7 @@
             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",
@@ -1871,35 +1885,36 @@
         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 */
+#endif /* #ifdef WITH_PYMALLOC */
 
 #ifdef Py_USING_MEMORY_DEBUGGER
 /* Make this function last so gcc won't inline it since the definition is
diff --git a/Objects/setobject.c b/Objects/setobject.c
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -1133,6 +1133,16 @@
     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/tupleobject.c b/Objects/tupleobject.c
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -45,6 +45,22 @@
 }
 #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/Python/pythonrun.c b/Python/pythonrun.c
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -642,7 +642,7 @@
 #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
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -997,6 +997,27 @@
 extern "C" {
 #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
 /* Defined in objects.c because it uses static globals if that file */
 extern PyObject *_Py_GetObjects(PyObject *, PyObject *);
@@ -1093,6 +1114,8 @@
     {"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 */
 };
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list