1 import gc
2 import inspect
3 import os
4 import sys
5 import time
6
7 try:
8 import objgraph
9 except ImportError:
10 objgraph = None
11
12 import cherrypy
13 from cherrypy import _cprequest, _cpwsgi
14 from cherrypy.process.plugins import SimplePlugin
15
16
18 """An object which gathers all referrers of an object to a given depth."""
19
20 peek_length = 40
21
22 - def __init__(self, ignore=None, maxdepth=2, maxparents=10):
23 self.ignore = ignore or []
24 self.ignore.append(inspect.currentframe().f_back)
25 self.maxdepth = maxdepth
26 self.maxparents = maxparents
27
28 - def ascend(self, obj, depth=1):
29 """Return a nested list containing referrers of the given object."""
30 depth += 1
31 parents = []
32
33
34
35 refs = gc.get_referrers(obj)
36 self.ignore.append(refs)
37 if len(refs) > self.maxparents:
38 return [("[%s referrers]" % len(refs), [])]
39
40 try:
41 ascendcode = self.ascend.__code__
42 except AttributeError:
43 ascendcode = self.ascend.im_func.func_code
44 for parent in refs:
45 if inspect.isframe(parent) and parent.f_code is ascendcode:
46 continue
47 if parent in self.ignore:
48 continue
49 if depth <= self.maxdepth:
50 parents.append((parent, self.ascend(parent, depth)))
51 else:
52 parents.append((parent, []))
53
54 return parents
55
57 """Return s, restricted to a sane length."""
58 if len(s) > (self.peek_length + 3):
59 half = self.peek_length // 2
60 return s[:half] + '...' + s[-half:]
61 else:
62 return s
63
88
97 ascend(tree)
98 return output
99
100
102 return [x for x in gc.get_objects() if isinstance(x, cls)]
103
104
115 request_counter = RequestCounter(cherrypy.engine)
116 request_counter.subscribe()
117
118
119 -def get_context(obj):
120 if isinstance(obj, _cprequest.Request):
121 return "path=%s;stage=%s" % (obj.path_info, obj.stage)
122 elif isinstance(obj, _cprequest.Response):
123 return "status=%s" % obj.status
124 elif isinstance(obj, _cpwsgi.AppResponse):
125 return "PATH_INFO=%s" % obj.environ.get('PATH_INFO', '')
126 elif hasattr(obj, "tb_lineno"):
127 return "tb_lineno=%s" % obj.tb_lineno
128 return ""
129
130
132 """A CherryPy page handler for testing reference leaks."""
133
134 classes = [(_cprequest.Request, 2, 2,
135 "Should be 1 in this request thread and 1 in the main thread."),
136 (_cprequest.Response, 2, 2,
137 "Should be 1 in this request thread and 1 in the main thread."),
138 (_cpwsgi.AppResponse, 1, 1,
139 "Should be 1 in this request thread only."),
140 ]
141
143 return "Hello, world!"
144 index.exposed = True
145
147 output = ["Statistics:"]
148
149 for trial in range(10):
150 if request_counter.count > 0:
151 break
152 time.sleep(0.5)
153 else:
154 output.append("\nNot all requests closed properly.")
155
156
157
158
159 gc.collect()
160 gc.collect()
161 unreachable = gc.collect()
162 if unreachable:
163 if objgraph is not None:
164 final = objgraph.by_type('Nondestructible')
165 if final:
166 objgraph.show_backrefs(final, filename='finalizers.png')
167
168 trash = {}
169 for x in gc.garbage:
170 trash[type(x)] = trash.get(type(x), 0) + 1
171 if trash:
172 output.insert(0, "\n%s unreachable objects:" % unreachable)
173 trash = [(v, k) for k, v in trash.items()]
174 trash.sort()
175 for pair in trash:
176 output.append(" " + repr(pair))
177
178
179
180
181
182 allobjs = {}
183 for cls, minobj, maxobj, msg in self.classes:
184 allobjs[cls] = get_instances(cls)
185
186 for cls, minobj, maxobj, msg in self.classes:
187 objs = allobjs[cls]
188 lenobj = len(objs)
189 if lenobj < minobj or lenobj > maxobj:
190 if minobj == maxobj:
191 output.append(
192 "\nExpected %s %r references, got %s." %
193 (minobj, cls, lenobj))
194 else:
195 output.append(
196 "\nExpected %s to %s %r references, got %s." %
197 (minobj, maxobj, cls, lenobj))
198
199 for obj in objs:
200 if objgraph is not None:
201 ig = [id(objs), id(inspect.currentframe())]
202 fname = "graph_%s_%s.png" % (cls.__name__, id(obj))
203 objgraph.show_backrefs(
204 obj, extra_ignore=ig, max_depth=4, too_many=20,
205 filename=fname, extra_info=get_context)
206 output.append("\nReferrers for %s (refcount=%s):" %
207 (repr(obj), sys.getrefcount(obj)))
208 t = ReferrerTree(ignore=[objs], maxdepth=3)
209 tree = t.ascend(obj)
210 output.extend(t.format(tree))
211
212 return "\n".join(output)
213 stats.exposed = True
214