Browse Source

first rev

Darren Ranalli 18 years ago
parent
commit
9dedde0da4
1 changed files with 113 additions and 0 deletions
  1. 113 0
      direct/src/showbase/MessengerLeakDetector.py

+ 113 - 0
direct/src/showbase/MessengerLeakDetector.py

@@ -0,0 +1,113 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase.Job import Job
+import gc, __builtin__
+
+class MessengerLeakObject(DirectObject):
+    def __init__(self):
+        self.accept('leakEvent', self._handleEvent)
+    def _handleEvent(self):
+        pass
+
+def _leakMessengerObject():
+    leakObject = MessengerLeakObject()
+
+class MessengerLeakDetector(Job):
+    # check for objects that are only referenced by the messenger
+    # and would otherwise be garbage collected
+    notify = directNotify.newCategory("MessengerLeakDetector")
+
+    def __init__(self, name):
+        Job.__init__(self, name)
+        self.setPriority(Job.Priorities.Normal*2)
+        jobMgr.add(self)
+
+    def run(self):
+        # set of ids of objects that we know are always attached to builtin;
+        # if an object is attached to one of these, it's attached to builtin
+        # this cuts down on the amount of searching that needs to be done
+        builtinIds = set()
+        builtinIds.add(id(__builtin__.__dict__))
+        try:
+            builtinIds.add(id(base))
+            builtinIds.add(id(base.cr))
+            builtinIds.add(id(base.cr.doId2do))
+        except:
+            pass
+        try:
+            builtinIds.add(id(simbase))
+            builtinIds.add(id(simbase.air))
+            builtinIds.add(id(simbase.air.doId2do))
+        except:
+            pass
+        try:
+            builtinIds.add(id(uber))
+            builtinIds.add(id(uber.air))
+            builtinIds.add(id(uber.air.doId2do))
+        except:
+            pass
+
+        while True:
+            yield None
+            objects = messenger._Messenger__objectEvents.keys()
+            assert self.notify.debug('%s objects in the messenger' % len(objects))
+            for object in objects:
+                yield None
+                assert self.notify.debug('---> new object: %s' % itype(object))
+                # try to find a path to builtin that doesn't involve the messenger
+                # lists of objects for breadth-first search
+                # iterate through one list while populating other list
+                objList1 = []
+                objList2 = []
+                curObjList = objList1
+                nextObjList = objList2
+                visitedObjIds = set()
+
+                # add the id of the object, and the messenger containers so that
+                # the search for builtin will stop at the messenger; we're looking
+                # for any path to builtin that don't involve the messenger
+                visitedObjIds.add(id(object))
+                visitedObjIds.add(id(messenger._Messenger__objectEvents))
+                visitedObjIds.add(id(messenger._Messenger__callbacks))
+
+                nextObjList.append(object)
+                foundBuiltin = False
+
+                # breadth-first search, go until you run out of new objects or you find __builtin__
+                while len(nextObjList):
+                    if foundBuiltin:
+                        break
+                    # swap the lists, prepare for the next pass
+                    curObjList = nextObjList
+                    nextObjList = []
+                    assert self.notify.debug('next search iteration, num objects: %s' % len(curObjList))
+                    for curObj in curObjList:
+                        if foundBuiltin:
+                            break
+                        yield None
+                        referrers = gc.get_referrers(curObj)
+                        assert self.notify.debug('curObj: %s @ %s, %s referrers, repr=%s' % (
+                            itype(curObj), hex(id(curObj)), len(referrers), fastRepr(curObj, maxLen=2)))
+                        for referrer in referrers:
+                            #assert self.notify.debug('referrer: %s' % itype(curObj))
+                            yield None
+                            refId = id(referrer)
+                            # don't go in a loop
+                            if refId in visitedObjIds:
+                                #assert self.notify.debug('already visited')
+                                continue
+                            # don't self-reference
+                            if referrer is curObjList or referrer is nextObjList:
+                                continue
+                            if refId in builtinIds:
+                                # not a leak, there is a path to builtin that does not involve the messenger
+                                #assert self.notify.debug('object has another path to __builtin__, it\'s not a messenger leak')
+                                foundBuiltin = True
+                                break
+                            else:
+                                visitedObjIds.add(refId)
+                                nextObjList.append(referrer)
+
+                if not foundBuiltin:
+                    self.notify.warning(
+                        '%s is referenced only by the messenger' % (itype(object)))