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   
 20  """ 
 21  Debugging helper code 
 22  """ 
 23   
 24   
 25  import gc 
 26  import sys 
 27  import re 
 28  import linecache 
 29  import types 
 30   
 31  from twisted.python.reflect import filenameToModuleName 
 32   
 33   
 34  _tracing = 0 
 35  _indent = '' 
 36   
37 -def trace_start(func_filter=None, ignore_files_re=None, print_returns=False, 38 write=None):
39 global _tracing, _indent 40 41 if func_filter: 42 func_filter = re.compile(func_filter) 43 44 if ignore_files_re: 45 ignore_files_re = re.compile(ignore_files_re) 46 47 if not write: 48 def write(indent, str, *args): 49 print (indent + str) % args
50 51 def do_trace(frame, event, arg): 52 global _tracing, _indent 53 54 if not _tracing: 55 print '[tracing stopped]' 56 return None 57 58 co = frame.f_code 59 60 if event == 'line': 61 return do_trace 62 if func_filter and not func_filter.search(co.co_name): 63 return None 64 if ignore_files_re and ignore_files_re.search(co.co_filename): 65 return None 66 elif event == 'call' or event == 'c_call': 67 if co.co_name == '?': 68 return None 69 module = filenameToModuleName(co.co_filename) 70 write(_indent, '%s:%d:%s():', module, frame.f_lineno, co.co_name) 71 _indent += ' ' 72 return do_trace 73 elif event == 'return' or event == 'c_return': 74 if print_returns: 75 write(_indent, 'return %r', arg) 76 _indent = _indent[:-2] 77 return None 78 elif event == 'exception' or event == 'c_exception': 79 if arg: 80 write(_indent, 'Exception: %s:%d: %s (%s)', co.co_filename, 81 frame.f_lineno, arg[0].__name__, arg[1]) 82 else: 83 write(_indent, 'Exception: (from C)') 84 return do_trace 85 else: 86 write(_indent, 'unknown event: %s', event) 87 return None 88 89 _tracing += 1 90 if _tracing == 1: 91 assert _indent == '' 92 sys.settrace(do_trace) 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 126
127 -class UncollectableMonitor(object):
128 - def __init__(self, period=120):
129 known = {} 130 131 # set this if you want python to print out when uncollectable 132 # objects are detected; will print out all objects in the cycle, 133 # not just the one(s) that caused the cycle to be uncollectable 134 # 135 # gc.set_debug(gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | 136 # gc.DEBUG_OBJECTS) 137 138 from twisted.internet import reactor 139 140 def sample(): 141 gc.collect() 142 for o in gc.garbage: 143 if o not in known: 144 known[o] = True 145 self.uncollectable(o) 146 reactor.callLater(period, sample)
147 148 reactor.callLater(period, sample)
149
150 - def uncollectable(self, obj):
151 print '\nUncollectable object cycle in gc.garbage:' 152 153 print "Parents:" 154 self._printParents(obj, 2) 155 print "Kids:" 156 self._printKids(obj, 2)
157
158 - def _printParents(self, obj, level, indent=' '):
159 print indent, self._shortRepr(obj) 160 if level > 0: 161 for p in gc.get_referrers(obj): 162 self._printParents(p, level - 1, indent + ' ')
163
164 - def _printKids(self, obj, level, indent=' '):
165 print indent, self._shortRepr(obj) 166 if level > 0: 167 for kid in gc.get_referents(obj): 168 self._printKids(kid, level - 1, indent + ' ')
169
170 - def _shortRepr(self, obj):
171 if not isinstance(obj, dict): 172 return '%s %r @ 0x%x' % (type(obj).__name__, obj, id(obj)) 173 else: 174 keys = obj.keys() 175 keys.sort() 176 return 'dict with keys %r @ 0x%x' % (keys, id(obj))
177
178 -class AllocMonitor(object):
179 - def __init__(self, period=10, analyze=None, allocPrint=None):
180 self.period = period 181 self.objset = None 182 183 from sizer import scanner, annotate 184 185 from twisted.internet import reactor 186 187 if analyze is not None: 188 self.analyze = analyze 189 if allocPrint is not None: 190 self.allocPrint = allocPrint 191 192 def sample(): 193 objset = scanner.Objects() 194 annotate.markparents(objset) 195 196 if self.objset: 197 self.analyze(self.objset, objset) 198 199 self.objset = objset 200 reactor.callLater(self.period, sample)
201 202 reactor.callLater(self.period, sample)
203
204 - def analyze(self, old, new):
205 from sizer import operations 206 207 size = 0 208 209 for k in operations.diff(new, old): 210 size -= old[k].size 211 212 allocators = {} 213 diff = operations.diff(old, new) 214 for k in diff: 215 w = new[k] 216 size += w.size 217 if not w.parents: 218 print "Unreferenced object %r, what?" % (w,) 219 for p in w.parents: 220 if id(p.obj) == id(self.__dict__): 221 continue 222 if id(p.obj) not in diff: 223 # print "Object %r alloced by %r" % (w, p) 224 if p not in allocators: 225 allocators[p] = [] 226 allocators[p].append(w) 227 print "Total alloc size:", size 228 for p in allocators: 229 if p.obj == old or p.obj == new: 230 print 'foo' 231 else: 232 self.allocPrint(p, allocators[p]) 233 for o in gc.garbage: 234 print '\nUncollectable object cycle in gc.garbage:' 235 self._printCycle(new[id(o)])
236
237 - def _printCycle(self, root):
238 print "Parents:" 239 self._printParents(root, 2) 240 print "Kids:" 241 self._printKids(root, 2)
242
243 - def _printParents(self, wrap, level, indent=' '):
244 print indent, self._wrapperRepr(wrap) 245 if level > 0: 246 for p in wrap.parents: 247 self._printParents(p, level - 1, indent + ' ')
248
249 - def _printKids(self, wrap, level, indent=' '):
250 print indent, self._wrapperRepr(wrap) 251 if level > 0: 252 for kid in wrap.children: 253 self._printKids(kid, level - 1, indent + ' ')
254
255 - def _allocStack(self, wrap, stack):
256 stack.append(wrap) 257 for p in wrap.parents: 258 if (isinstance(p.obj, types.ModuleType) 259 or isinstance(p.obj, type) 260 or isinstance(p.obj, types.InstanceType)): 261 stack.append(p) 262 return stack 263 if len(wrap.parents) == 1: 264 return self._allocStack(wrap.parents[0], stack) 265 return stack
266
267 - def _wrapperRepr(self, wrap):
268 o = wrap.obj 269 if wrap.type != dict: 270 return '%s %r @ 0x%x' % (wrap.type.__name__, o, id(o)) 271 else: 272 keys = o.keys() 273 keys.sort() 274 return 'dict with keys %r @ 0x%x' % (keys, id(o))
275
276 - def allocPrint(self, allocator, directAllocs):
277 allocStack = self._allocStack(allocator, []) 278 279 print '\nAlloc by ' + self._wrapperRepr(allocStack.pop(0)) 280 while allocStack: 281 print ' referenced by ' + self._wrapperRepr(allocStack.pop(0)) 282 283 print "%d new %s:" % (len(directAllocs), 284 len(directAllocs) == 1 and "object" or "objects") 285 for wrap in directAllocs: 286 print ' ' + self._wrapperRepr(wrap)
287