00001 import inspect, gc
00002 from collections import deque
00003 import yapgvb
00004 import os.path
00005
00006 def follow_back(a, n):
00007 """Handy function for seeing why an object is still live by walking
00008 back through its referrers."""
00009 def print_elem(e):
00010 print repr(e)[0:200]
00011 try:
00012 print e.f_code.co_filename, e.f_lineno
00013
00014
00015 except:
00016 pass
00017 print
00018 print "Follow back:"
00019 print_elem(a)
00020 for i in range(n):
00021 r = gc.get_referrers(a)
00022 r.remove(inspect.currentframe())
00023 print
00024 print len(r)
00025 for e in r:
00026 print_elem(e)
00027 a = r[0]
00028
00029 def compare_before_after(before, after):
00030 """Handy function to compare live objects before and after some
00031 operation, to debug why memory is leaking."""
00032 beforeids = set(id(e) for e in before)
00033 afterids = set(id(e) for e in after)
00034 delta = afterids - beforeids - set([id(before)])
00035 for e in after:
00036 if id(e) in delta:
00037 print e
00038
00039 def dump_back_reference_graph(obj, maxdepth):
00040 obj = obj()
00041 if obj is None:
00042 print "Weakref was freed."
00043 return
00044
00045 curframe = inspect.currentframe()
00046
00047 todo = deque([(obj, 0)])
00048 strings = {}
00049 objects = {}
00050 depths = {}
00051 edges = []
00052 skipped = set()
00053
00054 def element_string(e):
00055 if type(e) == list:
00056 return "list"
00057 if type(e) == type(curframe):
00058 return "frame: %s:%i"%(os.path.basename(e.f_code.co_filename), e.f_lineno)
00059 if type(e) == dict:
00060 return "\n".join(["%10s : %20s"%(str(k)[0:10], str(v)[0:10]) for k, v in e.iteritems()][0:10])
00061
00062
00063
00064 return str(e)[0:40]
00065
00066 def list_str_bounded(l, join_str, max_indices, max_elem):
00067 l = [str(e)[0:max_elem] for e in l]
00068 if len(l) > max_indices:
00069 l = l[0:max_indices]
00070 l.append('...')
00071 return join_str.join(l)
00072
00073 def edge_string(e1, e2):
00074 if type(e1) == list:
00075 return list_str_bounded([i for i in range(len(e1)) if e1[i] == e2], ", ", 10, 10)
00076
00077 if type(e1) == dict:
00078 keys = [str(k)[0:20] for (k,v) in e1.iteritems() if e2 == v]
00079 return list_str_bounded([k for (k,v) in e1.iteritems() if e2 == v], "\n", 10, 20)
00080
00081 return list_str_bounded([a for a in dir(e1) if e1.__getattribute__(a) == e2], "\n", 10, 20)
00082
00083 def dont_trace(e):
00084 if type(e) == type(inspect):
00085 return True
00086 return False
00087
00088 while todo:
00089 e, d = todo.popleft()
00090 ide = id(e)
00091 if ide in strings or d > maxdepth:
00092 continue
00093 strings[ide] = element_string(e)
00094 depths[ide] = d
00095 objects[ide] = e
00096 if dont_trace(e):
00097 skipped.add(ide)
00098 continue
00099 d += 1
00100 refs = gc.get_referrers(e)
00101 refs.remove(curframe)
00102 for r in list(refs):
00103 if r in todo or r == objects:
00104 refs.remove(r)
00105 todo.extend((r, d) for r in refs)
00106 edges.extend((id(r), ide) for r in refs)
00107 del refs
00108
00109 print "Found %i nodes and %i edges"%(len(strings), len(edges))
00110
00111
00112
00113 graph = yapgvb.Digraph('Referrers')
00114 nodes = {}
00115 colors = ["red", "orange", "yellow", "green", "blue", "purple", "black"]
00116 ncol = len(colors)
00117 for (ids, s) in strings.items():
00118 nodes[ids] = graph.add_node(str(ids), label=s,
00119 color=colors[depths[ids]%ncol])
00120 if ids in skipped:
00121 nodes[ids].shape = 'box'
00122 if depths[ids] == maxdepth:
00123 nodes[ids].shape = 'parallelogram'
00124
00125 for (id1, id2) in edges:
00126 if id1 in nodes and id2 in nodes:
00127 edge = nodes[id1] >> nodes[id2]
00128 s = edge_string(objects[id1], objects[id2])
00129 if s:
00130 edge.label = s
00131
00132 graph.root = str(id(obj))
00133
00134 graph.layout(yapgvb.engines.dot)
00135 graph.render('gcgraph.ps')
00136 del objects
00137
00138 if __name__ == "__main__":
00139 import weakref
00140 l1 = []
00141 l2 = [l1]
00142
00143 l3 = [l1, l2]
00144 l4 = [l3]
00145 l1.append(l4)
00146 class Foo:
00147 pass
00148 f = Foo()
00149 f.l = l1
00150 l4.append(f)
00151 wr = weakref.ref(f)
00152 del l1
00153 del l2
00154 del l3
00155 del l4
00156 del f
00157 dump_back_reference_graph(wr, 10)