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

Source Code for Module flumotion.common.debug

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