more fine-grained CPU usage, better notification messages

This commit is contained in:
Darren Ranalli 2007-04-07 21:21:52 +00:00
parent 42a7730537
commit 98e5e19348

View File

@ -51,27 +51,38 @@ class CheckContainers(Job):
for id in ids:
yield None
try:
container = self._leakDetector.getContainerById(id)
for result in self._leakDetector.getContainerByIdGen(id):
yield None
container = result
except Exception, e:
# this container no longer exists
self.notify.debug(
'%s no longer exists; caught exception in getContainerById (%s)' % (
self._leakDetector.getContainerNameById(id), e))
if self.notify.getDebug():
for contName in self._leakDetector.getContainerNameByIdGen(id):
yield None
self.notify.debug(
'%s no longer exists; caught exception in getContainerById (%s)' % (
contName, e))
self._leakDetector.removeContainerById(id)
continue
if container is None:
# this container no longer exists
self.notify.debug('%s no longer exists; getContainerById returned None' %
self._leakDetector.getContainerNameById(id))
if self.notify.getDebug():
for contName in self._leakDetector.getContainerNameByIdGen(id):
yield None
self.notify.debug('%s no longer exists; getContainerById returned None' %
contName)
self._leakDetector.removeContainerById(id)
continue
try:
cLen = len(container)
except Exception, e:
# this container no longer exists
self.notify.debug(
'%s is no longer a container, it is now %s (%s)' %
(self._leakDetector.getContainerNameById(id), safeRepr(container), e))
if self.notify.getDebug():
for contName in self._leakDetector.getContainerNameByIdGen(id):
yield None
self.notify.debug(
'%s is no longer a container, it is now %s (%s)' %
(contName, safeRepr(container), e))
self._leakDetector.removeContainerById(id)
continue
self._leakDetector._index2containerId2len[self._index][id] = cLen
@ -90,7 +101,7 @@ class CheckContainers(Job):
if idx2id2len[self._index-1][id] != 0:
percent = int(100. * (float(diff) / idx2id2len[self._index-1][id]))
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]))
yield None
if (self._index > 3 and
@ -101,7 +112,7 @@ class CheckContainers(Job):
if self._index <= 5:
if diff > 0 and diff2 > 0 and diff3 > 0:
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)' %
(name, idx2id2len[self._index][id]))
self.notify.warning(msg)
@ -114,12 +125,15 @@ class CheckContainers(Job):
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:
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)' %
(name, idx2id2len[self._index][id]))
self.notify.warning('%s, sending notification' % msg)
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
class PruneContainerRefs(Job):
@ -145,7 +159,9 @@ class PruneContainerRefs(Job):
for id in ids:
yield None
try:
container = self._leakDetector.getContainerById(id)
for result in self._leakDetector.getContainerByIdGen(id):
yield None
container = result
except:
# reference is invalid, remove it
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.
Each dictionary dereference is individually eval'd since the dict key might have been
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):
@ -313,12 +331,13 @@ class ContainerRef:
yield self._evalWithObj(evalStr, curObj)
def __repr__(self):
def getNameGen(self):
str = ''
prevIndirection = None
curIndirection = None
nextIndirection = None
for i in xrange(len(self._indirections)):
yield None
if i > 0:
prevIndirection = self._indirections[i-1]
else:
@ -330,7 +349,12 @@ class ContainerRef:
nextIndirection = None
str += curIndirection.getString(prevIndirection=prevIndirection,
nextIndirection=nextIndirection)
return str
yield str
def __repr__(self):
for result in self.getNameGen():
pass
return result
class ContainerLeakDetector(Job):
"""
@ -361,21 +385,24 @@ class ContainerLeakDetector(Job):
# set up the base/starting object
self._baseObjRef = ContainerRef(Indirection(evalStr='__builtin__.__dict__'))
self._nameContainer(__builtin__.__dict__, self._baseObjRef)
for i in self._nameContainerGen(__builtin__.__dict__, self._baseObjRef):
pass
try:
base
except:
pass
else:
self._baseObjRef = ContainerRef(Indirection(evalStr='base.__dict__'))
self._nameContainer(base.__dict__, self._baseObjRef)
for i in self._nameContainerGen(base.__dict__, self._baseObjRef):
pass
try:
simbase
except:
pass
else:
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):
_createContainerLeak()
@ -419,15 +446,23 @@ class ContainerLeakDetector(Job):
def getContainerIds(self):
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):
for result in self._id2ref[id].getContainer():
pass
return result
def getContainerNameByIdGen(self, id):
return self._id2ref[id].getNameGen()
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):
self._id2ref[id].destroy()
del self._id2ref[id]
if id in self._id2ref:
self._id2ref[id].destroy()
del self._id2ref[id]
def run(self):
taskMgr.doMethodLater(self._nextCheckDelay, self._checkForLeaks,
@ -467,7 +502,8 @@ class ContainerLeakDetector(Job):
objRef = ContainerRef(Indirection(evalStr='.__dict__'), curObjRef)
yield None
if isContainer:
self._nameContainer(child, objRef)
for i in self._nameContainerGen(child, objRef):
yield None
if notDeadEnd:
self._curObjRef = objRef
continue
@ -485,7 +521,8 @@ class ContainerLeakDetector(Job):
try:
attr = curObj[key]
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
isContainer = self._isContainer(attr)
notDeadEnd = False
@ -498,11 +535,14 @@ class ContainerLeakDetector(Job):
objRef = ContainerRef(Indirection(dictKey=key), curObjRef)
yield None
if isContainer:
self._nameContainer(attr, objRef)
for i in self._nameContainerGen(attr, objRef):
yield None
if notDeadEnd and nextObjRef is None:
if random.randrange(numKeysLeft) == 0:
nextObjRef = objRef
numKeysLeft -= 1
if nextObjRef is not None:
self._curObjRef = nextObjRef
del key
del attr
continue
@ -539,12 +579,15 @@ class ContainerLeakDetector(Job):
objRef = ContainerRef(Indirection(evalStr='[%s]' % index), curObjRef)
yield None
if isContainer:
self._nameContainer(attr, objRef)
for i in self._nameContainerGen(attr, objRef):
yield None
if notDeadEnd and nextObjRef is None:
if random.randrange(numAttrsLeft) == 0:
nextObjRef = objRef
numAttrsLeft -= 1
index += 1
if nextObjRef is not None:
self._curObjRef = nextObjRef
del attr
except StopIteration, e:
pass
@ -573,11 +616,14 @@ class ContainerLeakDetector(Job):
objRef = ContainerRef(Indirection(evalStr='.%s' % childName), curObjRef)
yield None
if isContainer:
self._nameContainer(child, objRef)
for i in self._nameContainerGen(child, objRef):
yield None
if notDeadEnd and nextObjRef is None:
if random.randrange(numChildrenLeft) == 0:
nextObjRef = objRef
numChildrenLeft -= 1
if nextObjRef is not None:
self._curObjRef = nextObjRef
del childName
del child
continue
@ -615,7 +661,7 @@ class ContainerLeakDetector(Job):
return False
return True
def _nameContainer(self, cont, objRef):
def _nameContainerGen(self, cont, objRef):
"""
if self.notify.getDebug():
self.notify.debug('_nameContainer: %s' % objRef)
@ -624,7 +670,12 @@ class ContainerLeakDetector(Job):
contId = id(cont)
# if this container is new, or the objRef repr is shorter than what we already have,
# 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:
self.removeContainerById(contId)
self._id2ref[contId] = objRef