mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 02:15:43 -04:00
more leak detection
This commit is contained in:
parent
a764c4e37c
commit
6f5a7d0bc6
@ -1,6 +1,8 @@
|
|||||||
# objects that report different types of leaks to the ContainerLeakDetector
|
# objects that report different types of leaks to the ContainerLeakDetector
|
||||||
|
|
||||||
from pandac.PandaModules import *
|
from pandac.PandaModules import *
|
||||||
|
from direct.showbase.DirectObject import DirectObject
|
||||||
|
from direct.showbase.Job import Job
|
||||||
import __builtin__, gc
|
import __builtin__, gc
|
||||||
|
|
||||||
class LeakDetector:
|
class LeakDetector:
|
||||||
@ -10,6 +12,8 @@ class LeakDetector:
|
|||||||
if not hasattr(__builtin__, "leakDetectors"):
|
if not hasattr(__builtin__, "leakDetectors"):
|
||||||
__builtin__.leakDetectors = {}
|
__builtin__.leakDetectors = {}
|
||||||
self._leakDetectorsKey = self.getLeakDetectorKey()
|
self._leakDetectorsKey = self.getLeakDetectorKey()
|
||||||
|
if __dev__:
|
||||||
|
assert self._leakDetectorsKey not in leakDetectors
|
||||||
leakDetectors[self._leakDetectorsKey] = self
|
leakDetectors[self._leakDetectorsKey] = self
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
del leakDetectors[self._leakDetectorsKey]
|
del leakDetectors[self._leakDetectorsKey]
|
||||||
@ -89,7 +93,7 @@ class _TaskNamePatternLeakDetector(LeakDetector, TaskLeakDetectorBase):
|
|||||||
return numTasks
|
return numTasks
|
||||||
|
|
||||||
def getLeakDetectorKey(self):
|
def getLeakDetectorKey(self):
|
||||||
return '%s-%s' % (self._taskNamePattern, LeakDetector.getLeakDetectorKey(self))
|
return '%s-%s' % (self._taskNamePattern, self.__class__.__name__)
|
||||||
|
|
||||||
class TaskLeakDetector(LeakDetector, TaskLeakDetectorBase):
|
class TaskLeakDetector(LeakDetector, TaskLeakDetectorBase):
|
||||||
# tracks the number task 'types' and creates leak detectors for each task type
|
# 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)
|
self._taskName2collector[namePattern] = _TaskNamePatternLeakDetector(namePattern)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
self._taskName2collector = {}
|
||||||
# update our table of task leak detectors
|
# update our table of task leak detectors
|
||||||
for task in taskMgr.getTasks():
|
for task in taskMgr.getTasks():
|
||||||
self._processTaskName(task.name)
|
self._processTaskName(task.name)
|
||||||
@ -117,3 +122,174 @@ class TaskLeakDetector(LeakDetector, TaskLeakDetectorBase):
|
|||||||
self._processTaskName(task.name)
|
self._processTaskName(task.name)
|
||||||
# are we leaking task types?
|
# are we leaking task types?
|
||||||
return len(self._taskName2collector)
|
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)
|
||||||
|
@ -61,7 +61,7 @@ class Messenger:
|
|||||||
# accept/ignore more than once over their lifetime)
|
# accept/ignore more than once over their lifetime)
|
||||||
# get unique messenger id for this object
|
# get unique messenger id for this object
|
||||||
if not hasattr(object, '_messengerId'):
|
if not hasattr(object, '_messengerId'):
|
||||||
object._messengerId = self._messengerIdGen
|
object._messengerId = (object.__class__.__name__, self._messengerIdGen)
|
||||||
self._messengerIdGen += 1
|
self._messengerIdGen += 1
|
||||||
return object._messengerId
|
return object._messengerId
|
||||||
|
|
||||||
@ -77,6 +77,18 @@ class Messenger:
|
|||||||
def _getObject(self, id):
|
def _getObject(self, id):
|
||||||
return self._id2object[id][1]
|
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):
|
def _releaseObject(self, object):
|
||||||
# assumes lock is held.
|
# assumes lock is held.
|
||||||
id = self._getMessengerId(object)
|
id = self._getMessengerId(object)
|
||||||
|
@ -31,7 +31,8 @@ __all__ = ['enumerate', 'unique', 'indent', 'nonRepeatingRandomList',
|
|||||||
'printStack', 'printReverseStack', 'listToIndex2item', 'listToItem2index',
|
'printStack', 'printReverseStack', 'listToIndex2item', 'listToItem2index',
|
||||||
'pandaBreak','pandaTrace','formatTimeCompact','DestructiveScratchPad',
|
'pandaBreak','pandaTrace','formatTimeCompact','DestructiveScratchPad',
|
||||||
'deeptype','getProfileResultString','StdoutCapture','StdoutPassthrough',
|
'deeptype','getProfileResultString','StdoutCapture','StdoutPassthrough',
|
||||||
'Averager', 'getRepository', 'formatTimeExact', 'startSuperLog', 'endSuperLog' ]
|
'Averager', 'getRepository', 'formatTimeExact', 'startSuperLog', 'endSuperLog',
|
||||||
|
'typeName', ]
|
||||||
|
|
||||||
import types
|
import types
|
||||||
import string
|
import string
|
||||||
@ -3852,6 +3853,12 @@ def configIsToday(configName):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def typeName(o):
|
||||||
|
if hasattr(o, '__class__'):
|
||||||
|
return o.__class__.__name__
|
||||||
|
else:
|
||||||
|
return o.__name__
|
||||||
|
|
||||||
import __builtin__
|
import __builtin__
|
||||||
__builtin__.Functor = Functor
|
__builtin__.Functor = Functor
|
||||||
__builtin__.Stack = Stack
|
__builtin__.Stack = Stack
|
||||||
@ -3905,3 +3912,4 @@ __builtin__.deeptype = deeptype
|
|||||||
__builtin__.Default = Default
|
__builtin__.Default = Default
|
||||||
__builtin__.isInteger = isInteger
|
__builtin__.isInteger = isInteger
|
||||||
__builtin__.configIsToday = configIsToday
|
__builtin__.configIsToday = configIsToday
|
||||||
|
__builtin__.typeName = typeName
|
||||||
|
Loading…
x
Reference in New Issue
Block a user