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: 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