trackrefs.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. ##############################################################################
  2. #
  3. # Copyright (c) 2007 Zope Corporation and Contributors.
  4. # All Rights Reserved.
  5. #
  6. # This software is subject to the provisions of the Zope Public License,
  7. # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
  8. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  9. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  10. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  11. # FOR A PARTICULAR PURPOSE
  12. #
  13. ##############################################################################
  14. """ Code from the zope.testing module to help track down memory leaks
  15. TrackRefs works only in a python compiled with the --with-pydebug flag.
  16. An example of how to use TrackRefs in a function is below.
  17. glen = 0
  18. rc = 0
  19. def doit():
  20. newglen = gc.collect()
  21. global glen
  22. if newglen > glen:
  23. print
  24. print "-------------------------------------"
  25. print "more garbage", newglen - glen
  26. glen = newglen
  27. print "-------------------------------------"
  28. print
  29. if refs:
  30. newrc = sys.gettotalrefcount()
  31. global rc
  32. if newrc > rc:
  33. refs.update()
  34. refs.detailed_refcounts(newrc, rc)
  35. rc = newrc
  36. """
  37. import sys
  38. import gc
  39. import types
  40. class TrackRefs(object):
  41. """Object to track reference counts across test runs."""
  42. def __init__(self):
  43. self.type2count = {}
  44. self.type2all = {}
  45. self.delta = None
  46. self.n = 0
  47. self.update()
  48. self.delta = None
  49. def update(self):
  50. gc.collect()
  51. obs = sys.getobjects(0)
  52. type2count = {}
  53. type2all = {}
  54. n = 0
  55. for o in obs:
  56. if type(o) is str and o == '<dummy key>':
  57. # avoid dictionary madness
  58. continue
  59. all = sys.getrefcount(o) - 3
  60. n += all
  61. t = type(o)
  62. if t is types.InstanceType:
  63. t = o.__class__
  64. if t in type2count:
  65. type2count[t] += 1
  66. type2all[t] += all
  67. else:
  68. type2count[t] = 1
  69. type2all[t] = all
  70. ct = [(
  71. type_or_class_title(t),
  72. type2count[t] - self.type2count.get(t, 0),
  73. type2all[t] - self.type2all.get(t, 0),
  74. )
  75. for t in type2count.iterkeys()]
  76. ct += [(
  77. type_or_class_title(t),
  78. - self.type2count[t],
  79. - self.type2all[t],
  80. )
  81. for t in self.type2count.iterkeys()
  82. if t not in type2count]
  83. ct.sort()
  84. self.delta = ct
  85. self.type2count = type2count
  86. self.type2all = type2all
  87. self.n = n
  88. def output(self):
  89. printed = False
  90. s1 = s2 = 0
  91. for t, delta1, delta2 in self.delta:
  92. if delta1 or delta2:
  93. if not printed:
  94. print (
  95. ' Leak details, changes in instances and refcounts'
  96. ' by type/class:')
  97. print " %-55s %6s %6s" % ('type/class', 'insts', 'refs')
  98. print " %-55s %6s %6s" % ('-' * 55, '-----', '----')
  99. printed = True
  100. print " %-55s %6d %6d" % (t, delta1, delta2)
  101. s1 += delta1
  102. s2 += delta2
  103. if printed:
  104. print " %-55s %6s %6s" % ('-' * 55, '-----', '----')
  105. print " %-55s %6s %6s" % ('total', s1, s2)
  106. self.delta = None
  107. def detailed_refcounts(self, rc, prev):
  108. """Report a change in reference counts, with extra detail."""
  109. print (" sum detail refcount=%-8d"
  110. " sys refcount=%-8d"
  111. " change=%-6d"
  112. % (self.n, rc, rc - prev))
  113. self.output()
  114. def type_or_class_title(t):
  115. module = getattr(t, '__module__', '__builtin__')
  116. if module == '__builtin__':
  117. return t.__name__
  118. return "%s.%s" % (module, t.__name__)