more leak detection

This commit is contained in:
Darren Ranalli 2009-02-07 00:00:02 +00:00
parent a764c4e37c
commit 6f5a7d0bc6
3 changed files with 199 additions and 3 deletions

View File

@ -1,6 +1,8 @@
# objects that report different types of leaks to the ContainerLeakDetector
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.showbase.Job import Job
import __builtin__, gc
class LeakDetector:
@ -10,6 +12,8 @@ class LeakDetector:
if not hasattr(__builtin__, "leakDetectors"):
__builtin__.leakDetectors = {}
self._leakDetectorsKey = self.getLeakDetectorKey()
if __dev__:
assert self._leakDetectorsKey not in leakDetectors
leakDetectors[self._leakDetectorsKey] = self
def destroy(self):
del leakDetectors[self._leakDetectorsKey]
@ -89,7 +93,7 @@ class _TaskNamePatternLeakDetector(LeakDetector, TaskLeakDetectorBase):
return numTasks
def getLeakDetectorKey(self):
return '%s-%s' % (self._taskNamePattern, LeakDetector.getLeakDetectorKey(self))
return '%s-%s' % (self._taskNamePattern, self.__class__.__name__)
class TaskLeakDetector(LeakDetector, TaskLeakDetectorBase):
# tracks the number task 'types' and creates leak detectors for each task type
@ -110,6 +114,7 @@ class TaskLeakDetector(LeakDetector, TaskLeakDetectorBase):
self._taskName2collector[namePattern] = _TaskNamePatternLeakDetector(namePattern)
def __len__(self):
self._taskName2collector = {}
# update our table of task leak detectors
for task in taskMgr.getTasks():
self._processTaskName(task.name)
@ -117,3 +122,174 @@ class TaskLeakDetector(LeakDetector, TaskLeakDetectorBase):
self._processTaskName(task.name)
# are we leaking task types?
return len(self._taskName2collector)
class MessageLeakDetectorBase:
def _getMessageNamePattern(self, msgName):
# get a generic string pattern from a message name by removing numeric characters
for i in xrange(10):
msgName = msgName.replace('%s' % i, '')
return msgName
class _MessageTypeLeakDetector(LeakDetector, MessageLeakDetectorBase):
# tracks the number of objects that are listening to each message
def __init__(self, msgNamePattern):
self._msgNamePattern = msgNamePattern
self._msgNames = set()
LeakDetector.__init__(self)
def addMsgName(self, msgName):
# for efficiency, we keep the actual message names around
# for queries on the messenger
self._msgNames.add(msgName)
def __len__(self):
toRemove = set()
num = 0
for msgName in self._msgNames:
n = messenger._getNumListeners(msgName)
if n == 0:
toRemove.add(msgName)
else:
num += n
# remove message names that are no longer in the messenger
self._msgNames.difference_update(toRemove)
return num
def getLeakDetectorKey(self):
return '%s-%s' % (self._msgNamePattern, self.__class__.__name__)
class _MessageTypeLeakDetectorCreator(Job):
def __init__(self, creator):
Job.__init__(self, uniqueName(typeName(self)))
self._creator = creator
def destroy(self):
self._creator = None
Job.destroy(self)
def finished(self):
Job.finished(self)
def run(self):
for msgName in messenger._getEvents():
yield None
namePattern = self._creator._getMessageNamePattern(msgName)
if namePattern not in self._creator._msgName2detector:
self._creator._msgName2detector[namePattern] = _MessageTypeLeakDetector(namePattern)
self._creator._msgName2detector[namePattern].addMsgName(msgName)
yield Job.Done
class MessageTypesLeakDetector(LeakDetector, MessageLeakDetectorBase):
def __init__(self):
LeakDetector.__init__(self)
self._msgName2detector = {}
self._createJob = None
if config.GetBool('leak-message-types', 0):
self._leakers = []
self._leakTaskName = uniqueName('leak-message-types')
taskMgr.add(self._leak, self._leakTaskName)
def _leak(self, task):
self._leakers.append(DirectObject())
self._leakers[-1].accept('leak-msg', self._leak)
return task.cont
def destroy(self):
if hasattr(self, '_leakTaskName'):
taskMgr.remove(self._leakTaskName)
for leaker in self._leakers:
leaker.ignoreAll()
self._leakers = None
if self._createJob:
self._createJob.destroy()
self._createJob = None
for msgName, detector in self._msgName2detector.iteritems():
detector.destroy()
del self._msgName2detector
LeakDetector.destroy(self)
def __len__(self):
if self._createJob:
if self._createJob.isFinished():
self._createJob.destroy()
self._createJob = None
self._createJob = _MessageTypeLeakDetectorCreator(self)
jobMgr.add(self._createJob)
# are we leaking message types?
return len(self._msgName2detector)
class _MessageListenerTypeLeakDetector(LeakDetector):
# tracks the number of each object type that is listening for events
def __init__(self, typeName):
self._typeName = typeName
LeakDetector.__init__(self)
def __len__(self):
numObjs = 0
for obj in messenger._getObjects():
if typeName(obj) == self._typeName:
numObjs += 1
return numObjs
def getLeakDetectorKey(self):
return '%s-%s' % (self._typeName, self.__class__.__name__)
class _MessageListenerTypeLeakDetectorCreator(Job):
def __init__(self, creator):
Job.__init__(self, uniqueName(typeName(self)))
self._creator = creator
def destroy(self):
self._creator = None
Job.destroy(self)
def finished(self):
Job.finished(self)
def run(self):
for obj in messenger._getObjects():
yield None
tName = typeName(obj)
if tName not in self._creator._typeName2detector:
self._creator._typeName2detector[tName] = (
_MessageListenerTypeLeakDetector(tName))
yield Job.Done
class MessageListenerTypesLeakDetector(LeakDetector):
def __init__(self):
LeakDetector.__init__(self)
self._typeName2detector = {}
self._createJob = None
if config.GetBool('leak-message-listeners', 0):
self._leakers = []
self._leakTaskName = uniqueName('leak-message-listeners')
taskMgr.add(self._leak, self._leakTaskName)
def _leak(self, task):
self._leakers.append(DirectObject())
self._leakers[-1].accept(uniqueName('leak-msg-listeners'), self._leak)
return task.cont
def destroy(self):
if hasattr(self, '_leakTaskName'):
taskMgr.remove(self._leakTaskName)
for leaker in self._leakers:
leaker.ignoreAll()
self._leakers = None
if self._createJob:
self._createJob.destroy()
self._createJob = None
for typeName, detector in self._typeName2detector.iteritems():
detector.destroy()
del self._typeName2detector
LeakDetector.destroy(self)
def __len__(self):
if self._createJob:
if self._createJob.isFinished():
self._createJob.destroy()
self._createJob = None
self._createJob = _MessageListenerTypeLeakDetectorCreator(self)
jobMgr.add(self._createJob)
# are we leaking listener types?
return len(self._typeName2detector)

