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: 6982 $" 
 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 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 -def trace_stop():
93 global _tracing, _indent 94 assert _tracing > 0 95 _tracing -= 1 96 if not _tracing: 97 sys.settrace(None) 98 _indent = ''
99 124
125 -class UncollectableMonitor(object):
126 - def __init__(self, period=120):
127 known = {} 128 129 # set this if you want python to print out when uncollectable 130 # objects are detected; will print out all objects in the cycle, 131 # not just the one(s) that caused the cycle to be uncollectable 132 # 133 # gc.set_debug(gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | 134 # gc.DEBUG_OBJECTS) 135 136 from twisted.internet import reactor 137 138 def sample(): 139 gc.collect() 140 for o in gc.garbage: 141 if o not in known: 142 known[o] = True 143 self.uncollectable(o) 144 reactor.callLater(period, sample)
145 146 reactor.callLater(period, sample)
147
148 - def uncollectable(self, obj):
149 print '\nUncollectable object cycle in gc.garbage:' 150 151 print "Parents:" 152 self._printParents(obj, 2) 153 print "Kids:" 154 self._printKids(obj, 2)
155
156 - def _printParents(self, obj, level, indent=' '):
157 print indent, self._shortRepr(obj) 158 if level > 0: 159 for p in gc.get_referrers(obj): 160 self._printParents(p, level - 1, indent + ' ')
161
162 - def _printKids(self, obj, level, indent=' '):
163 print indent, self._shortRepr(obj) 164 if level > 0: 165 for kid in gc.get_referents(obj): 166 self._printKids(kid, level - 1, indent + ' ')
167
168 - def _shortRepr(self, obj):
169 if not isinstance(obj, dict): 170 return '%s %r @ 0x%x' % (type(obj).__name__, obj, id(obj)) 171 else: 172 keys = obj.keys() 173 keys.sort() 174 return 'dict with keys %r @ 0x%x' % (keys, id(obj))
175
176 -class AllocMonitor(object):
177 - def __init__(self, period=10, analyze=None, allocPrint=None):
178 self.period = period 179 self.objset = None 180 181 from sizer import scanner, annotate 182 183 from twisted.internet import reactor 184 185 if analyze is not None: 186 self.analyze = analyze 187 if allocPrint is not None: 188 self.allocPrint = allocPrint 189 190 def sample(): 191 objset = scanner.Objects() 192 annotate.markparents(objset) 193 194 if self.objset: 195 self.analyze(self.objset, objset) 196 197 self.objset = objset 198 reactor.callLater(self.period, sample)
199 200 reactor.callLater(self.period, sample)
201
202 - def analyze(self, old, new):
203 from sizer import operations 204 205 size = 0 206 207 for k in operations.diff(new, old): 208 size -= old[k].size 209 210 allocators = {} 211 diff = operations.diff(old, new) 212 for k in diff: 213 w = new[k] 214 size += w.size 215 if not w.parents: 216 print "Unreferenced object %r, what?" % (w,) 217 for p in w.parents: 218 if id(p.obj) == id(self.__dict__): 219 continue 220 if id(p.obj) not in diff: 221 # print "Object %r alloced by %r" % (w, p) 222 if p not in allocators: 223 allocators[p] = [] 224 allocators[p].append(w) 225 print "Total alloc size:", size 226 for p in allocators: 227 if p.obj == old or p.obj == new: 228 print 'foo' 229 else: 230 self.allocPrint(p, allocators[p]) 231 for o in gc.garbage: 232 print '\nUncollectable object cycle in gc.garbage:' 233 self._printCycle(new[id(o)])
234
235 - def _printCycle(self, root):
236 print "Parents:" 237 self._printParents(root, 2) 238 print "Kids:" 239 self._printKids(root, 2)
240
241 - def _printParents(self, wrap, level, indent=' '):
242 print indent, self._wrapperRepr(wrap) 243 if level > 0: 244 for p in wrap.parents: 245 self._printParents(p, level - 1, indent + ' ')
246
247 - def _printKids(self, wrap, level, indent=' '):
248 print indent, self._wrapperRepr(wrap) 249 if level > 0: 250 for kid in wrap.children: 251 self._printKids(kid, level - 1, indent + ' ')
252
253 - def _allocStack(self, wrap, stack):
254 stack.append(wrap) 255 for p in wrap.parents: 256 if (isinstance(p.obj, types.ModuleType) 257 or isinstance(p.obj, type) 258 or isinstance(p.obj, types.InstanceType)): 259 stack.append(p) 260 return stack 261 if len(wrap.parents) == 1: 262 return self._allocStack(wrap.parents[0], stack) 263 return stack
264
265 - def _wrapperRepr(self, wrap):
266 o = wrap.obj 267 if wrap.type != dict: 268 return '%s %r @ 0x%x' % (wrap.type.__name__, o, id(o)) 269 else: 270 keys = o.keys() 271 keys.sort() 272 return 'dict with keys %r @ 0x%x' % (keys, id(o))
273
274 - def allocPrint(self, allocator, directAllocs):
275 allocStack = self._allocStack(allocator, []) 276 277 print '\nAlloc by ' + self._wrapperRepr(allocStack.pop(0)) 278 while allocStack: 279 print ' referenced by ' + self._wrapperRepr(allocStack.pop(0)) 280 281 print "%d new %s:" % (len(directAllocs), 282 len(directAllocs) == 1 and "object" or "objects") 283 for wrap in directAllocs: 284 print ' ' + self._wrapperRepr(wrap)
285
286 -def getVersions():
287 """ 288 Get versions of all flumotion modules based on svn Rev keyword. 289 """ 290 r = {} 291 for modname in sys.modules: 292 mod = sys.modules[modname] 293 if modname.startswith('flumotion.') and hasattr(mod, "__version__"): 294 # Has the form: "$Rev: 6982 $" 295 try: 296 versionnum = int(mod.__version__[6:-2]) 297 r[modname] = versionnum 298 except IndexError: 299 pass 300 except ValueError: 301 pass 302 303 return r
304