An interpreted, interactive, object-oriented programming language
CentOS Sources
2017-08-01 71084d584ff953f5463757ec6536406320560b4d
commit | author | age
71084d 1 diff --git a/Include/dictobject.h b/Include/dictobject.h
CS 2 index ece01c6..acc1df0 100644
3 --- a/Include/dictobject.h
4 +++ b/Include/dictobject.h
5 @@ -150,6 +150,8 @@ PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key);
6e8c2f 6  PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item);
CB 7  PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key);
8  
9 +PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out);
10 +
11  #ifdef __cplusplus
12  }
13  #endif
71084d 14 diff --git a/Include/floatobject.h b/Include/floatobject.h
CS 15 index 54e8825..33c6ac0 100644
16 --- a/Include/floatobject.h
17 +++ b/Include/floatobject.h
18 @@ -132,6 +132,7 @@ PyAPI_FUNC(PyObject *) _PyFloat_FormatAdvanced(PyObject *obj,
6e8c2f 19     failure.  Used in builtin_round in bltinmodule.c. */
CB 20  PyAPI_FUNC(PyObject *) _Py_double_round(double x, int ndigits);
21  
22 +PyAPI_FUNC(void) _PyFloat_DebugMallocStats(FILE* out);
23  
24  
25  #ifdef __cplusplus
71084d 26 diff --git a/Include/frameobject.h b/Include/frameobject.h
CS 27 index 17e7679..66d9d8b 100644
28 --- a/Include/frameobject.h
29 +++ b/Include/frameobject.h
30 @@ -80,6 +80,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
6e8c2f 31  
CB 32  PyAPI_FUNC(int) PyFrame_ClearFreeList(void);
33  
34 +PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out);
35 +
36  /* Return the line of code the frame is currently executing. */
37  PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *);
38  
71084d 39 diff --git a/Include/intobject.h b/Include/intobject.h
CS 40 index 252eea9..4003736 100644
41 --- a/Include/intobject.h
42 +++ b/Include/intobject.h
43 @@ -75,6 +75,8 @@ PyAPI_FUNC(PyObject *) _PyInt_FormatAdvanced(PyObject *obj,
6e8c2f 44                           char *format_spec,
CB 45                           Py_ssize_t format_spec_len);
46  
47 +PyAPI_FUNC(void) _PyInt_DebugMallocStats(FILE *out);
48 +
49  #ifdef __cplusplus
50  }
51  #endif
71084d 52 diff --git a/Include/listobject.h b/Include/listobject.h
CS 53 index c445873..04664d7 100644
54 --- a/Include/listobject.h
55 +++ b/Include/listobject.h
56 @@ -62,6 +62,8 @@ PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *);
6e8c2f 57  #define PyList_SET_ITEM(op, i, v) (((PyListObject *)(op))->ob_item[i] = (v))
CB 58  #define PyList_GET_SIZE(op)    Py_SIZE(op)
59  
60 +PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out);
61 +
62  #ifdef __cplusplus
63  }
64  #endif
71084d 65 diff --git a/Include/methodobject.h b/Include/methodobject.h
CS 66 index 6e160b6..1944517 100644
67 --- a/Include/methodobject.h
68 +++ b/Include/methodobject.h
6e8c2f 69 @@ -87,6 +87,10 @@ typedef struct {
CB 70  
71  PyAPI_FUNC(int) PyCFunction_ClearFreeList(void);
72  
73 +PyAPI_FUNC(void) _PyCFunction_DebugMallocStats(FILE *out);
74 +PyAPI_FUNC(void) _PyMethod_DebugMallocStats(FILE *out);
75 +
76 +
77  #ifdef __cplusplus
78  }
79  #endif
71084d 80 diff --git a/Include/object.h b/Include/object.h
CS 81 index afbc68d..ce5febf 100644
82 --- a/Include/object.h
83 +++ b/Include/object.h
84 @@ -1005,6 +1005,13 @@ PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(void);
6e8c2f 85              _PyTrash_thread_deposit_object((PyObject*)op); \
CB 86      } while (0);
87  
88 +PyAPI_FUNC(void)
89 +_PyDebugAllocatorStats(FILE *out, const char *block_name, int num_blocks,
90 +               size_t sizeof_block);
91 +
92 +PyAPI_FUNC(void)
93 +_PyObject_DebugTypeStats(FILE *out);
94 +
95  #ifdef __cplusplus
96  }
97  #endif
71084d 98 diff --git a/Include/objimpl.h b/Include/objimpl.h
CS 99 index 55e83ec..331b456 100644
100 --- a/Include/objimpl.h
101 +++ b/Include/objimpl.h
6e8c2f 102 @@ -101,13 +101,13 @@ PyAPI_FUNC(void) PyObject_Free(void *);
CB 103  
104  /* Macros */
105  #ifdef WITH_PYMALLOC
106 +PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out);
107  #ifdef PYMALLOC_DEBUG   /* WITH_PYMALLOC && PYMALLOC_DEBUG */
108  PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes);
109  PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes);
110  PyAPI_FUNC(void) _PyObject_DebugFree(void *p);
111  PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p);
112  PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p);
113 -PyAPI_FUNC(void) _PyObject_DebugMallocStats(void);
114  PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes);
115  PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes);
116  PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p);
71084d 117 diff --git a/Include/setobject.h b/Include/setobject.h
CS 118 index 52b07d5..143b175 100644
119 --- a/Include/setobject.h
120 +++ b/Include/setobject.h
121 @@ -93,6 +93,7 @@ PyAPI_FUNC(int) _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key,
122  PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set);
123  PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable);
124  
125 +PyAPI_FUNC(void) _PySet_DebugMallocStats(FILE *out);
126  #ifdef __cplusplus
127  }
128  #endif
129 diff --git a/Include/stringobject.h b/Include/stringobject.h
130 index 18b5b41..de78d76 100644
131 --- a/Include/stringobject.h
132 +++ b/Include/stringobject.h
133 @@ -204,6 +204,8 @@ PyAPI_FUNC(PyObject *) _PyBytes_FormatAdvanced(PyObject *obj,
6e8c2f 134                             char *format_spec,
CB 135                             Py_ssize_t format_spec_len);
136  
137 +PyAPI_FUNC(void) _PyString_DebugMallocStats(FILE *out);
138 +
139  #ifdef __cplusplus
140  }
141  #endif
71084d 142 diff --git a/Include/tupleobject.h b/Include/tupleobject.h
CS 143 index a5ab733..e233f47 100644
144 --- a/Include/tupleobject.h
145 +++ b/Include/tupleobject.h
146 @@ -54,7 +54,7 @@ PyAPI_FUNC(void) _PyTuple_MaybeUntrack(PyObject *);
147  #define PyTuple_SET_ITEM(op, i, v) (((PyTupleObject *)(op))->ob_item[i] = v)
148  
149  PyAPI_FUNC(int) PyTuple_ClearFreeList(void);
150 -
151 +PyAPI_FUNC(void) _PyTuple_DebugMallocStats(FILE *out);
152  #ifdef __cplusplus
153  }
154  #endif
155 diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h
156 index 9ab724a..b91250a 100644
157 --- a/Include/unicodeobject.h
158 +++ b/Include/unicodeobject.h
6e8c2f 159 @@ -1406,6 +1406,8 @@ PyAPI_FUNC(int) _PyUnicode_IsAlpha(
CB 160      Py_UNICODE ch       /* Unicode character */
161      );
162  
163 +PyAPI_FUNC(void) _PyUnicode_DebugMallocStats(FILE *out);
164 +
165  #ifdef __cplusplus
166  }
167  #endif
71084d 168 diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
CS 169 index 82243f3..8f1e1a0 100644
170 --- a/Lib/test/test_sys.py
171 +++ b/Lib/test/test_sys.py
172 @@ -488,6 +488,32 @@ class SysModuleTest(unittest.TestCase):
6e8c2f 173          p.wait()
CB 174          self.assertIn(executable, ["''", repr(sys.executable)])
175  
176 +    def test_debugmallocstats(self):
177 +        # Test sys._debugmallocstats()
178 +
179 +        import subprocess
180 +
181 +        # Verify the default of writing to stderr:
182 +        p = subprocess.Popen([sys.executable,
183 +                              '-c', 'import sys; sys._debugmallocstats()'],
184 +                             stderr=subprocess.PIPE)
185 +        out, err = p.communicate()
186 +        p.wait()
187 +        self.assertIn("arenas allocated current", err)
71084d 188 +
6e8c2f 189 +        # Verify that we can redirect the output to a file (not a file-like
CB 190 +        # object, though):
191 +        with open('mallocstats.txt', 'w') as out:
192 +            sys._debugmallocstats(out)
193 +        result = open('mallocstats.txt').read()
194 +        self.assertIn("arenas allocated current", result)
195 +        os.unlink('mallocstats.txt')
196 +
197 +        # Verify that the destination must be a file:
198 +        with self.assertRaises(TypeError):
199 +            sys._debugmallocstats(42)
71084d 200 +
6e8c2f 201 +
CB 202  class SizeofTest(unittest.TestCase):
203  
204      def setUp(self):
71084d 205 diff --git a/Objects/classobject.c b/Objects/classobject.c
CS 206 index 2c9c216..2ba7077 100644
207 --- a/Objects/classobject.c
208 +++ b/Objects/classobject.c
209 @@ -2694,3 +2694,12 @@ PyMethod_Fini(void)
6e8c2f 210  {
CB 211      (void)PyMethod_ClearFreeList();
212  }
213 +
214 +/* Print summary info about the state of the optimized allocator */
215 +void
216 +_PyMethod_DebugMallocStats(FILE *out)
217 +{
218 +    _PyDebugAllocatorStats(out,
219 +                           "free PyMethodObject",
220 +                           numfree, sizeof(PyMethodObject));
221 +}
71084d 222 diff --git a/Objects/dictobject.c b/Objects/dictobject.c
CS 223 index ba36b18..b8a5c7f 100644
224 --- a/Objects/dictobject.c
225 +++ b/Objects/dictobject.c
6e8c2f 226 @@ -225,6 +225,15 @@ show_track(void)
CB 227  static PyDictObject *free_list[PyDict_MAXFREELIST];
228  static int numfree = 0;
229  
230 +/* Print summary info about the state of the optimized allocator */
231 +void
232 +_PyDict_DebugMallocStats(FILE *out)
233 +{
234 +    _PyDebugAllocatorStats(out,
235 +                           "free PyDictObject", numfree, sizeof(PyDictObject));
236 +}
237 +
238 +
239  void
240  PyDict_Fini(void)
241  {
71084d 242 diff --git a/Objects/floatobject.c b/Objects/floatobject.c
CS 243 index ba867ef..533511d 100644
244 --- a/Objects/floatobject.c
245 +++ b/Objects/floatobject.c
6e8c2f 246 @@ -35,6 +35,22 @@ typedef struct _floatblock PyFloatBlock;
CB 247  static PyFloatBlock *block_list = NULL;
248  static PyFloatObject *free_list = NULL;
249  
250 +/* Print summary info about the state of the optimized allocator */
251 +void
252 +_PyFloat_DebugMallocStats(FILE *out)
253 +{
254 +  int num_blocks = 0;
255 +  PyFloatBlock *block;
256 +
257 +  /* Walk the block list, counting */
258 +  for (block = block_list; block ; block = block->next) {
259 +      num_blocks++;
260 +  }
261 +
262 +  _PyDebugAllocatorStats(out,
263 +                         "PyFloatBlock", num_blocks, sizeof(PyFloatBlock));
264 +}
265 +
266  static PyFloatObject *
267  fill_free_list(void)
268  {
71084d 269 diff --git a/Objects/frameobject.c b/Objects/frameobject.c
CS 270 index f9e4a0e..337fc58 100644
271 --- a/Objects/frameobject.c
272 +++ b/Objects/frameobject.c
273 @@ -982,3 +982,13 @@ PyFrame_Fini(void)
6e8c2f 274      Py_XDECREF(builtin_object);
CB 275      builtin_object = NULL;
276  }
277 +
278 +/* Print summary info about the state of the optimized allocator */
279 +void
280 +_PyFrame_DebugMallocStats(FILE *out)
281 +{
282 +    _PyDebugAllocatorStats(out,
283 +                           "free PyFrameObject",
284 +                           numfree, sizeof(PyFrameObject));
285 +}
286 +
71084d 287 diff --git a/Objects/intobject.c b/Objects/intobject.c
CS 288 index 28182f9..f442ea0 100644
289 --- a/Objects/intobject.c
290 +++ b/Objects/intobject.c
6e8c2f 291 @@ -44,6 +44,23 @@ typedef struct _intblock PyIntBlock;
CB 292  static PyIntBlock *block_list = NULL;
293  static PyIntObject *free_list = NULL;
294  
295 +
296 +/* Print summary info about the state of the optimized allocator */
297 +void
298 +_PyInt_DebugMallocStats(FILE *out)
299 +{
300 +    int num_blocks = 0;
301 +    PyIntBlock *block;
302 +
303 +    /* Walk the block list, counting */
304 +    for (block = block_list; block ; block = block->next) {
305 +        num_blocks++;
306 +    }
307 +
308 +    _PyDebugAllocatorStats(out,
309 +                           "PyIntBlock", num_blocks, sizeof(PyIntBlock));
310 +}
311 +
312  static PyIntObject *
313  fill_free_list(void)
314  {
71084d 315 diff --git a/Objects/listobject.c b/Objects/listobject.c
CS 316 index f753643..e6fa17d 100644
317 --- a/Objects/listobject.c
318 +++ b/Objects/listobject.c
6e8c2f 319 @@ -109,6 +109,15 @@ PyList_Fini(void)
CB 320      }
321  }
322  
323 +/* Print summary info about the state of the optimized allocator */
324 +void
325 +_PyList_DebugMallocStats(FILE *out)
326 +{
327 +    _PyDebugAllocatorStats(out,
328 +                           "free PyListObject",
329 +                           numfree, sizeof(PyListObject));
330 +}
331 +
332  PyObject *
333  PyList_New(Py_ssize_t size)
334  {
71084d 335 diff --git a/Objects/methodobject.c b/Objects/methodobject.c
CS 336 index 0b60ca3..3193135 100644
337 --- a/Objects/methodobject.c
338 +++ b/Objects/methodobject.c
6e8c2f 339 @@ -412,6 +412,15 @@ PyCFunction_Fini(void)
CB 340      (void)PyCFunction_ClearFreeList();
341  }
342  
343 +/* Print summary info about the state of the optimized allocator */
344 +void
345 +_PyCFunction_DebugMallocStats(FILE *out)
346 +{
347 +    _PyDebugAllocatorStats(out,
348 +                           "free PyCFunction",
349 +                           numfree, sizeof(PyCFunction));
350 +}
351 +
352  /* PyCFunction_New() is now just a macro that calls PyCFunction_NewEx(),
353     but it's part of the API so we need to keep a function around that
354     existing C extensions can call.
71084d 355 diff --git a/Objects/object.c b/Objects/object.c
CS 356 index 14f4e9f..68aedcd 100644
357 --- a/Objects/object.c
358 +++ b/Objects/object.c
359 @@ -2355,6 +2355,23 @@ PyMem_Free(void *p)
6e8c2f 360      PyMem_FREE(p);
CB 361  }
362  
363 +void
364 +_PyObject_DebugTypeStats(FILE *out)
365 +{
366 +    _PyString_DebugMallocStats(out);
367 +    _PyCFunction_DebugMallocStats(out);
368 +    _PyDict_DebugMallocStats(out);
369 +    _PyFloat_DebugMallocStats(out);
370 +    _PyFrame_DebugMallocStats(out);
371 +    _PyInt_DebugMallocStats(out);
372 +    _PyList_DebugMallocStats(out);
373 +    _PyMethod_DebugMallocStats(out);
374 +    _PySet_DebugMallocStats(out);
375 +    _PyTuple_DebugMallocStats(out);
376 +#if Py_USING_UNICODE
377 +    _PyUnicode_DebugMallocStats(out);
378 +#endif
379 +}
380  
381  /* These methods are used to control infinite recursion in repr, str, print,
382     etc.  Container objects that may recursively contain themselves,
71084d 383 diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
CS 384 index 38ebc37..2c05359 100644
385 --- a/Objects/obmalloc.c
386 +++ b/Objects/obmalloc.c
387 @@ -508,12 +508,10 @@ static struct arena_object* usable_arenas = NULL;
6e8c2f 388  /* Number of arenas allocated that haven't been free()'d. */
CB 389  static size_t narenas_currently_allocated = 0;
390  
391 -#ifdef PYMALLOC_DEBUG
392  /* Total number of times malloc() called to allocate an arena. */
393  static size_t ntimes_arena_allocated = 0;
394  /* High water mark (max value ever seen) for narenas_currently_allocated. */
395  static size_t narenas_highwater = 0;
396 -#endif
397  
398  /* Allocate a new arena.  If we run out of memory, return NULL.  Else
399   * allocate a new arena, and return the address of an arena_object
400 @@ -528,7 +526,7 @@ new_arena(void)
401  
402  #ifdef PYMALLOC_DEBUG
403      if (Py_GETENV("PYTHONMALLOCSTATS"))
404 -        _PyObject_DebugMallocStats();
405 +        _PyObject_DebugMallocStats(stderr);
406  #endif
407      if (unused_arena_objects == NULL) {
408          uint i;
409 @@ -588,11 +586,9 @@ new_arena(void)
410      }
411  
412      ++narenas_currently_allocated;
413 -#ifdef PYMALLOC_DEBUG
414      ++ntimes_arena_allocated;
415      if (narenas_currently_allocated > narenas_highwater)
416          narenas_highwater = narenas_currently_allocated;
417 -#endif
418      arenaobj->freepools = NULL;
419      /* pool_address <- first pool-aligned address in the arena
420         nfreepools <- number of whole pools that fit after alignment */
71084d 421 @@ -1694,17 +1690,19 @@ _PyObject_DebugDumpAddress(const void *p)
6e8c2f 422      }
CB 423  }
424  
425 +#endif  /* PYMALLOC_DEBUG */
426 +
427  static size_t
428 -printone(const char* msg, size_t value)
429 +printone(FILE *out, const char* msg, size_t value)
430  {
431      int i, k;
432      char buf[100];
433      size_t origvalue = value;
434  
435 -    fputs(msg, stderr);
436 +    fputs(msg, out);
437      for (i = (int)strlen(msg); i < 35; ++i)
438 -        fputc(' ', stderr);
439 -    fputc('=', stderr);
440 +        fputc(' ', out);
441 +    fputc('=', out);
442  
443      /* Write the value with commas. */
444      i = 22;
445 @@ -1725,17 +1723,32 @@ printone(const char* msg, size_t value)
446  
447      while (i >= 0)
448          buf[i--] = ' ';
449 -    fputs(buf, stderr);
450 +    fputs(buf, out);
451  
452      return origvalue;
453  }
454  
455 -/* Print summary info to stderr about the state of pymalloc's structures.
456 +void
457 +_PyDebugAllocatorStats(FILE *out,
458 +                       const char *block_name, int num_blocks, size_t sizeof_block)
459 +{
460 +       char buf1[128];
461 +       char buf2[128];
462 +       PyOS_snprintf(buf1, sizeof(buf1),
463 +                     "%d %ss * %zd bytes each",
464 +                     num_blocks, block_name, sizeof_block);
465 +       PyOS_snprintf(buf2, sizeof(buf2),
466 +                     "%48s ", buf1);
467 +      (void)printone(out, buf2, num_blocks * sizeof_block);
468 +}
469 +
470 +
471 +/* Print summary info to "out" about the state of pymalloc's structures.
472   * In Py_DEBUG mode, also perform some expensive internal consistency
473   * checks.
474   */
475  void
476 -_PyObject_DebugMallocStats(void)
477 +_PyObject_DebugMallocStats(FILE *out)
478  {
479      uint i;
480      const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT;
481 @@ -1764,7 +1777,7 @@ _PyObject_DebugMallocStats(void)
482      size_t total;
483      char buf[128];
484  
485 -    fprintf(stderr, "Small block threshold = %d, in %u size classes.\n",
486 +    fprintf(out, "Small block threshold = %d, in %u size classes.\n",
487              SMALL_REQUEST_THRESHOLD, numclasses);
488  
489      for (i = 0; i < numclasses; ++i)
490 @@ -1818,10 +1831,10 @@ _PyObject_DebugMallocStats(void)
491      }
492      assert(narenas == narenas_currently_allocated);
493  
494 -    fputc('\n', stderr);
495 +    fputc('\n', out);
496      fputs("class   size   num pools   blocks in use  avail blocks\n"
497            "-----   ----   ---------   -------------  ------------\n",
498 -          stderr);
499 +          out);
500  
501      for (i = 0; i < numclasses; ++i) {
502          size_t p = numpools[i];
503 @@ -1832,7 +1845,7 @@ _PyObject_DebugMallocStats(void)
504              assert(b == 0 && f == 0);
505              continue;
506          }
507 -        fprintf(stderr, "%5u %6u "
508 +        fprintf(out, "%5u %6u "
509                          "%11" PY_FORMAT_SIZE_T "u "
510                          "%15" PY_FORMAT_SIZE_T "u "
511                          "%13" PY_FORMAT_SIZE_T "u\n",
512 @@ -1842,36 +1855,35 @@ _PyObject_DebugMallocStats(void)
513          pool_header_bytes += p * POOL_OVERHEAD;
514          quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size);
515      }
516 -    fputc('\n', stderr);
517 -    (void)printone("# times object malloc called", serialno);
518 -
519 -    (void)printone("# arenas allocated total", ntimes_arena_allocated);
520 -    (void)printone("# arenas reclaimed", ntimes_arena_allocated - narenas);
521 -    (void)printone("# arenas highwater mark", narenas_highwater);
522 -    (void)printone("# arenas allocated current", narenas);
523 +    fputc('\n', out);
524 +#ifdef PYMALLOC_DEBUG
525 +    (void)printone(out, "# times object malloc called", serialno);
526 +#endif
527 +    (void)printone(out, "# arenas allocated total", ntimes_arena_allocated);
528 +    (void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas);
529 +    (void)printone(out, "# arenas highwater mark", narenas_highwater);
530 +    (void)printone(out, "# arenas allocated current", narenas);
531  
532      PyOS_snprintf(buf, sizeof(buf),
533          "%" PY_FORMAT_SIZE_T "u arenas * %d bytes/arena",
534          narenas, ARENA_SIZE);
535 -    (void)printone(buf, narenas * ARENA_SIZE);
536 +    (void)printone(out, buf, narenas * ARENA_SIZE);
537  
538 -    fputc('\n', stderr);
539 +    fputc('\n', out);
540  
541 -    total = printone("# bytes in allocated blocks", allocated_bytes);
542 -    total += printone("# bytes in available blocks", available_bytes);
543 +    total = printone(out, "# bytes in allocated blocks", allocated_bytes);
544 +    total += printone(out, "# bytes in available blocks", available_bytes);
545  
546      PyOS_snprintf(buf, sizeof(buf),
547          "%u unused pools * %d bytes", numfreepools, POOL_SIZE);
548 -    total += printone(buf, (size_t)numfreepools * POOL_SIZE);
549 +    total += printone(out, buf, (size_t)numfreepools * POOL_SIZE);
550  
551 -    total += printone("# bytes lost to pool headers", pool_header_bytes);
552 -    total += printone("# bytes lost to quantization", quantization);
553 -    total += printone("# bytes lost to arena alignment", arena_alignment);
554 -    (void)printone("Total", total);
555 +    total += printone(out, "# bytes lost to pool headers", pool_header_bytes);
556 +    total += printone(out, "# bytes lost to quantization", quantization);
557 +    total += printone(out, "# bytes lost to arena alignment", arena_alignment);
558 +    (void)printone(out, "Total", total);
559  }
560  
561 -#endif  /* PYMALLOC_DEBUG */
562 -
563  #ifdef Py_USING_MEMORY_DEBUGGER
564  /* Make this function last so gcc won't inline it since the definition is
565   * after the reference.
71084d 566 diff --git a/Objects/setobject.c b/Objects/setobject.c
CS 567 index af1ce16..3439b7c 100644
568 --- a/Objects/setobject.c
569 +++ b/Objects/setobject.c
6e8c2f 570 @@ -1088,6 +1088,16 @@ PySet_Fini(void)
CB 571      Py_CLEAR(emptyfrozenset);
572  }
573  
574 +/* Print summary info about the state of the optimized allocator */
575 +void
576 +_PySet_DebugMallocStats(FILE *out)
577 +{
578 +    _PyDebugAllocatorStats(out,
579 +                           "free PySetObject",
580 +                           numfree, sizeof(PySetObject));
581 +}
582 +
583 +
584  static PyObject *
585  set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
586  {
71084d 587 diff --git a/Objects/stringobject.c b/Objects/stringobject.c
CS 588 index 1209197..b8646dd 100644
589 --- a/Objects/stringobject.c
590 +++ b/Objects/stringobject.c
591 @@ -4843,3 +4843,43 @@ void _Py_ReleaseInternedStrings(void)
6e8c2f 592      PyDict_Clear(interned);
CB 593      Py_CLEAR(interned);
594  }
595 +
596 +void _PyString_DebugMallocStats(FILE *out)
597 +{
598 +    ssize_t i;
599 +    int num_immortal = 0, num_mortal = 0;
600 +    ssize_t immortal_size = 0, mortal_size = 0;
601 +
602 +    if (interned == NULL || !PyDict_Check(interned))
603 +        return;
604 +
605 +    for (i = 0; i <= ((PyDictObject*)interned)->ma_mask; i++) {
606 +        PyDictEntry *ep = ((PyDictObject*)interned)->ma_table + i;
607 +        PyObject *pvalue = ep->me_value;
608 +        if (pvalue != NULL) {
609 +            PyStringObject *s = (PyStringObject *)ep->me_key;
610 +
611 +            switch (s->ob_sstate) {
612 +            case SSTATE_NOT_INTERNED:
613 +                /* XXX Shouldn't happen */
614 +                break;
615 +            case SSTATE_INTERNED_IMMORTAL:
616 +                num_immortal ++;
617 +                immortal_size += s->ob_size;
618 +                break;
619 +            case SSTATE_INTERNED_MORTAL:
620 +                num_mortal ++;
621 +                mortal_size += s->ob_size;
622 +                break;
623 +            default:
624 +                Py_FatalError("Inconsistent interned string state.");
625 +            }
626 +        }
627 +    }
628 +
629 +    fprintf(out, "%d mortal interned strings\n", num_mortal);
630 +    fprintf(out, "%d immortal interned strings\n", num_immortal);
631 +    fprintf(out, "total size of all interned strings: "
632 +            "%zi/%zi "
633 +            "mortal/immortal\n", mortal_size, immortal_size);
634 +}
71084d 635 diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
CS 636 index 00f2e47..7682d81 100644
637 --- a/Objects/tupleobject.c
638 +++ b/Objects/tupleobject.c
6e8c2f 639 @@ -44,6 +44,22 @@ show_track(void)
CB 640  }
641  #endif
642  
643 +/* Print summary info about the state of the optimized allocator */
644 +void
645 +_PyTuple_DebugMallocStats(FILE *out)
646 +{
647 +#if PyTuple_MAXSAVESIZE > 0
648 +    int i;
649 +    char buf[128];
650 +    for (i = 1; i < PyTuple_MAXSAVESIZE; i++) {
651 +        PyOS_snprintf(buf, sizeof(buf),
652 +                      "free %d-sized PyTupleObject", i);
653 +        _PyDebugAllocatorStats(out,
654 +                               buf,
655 +                               numfree[i], _PyObject_VAR_SIZE(&PyTuple_Type, i));
656 +    }
657 +#endif
658 +}
659  
660  PyObject *
661  PyTuple_New(register Py_ssize_t size)
71084d 662 diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
CS 663 index 6bea370..ced9acf 100644
664 --- a/Objects/unicodeobject.c
665 +++ b/Objects/unicodeobject.c
666 @@ -8920,6 +8920,12 @@ _PyUnicode_Fini(void)
6e8c2f 667      (void)PyUnicode_ClearFreeList();
CB 668  }
669  
670 +void _PyUnicode_DebugMallocStats(FILE *out)
671 +{
672 +    _PyDebugAllocatorStats(out, "free PyUnicodeObject", numfree,
673 +                           sizeof(PyUnicodeObject));
674 +}
675 +
676  #ifdef __cplusplus
677  }
678  #endif
71084d 679 diff --git a/Python/pythonrun.c b/Python/pythonrun.c
CS 680 index f0fbd74..0b73f3a 100644
681 --- a/Python/pythonrun.c
682 +++ b/Python/pythonrun.c
683 @@ -557,7 +557,7 @@ Py_Finalize(void)
6e8c2f 684  #endif /* Py_TRACE_REFS */
CB 685  #ifdef PYMALLOC_DEBUG
686      if (Py_GETENV("PYTHONMALLOCSTATS"))
687 -        _PyObject_DebugMallocStats();
688 +        _PyObject_DebugMallocStats(stderr);
689  #endif
690  
691      call_ll_exitfuncs();
71084d 692 diff --git a/Python/sysmodule.c b/Python/sysmodule.c
CS 693 index 2a7c207..fbb637b 100644
694 --- a/Python/sysmodule.c
695 +++ b/Python/sysmodule.c
696 @@ -873,6 +873,57 @@ a 11-tuple where the entries in the tuple are counts of:\n\
6e8c2f 697  extern "C" {
CB 698  #endif
699  
700 +static PyObject *
701 +sys_debugmallocstats(PyObject *self, PyObject *args)
702 +{
703 +    PyObject *file = NULL;
704 +    FILE *fp;
705 +
706 +    if (!PyArg_ParseTuple(args, "|O!",
707 +                          &PyFile_Type, &file)) {
708 +      return NULL;
709 +    }
710 +    if (!file) {
711 +        /* Default to sys.stderr: */
712 +      file = PySys_GetObject("stderr");
713 +      if (!file) {
714 +          PyErr_SetString(PyExc_ValueError, "sys.stderr not set");
715 +          return NULL;
716 +      }
717 +      if (!PyFile_Check(file)) {
718 +          PyErr_SetString(PyExc_TypeError, "sys.stderr is not a file");
719 +          return NULL;
720 +      }
721 +    }
722 +
723 +    Py_INCREF(file);
724 +    /* OK, we now own a ref on non-NULL "file" */
725 +
726 +    fp = PyFile_AsFile(file);
727 +    if (!fp) {
728 +        PyErr_SetString(PyExc_ValueError, "file is closed");
729 +        Py_DECREF(file);
71084d 730 +        return NULL;
6e8c2f 731 +    }
CB 732 +
733 +    _PyObject_DebugMallocStats(fp);
734 +    fputc('\n', fp);
735 +    _PyObject_DebugTypeStats(fp);
736 +
737 +    Py_DECREF(file);
738 +
739 +    Py_RETURN_NONE;
740 +}
741 +PyDoc_STRVAR(debugmallocstats_doc,
742 +"_debugmallocstats([file])\n\
743 +\n\
744 +Print summary info to the given file (or sys.stderr) about the state of\n\
745 +pymalloc's structures.\n\
746 +\n\
747 +In Py_DEBUG mode, also perform some expensive internal consistency\n\
748 +checks.\n\
749 +");
750 +
751  #ifdef Py_TRACE_REFS
752  /* Defined in objects.c because it uses static globals if that file */
753  extern PyObject *_Py_GetObjects(PyObject *, PyObject *);
71084d 754 @@ -971,6 +1022,8 @@ static PyMethodDef sys_methods[] = {
6e8c2f 755      {"settrace",        sys_settrace, METH_O, settrace_doc},
CB 756      {"gettrace",        sys_gettrace, METH_NOARGS, gettrace_doc},
757      {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc},
758 +    {"_debugmallocstats", sys_debugmallocstats, METH_VARARGS,
759 +     debugmallocstats_doc},
760      {NULL,              NULL}           /* sentinel */
761  };
762