/circuits/web/apps/memorymonitor/reftree.py

https://bitbucket.org/prologic/circuits/ · Python · 188 lines · 138 code · 43 blank · 7 comment · 39 complexity · 1f34d35945ebb3c675303861378515b8 MD5 · raw file

  1. import gc
  2. import sys
  3. from types import FrameType
  4. class Tree:
  5. def __init__(self, obj):
  6. self.obj = obj
  7. self.filename = sys._getframe().f_code.co_filename
  8. self._ignore = {}
  9. def ignore(self, *objects):
  10. for obj in objects:
  11. self._ignore[id(obj)] = None
  12. def ignore_caller(self):
  13. f = sys._getframe() # = this function
  14. cur = f.f_back # = the function that called us (probably 'walk')
  15. self.ignore(cur, cur.f_builtins, cur.f_locals, cur.f_globals)
  16. caller = f.f_back # = the 'real' caller
  17. self.ignore(caller, caller.f_builtins, caller.f_locals, caller.f_globals)
  18. def walk(self, maxresults=100, maxdepth=None):
  19. """Walk the object tree, ignoring duplicates and circular refs."""
  20. self.seen = {}
  21. self.ignore(self, self.__dict__, self.obj, self.seen, self._ignore)
  22. # Ignore the calling frame, its builtins, globals and locals
  23. self.ignore_caller()
  24. self.maxdepth = maxdepth
  25. count = 0
  26. for result in self._gen(self.obj):
  27. yield result
  28. count += 1
  29. if maxresults and count >= maxresults:
  30. yield 0, 0, "==== Max results reached ===="
  31. raise StopIteration
  32. def print_tree(self, maxresults=100, maxdepth=None):
  33. """Walk the object tree, pretty-printing each branch."""
  34. self.ignore_caller()
  35. for depth, refid, rep in self.walk(maxresults, maxdepth):
  36. print ("%9d" % refid), (" " * depth * 2), rep
  37. def _repr_container(obj):
  38. return "%s of len %s: %r" % (type(obj).__name__, len(obj), obj)
  39. repr_dict = _repr_container
  40. repr_set = _repr_container
  41. repr_list = _repr_container
  42. repr_tuple = _repr_container
  43. def repr_str(obj):
  44. return "%s of len %s: %r" % (type(obj).__name__, len(obj), obj)
  45. repr_unicode = repr_str
  46. def repr_frame(obj):
  47. return "frame from %s line %s" % (obj.f_code.co_filename, obj.f_lineno)
  48. def get_repr(obj, limit=250):
  49. typename = getattr(type(obj), "__name__", None)
  50. handler = globals().get("repr_%s" % typename, repr)
  51. try:
  52. result = handler(obj)
  53. except:
  54. result = "unrepresentable object: %r" % sys.exc_info()[1]
  55. if len(result) > limit:
  56. result = result[:limit] + "..."
  57. return result
  58. class ReferentTree(Tree):
  59. def _gen(self, obj, depth=0):
  60. if self.maxdepth and depth >= self.maxdepth:
  61. yield depth, 0, "---- Max depth reached ----"
  62. raise StopIteration
  63. for ref in gc.get_referents(obj):
  64. if id(ref) in self._ignore:
  65. continue
  66. elif id(ref) in self.seen:
  67. yield depth, id(ref), "!" + get_repr(ref)
  68. continue
  69. else:
  70. self.seen[id(ref)] = None
  71. yield depth, id(ref), get_repr(ref)
  72. for child in self._gen(ref, depth + 1):
  73. yield child
  74. class ReferrerTree(Tree):
  75. def _gen(self, obj, depth=0):
  76. if self.maxdepth and depth >= self.maxdepth:
  77. yield depth, 0, "---- Max depth reached ----"
  78. raise StopIteration
  79. refs = gc.get_referrers(obj)
  80. refiter = iter(refs)
  81. self.ignore(refs, refiter)
  82. for ref in refiter:
  83. # Exclude all frames that are from this module.
  84. if isinstance(ref, FrameType):
  85. if ref.f_code.co_filename == self.filename:
  86. continue
  87. if id(ref) in self._ignore:
  88. continue
  89. elif id(ref) in self.seen:
  90. yield depth, id(ref), "!" + get_repr(ref)
  91. continue
  92. else:
  93. self.seen[id(ref)] = None
  94. yield depth, id(ref), get_repr(ref)
  95. for parent in self._gen(ref, depth + 1):
  96. yield parent
  97. class CircularReferents(Tree):
  98. def walk(self, maxresults=100, maxdepth=None):
  99. """Walk the object tree, showing circular referents."""
  100. self.stops = 0
  101. self.seen = {}
  102. self.ignore(self, self.__dict__, self.seen, self._ignore)
  103. # Ignore the calling frame, its builtins, globals and locals
  104. self.ignore_caller()
  105. self.maxdepth = maxdepth
  106. count = 0
  107. for result in self._gen(self.obj):
  108. yield result
  109. count += 1
  110. if maxresults and count >= maxresults:
  111. yield 0, 0, "==== Max results reached ===="
  112. raise StopIteration
  113. def _gen(self, obj, depth=0, trail=None):
  114. if self.maxdepth and depth >= self.maxdepth:
  115. self.stops += 1
  116. raise StopIteration
  117. if trail is None:
  118. trail = []
  119. for ref in gc.get_referents(obj):
  120. if id(ref) in self._ignore:
  121. continue
  122. elif id(ref) in self.seen:
  123. continue
  124. else:
  125. self.seen[id(ref)] = None
  126. refrepr = get_repr(ref)
  127. if id(ref) == id(self.obj):
  128. yield trail + [refrepr,]
  129. for child in self._gen(ref, depth + 1, trail + [refrepr,]):
  130. yield child
  131. def print_tree(self, maxresults=100, maxdepth=None):
  132. """Walk the object tree, pretty-printing each branch."""
  133. self.ignore_caller()
  134. for trail in self.walk(maxresults, maxdepth):
  135. print trail
  136. if self.stops:
  137. print "%s paths stopped because max depth reached" % self.stops
  138. def count_objects():
  139. d = {}
  140. for obj in gc.get_objects():
  141. objtype = type(obj)
  142. d[objtype] = d.get(objtype, 0) + 1
  143. d = [(v, k) for k, v in d.iteritems()]
  144. d.sort()
  145. return d