View File

@ -61,7 +61,7 @@ class Messenger:
# accept/ignore more than once over their lifetime)
# get unique messenger id for this object
if not hasattr(object, '_messengerId'):
object._messengerId = self._messengerIdGen
object._messengerId = (object.__class__.__name__, self._messengerIdGen)
self._messengerIdGen += 1
return object._messengerId
@ -77,6 +77,18 @@ class Messenger:
def _getObject(self, id):
return self._id2object[id][1]
def _getObjects(self):
objs = []
for refCount, obj in self._id2object.itervalues():
objs.append(obj)
return objs
def _getNumListeners(self, event):
return len(self.__callbacks.get(event, {}))
def _getEvents(self):
return self.__callbacks.keys()
def _releaseObject(self, object):
# assumes lock is held.
id = self._getMessengerId(object)

View File

@ -31,7 +31,8 @@ __all__ = ['enumerate', 'unique', 'indent', 'nonRepeatingRandomList',
'printStack', 'printReverseStack', 'listToIndex2item', 'listToItem2index',
'pandaBreak','pandaTrace','formatTimeCompact','DestructiveScratchPad',
'deeptype','getProfileResultString','StdoutCapture','StdoutPassthrough',
'Averager', 'getRepository', 'formatTimeExact', 'startSuperLog', 'endSuperLog' ]
'Averager', 'getRepository', 'formatTimeExact', 'startSuperLog', 'endSuperLog',
'typeName', ]
import types
import string
@ -3852,6 +3853,12 @@ def configIsToday(configName):
return True
return False
def typeName(o):
if hasattr(o, '__class__'):
return o.__class__.__name__
else:
return o.__name__
import __builtin__
__builtin__.Functor = Functor
__builtin__.Stack = Stack
@ -3905,3 +3912,4 @@ __builtin__.deeptype = deeptype
__builtin__.Default = Default
__builtin__.isInteger = isInteger
__builtin__.configIsToday = configIsToday
__builtin__.typeName = typeName