Package flumotion :: Package common :: Module debug
[hide private]

Source Code for Module flumotion.common.debug

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3   
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  """debugging helper code 
 19  """ 
 20   
 21  import linecache 
 22  import gc 
 23  import re 
 24  import sys 
 25  import types 
 26   
 27  from twisted.python.reflect import filenameToModuleName 
 28   
 29  __version__ = "$Rev$" 
 30  _tracing = 0 
 31  _indent = '' 
 32   
 33   
34 -def trace_start(func_filter=None, ignore_files_re=None, print_returns=False, 35 write=None):
36 global _tracing, _indent 37 38 if func_filter: 39 func_filter = re.compile(func_filter) 40 41 if ignore_files_re: 42 ignore_files_re = re.compile(ignore_files_re) 43 44 if not write: 45 46 def write(indent, str, *args): 47 print (indent + str) % args
48 49 def do_trace(frame, event, arg): 50 global _tracing, _indent 51 52 if not _tracing: 53 print '[tracing stopped]' 54 return None 55 56 co = frame.f_code 57 58 if event == 'line': 59 return do_trace 60 if func_filter and not func_filter.search(co.co_name): 61 return None 62 if ignore_files_re and ignore_files_re.search(co.co_filename): 63 return None 64 elif event == 'call' or event == 'c_call': 65 if co.co_name == '?': 66 return None 67 module = filenameToModuleName(co.co_filename) 68 write(_indent, '%s:%d:%s():', module, frame.f_lineno, co.co_name) 69 _indent += ' ' 70 return do_trace 71 elif event == 'return' or event == 'c_return': 72 if print_returns: 73 write(_indent, 'return %r', arg) 74 _indent = _indent[:-2] 75 return None 76 elif event == 'exception' or event == 'c_exception': 77 if arg: 78 write(_indent, 'Exception: %s:%d: %s (%s)', co.co_filename, 79 frame.f_lineno, arg[0].__name__, arg[1]) 80 else: 81 write(_indent, 'Exception: (from C)') 82 return do_trace 83 else: 84 write(_indent, 'unknown event: %s', event) 85 return None 86 87 _tracing += 1 88 if _tracing == 1: 89 assert _indent == '' 90 sys.settrace(do_trace) 91 92
93 -def trace_stop():
94 global _tracing, _indent 95 assert _tracing > 0 96 _tracing -= 1 97 if not _tracing: 98 sys.settrace(None) 99 _indent = ''
100 101 127 128
129 -class UncollectableMonitor(object):
130
131 - def __init__(self, period=120):
132 known = {} 133 134 # set this if you want python to print out when uncollectable 135 # objects are detected; will print out all objects in the cycle, 136 # not just the one(s) that caused the cycle to be uncollectable 137 # 138 # gc.set_debug(gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | 139 # gc.DEBUG_OBJECTS) 140 141 from twisted.internet import reactor 142 143 def sample(): 144 gc.collect() 145 for o in gc.garbage: 146 if o not in known: 147 known[o] = True 148 self.uncollectable(o) 149 reactor.callLater(period, sample)
150 151 reactor.callLater(period, sample)
152
153 - def uncollectable(self, obj):
154 print '\nUncollectable object cycle in gc.garbage:' 155 156 print "Parents:" 157 self._printParents(obj, 2) 158 print "Kids:" 159 self._printKids(obj, 2)
160
161 - def _printParents(self, obj, level, indent=' '):
162 print indent, self._shortRepr(obj) 163 if level > 0: 164 for p in gc.get_referrers(obj): 165 self._printParents(p, level - 1, indent + ' ')
166
167 - def _printKids(self, obj, level, indent=' '):
168 print indent, self._shortRepr(obj) 169 if level > 0: 170 for kid in gc.get_referents(obj): 171 self._printKids(kid, level - 1, indent + ' ')
172
173 - def _shortRepr(self, obj):
174 if not isinstance(obj, dict): 175 return '%s %r @ 0x%x' % (type(obj).__name__, obj, id(obj)) 176 else: 177 keys = obj.keys() 178 keys.sort() 179 return 'dict with keys %r @ 0x%x' % (keys, id(obj))
180 181
182 -class AllocMonitor(object):
183
184 - def __init__(self, period=10, analyze=None, allocPrint=None):
185 self.period = period 186 self.objset = None 187 188 from sizer import scanner, annotate 189 190 from twisted.internet import reactor 191 192 if analyze is not None: 193 self.analyze = analyze 194 if allocPrint is not None: 195 self.allocPrint = allocPrint 196 197 def sample(): 198 objset = scanner.Objects() 199 annotate.markparents(objset) 200 201 if self.objset: 202 self.analyze(self.objset, objset) 203 204 self.objset = objset 205 reactor.callLater(self.period, sample)
206 207 reactor.callLater(self.period, sample)
208
209 - def analyze(self, old, new):
210 from sizer import operations 211 212 size = 0 213 214 for k in operations.diff(new, old): 215 size -= old[k].size 216 217 allocators = {} 218 diff = operations.diff(old, new) 219 for k in diff: 220 w = new[k] 221 size += w.size 222 if not w.parents: 223 print "Unreferenced object %r, what?" % (w, ) 224 for p in w.parents: 225 if id(p.obj) == id(self.__dict__): 226 continue 227 if id(p.obj) not in diff: 228 # print "Object %r alloced by %r" % (w, p) 229 if p not in allocators: 230 allocators[p] = [] 231 allocators[p].append(w) 232 print "Total alloc size:", size 233 for p in allocators: 234 if p.obj == old or p.obj == new: 235 print 'foo' 236 else: 237 self.allocPrint(p, allocators[p]) 238 for o in gc.garbage: 239 print '\nUncollectable object cycle in gc.garbage:' 240 self._printCycle(new[id(o)])
241
242 - def _printCycle(self, root):
243 print "Parents:" 244 self._printParents(root, 2) 245 print "Kids:" 246 self._printKids(root, 2)
247
248 - def _printParents(self, wrap, level, indent=' '):
249 print indent, self._wrapperRepr(wrap) 250 if level > 0: 251 for p in wrap.parents: 252 self._printParents(p, level - 1, indent + ' ')
253
254 - def _printKids(self, wrap, level, indent=' '):
255 print indent, self._wrapperRepr(wrap) 256 if level > 0: 257 for kid in wrap.children: 258 self._printKids(kid, level - 1, indent + ' ')
259
260 - def _allocStack(self, wrap, stack):
261 stack.append(wrap) 262 for p in wrap.parents: 263 if (isinstance(p.obj, types.ModuleType) 264 or isinstance(p.obj, type) 265 or isinstance(p.obj, types.InstanceType)): 266 stack.append(p) 267 return stack 268 if len(wrap.parents) == 1: 269 return self._allocStack(wrap.parents[0], stack) 270 return stack
271
272 - def _wrapperRepr(self, wrap):
273 o = wrap.obj 274 if wrap.type != dict: 275 return '%s %r @ 0x%x' % (wrap.type.__name__, o, id(o)) 276 else: 277 keys = o.keys() 278 keys.sort() 279 return 'dict with keys %r @ 0x%x' % (keys, id(o))
280
281 - def allocPrint(self, allocator, directAllocs):
282 allocStack = self._allocStack(allocator, []) 283 284 print '\nAlloc by ' + self._wrapperRepr(allocStack.pop(0)) 285 while allocStack: 286 print ' referenced by ' + self._wrapperRepr(allocStack.pop(0)) 287 288 print "%d new %s:" % (len(directAllocs), 289 len(directAllocs) == 1 and "object" or "objects") 290 for wrap in directAllocs: 291 print ' ' + self._wrapperRepr(wrap)
292 293
294 -def getVersions():
295 """ 296 Get versions of all flumotion modules based on svn Rev keyword. 297 """ 298 r = {} 299 for modname in sys.modules: 300 mod = sys.modules[modname] 301 if modname.startswith('flumotion.') and hasattr(mod, "__version__"): 302 # Has the form: "$Rev$" 303 try: 304 versionnum = int(mod.__version__[6:-2]) 305 r[modname] = versionnum 306 except IndexError: 307 pass 308 except ValueError: 309 pass 310 311 return r
312