def __init__(self, evalStr=None, dictKey=NoDictKey): # if this is a dictionary lookup, pass dictKey instead of evalStr self.evalStr = evalStr self.dictKey = NoDictKey # is the dictKey a weak reference? self._isWeakRef = False self._refCount = 0 if dictKey is not NoDictKey: # if we can repr/eval the key, store it as an evalStr keyRepr = safeRepr(dictKey) useEval = False try: keyEval = eval(keyRepr) useEval = True except: pass if useEval: # check to make sure the eval succeeded if hash(keyEval) != hash(dictKey): useEval = False if useEval: # eval/repr succeeded, store as an evalStr self.evalStr = '[%s]' % keyRepr else: try: # store a weakref to the key self.dictKey = weakref.ref(dictKey) self._isWeakRef = True except TypeError, e: ContainerLeakDetector.notify.debug('could not weakref dict key %s' % keyRepr) self.dictKey = dictKey self._isWeakRef = False
def printReferrers(self, numEach=3): """referrers of the first few of each type of object""" counts = list(set(self._count2types.keys())) counts.sort() counts.reverse() for count in counts: types = makeList(self._count2types[count]) for typ in types: print "\n\nTYPE: %s" % repr(typ) for i in xrange(min(numEach, len(self._type2objs[typ]))): obj = self._type2objs[typ][i] print "\nOBJ: %s\n" % safeRepr(obj) referrers = gc.get_referrers(obj) print "%s REFERRERS:\n" % len(referrers) if len(referrers): print getNumberedTypedString(referrers, maxLen=80, numPrefix="REF") else: print "<No Referrers>"
def run(self): try: self._leakDetector._index2containerId2len[self._index] = {} ids = self._leakDetector.getContainerIds() # record the current len of each container for objId in ids: yield None try: for result in self._leakDetector.getContainerByIdGen(objId): yield None container = result except Exception, e: # this container no longer exists if self.notify.getDebug(): for contName in self._leakDetector.getContainerNameByIdGen(objId): yield None self.notify.debug( '%s no longer exists; caught exception in getContainerById (%s)' % ( contName, e)) self._leakDetector.removeContainerById(objId) continue if container is None: # this container no longer exists if self.notify.getDebug(): for contName in self._leakDetector.getContainerNameByIdGen(objId): yield None self.notify.debug('%s no longer exists; getContainerById returned None' % contName) self._leakDetector.removeContainerById(objId) continue try: cLen = len(container) except Exception, e: # this container no longer exists if self.notify.getDebug(): for contName in self._leakDetector.getContainerNameByIdGen(objId): yield None self.notify.debug( '%s is no longer a container, it is now %s (%s)' % (contName, safeRepr(container), e)) self._leakDetector.removeContainerById(objId) continue self._leakDetector._index2containerId2len[self._index][objId] = cLen
def getString(self, prevIndirection=None, nextIndirection=None): # return our contribution to the full name of an object instanceDictStr = '.__dict__' if self.evalStr is not None: # if we're an instance dict, skip over this one (obj.__dict__[keyName] == obj.keyName) if nextIndirection is not None and self.evalStr[-len(instanceDictStr):] == instanceDictStr: return self.evalStr[:-len(instanceDictStr)] # if the previous indirection was an instance dict, change our syntax from ['key'] to .key if prevIndirection is not None and prevIndirection.evalStr is not None: if prevIndirection.evalStr[-len(instanceDictStr):] == instanceDictStr: return '.%s' % self.evalStr[2:-2] return self.evalStr # we're stored as a dict key keyRepr = safeRepr(self._getNonWeakDictKey()) # if the previous indirection was an instance dict, change our syntax from ['key'] to .key if prevIndirection is not None and prevIndirection.evalStr is not None: if prevIndirection.evalStr[-len(instanceDictStr):] == instanceDictStr: return '.%s' % keyRepr return '[%s]' % keyRepr
def printReferrers(self, numEach=3): """referrers of the first few of each type of object""" counts = list(set(self._count2types.keys())) counts.sort() counts.reverse() for count in counts: types = makeList(self._count2types[count]) for typ in types: print '\n\nTYPE: %s' % repr(typ) for i in xrange(min(numEach, len(self._type2objs[typ]))): obj = self._type2objs[typ][i] print '\nOBJ: %s\n' % safeRepr(obj) referrers = gc.get_referrers(obj) print '%s REFERRERS:\n' % len(referrers) if len(referrers): print getNumberedTypedString(referrers, maxLen=80, numPrefix='REF') else: print '<No Referrers>'
def leakContainer(task=None): base = getBase() if not hasattr(base, 'leakContainer'): base.leakContainer = {} # use tuples as keys since they can't be weakref'd, and use an instance # since it can't be repr/eval'd # that will force the leak detector to hold a normal 'non-weak' reference class LeakKey: pass base.leakContainer[(LeakKey(),)] = {} # test the non-weakref object reference handling if random.random() < .01: key = random.choice(base.leakContainer.keys()) ContainerLeakDetector.notify.debug( 'removing reference to leakContainer key %s so it will be garbage-collected' % safeRepr(key)) del base.leakContainer[key] taskMgr.doMethodLater(10, leakContainer, 'leakContainer-%s' % serialNum()) if task: return task.done
def run(self): try: # this yields a different set of start refs every time we start a new traversal # force creation of a new workingListSelector inside the while loop right off the bat workingListSelector = nullGen() # this holds the current step of the current traversal curObjRef = None while True: # yield up here instead of at the end, since we skip back to the # top of the while loop from various points yield None #import pdb;pdb.set_trace() if curObjRef is None: # choose an object to start a traversal from try: startRefWorkingList = workingListSelector.next() except StopIteration: # do relative # of traversals on each set based on how many refs it contains baseLen = len(self._baseStartRefWorkingList.source) discLen = len(self._discoveredStartRefWorkingList.source) minLen = float(max(1, min(baseLen, discLen))) # this will cut down the traversals of the larger set by 2/3 minLen *= 3. workingListSelector = flywheel([self._baseStartRefWorkingList, self._discoveredStartRefWorkingList], [baseLen/minLen, discLen/minLen]) yield None continue # grab the next start ref from this sequence and see if it's still valid while True: yield None try: curObjRef = startRefWorkingList.refGen.next() break except StopIteration: # we've run out of refs, grab a new set if len(startRefWorkingList.source) == 0: # ref set is empty, choose another break # make a generator that yields containers a # of times that is # proportional to their length for fw in makeFlywheelGen( startRefWorkingList.source.values(), countFunc=lambda x: self.getStartObjAffinity(x), scale=.05): yield None startRefWorkingList.refGen = fw if curObjRef is None: # this ref set is empty, choose another # the base set should never be empty (__builtin__ etc.) continue # do we need to go look up the object in _id2ref? sometimes we do that # to avoid storing multiple redundant refs to a single item if type(curObjRef) in (types.IntType, types.LongType): startId = curObjRef curObjRef = None try: for containerRef in self._leakDetector.getContainerByIdGen(startId): yield None except: # ref is invalid self.notify.debug('invalid startRef, stored as id %s' % startId) self._leakDetector.removeContainerById(startId) continue curObjRef = containerRef try: for curObj in curObjRef.getContainerGen(): yield None except: self.notify.debug('lost current container, ref.getContainerGen() failed') # that container is gone, try again curObjRef = None continue self.notify.debug('--> %s' % curObjRef) #import pdb;pdb.set_trace() # store a copy of the current objRef parentObjRef = curObjRef # if we hit a dead end, start over from another container curObjRef = None if hasattr(curObj, '__dict__'): child = curObj.__dict__ hasLength = self._hasLength(child) notDeadEnd = not self._isDeadEnd(child) if hasLength or notDeadEnd: # prevent cycles in the references (i.e. base.loader.base) for goesThrough in parentObjRef.goesThroughGen(child): # don't yield, container might lose this element pass if not goesThrough: objRef = ObjectRef(Indirection(evalStr='.__dict__'), id(child), parentObjRef) yield None if hasLength: for i in self._addContainerGen(child, objRef): yield None if notDeadEnd: self._addDiscoveredStartRef(child, objRef) curObjRef = objRef continue if type(curObj) is types.DictType: key = None attr = None keys = curObj.keys() # we will continue traversing the object graph via one key of the dict, # choose it at random without taking a big chunk of CPU time numKeysLeft = len(keys) + 1 for key in keys: yield None numKeysLeft -= 1 try: attr = curObj[key] except KeyError, e: # this is OK because we are yielding during the iteration self.notify.debug('could not index into %s with key %s' % ( parentObjRef, safeRepr(key))) continue hasLength = self._hasLength(attr) notDeadEnd = False # if we haven't picked the next ref, check if this one is a candidate if curObjRef is None: notDeadEnd = not self._isDeadEnd(attr, key) if hasLength or notDeadEnd: # prevent cycles in the references (i.e. base.loader.base) for goesThrough in parentObjRef.goesThroughGen(curObj[key]): # don't yield, container might lose this element pass if not goesThrough: if curObj is __builtin__.__dict__: objRef = ObjectRef(Indirection(evalStr='%s' % key), id(curObj[key])) else: objRef = ObjectRef(Indirection(dictKey=key), id(curObj[key]), parentObjRef) yield None if hasLength: for i in self._addContainerGen(attr, objRef): yield None if notDeadEnd: self._addDiscoveredStartRef(attr, objRef) if curObjRef is None and random.randrange(numKeysLeft) == 0: curObjRef = objRef del key del attr continue try: childNames = dir(curObj) except: pass else: try: index = -1 attrs = [] while 1: yield None try: attr = itr.next() except: # some custom classes don't do well when iterated attr = None break attrs.append(attr) # we will continue traversing the object graph via one attr, # choose it at random without taking a big chunk of CPU time numAttrsLeft = len(attrs) + 1 for attr in attrs: yield None index += 1 numAttrsLeft -= 1 hasLength = self._hasLength(attr) notDeadEnd = False if curObjRef is None: notDeadEnd = not self._isDeadEnd(attr) if hasLength or notDeadEnd: # prevent cycles in the references (i.e. base.loader.base) for goesThrough in parentObjRef.goesThrough(curObj[index]): # don't yield, container might lose this element pass if not goesThrough: objRef = ObjectRef(Indirection(evalStr='[%s]' % index), id(curObj[index]), parentObjRef) yield None if hasLength: for i in self._addContainerGen(attr, objRef): yield None if notDeadEnd: self._addDiscoveredStartRef(attr, objRef) if curObjRef is None and random.randrange(numAttrsLeft) == 0: curObjRef = objRef del attr except StopIteration, e: pass del itr continue
def run(self): ContainerReport.PrivateIds.update(set([ id(ContainerReport.PrivateIds), id(self._visitedIds), id(self._id2pathStr), id(self._id2container), id(self._type2id2len), id(self._queue), id(self._instanceDictIds), ])) # push on a few things that we want to give priority # for the sake of the variable-name printouts try: base except: pass else: self._enqueueContainer( base.__dict__, 'base') try: simbase except: pass else: self._enqueueContainer( simbase.__dict__, 'simbase') self._queue.push(__builtins__) self._id2pathStr[id(__builtins__)] = '' while len(self._queue) > 0: # yield up here instead of at the end, since we skip back to the # top of the while loop from various points yield None parentObj = self._queue.pop() #print '%s: %s, %s' % (id(parentObj), type(parentObj), self._id2pathStr[id(parentObj)]) isInstanceDict = False if id(parentObj) in self._instanceDictIds: isInstanceDict = True try: if parentObj.__class__.__name__ == 'method-wrapper': continue except: pass if type(parentObj) in (types.StringType, types.UnicodeType): continue if type(parentObj) in (types.ModuleType, types.InstanceType): child = parentObj.__dict__ if self._examine(child): assert (self._queue.back() is child) self._instanceDictIds.add(id(child)) self._id2pathStr[id(child)] = str(self._id2pathStr[id(parentObj)]) continue if type(parentObj) is types.DictType: key = None attr = None keys = parentObj.keys() try: keys.sort() except TypeError, e: self.notify.warning('non-sortable dict keys: %s: %s' % (self._id2pathStr[id(parentObj)], repr(e))) for key in keys: try: attr = parentObj[key] except KeyError, e: self.notify.warning('could not index into %s with key %s' % (self._id2pathStr[id(parentObj)], key)) if id(attr) not in self._visitedIds: self._visitedIds.add(id(attr)) if self._examine(attr): assert (self._queue.back() is attr) if parentObj is __builtins__: self._id2pathStr[id(attr)] = key else: if isInstanceDict: self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '.%s' % key else: self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % safeRepr(key) del key del attr continue