mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
added task leak detection to ContainerLeakDetector
This commit is contained in:
parent
532a37945f
commit
ded092dc32
@ -27,6 +27,22 @@ def _createContainerLeak():
|
||||
return task.done
|
||||
leakContainer()
|
||||
|
||||
def _createTaskLeak():
|
||||
leakTaskName = uniqueName('leakedTask')
|
||||
leakDoLaterName = uniqueName('leakedDoLater')
|
||||
def nullTask(task=None):
|
||||
return task.cont
|
||||
def nullDoLater(task=None):
|
||||
return task.done
|
||||
def leakTask(task=None, leakTaskName=leakTaskName):
|
||||
base = getBase()
|
||||
taskMgr.add(nullTask, uniqueName(leakTaskName))
|
||||
taskMgr.doMethodLater(1 << 31, nullDoLater, uniqueName(leakDoLaterName))
|
||||
taskMgr.doMethodLater(10, leakTask, 'doLeakTask-%s' % serialNum())
|
||||
if task:
|
||||
return task.done
|
||||
leakTask()
|
||||
|
||||
class NoDictKey:
|
||||
pass
|
||||
|
||||
@ -336,7 +352,8 @@ class FindContainers(Job):
|
||||
# if it's an internal object, ignore it
|
||||
if id(obj) in ContainerLeakDetector.PrivateIds:
|
||||
return True
|
||||
if objName in ('im_self', 'im_class'):
|
||||
# prevent crashes in objects that define __cmp__ and don't handle strings
|
||||
if type(objName) == types.StringType and objName in ('im_self', 'im_class'):
|
||||
return True
|
||||
try:
|
||||
className = obj.__class__.__name__
|
||||
@ -818,6 +835,8 @@ class ContainerLeakDetector(Job):
|
||||
|
||||
if config.GetBool('leak-container', 0):
|
||||
_createContainerLeak()
|
||||
if config.GetBool('leak-tasks', 0):
|
||||
_createTaskLeak()
|
||||
|
||||
# don't check our own tables for leaks
|
||||
ContainerLeakDetector.addPrivateObj(ContainerLeakDetector.PrivateIds)
|
||||
|
@ -10,9 +10,15 @@ class LeakDetector:
|
||||
# ContainerLeakDetector will find it quickly
|
||||
if not hasattr(__builtin__, "leakDetectors"):
|
||||
__builtin__.leakDetectors = {}
|
||||
leakDetectors[id(self)] = self
|
||||
self._leakDetectorsKey = self.getLeakDetectorKey()
|
||||
leakDetectors[self._leakDetectorsKey] = self
|
||||
def destroy(self):
|
||||
del leakDetectors[id(self)]
|
||||
del leakDetectors[self._leakDetectorsKey]
|
||||
|
||||
def getLeakDetectorKey(self):
|
||||
# this string will be shown to the end user and should ideally contain enough information to
|
||||
# point to what is leaking
|
||||
return '%s-%s' % (self.__class__.__name__, id(self))
|
||||
|
||||
class GarbageLeakDetector(LeakDetector):
|
||||
# are we accumulating Python garbage?
|
||||
@ -60,3 +66,58 @@ class CppMemoryUsage(LeakDetector):
|
||||
return int(MemoryUsage.getCppSize())
|
||||
else:
|
||||
return 0
|
||||
|
||||
class TaskLeakDetectorBase:
|
||||
def _getTaskNamePattern(self, taskName):
|
||||
# get a generic string pattern from a task name by removing numeric characters
|
||||
for i in xrange(10):
|
||||
taskName = taskName.replace('%s' % i, '')
|
||||
return taskName
|
||||
|
||||
class _TaskNamePatternLeakDetector(LeakDetector, TaskLeakDetectorBase):
|
||||
# tracks the number of each individual task type
|
||||
# e.g. are we leaking 'examine-<doId>' tasks
|
||||
def __init__(self, taskNamePattern):
|
||||
self._taskNamePattern = taskNamePattern
|
||||
LeakDetector.__init__(self)
|
||||
|
||||
def __len__(self):
|
||||
# count the number of tasks that match our task name pattern
|
||||
numTasks = 0
|
||||
for task in taskMgr.getTasks():
|
||||
if self._getTaskNamePattern(task.name) == self._taskNamePattern:
|
||||
numTasks += 1
|
||||
for task in taskMgr.getDoLaters():
|
||||
if self._getTaskNamePattern(task.name) == self._taskNamePattern:
|
||||
numTasks += 1
|
||||
return numTasks
|
||||
|
||||
def getLeakDetectorKey(self):
|
||||
return '%s-%s' % (self._taskNamePattern, LeakDetector.getLeakDetectorKey(self))
|
||||
|
||||
class TaskLeakDetector(LeakDetector, TaskLeakDetectorBase):
|
||||
# tracks the number task 'types' and creates leak detectors for each task type
|
||||
def __init__(self):
|
||||
LeakDetector.__init__(self)
|
||||
self._taskName2collector = {}
|
||||
|
||||
def destroy(self):
|
||||
for taskName, collector in self._taskName2collector.iteritems():
|
||||
collector.destroy()
|
||||
del self._taskName2collector
|
||||
LeakDetector.destroy(self)
|
||||
|
||||
def _processTaskName(self, taskName):
|
||||
# if this is a new task name pattern, create a leak detector for that pattern
|
||||
namePattern = self._getTaskNamePattern(taskName)
|
||||
if namePattern not in self._taskName2collector:
|
||||
self._taskName2collector[namePattern] = _TaskNamePatternLeakDetector(namePattern)
|
||||
|
||||
def __len__(self):
|
||||
# update our table of task leak detectors
|
||||
for task in taskMgr.getTasks():
|
||||
self._processTaskName(task.name)
|
||||
for task in taskMgr.getDoLaters():
|
||||
self._processTaskName(task.name)
|
||||
# are we leaking task types?
|
||||
return len(self._taskName2collector)
|
||||
|
@ -1105,6 +1105,22 @@ class TaskManager:
|
||||
str += "End of taskMgr info\n"
|
||||
return str
|
||||
|
||||
def getTasks(self):
|
||||
# returns list of all tasks in arbitrary order
|
||||
tasks = []
|
||||
for taskPriList in self.taskList:
|
||||
for task in taskPriList:
|
||||
if task is not None:
|
||||
tasks.append(task)
|
||||
for pri, taskList in self.pendingTaskDict.iteritems():
|
||||
for task in taskList:
|
||||
tasks.append(task)
|
||||
return tasks
|
||||
|
||||
def getDoLaters(self):
|
||||
# returns list of all doLaters in arbitrary order
|
||||
return self.__doLaterList[:]
|
||||
|
||||
def resetStats(self):
|
||||
# WARNING: this screws up your do-later timings
|
||||
if self.taskTimerVerbose:
|
||||
|
Loading…
x
Reference in New Issue
Block a user