mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
more fine-grained CPU usage, better notification messages
This commit is contained in:
parent
42a7730537
commit
98e5e19348
@ -51,27 +51,38 @@ class CheckContainers(Job):
|
|||||||
for id in ids:
|
for id in ids:
|
||||||
yield None
|
yield None
|
||||||
try:
|
try:
|
||||||
container = self._leakDetector.getContainerById(id)
|
for result in self._leakDetector.getContainerByIdGen(id):
|
||||||
|
yield None
|
||||||
|
container = result
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# this container no longer exists
|
# this container no longer exists
|
||||||
self.notify.debug(
|
if self.notify.getDebug():
|
||||||
'%s no longer exists; caught exception in getContainerById (%s)' % (
|
for contName in self._leakDetector.getContainerNameByIdGen(id):
|
||||||
self._leakDetector.getContainerNameById(id), e))
|
yield None
|
||||||
|
self.notify.debug(
|
||||||
|
'%s no longer exists; caught exception in getContainerById (%s)' % (
|
||||||
|
contName, e))
|
||||||
self._leakDetector.removeContainerById(id)
|
self._leakDetector.removeContainerById(id)
|
||||||
continue
|
continue
|
||||||
if container is None:
|
if container is None:
|
||||||
# this container no longer exists
|
# this container no longer exists
|
||||||
self.notify.debug('%s no longer exists; getContainerById returned None' %
|
if self.notify.getDebug():
|
||||||
self._leakDetector.getContainerNameById(id))
|
for contName in self._leakDetector.getContainerNameByIdGen(id):
|
||||||
|
yield None
|
||||||
|
self.notify.debug('%s no longer exists; getContainerById returned None' %
|
||||||
|
contName)
|
||||||
self._leakDetector.removeContainerById(id)
|
self._leakDetector.removeContainerById(id)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
cLen = len(container)
|
cLen = len(container)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# this container no longer exists
|
# this container no longer exists
|
||||||
self.notify.debug(
|
if self.notify.getDebug():
|
||||||
'%s is no longer a container, it is now %s (%s)' %
|
for contName in self._leakDetector.getContainerNameByIdGen(id):
|
||||||
(self._leakDetector.getContainerNameById(id), safeRepr(container), e))
|
yield None
|
||||||
|
self.notify.debug(
|
||||||
|
'%s is no longer a container, it is now %s (%s)' %
|
||||||
|
(contName, safeRepr(container), e))
|
||||||
self._leakDetector.removeContainerById(id)
|
self._leakDetector.removeContainerById(id)
|
||||||
continue
|
continue
|
||||||
self._leakDetector._index2containerId2len[self._index][id] = cLen
|
self._leakDetector._index2containerId2len[self._index][id] = cLen
|
||||||
@ -90,7 +101,7 @@ class CheckContainers(Job):
|
|||||||
if idx2id2len[self._index-1][id] != 0:
|
if idx2id2len[self._index-1][id] != 0:
|
||||||
percent = int(100. * (float(diff) / idx2id2len[self._index-1][id]))
|
percent = int(100. * (float(diff) / idx2id2len[self._index-1][id]))
|
||||||
self.notify.warning(
|
self.notify.warning(
|
||||||
'%s grew %s%% in %s minutes (currently %s items)' % (
|
'%s grew %s%% in %.2f minutes (currently %s items)' % (
|
||||||
name, percent, minutes, idx2id2len[self._index][id]))
|
name, percent, minutes, idx2id2len[self._index][id]))
|
||||||
yield None
|
yield None
|
||||||
if (self._index > 3 and
|
if (self._index > 3 and
|
||||||
@ -101,7 +112,7 @@ class CheckContainers(Job):
|
|||||||
if self._index <= 5:
|
if self._index <= 5:
|
||||||
if diff > 0 and diff2 > 0 and diff3 > 0:
|
if diff > 0 and diff2 > 0 and diff3 > 0:
|
||||||
name = self._leakDetector.getContainerNameById(id)
|
name = self._leakDetector.getContainerNameById(id)
|
||||||
msg = ('%s consistently increased in length over the last '
|
msg = ('%s consistently increased in size over the last '
|
||||||
'3 periods (currently %s items)' %
|
'3 periods (currently %s items)' %
|
||||||
(name, idx2id2len[self._index][id]))
|
(name, idx2id2len[self._index][id]))
|
||||||
self.notify.warning(msg)
|
self.notify.warning(msg)
|
||||||
@ -114,12 +125,15 @@ class CheckContainers(Job):
|
|||||||
diff5 = idx2id2len[self._index-4][id] - idx2id2len[self._index-5][id]
|
diff5 = idx2id2len[self._index-4][id] - idx2id2len[self._index-5][id]
|
||||||
if diff > 0 and diff2 > 0 and diff3 > 0 and diff4 > 0 and diff5 > 0:
|
if diff > 0 and diff2 > 0 and diff3 > 0 and diff4 > 0 and diff5 > 0:
|
||||||
name = self._leakDetector.getContainerNameById(id)
|
name = self._leakDetector.getContainerNameById(id)
|
||||||
msg = ('%s consistently increased in length over the last '
|
msg = ('%s consistently increased in size over the last '
|
||||||
'5 periods (currently %s items)' %
|
'5 periods (currently %s items)' %
|
||||||
(name, idx2id2len[self._index][id]))
|
(name, idx2id2len[self._index][id]))
|
||||||
self.notify.warning('%s, sending notification' % msg)
|
self.notify.warning('%s, sending notification' % msg)
|
||||||
yield None
|
yield None
|
||||||
messenger.send(self._leakDetector.getLeakEvent(), [msg])
|
for result in self._leakDetector.getContainerByIdGen(id):
|
||||||
|
yield None
|
||||||
|
container = result
|
||||||
|
messenger.send(self._leakDetector.getLeakEvent(), [container, name])
|
||||||
yield Job.Done
|
yield Job.Done
|
||||||
|
|
||||||
class PruneContainerRefs(Job):
|
class PruneContainerRefs(Job):
|
||||||
@ -145,7 +159,9 @@ class PruneContainerRefs(Job):
|
|||||||
for id in ids:
|
for id in ids:
|
||||||
yield None
|
yield None
|
||||||
try:
|
try:
|
||||||
container = self._leakDetector.getContainerById(id)
|
for result in self._leakDetector.getContainerByIdGen(id):
|
||||||
|
yield None
|
||||||
|
container = result
|
||||||
except:
|
except:
|
||||||
# reference is invalid, remove it
|
# reference is invalid, remove it
|
||||||
self._leakDetector.removeContainerById(id)
|
self._leakDetector.removeContainerById(id)
|
||||||
@ -160,6 +176,8 @@ class Indirection:
|
|||||||
Stored as a string to be used as part of an eval, or as a key to be looked up in a dict.
|
Stored as a string to be used as part of an eval, or as a key to be looked up in a dict.
|
||||||
Each dictionary dereference is individually eval'd since the dict key might have been
|
Each dictionary dereference is individually eval'd since the dict key might have been
|
||||||
garbage-collected
|
garbage-collected
|
||||||
|
TODO: store string components that are duplicates of strings in the actual system so that
|
||||||
|
Python will keep one copy and reduce memory usage
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, evalStr=None, dictKey=NoDictKey):
|
def __init__(self, evalStr=None, dictKey=NoDictKey):
|
||||||
@ -313,12 +331,13 @@ class ContainerRef:
|
|||||||
|
|
||||||
yield self._evalWithObj(evalStr, curObj)
|
yield self._evalWithObj(evalStr, curObj)
|
||||||
|
|
||||||
def __repr__(self):
|
def getNameGen(self):
|
||||||
str = ''
|
str = ''
|
||||||
prevIndirection = None
|
prevIndirection = None
|
||||||
curIndirection = None
|
curIndirection = None
|
||||||
nextIndirection = None
|
nextIndirection = None
|
||||||
for i in xrange(len(self._indirections)):
|
for i in xrange(len(self._indirections)):
|
||||||
|
yield None
|
||||||
if i > 0:
|
if i > 0:
|
||||||
prevIndirection = self._indirections[i-1]
|
prevIndirection = self._indirections[i-1]
|
||||||
else:
|
else:
|
||||||
@ -330,7 +349,12 @@ class ContainerRef:
|
|||||||
nextIndirection = None
|
nextIndirection = None
|
||||||
str += curIndirection.getString(prevIndirection=prevIndirection,
|
str += curIndirection.getString(prevIndirection=prevIndirection,
|
||||||
nextIndirection=nextIndirection)
|
nextIndirection=nextIndirection)
|
||||||
return str
|
yield str
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
for result in self.getNameGen():
|
||||||
|
pass
|
||||||
|
return result
|
||||||
|
|
||||||
class ContainerLeakDetector(Job):
|
class ContainerLeakDetector(Job):
|
||||||
"""
|
"""
|
||||||
@ -361,21 +385,24 @@ class ContainerLeakDetector(Job):
|
|||||||
|
|
||||||
# set up the base/starting object
|
# set up the base/starting object
|
||||||
self._baseObjRef = ContainerRef(Indirection(evalStr='__builtin__.__dict__'))
|
self._baseObjRef = ContainerRef(Indirection(evalStr='__builtin__.__dict__'))
|
||||||
self._nameContainer(__builtin__.__dict__, self._baseObjRef)
|
for i in self._nameContainerGen(__builtin__.__dict__, self._baseObjRef):
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
base
|
base
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self._baseObjRef = ContainerRef(Indirection(evalStr='base.__dict__'))
|
self._baseObjRef = ContainerRef(Indirection(evalStr='base.__dict__'))
|
||||||
self._nameContainer(base.__dict__, self._baseObjRef)
|
for i in self._nameContainerGen(base.__dict__, self._baseObjRef):
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
simbase
|
simbase
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self._baseObjRef = ContainerRef(Indirection(evalStr='simbase.__dict__'))
|
self._baseObjRef = ContainerRef(Indirection(evalStr='simbase.__dict__'))
|
||||||
self._nameContainer(simbase.__dict__, self._baseObjRef)
|
for i in self._nameContainerGen(simbase.__dict__, self._baseObjRef):
|
||||||
|
pass
|
||||||
|
|
||||||
if config.GetBool('leak-container', 0):
|
if config.GetBool('leak-container', 0):
|
||||||
_createContainerLeak()
|
_createContainerLeak()
|
||||||
@ -419,15 +446,23 @@ class ContainerLeakDetector(Job):
|
|||||||
def getContainerIds(self):
|
def getContainerIds(self):
|
||||||
return self._id2ref.keys()
|
return self._id2ref.keys()
|
||||||
|
|
||||||
|
def getContainerByIdGen(self, id):
|
||||||
|
# return a generator to look up a container
|
||||||
|
return self._id2ref[id].getContainer()
|
||||||
def getContainerById(self, id):
|
def getContainerById(self, id):
|
||||||
for result in self._id2ref[id].getContainer():
|
for result in self._id2ref[id].getContainer():
|
||||||
pass
|
pass
|
||||||
return result
|
return result
|
||||||
|
def getContainerNameByIdGen(self, id):
|
||||||
|
return self._id2ref[id].getNameGen()
|
||||||
def getContainerNameById(self, id):
|
def getContainerNameById(self, id):
|
||||||
return repr(self._id2ref[id])
|
if id in self._id2ref:
|
||||||
|
return repr(self._id2ref[id])
|
||||||
|
return '<unknown container>'
|
||||||
def removeContainerById(self, id):
|
def removeContainerById(self, id):
|
||||||
self._id2ref[id].destroy()
|
if id in self._id2ref:
|
||||||
del self._id2ref[id]
|
self._id2ref[id].destroy()
|
||||||
|
del self._id2ref[id]
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
taskMgr.doMethodLater(self._nextCheckDelay, self._checkForLeaks,
|
taskMgr.doMethodLater(self._nextCheckDelay, self._checkForLeaks,
|
||||||
@ -467,7 +502,8 @@ class ContainerLeakDetector(Job):
|
|||||||
objRef = ContainerRef(Indirection(evalStr='.__dict__'), curObjRef)
|
objRef = ContainerRef(Indirection(evalStr='.__dict__'), curObjRef)
|
||||||
yield None
|
yield None
|
||||||
if isContainer:
|
if isContainer:
|
||||||
self._nameContainer(child, objRef)
|
for i in self._nameContainerGen(child, objRef):
|
||||||
|
yield None
|
||||||
if notDeadEnd:
|
if notDeadEnd:
|
||||||
self._curObjRef = objRef
|
self._curObjRef = objRef
|
||||||
continue
|
continue
|
||||||
@ -485,7 +521,8 @@ class ContainerLeakDetector(Job):
|
|||||||
try:
|
try:
|
||||||
attr = curObj[key]
|
attr = curObj[key]
|
||||||
except KeyError, e:
|
except KeyError, e:
|
||||||
self.notify.warning('could not index into %s with key %s' % (curObjRef, key))
|
# this is OK because we are yielding during the iteration
|
||||||
|
self.notify.debug('could not index into %s with key %s' % (curObjRef, key))
|
||||||
continue
|
continue
|
||||||
isContainer = self._isContainer(attr)
|
isContainer = self._isContainer(attr)
|
||||||
notDeadEnd = False
|
notDeadEnd = False
|
||||||
@ -498,11 +535,14 @@ class ContainerLeakDetector(Job):
|
|||||||
objRef = ContainerRef(Indirection(dictKey=key), curObjRef)
|
objRef = ContainerRef(Indirection(dictKey=key), curObjRef)
|
||||||
yield None
|
yield None
|
||||||
if isContainer:
|
if isContainer:
|
||||||
self._nameContainer(attr, objRef)
|
for i in self._nameContainerGen(attr, objRef):
|
||||||
|
yield None
|
||||||
if notDeadEnd and nextObjRef is None:
|
if notDeadEnd and nextObjRef is None:
|
||||||
if random.randrange(numKeysLeft) == 0:
|
if random.randrange(numKeysLeft) == 0:
|
||||||
nextObjRef = objRef
|
nextObjRef = objRef
|
||||||
numKeysLeft -= 1
|
numKeysLeft -= 1
|
||||||
|
if nextObjRef is not None:
|
||||||
|
self._curObjRef = nextObjRef
|
||||||
del key
|
del key
|
||||||
del attr
|
del attr
|
||||||
continue
|
continue
|
||||||
@ -539,12 +579,15 @@ class ContainerLeakDetector(Job):
|
|||||||
objRef = ContainerRef(Indirection(evalStr='[%s]' % index), curObjRef)
|
objRef = ContainerRef(Indirection(evalStr='[%s]' % index), curObjRef)
|
||||||
yield None
|
yield None
|
||||||
if isContainer:
|
if isContainer:
|
||||||
self._nameContainer(attr, objRef)
|
for i in self._nameContainerGen(attr, objRef):
|
||||||
|
yield None
|
||||||
if notDeadEnd and nextObjRef is None:
|
if notDeadEnd and nextObjRef is None:
|
||||||
if random.randrange(numAttrsLeft) == 0:
|
if random.randrange(numAttrsLeft) == 0:
|
||||||
nextObjRef = objRef
|
nextObjRef = objRef
|
||||||
numAttrsLeft -= 1
|
numAttrsLeft -= 1
|
||||||
index += 1
|
index += 1
|
||||||
|
if nextObjRef is not None:
|
||||||
|
self._curObjRef = nextObjRef
|
||||||
del attr
|
del attr
|
||||||
except StopIteration, e:
|
except StopIteration, e:
|
||||||
pass
|
pass
|
||||||
@ -573,11 +616,14 @@ class ContainerLeakDetector(Job):
|
|||||||
objRef = ContainerRef(Indirection(evalStr='.%s' % childName), curObjRef)
|
objRef = ContainerRef(Indirection(evalStr='.%s' % childName), curObjRef)
|
||||||
yield None
|
yield None
|
||||||
if isContainer:
|
if isContainer:
|
||||||
self._nameContainer(child, objRef)
|
for i in self._nameContainerGen(child, objRef):
|
||||||
|
yield None
|
||||||
if notDeadEnd and nextObjRef is None:
|
if notDeadEnd and nextObjRef is None:
|
||||||
if random.randrange(numChildrenLeft) == 0:
|
if random.randrange(numChildrenLeft) == 0:
|
||||||
nextObjRef = objRef
|
nextObjRef = objRef
|
||||||
numChildrenLeft -= 1
|
numChildrenLeft -= 1
|
||||||
|
if nextObjRef is not None:
|
||||||
|
self._curObjRef = nextObjRef
|
||||||
del childName
|
del childName
|
||||||
del child
|
del child
|
||||||
continue
|
continue
|
||||||
@ -615,7 +661,7 @@ class ContainerLeakDetector(Job):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _nameContainer(self, cont, objRef):
|
def _nameContainerGen(self, cont, objRef):
|
||||||
"""
|
"""
|
||||||
if self.notify.getDebug():
|
if self.notify.getDebug():
|
||||||
self.notify.debug('_nameContainer: %s' % objRef)
|
self.notify.debug('_nameContainer: %s' % objRef)
|
||||||
@ -624,7 +670,12 @@ class ContainerLeakDetector(Job):
|
|||||||
contId = id(cont)
|
contId = id(cont)
|
||||||
# if this container is new, or the objRef repr is shorter than what we already have,
|
# if this container is new, or the objRef repr is shorter than what we already have,
|
||||||
# put it in the table
|
# put it in the table
|
||||||
if contId not in self._id2ref or len(repr(objRef)) < len(repr(self._id2ref[contId])):
|
if contId in self._id2ref:
|
||||||
|
for existingRepr in self._id2ref[contId].getNameGen():
|
||||||
|
yield None
|
||||||
|
for newRepr in objRef.getNameGen():
|
||||||
|
yield None
|
||||||
|
if contId not in self._id2ref or len(newRepr) < len(existingRepr):
|
||||||
if contId in self._id2ref:
|
if contId in self._id2ref:
|
||||||
self.removeContainerById(contId)
|
self.removeContainerById(contId)
|
||||||
self._id2ref[contId] = objRef
|
self._id2ref[contId] = objRef
|
||||||
|
Loading…
x
Reference in New Issue
Block a user