def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True, threaded=False, doneCallback=None, autoDestroy=False, priority=None, safeMode=False, delOnly=False, collect=True): # if autoDestroy is True, GarbageReport will self-destroy after logging # if false, caller is responsible for calling destroy() # if threaded is True, processing will be performed over multiple frames # if collect is False, we assume that the caller just did a collect and the results # are still in gc.garbage Job.__init__(self, name) # stick the arguments onto a ScratchPad so we can delete them all at once self._args = ScratchPad(name=name, log=log, verbose=verbose, fullReport=fullReport, findCycles=findCycles, doneCallback=doneCallback, autoDestroy=autoDestroy, safeMode=safeMode, delOnly=delOnly, collect=collect) if priority is not None: self.setPriority(priority) jobMgr.add(self) if not threaded: jobMgr.finish(self)
def getAvatarDetails(self, avatar, func, *args): pad = ScratchPad() pad.func = func pad.args = args pad.avatar = avatar pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails') avId = avatar.doId self.__queryAvatarMap[avId] = pad self.__sendGetAvatarDetails(avId)
def getAvatarDetails(self, avatar, func, *args): avId = avatar.doId if avId in self.doId2do: func(1, self.doId2do[avId], *args) return pad = ScratchPad() pad.func = func pad.args = args pad.avatar = avatar pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails') self.__queryAvatarMap[avId] = pad self.__sendGetAvatarDetails(avId)
def getAvatarDetails(self, avatar, func, *args): pad = ScratchPad() pad.func = func pad.args = args pad.avatar = avatar pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails') self.__queryAvatarMap[avatar.doId] = pad self.__sendGetAvatarDetails(avatar.doId, pet=(args[0].endswith("Pet")))
def doMagicWord(self, word, avId, zoneId): wordIs = self.getWordIs(word) print word if wordIs('~oobe'): base.oobe() elif wordIs('~oobeCull'): base.oobeCull() elif wordIs('~tex'): self.doTex(word) elif wordIs('~texmem'): base.toggleTexMem() elif wordIs('~verts'): base.toggleShowVertices() elif wordIs('~wire'): base.toggleWireframe() elif wordIs('~stereo'): base.toggleStereo() elif wordIs('~showfont'): self.showfont(word[9:]) elif wordIs('~hidefont'): self.hidefont() elif wordIs('~guiPopup'): self.toggleGuiPopup() elif wordIs('~showCS') or wordIs('~showcs'): bitmask = self.getCSBitmask(word[7:]) render.showCS(bitmask) self.csShown = 1 elif wordIs('~hideCS') or wordIs('~hidecs'): bitmask = self.getCSBitmask(word[7:]) render.hideCS(bitmask) self.csShown = 0 elif wordIs('~cs'): bitmask = self.getCSBitmask(word[3:]) if self.csShown: render.hideCS(bitmask) self.csShown = 0 else: render.showCS(bitmask) self.csShown = 1 elif wordIs('~showShadowCollisions'): self.showShadowCollisions() elif wordIs('~hideShadowCollisions'): self.hideShadowCollisions() elif wordIs('~showCollisions'): self.showCollisions() elif wordIs('~hideCollisions'): self.hideCollisions() elif wordIs('~showCameraCollisions'): self.showCameraCollisions() elif wordIs('~hideCameraCollisions'): self.hideCameraCollisions() elif wordIs('~collidespam'): n = Notify.ptr().getCategory(':collide') if hasattr(self, '_collideSpamSeverity'): n.setSeverity(self._collideSpamSeverity) del self._collideSpamSeverity else: self._collideSpamSeverity = n.getSeverity() n.setSeverity(NSSpam) elif wordIs('~notify'): args = word.split() n = Notify.ptr().getCategory(args[1]) n.setSeverity({ 'error': NSError, 'warning': NSWarning, 'info': NSInfo, 'debug': NSDebug, 'spam': NSSpam }[args[2]]) elif wordIs('~stress'): factor = word[7:] if factor: factor = float(factor) LOD.setStressFactor(factor) response = 'Set LOD stress factor to %s' % factor else: factor = LOD.getStressFactor() response = 'LOD stress factor is %s' % factor self.setMagicWordResponse(response) elif wordIs('~for'): self.forAnother(word, avId, zoneId) elif wordIs('~badname'): word = '~for %s ~badname' % word[9:] print 'word is %s' % word self.forAnother(word, avId, zoneId) elif wordIs('~avId'): self.setMagicWordResponse(str(localAvatar.doId)) elif wordIs('~doId'): name = string.strip(word[6:]) objs = self.identifyDistributedObjects(name) if len(objs) == 0: response = '%s is unknown.' % name else: response = '' for (name, obj) in objs: response += '\n%s %d' % (name, obj.doId) response = response[1:] self.setMagicWordResponse(response) elif wordIs('~exec'): ChatManager = ChatManager import otp.chat ChatManager.ChatManager.execChat = 1 elif wordIs('~run'): self.toggleRun() elif wordIs('~runFaster'): if config.GetBool('want-running', 1): args = word.split() if len(args) > 1: base.debugRunningMultiplier = float(args[1]) else: base.debugRunningMultiplier = 10 inputState.set('debugRunning', True) elif wordIs('~who'): avIds = [] for av in Avatar.Avatar.ActiveAvatars: if hasattr(av, 'getFriendsList'): avIds.append(av.doId) continue self.d_setWho(avIds) elif wordIs('~sync'): tm = self.cr.timeManager if tm == None: response = 'No TimeManager.' self.setMagicWordResponse(response) else: tm.extraSkew = 0.0 skew = string.strip(word[5:]) if skew != '': tm.extraSkew = float(skew) globalClockDelta.clear() tm.handleHotkey() elif wordIs('~period'): timeout = string.strip(word[7:]) if timeout != '': seconds = int(timeout) self.cr.stopPeriodTimer() self.cr.resetPeriodTimer(seconds) self.cr.startPeriodTimer() if self.cr.periodTimerExpired: response = 'Period timer has expired.' elif self.cr.periodTimerStarted: elapsed = globalClock.getFrameTime() - self.cr.periodTimerStarted secondsRemaining = self.cr.periodTimerSecondsRemaining - elapsed response = 'Period timer expires in %s seconds.' % int(secondsRemaining) else: response = 'Period timer not set.' self.setMagicWordResponse(response) elif wordIs('~DIRECT'): args = word.split() fEnableLight = 0 if len(args) > 1: if direct and args[1] == 'CAM': direct.enable() taskMgr.removeTasksMatching('updateSmartCamera*') camera.wrtReparentTo(render) direct.cameraControl.enableMouseFly() self.setMagicWordResponse('Enabled DIRECT camera') return None elif args[1] == 'LIGHT': fEnableLight = 1 base.startTk() DirectSession = DirectSession import direct.directtools if fEnableLight: direct.enableLight() else: direct.enable() self.setMagicWordResponse('Enabled DIRECT') elif wordIs('~TT'): if not direct: return None args = word.split() if len(args) > 1: if args[1] == 'CAM': direct.cameraControl.disableMouseFly() camera.wrtReparentTo(base.localAvatar) base.localAvatar.startUpdateSmartCamera() self.setMagicWordResponse('Disabled DIRECT camera') return None direct.disable() camera.wrtReparentTo(base.localAvatar) base.localAvatar.startUpdateSmartCamera() self.setMagicWordResponse('Disabled DIRECT') elif wordIs('~net'): if self.cr.networkPlugPulled(): self.cr.restoreNetworkPlug() self.cr.startHeartbeat() response = 'Network restored.' else: self.cr.pullNetworkPlug() self.cr.stopHeartbeat() response = 'Network disconnected.' self.setMagicWordResponse(response) elif wordIs('~disconnect'): base.cr.distributedDistrict.sendUpdate('broadcastMessage') elif wordIs('~model'): args = word.split() path = args[1] model = loader.loadModel(path) model.reparentTo(localAvatar) model.wrtReparentTo(render) self.setMagicWordResponse('loaded %s' % path) elif wordIs('~axis'): axis = loader.loadModel('models/misc/xyzAxis.bam') axis.reparentTo(render) axis.setPos(base.localAvatar, 0, 0, 0) axis.setHpr(render, 0, 0, 0) axis10 = loader.loadModel('models/misc/xyzAxis.bam') axis10.reparentTo(render) axis10.setPos(base.localAvatar, 0, 0, 0) axis10.setScale(10) axis10.setHpr(render, 0, 0, 0) axis10.setColorScale(1, 1, 1, 0.40000000000000002) axis10.setTransparency(1) elif wordIs('~clearAxes') or wordIs('~clearAxis'): render.findAllMatches('**/xyzAxis.egg').detach() elif wordIs('~myAxis'): if hasattr(self, 'myAxis'): self.myAxis.detachNode() del self.myAxis else: self.myAxis = loader.loadModel('models/misc/xyzAxis.bam') self.myAxis.reparentTo(localAvatar) elif wordIs('~osd'): onScreenDebug.enabled = not (onScreenDebug.enabled) elif wordIs('~osdScale'): args = word.split() defScale = 0.050000000000000003 if len(args) > 1: scale = float(args[1]) else: scale = 1.0 onScreenDebug.onScreenText.setScale(defScale * scale) elif wordIs('~osdTaskMgr'): if taskMgr.osdEnabled(): taskMgr.stopOsd() elif not onScreenDebug.enabled: onScreenDebug.enabled = True taskMgr.startOsd() elif wordIs('~fps'): self.doFps(word, avId, zoneId) elif wordIs('~sleep'): args = word.split() if len(args) > 1: s = float(args[1]) base.setSleep(s) response = 'sleeping %s' % s else: base.setSleep(0.0) response = 'not sleeping' self.setMagicWordResponse(response) elif wordIs('~objects'): args = word.split() ObjectReport = ObjectReport import direct.showbase report = ObjectReport.ObjectReport('client ~objects') if 'all' in args: self.notify.info('printing full object set...') report.getObjectPool().printObjsByType(printReferrers = 'ref' in args) if hasattr(self, 'baselineObjReport'): self.notify.info('calculating diff from baseline ObjectReport...') self.lastDiff = self.baselineObjReport.diff(report) if not 'diff' in args: pass self.lastDiff.printOut(full = 'dif' in args) if 'baseline' in args or not hasattr(self, 'baselineObjReport'): self.notify.info('recording baseline ObjectReport...') if hasattr(self, 'baselineObjReport'): self.baselineObjReport.destroy() self.baselineObjReport = report self.setMagicWordResponse('objects logged') elif wordIs('~objectcount'): def handleObjectCountDone(objectCount): self.setMagicWordResponse('object count logged') oc = ObjectCount('~objectcount', doneCallback = handleObjectCountDone) elif wordIs('~objecthg'): import gc as gc objs = gc.get_objects() type2count = { } for obj in objs: tn = safeTypeName(obj) type2count.setdefault(tn, 0) type2count[tn] += 1 count2type = invertDictLossless(type2count) counts = count2type.keys() counts.sort() counts.reverse() for count in counts: print '%s: %s' % (count, count2type[count]) self.setMagicWordResponse('~aiobjecthg complete') elif wordIs('~containers'): args = word.split() limit = 30 if 'full' in args: limit = None ContainerReport.ContainerReport('~containers', log = True, limit = limit, threaded = True) elif wordIs('~garbage'): args = word.split() full = 'full' in args safeMode = 'safe' in args delOnly = 'delonly' in args cycleLimit = None for arg in args: try: cycleLimit = int(arg) continue continue GarbageReport.GarbageLogger('~garbage', fullReport = full, threaded = True, safeMode = safeMode, delOnly = delOnly, cycleLimit = cycleLimit, doneCallback = self.garbageReportDone) elif wordIs('~guicreates'): base.printGuiCreates = True self.setMagicWordResponse('printing gui creation stacks') elif wordIs('~creategarbage'): args = word.split() num = 1 if len(args) > 1: num = int(args[1]) GarbageReport._createGarbage(num) elif wordIs('~leakTask'): def leakTask(task): return task.cont taskMgr.add(leakTask, uniqueName('leakedTask')) leakTask = None elif wordIs('~leakmessage'): MessengerLeakDetector._leakMessengerObject() self.down_setMagicWordResponse(senderId, 'messenger leak object created') elif wordIs('~pstats'): args = word.split() hostname = None port = None if len(args) > 1: hostname = args[1] if len(args) > 2: port = int(args[2]) base.wantStats = 1 Task.TaskManager.pStatsTasks = 1 result = base.createStats(hostname, port) connectionName = '%s' % hostname if port is not None: connectionName += ':%s' % port if result: response = 'connected client pstats to %s' % connectionName else: response = 'could not connect pstats to %s' % connectionName self.setMagicWordResponse(response) elif wordIs('~profile'): args = word.split() if len(args) > 1: num = int(args[1]) else: num = 5 session = taskMgr.getProfileSession('~profile') session.setLogAfterProfile(True) taskMgr.profileFrames(num, session) self.setMagicWordResponse('profiling %s client frames...' % num) elif wordIs('~frameprofile'): args = word.split() wasOn = bool(taskMgr.getProfileFrames()) if len(args) > 1: setting = bool(int(args[1])) else: setting = not wasOn taskMgr.setProfileFrames(setting) self.setMagicWordResponse('frame profiling %s%s' % (choice(setting, 'ON', 'OFF'), choice(wasOn == setting, ' already', ''))) elif wordIs('~taskprofile'): args = word.split() wasOn = bool(taskMgr.getProfileTasks()) if len(args) > 1: setting = bool(int(args[1])) else: setting = not wasOn taskMgr.setProfileTasks(setting) self.setMagicWordResponse('task profiling %s%s' % (choice(setting, 'ON', 'OFF'), choice(wasOn == setting, ' already', ''))) elif wordIs('~taskspikethreshold'): args = word.split() if len(args) > 1: threshold = float(args[1]) response = 'task spike threshold set to %ss' % threshold else: threshold = TaskProfiler.GetDefaultSpikeThreshold() response = 'task spike threshold reset to %ss' % threshold TaskProfiler.SetSpikeThreshold(threshold) self.setMagicWordResponse(response) elif wordIs('~logtaskprofiles'): args = word.split() if len(args) > 1: name = args[1] else: name = None taskMgr.logTaskProfiles(name) response = 'logged task profiles%s' % choice(name, ' for %s' % name, '') self.setMagicWordResponse(response) elif wordIs('~taskprofileflush'): args = word.split() if len(args) > 1: name = args[1] else: name = None taskMgr.flushTaskProfiles(name) response = 'flushed AI task profiles%s' % choice(name, ' for %s' % name, '') self.setMagicWordResponse(response) elif wordIs('~dobjectcount'): base.cr.printObjectCount() self.setMagicWordResponse('logging client distributed object count...') elif wordIs('~taskmgr'): print taskMgr self.setMagicWordResponse('logging client taskMgr...') elif wordIs('~jobmgr'): print jobMgr self.setMagicWordResponse('logging client jobMgr...') elif wordIs('~jobtime'): args = word.split() if len(args) > 1: time = float(args[1]) else: time = None response = '' if time is None: time = jobMgr.getDefaultTimeslice() response = 'reset client jobMgr timeslice to %s ms' % time else: response = 'set client jobMgr timeslice to %s ms' % time time = time / 1000.0 jobMgr.setTimeslice(time) self.setMagicWordResponse(response) elif wordIs('~detectleaks'): started = self.cr.startLeakDetector() self.setMagicWordResponse(choice(started, 'leak detector started', 'leak detector already started')) elif wordIs('~taskthreshold'): args = word.split() if len(args) > 1.0: threshold = float(args[1]) else: threshold = None response = '' if threshold is None: threshold = taskMgr.DefTaskDurationWarningThreshold response = 'reset task duration warning threshold to %s' % threshold else: response = 'set task duration warning threshold to %s' % threshold taskMgr.setTaskDurationWarningThreshold(threshold) self.setMagicWordResponse(response) elif wordIs('~messenger'): print messenger self.setMagicWordResponse('logging client messenger...') elif wordIs('~clientcrash'): DelayedCall(Functor(self.notify.error, '~clientcrash: simulating a client crash')) elif wordIs('~badDelete'): doId = 0 while doId in base.cr.doId2do: doId += 1 DelayedCall(Functor(base.cr.deleteObjectLocation, ScratchPad(doId = doId), 1, 1)) self.setMagicWordResponse('doing bad delete') elif wordIs('~idTags'): messenger.send('nameTagShowAvId', []) base.idTags = 1 elif wordIs('~nameTags'): messenger.send('nameTagShowName', []) base.idTags = 0 elif wordIs('~hideNames'): if NametagGlobals.getMasterNametagsVisible(): NametagGlobals.setMasterNametagsVisible(0) else: NametagGlobals.setMasterNametagsVisible(1) elif wordIs('~hideGui'): if aspect2d.isHidden(): aspect2d.show() else: aspect2d.hide() elif wordIs('~flush'): base.cr.doDataCache.flush() base.cr.cache.flush() self.setMagicWordResponse('client object and data caches flushed') elif wordIs('~prof'): import time as time name = 'default' p = Point3() ts = time.time() for i in xrange(1000000): p.set(1, 2, 3) tf = time.time() dt = tf - ts response = 'prof(%s): %s secs' % (name, dt) print response self.setMagicWordResponse(response) elif wordIs('~gptc'): args = word.split() if len(args) > 1.0 and hasattr(self.cr, 'leakDetector'): gptcJob = self.cr.leakDetector.getPathsToContainers('~gptc', args[1], Functor(self._handleGPTCfinished, args[1])) else: self.setMagicWordResponse('error') elif wordIs('~gptcn'): args = word.split() if len(args) > 1.0 and hasattr(self.cr, 'leakDetector'): gptcnJob = self.cr.leakDetector.getPathsToContainersNamed('~gptcn', args[1], Functor(self._handleGPTCNfinished, args[1])) else: self.setMagicWordResponse('error') else: return 0 return 1
class Job(DirectObject): """Base class for cpu-intensive or non-time-critical operations that are run through the :class:`.JobManager`. To use, subclass and override the `run()` method. """ #: Yielded from the `run()` generator method when the job is done. Done = object() #: ``yield None`` is acceptable in place of ``yield Job.Continue`` Continue = None #: Yield any remaining time for this job until next frame. Sleep = object() # These priorities determine how many timeslices a job gets relative to other # jobs. A job with priority of 1000 will run 10 times more often than a job # with priority of 100. Priorities = ScratchPad(Min=1, Low=100, Normal=1000, High=10000) _SerialGen = SerialNumGen() def __init__(self, name): self._name = name self._generator = None self._id = Job._SerialGen.next() self._printing = False self._priority = Job.Priorities.Normal self._finished = False if __debug__: self._pstats = PStatCollector("App:Show code:jobManager:%s" % self._name) def destroy(self): del self._name del self._generator del self._printing def getFinishedEvent(self): return 'job-finished-%s' % self._id def run(self): """This should be overridden with a generator that does the needful processing. yield `Job.Continue` when possible/reasonable, and try not to run longer than the JobManager's timeslice between yields. When done, yield `Job.Done`. """ raise NotImplementedError("don't call down") def getPriority(self): return self._priority def setPriority(self, priority): self._priority = priority def printingBegin(self): self._printing = True def printingEnd(self): self._printing = False def resume(self): """Called every time JobManager is going to start running this job.""" #if self._printing: # # we may be suspended/resumed multiple times per frame, that gets spammy # # if we need to pick out the output of a job, put a prefix onto each line # # of the output # print('JOB:%s:RESUME' % self._name) def suspend(self): """Called when JobManager is going to stop running this job for a while. """ #if self._printing: # #print('JOB:%s:SUSPEND' % self._name) # pass # """ def _setFinished(self): self._finished = True self.finished() def isFinished(self): return self._finished def finished(self): # called when the job finishes and has been removed from the JobManager pass def getJobName(self): return self._name def _getJobId(self): return self._id def _getGenerator(self): if self._generator is None: self._generator = self.run() return self._generator def _cleanupGenerator(self): if self._generator is not None: self._generator = None
self._handlePotentialStateChange(args[self._index]) class AttrSetter(StateChangeNode): def __init__(self, source, object, attrName): self._object = object self._attrName = attrName StateChangeNode.__init__(self, source) self._handleStateChange() def _handleStateChange(self): setattr(self._object, self._attrName, self._value) StateChangeNode._handleStateChange(self) if __debug__: from direct.showbase.PythonUtil import ScratchPad o = ScratchPad() svar = StateVar(0) aset = AttrSetter(svar, o, 'testAttr') assert hasattr(o, 'testAttr') assert o.testAttr == 0 svar.set('red') assert o.testAttr == 'red' aset.destroy() svar.destroy() o.destroy() del aset del svar del o
def doMagicWord(self, word, avId, zoneId): wordIs = self.getWordIs(word) print word if wordIs("~oobe"): base.oobe() elif wordIs("~oobeCull"): base.oobeCull() elif wordIs("~tex"): self.doTex(word) elif wordIs("~texmem"): base.toggleTexMem() elif wordIs("~verts"): base.toggleShowVertices() elif wordIs("~wire"): base.toggleWireframe() elif wordIs("~stereo"): base.toggleStereo() elif wordIs("~showfont"): self.showfont(word[9:]) elif wordIs("~hidefont"): self.hidefont() elif wordIs("~guiPopup"): self.toggleGuiPopup() elif wordIs("~showCS") or wordIs("~showcs"): bitmask = self.getCSBitmask(word[7:]) render.showCS(bitmask) self.csShown = 1 elif wordIs("~hideCS") or wordIs("~hidecs"): bitmask = self.getCSBitmask(word[7:]) render.hideCS(bitmask) self.csShown = 0 elif wordIs("~cs"): # Toggle hide/show collision solids: # (Also a shorthand for ~hideCS and ~showCS). bitmask = self.getCSBitmask(word[3:]) if self.csShown: render.hideCS(bitmask) self.csShown = 0 else: render.showCS(bitmask) self.csShown = 1 elif wordIs("~showShadowCollisions"): self.showShadowCollisions() elif wordIs("~hideShadowCollisions"): self.hideShadowCollisions() elif wordIs("~showCollisions"): self.showCollisions() elif wordIs("~hideCollisions"): self.hideCollisions() elif wordIs("~showCameraCollisions"): self.showCameraCollisions() elif wordIs("~hideCameraCollisions"): self.hideCameraCollisions() elif wordIs("~collidespam"): n = Notify.ptr().getCategory(':collide') if hasattr(self, '_collideSpamSeverity'): n.setSeverity(self._collideSpamSeverity) del self._collideSpamSeverity else: self._collideSpamSeverity = n.getSeverity() n.setSeverity(NSSpam) elif wordIs("~notify"): args = word.split() n = Notify.ptr().getCategory(args[1]) n.setSeverity({ 'error': NSError, 'warning': NSWarning, 'info': NSInfo, 'debug': NSDebug, 'spam': NSSpam, }[args[2]]) # MPG we probably need generic versions of these #elif wordIs("~listen"): # base.localAvatar.garbleChat = 0 #elif wordIs("~nochat") or wordIs("~chat") or wordIs("~superchat"): # base.localAvatar.garbleChat = 1 elif wordIs("~stress"): factor = word[7:] if factor: factor = float(factor) LOD.setStressFactor(factor) response = "Set LOD stress factor to %s" % (factor) else: factor = LOD.getStressFactor() response = "LOD stress factor is %s" % (factor) self.setMagicWordResponse(response) elif wordIs("~for"): self.forAnother(word, avId, zoneId) elif wordIs("~badname"): # ~badname with an argument becomes ~for ... ~badname word = "~for %s ~badname" % (word[9:]) print "word is %s" % (word) self.forAnother(word, avId, zoneId) elif wordIs('~avId'): self.setMagicWordResponse(str(localAvatar.doId)) elif wordIs("~doId"): name = string.strip(word[6:]) objs = self.identifyDistributedObjects(name) if (len(objs) == 0): response = "%s is unknown." % (name) else: response = "" for name, obj in objs: response += "\n%s %d" % (name, obj.doId) response = response[1:] self.setMagicWordResponse(response) # MPG - need generic versions of these #elif wordIs("~collisions_on"): # base.localAvatar.collisionsOn() #elif wordIs("~collisions_off"): # base.localAvatar.collisionsOff() #elif wordIs('~addCameraPosition'): # base.localAvatar.addCameraPosition() #elif wordIs('~removeCameraPosition'): # base.localAvatar.removeCameraPosition() #elif wordIs('~printCameraPosition'): # base.localAvatar.printCameraPosition( # base.localAvatar.cameraIndex) #elif wordIs('~printCameraPositions'): # base.localAvatar.printCameraPositions() elif wordIs("~exec"): # Enable execChat. from otp.chat import ChatManager ChatManager.ChatManager.execChat = 1 elif wordIs("~run"): self.toggleRun() elif wordIs("~runFaster"): if (config.GetBool("want-running", 1)): args = word.split() if (len(args) > 1): base.debugRunningMultiplier = float(args[1]) else: base.debugRunningMultiplier = 10 inputState.set("debugRunning", True) elif wordIs("~who"): # Get all the nearby avIds and send them to the AI. avIds = [] for av in Avatar.Avatar.ActiveAvatars: # If the avatar has a friends list, it's probably a # real avatar and not an NPC. if hasattr(av, "getFriendsList"): avIds.append(av.doId) self.d_setWho(avIds) elif wordIs("~sync"): # Sync with the AI, like F6, but rather than accumulating # sync informatoin, throw away whatever information was # there from before. If a second parameter is supplied, # it is a number of seconds of temporary extra skew to # apply; the default is 0. tm = self.cr.timeManager if tm == None: response = "No TimeManager." self.setMagicWordResponse(response) else: tm.extraSkew = 0.0 skew = string.strip(word[5:]) if skew != "": tm.extraSkew = float(skew) globalClockDelta.clear() tm.handleHotkey() elif wordIs("~period"): # Reset the period timer to expire in the indicated number # of seconds, or with no parameter, report the number of # seconds remaining. timeout = string.strip(word[7:]) if timeout != "": seconds = int(timeout) self.cr.stopPeriodTimer() self.cr.resetPeriodTimer(seconds) self.cr.startPeriodTimer() # Now report the number of seconds remaining. if self.cr.periodTimerExpired: response = "Period timer has expired." elif self.cr.periodTimerStarted: elapsed = globalClock.getFrameTime( ) - self.cr.periodTimerStarted secondsRemaining = self.cr.periodTimerSecondsRemaining - elapsed response = "Period timer expires in %s seconds." % ( int(secondsRemaining)) else: response = "Period timer not set." self.setMagicWordResponse(response) elif wordIs("~DIRECT"): args = word.split() fEnableLight = 0 if len(args) > 1: if direct and (args[1] == 'CAM'): direct.enable() taskMgr.removeTasksMatching('updateSmartCamera*') camera.wrtReparentTo(render) direct.cameraControl.enableMouseFly() self.setMagicWordResponse("Enabled DIRECT camera") return elif args[1] == 'LIGHT': fEnableLight = 1 # Start up DIRECT base.startTk() from direct.directtools import DirectSession if fEnableLight: direct.enableLight() else: direct.enable() self.setMagicWordResponse("Enabled DIRECT") elif wordIs("~TT"): if not direct: return args = word.split() if len(args) > 1: if (args[1] == 'CAM'): direct.cameraControl.disableMouseFly() camera.wrtReparentTo(base.localAvatar) base.localAvatar.startUpdateSmartCamera() self.setMagicWordResponse("Disabled DIRECT camera") return # Return to toontown mode direct.disable() camera.wrtReparentTo(base.localAvatar) base.localAvatar.startUpdateSmartCamera() self.setMagicWordResponse("Disabled DIRECT") elif wordIs("~net"): # Simulate pulling or restoring the network plug. if self.cr.networkPlugPulled(): self.cr.restoreNetworkPlug() self.cr.startHeartbeat() response = "Network restored." else: self.cr.pullNetworkPlug() self.cr.stopHeartbeat() response = "Network disconnected." self.setMagicWordResponse(response) elif wordIs('~disconnect'): # force a simulated disconnect # you can also do this from the OTP webpage base.cr.distributedDistrict.sendUpdate('broadcastMessage') elif wordIs("~model"): # load a model into the scene graph at the location of localAvatar args = word.split() path = args[1] model = loader.loadModel(path) model.reparentTo(localAvatar) model.wrtReparentTo(render) self.setMagicWordResponse('loaded %s' % path) elif wordIs("~axis"): # Show a 10 foot and 100 foot axis at the spot of the avatar # axis aligned to render axis = loader.loadModel("models/misc/xyzAxis.bam") axis.reparentTo(render) axis.setPos(base.localAvatar, 0, 0, 0) axis.setHpr(render, 0, 0, 0) axis10 = loader.loadModel("models/misc/xyzAxis.bam") axis10.reparentTo(render) axis10.setPos(base.localAvatar, 0, 0, 0) axis10.setScale(10) axis10.setHpr(render, 0, 0, 0) axis10.setColorScale(1, 1, 1, 0.4) axis10.setTransparency(1) elif (wordIs("~clearAxes") or wordIs("~clearAxis")): # Remove the effects of ~axis calls render.findAllMatches("**/xyzAxis.egg").detach() elif wordIs("~myAxis"): if hasattr(self, 'myAxis'): self.myAxis.detachNode() del self.myAxis else: self.myAxis = loader.loadModel("models/misc/xyzAxis.bam") self.myAxis.reparentTo(localAvatar) elif (wordIs("~osd")): onScreenDebug.enabled = not onScreenDebug.enabled elif wordIs("~osdScale"): args = word.split() defScale = .05 if len(args) > 1: scale = float(args[1]) else: scale = 1. onScreenDebug.onScreenText.setScale(defScale * scale) elif wordIs('~osdTaskMgr'): if taskMgr.osdEnabled(): taskMgr.stopOsd() else: if not onScreenDebug.enabled: onScreenDebug.enabled = True taskMgr.startOsd() elif wordIs("~fps"): self.doFps(word, avId, zoneId) elif wordIs("~sleep"): args = word.split() if len(args) > 1: s = float(args[1]) base.setSleep(s) response = 'sleeping %s' % s else: base.setSleep(0.0) response = 'not sleeping' self.setMagicWordResponse(response) elif wordIs('~objects'): args = word.split() from direct.showbase import ObjectReport report = ObjectReport.ObjectReport('client ~objects') if 'all' in args: self.notify.info('printing full object set...') report.getObjectPool().printObjsByType( printReferrers='ref' in args) if hasattr(self, 'baselineObjReport'): self.notify.info( 'calculating diff from baseline ObjectReport...') self.lastDiff = self.baselineObjReport.diff(report) self.lastDiff.printOut(full=('diff' in args or 'dif' in args)) if 'baseline' in args or not hasattr(self, 'baselineObjReport'): self.notify.info('recording baseline ObjectReport...') if hasattr(self, 'baselineObjReport'): self.baselineObjReport.destroy() self.baselineObjReport = report self.setMagicWordResponse('objects logged') elif wordIs('~objecthg'): import gc objs = gc.get_objects() type2count = {} for obj in objs: tn = safeTypeName(obj) type2count.setdefault(tn, 0) type2count[tn] += 1 count2type = invertDictLossless(type2count) counts = count2type.keys() counts.sort() counts.reverse() for count in counts: print '%s: %s' % (count, count2type[count]) self.setMagicWordResponse('~aiobjecthg complete') elif wordIs('~containers'): args = word.split() limit = 30 if 'full' in args: limit = None ContainerReport.ContainerReport('~containers', log=True, limit=limit, threaded=True) elif wordIs('~garbage'): args = word.split() # it can take a LOOONG time to print out the garbage referrers and referents # by reference (as opposed to by number) full = 'full' in args safeMode = 'safe' in args delOnly = 'delonly' in args # This does a garbage collection and dumps the list of leaked (uncollectable) objects to the log. GarbageReport.GarbageLogger('~garbage', fullReport=full, threaded=True, safeMode=safeMode, delOnly=delOnly, doneCallback=self.garbageReportDone) # this is coming back from the AI #self.setMagicWordResponse('garbage logged') elif wordIs('~guicreates'): base.printGuiCreates = True self.setMagicWordResponse('printing gui creation stacks') elif wordIs("~creategarbage"): GarbageReport._createGarbage() # this is coming back from the AI #self.setMagicWordResponse(senderId, 'leaked garbage created') elif wordIs('~leakTask'): def leakTask(task): return task.cont taskMgr.add(leakTask, uniqueName('leakedTask')) leakTask = None # this is coming back from the AI #self.setMagicWordResponse(senderId, 'leaked task created') elif wordIs('~leakmessage'): MessengerLeakDetector._leakMessengerObject() self.down_setMagicWordResponse(senderId, 'messenger leak object created') elif wordIs('~pstats'): args = word.split() hostname = None port = None if len(args) > 1: hostname = args[1] if len(args) > 2: port = int(args[2]) # make sure pstats is enabled base.wantStats = 1 Task.TaskManager.pStatsTasks = 1 result = base.createStats(hostname, port) connectionName = '%s' % hostname if port is not None: connectionName += ':%s' % port if result: response = 'connected client pstats to %s' % connectionName else: response = 'could not connect pstats to %s' % connectionName self.setMagicWordResponse(response) elif wordIs('~profile'): args = word.split() if len(args) > 1: num = int(args[1]) else: num = 5 session = taskMgr.getProfileSession('~profile') session.setLogAfterProfile(True) taskMgr.profileFrames(num, session) self.setMagicWordResponse('profiling %s client frames...' % num) elif wordIs('~frameprofile'): args = word.split() wasOn = bool(taskMgr.getProfileFrames()) if len(args) > 1: setting = bool(int(args[1])) else: setting = not wasOn taskMgr.setProfileFrames(setting) self.setMagicWordResponse( 'frame profiling %s%s' % (choice(setting, 'ON', 'OFF'), choice(wasOn == setting, ' already', ''))) elif wordIs('~taskprofile'): args = word.split() wasOn = bool(taskMgr.getProfileTasks()) if len(args) > 1: setting = bool(int(args[1])) else: setting = not wasOn taskMgr.setProfileTasks(setting) self.setMagicWordResponse( 'task profiling %s%s' % (choice(setting, 'ON', 'OFF'), choice(wasOn == setting, ' already', ''))) elif wordIs('~taskspikethreshold'): args = word.split() if len(args) > 1: threshold = float(args[1]) response = 'task spike threshold set to %ss' % threshold else: threshold = TaskProfiler.GetDefaultSpikeThreshold() response = 'task spike threshold reset to %ss' % threshold TaskProfiler.SetSpikeThreshold(threshold) self.setMagicWordResponse(response) elif wordIs('~logtaskprofiles'): args = word.split() if len(args) > 1: name = args[1] else: name = None taskMgr.logTaskProfiles(name) response = 'logged task profiles%s' % choice( name, ' for %s' % name, '') self.setMagicWordResponse(response) elif wordIs('~taskprofileflush'): args = word.split() if len(args) > 1: name = args[1] else: name = None taskMgr.flushTaskProfiles(name) response = 'flushed AI task profiles%s' % choice( name, ' for %s' % name, '') self.setMagicWordResponse(response) elif wordIs('~objectcount'): base.cr.printObjectCount() self.setMagicWordResponse( 'logging client distributed object count...') elif wordIs('~taskmgr'): print taskMgr self.setMagicWordResponse('logging client taskMgr...') elif wordIs('~jobmgr'): print jobMgr self.setMagicWordResponse('logging client jobMgr...') elif wordIs('~jobtime'): args = word.split() if len(args) > 1: time = float(args[1]) else: time = None response = '' if time is None: time = jobMgr.getDefaultTimeslice() response = 'reset client jobMgr timeslice to %s ms' % time else: response = 'set client jobMgr timeslice to %s ms' % time time = time / 1000. jobMgr.setTimeslice(time) self.setMagicWordResponse(response) elif wordIs('~detectleaks'): started = self.cr.startLeakDetector() self.setMagicWordResponse( choice( started, 'leak detector started', 'leak detector already started', )) elif wordIs('~taskthreshold'): args = word.split() if len(args) > 1.: threshold = float(args[1]) else: threshold = None response = '' if threshold is None: threshold = taskMgr.DefTaskDurationWarningThreshold response = 'reset task duration warning threshold to %s' % threshold else: response = 'set task duration warning threshold to %s' % threshold taskMgr.setTaskDurationWarningThreshold(threshold) self.setMagicWordResponse(response) elif wordIs('~messenger'): print messenger self.setMagicWordResponse('logging client messenger...') elif wordIs('~clientcrash'): # if we call notify.error directly, the magic word mgr will catch it # self.notify.error doesn't seem to work either DelayedCall( Functor(self.notify.error, '~clientcrash: simulating a client crash')) elif wordIs('~badDelete'): doId = 0 while doId in base.cr.doId2do: doId += 1 # location (0,0) is special, pass in (1,1) # deleteObjectLocation expects a DO, pass in a ScratchPad instead # we must delay the call because magicWordMgr is in a big try/except block DelayedCall( Functor(base.cr.deleteObjectLocation, ScratchPad(doId=doId), 1, 1)) self.setMagicWordResponse('doing bad delete') elif wordIs("~idTags"): messenger.send('nameTagShowAvId', []) base.idTags = 1 elif wordIs("~nameTags"): messenger.send('nameTagShowName', []) base.idTags = 0 elif wordIs("~hideNames"): # note do ~hideNames before ~hideGui if you want both off if NametagGlobals.getMasterNametagsVisible(): NametagGlobals.setMasterNametagsVisible(0) else: NametagGlobals.setMasterNametagsVisible(1) elif wordIs("~hideGui"): if aspect2d.isHidden(): aspect2d.show() else: aspect2d.hide() elif wordIs('~flush'): base.cr.doDataCache.flush() base.cr.cache.flush() self.setMagicWordResponse('client object and data caches flushed') elif wordIs('~prof'): import time ### set up ### name = 'default' p = Point3() ############## ts = time.time() for i in xrange(1000000): ### code to be timed ### p.set(1, 2, 3) ######################## tf = time.time() dt = tf - ts response = 'prof(%s): %s secs' % (name, dt) print response self.setMagicWordResponse(response) elif wordIs('~gptc'): args = word.split() if len(args) > 1. and hasattr(self.cr, 'leakDetector'): gptcJob = self.cr.leakDetector.getPathsToContainers( '~gptc', args[1], Functor(self._handleGPTCfinished, args[1])) else: self.setMagicWordResponse('error') elif wordIs('~gptcn'): args = word.split() if len(args) > 1. and hasattr(self.cr, 'leakDetector'): gptcnJob = self.cr.leakDetector.getPathsToContainersNamed( '~gptcn', args[1], Functor(self._handleGPTCNfinished, args[1])) else: self.setMagicWordResponse('error') else: # Not a magic word I know! return 0 return 1
class GarbageReport(Job): """Detects leaked Python objects (via gc.collect()) and reports on garbage items, garbage-to-garbage references, and garbage cycles. If you just want to dump the report to the log, use GarbageLogger.""" notify = directNotify.newCategory("GarbageReport") def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True, threaded=False, doneCallback=None, autoDestroy=False, priority=None, safeMode=False, delOnly=False, collect=True): # if autoDestroy is True, GarbageReport will self-destroy after logging # if false, caller is responsible for calling destroy() # if threaded is True, processing will be performed over multiple frames # if collect is False, we assume that the caller just did a collect and the results # are still in gc.garbage Job.__init__(self, name) # stick the arguments onto a ScratchPad so we can delete them all at once self._args = ScratchPad(name=name, log=log, verbose=verbose, fullReport=fullReport, findCycles=findCycles, doneCallback=doneCallback, autoDestroy=autoDestroy, safeMode=safeMode, delOnly=delOnly, collect=collect) if priority is not None: self.setPriority(priority) jobMgr.add(self) if not threaded: jobMgr.finish(self) def run(self): # do the garbage collection oldFlags = gc.get_debug() if self._args.delOnly: # do a collect without SAVEALL, to identify the instances that are involved in # cycles with instances that define __del__ # cycles that do not involve any instances that define __del__ are cleaned up # automatically by Python, but they also appear in gc.garbage when SAVEALL is set gc.set_debug(0) if self._args.collect: gc.collect() garbageInstances = gc.garbage[:] del gc.garbage[:] # only yield if there's more time-consuming work to do, # if there's no garbage, give instant feedback if len(garbageInstances) > 0: yield None # don't repr the garbage list if we don't have to if self.notify.getDebug(): self.notify.debug('garbageInstances == %s' % fastRepr(garbageInstances)) self.numGarbageInstances = len(garbageInstances) # grab the ids of the garbage instances (objects with __del__) self.garbageInstanceIds = set() for i in range(len(garbageInstances)): self.garbageInstanceIds.add(id(garbageInstances[i])) if i % 20 == 0: yield None # then release the list of instances so that it doesn't interfere with the gc.collect() below del garbageInstances else: self.garbageInstanceIds = set() # do a SAVEALL pass so that we have all of the objects involved in legitimate garbage cycles # without SAVEALL, gc.garbage only contains objects with __del__ methods gc.set_debug(gc.DEBUG_SAVEALL) if self._args.collect: gc.collect() self.garbage = gc.garbage[:] del gc.garbage[:] # only yield if there's more time-consuming work to do, # if there's no garbage, give instant feedback if len(self.garbage) > 0: yield None # don't repr the garbage list if we don't have to if self.notify.getDebug(): self.notify.debug('self.garbage == %s' % fastRepr(self.garbage)) gc.set_debug(oldFlags) self.numGarbage = len(self.garbage) # only yield if there's more time-consuming work to do, # if there's no garbage, give instant feedback if self.numGarbage > 0: yield None if self._args.verbose: self.notify.info('found %s garbage items' % self.numGarbage) # print the types of the garbage first, in case the repr of an object # causes a crash #if self.numGarbage > 0: # self.notify.info('TYPES ONLY (this is only needed if a crash occurs before GarbageReport finishes):') # for result in printNumberedTypesGen(self.garbage): # yield None # Py obj id -> garbage list index self._id2index = {} self.referrersByReference = {} self.referrersByNumber = {} self.referentsByReference = {} self.referentsByNumber = {} self._id2garbageInfo = {} self.cycles = [] self.cyclesBySyntax = [] self.uniqueCycleSets = set() self.cycleIds = set() # make the id->index table to speed up the next steps for i in range(self.numGarbage): self._id2index[id(self.garbage[i])] = i if i % 20 == 0: yield None # grab the referrers (pointing to garbage) if self._args.fullReport and (self.numGarbage != 0): if self._args.verbose: self.notify.info('getting referrers...') for i in range(self.numGarbage): yield None for result in self._getReferrers(self.garbage[i]): yield None byNum, byRef = result self.referrersByNumber[i] = byNum self.referrersByReference[i] = byRef # grab the referents (pointed to by garbage) if self.numGarbage > 0: if self._args.verbose: self.notify.info('getting referents...') for i in range(self.numGarbage): yield None for result in self._getReferents(self.garbage[i]): yield None byNum, byRef = result self.referentsByNumber[i] = byNum self.referentsByReference[i] = byRef for i in range(self.numGarbage): if hasattr(self.garbage[i], '_garbageInfo') and callable( self.garbage[i]._garbageInfo): try: info = self.garbage[i]._garbageInfo() except Exception as e: info = str(e) self._id2garbageInfo[id(self.garbage[i])] = info yield None else: if i % 20 == 0: yield None # find the cycles if self._args.findCycles and self.numGarbage > 0: if self._args.verbose: self.notify.info('calculating cycles...') for i in range(self.numGarbage): yield None for newCycles in self._getCycles(i, self.uniqueCycleSets): yield None self.cycles.extend(newCycles) # create a representation of the cycle in human-readable form newCyclesBySyntax = [] for cycle in newCycles: cycleBySyntax = '' objs = [] # leave off the last index, it's a repeat of the first index for index in cycle[:-1]: objs.append(self.garbage[index]) yield None # make the list repeat so we can safely iterate off the end numObjs = len(objs) - 1 objs.extend(objs) # state variables for our loop below numToSkip = 0 objAlreadyRepresented = False # if cycle starts off with an instance dict, start with the instance instead startIndex = 0 # + 1 to include a reference back to the first object endIndex = numObjs + 1 if inspect.isclass(objs[-1]) and type(objs[0]) is dict: startIndex -= 1 endIndex -= 1 for index in range(startIndex, endIndex): if numToSkip: numToSkip -= 1 continue obj = objs[index] if inspect.isclass(obj): if not objAlreadyRepresented: cycleBySyntax += '%s' % obj.__class__.__name__ cycleBySyntax += '.' # skip past the instance dict and get the member obj numToSkip += 1 member = objs[index + 2] for key, value in obj.__dict__.items(): if value is member: break yield None else: key = '<unknown member name>' cycleBySyntax += '%s' % key objAlreadyRepresented = True elif type(obj) is dict: cycleBySyntax += '{' # get object referred to by dict val = objs[index + 1] for key, value in obj.items(): if value is val: break yield None else: key = '<unknown key>' cycleBySyntax += '%s}' % fastRepr(key) objAlreadyRepresented = True elif type(obj) in (tuple, list): brackets = { tuple: '()', list: '[]', }[type(obj)] # get object being referenced by container nextObj = objs[index + 1] cycleBySyntax += brackets[0] for index in range(len(obj)): if obj[index] is nextObj: index = str(index) break yield None else: index = '<unknown index>' cycleBySyntax += '%s%s' % (index, brackets[1]) objAlreadyRepresented = True else: cycleBySyntax += '%s --> ' % itype(obj) objAlreadyRepresented = False newCyclesBySyntax.append(cycleBySyntax) yield None self.cyclesBySyntax.extend(newCyclesBySyntax) # if we're not doing a full report, add this cycle's IDs to the master set if not self._args.fullReport: for cycle in newCycles: yield None self.cycleIds.update(set(cycle)) self.numCycles = len(self.cycles) if self._args.findCycles: s = [ '===== GarbageReport: \'%s\' (%s %s) =====' % (self._args.name, self.numCycles, ('cycle' if self.numCycles == 1 else 'cycles')) ] else: s = ['===== GarbageReport: \'%s\' =====' % (self._args.name)] if self.numGarbage > 0: # make a list of the ids we will actually be printing if self._args.fullReport: garbageIndices = range(self.numGarbage) else: garbageIndices = list(self.cycleIds) garbageIndices.sort() numGarbage = len(garbageIndices) # log each individual item with a number in front of it if not self._args.fullReport: abbrev = '(abbreviated) ' else: abbrev = '' s.append('===== Garbage Items %s=====' % abbrev) digits = 0 n = numGarbage while n > 0: yield None digits += 1 n /= 10 digits = digits format = '%0' + '%s' % digits + 'i:%s \t%s' for i in range(numGarbage): yield None idx = garbageIndices[i] if self._args.safeMode: # in safe mode, don't try to repr any of the objects objStr = repr(itype(self.garbage[idx])) else: objStr = fastRepr(self.garbage[idx]) maxLen = 5000 if len(objStr) > maxLen: snip = '<SNIP>' objStr = '%s%s' % (objStr[:(maxLen - len(snip))], snip) s.append(format % (idx, itype(self.garbage[idx]), objStr)) # also log the types of the objects s.append('===== Garbage Item Types %s=====' % abbrev) for i in range(numGarbage): yield None idx = garbageIndices[i] objStr = str(deeptype(self.garbage[idx])) maxLen = 5000 if len(objStr) > maxLen: snip = '<SNIP>' objStr = '%s%s' % (objStr[:(maxLen - len(snip))], snip) s.append(format % (idx, itype(self.garbage[idx]), objStr)) if self._args.findCycles: s.append('===== Garbage Cycles (Garbage Item Numbers) =====') ac = AlphabetCounter() for i in range(self.numCycles): yield None s.append('%s:%s' % (ac.next(), self.cycles[i])) if self._args.findCycles: s.append('===== Garbage Cycles (Python Syntax) =====') ac = AlphabetCounter() for i in range(len(self.cyclesBySyntax)): yield None s.append('%s:%s' % (ac.next(), self.cyclesBySyntax[i])) if len(self._id2garbageInfo) > 0: s.append('===== Garbage Custom Info =====') ac = AlphabetCounter() for i in range(len(self.cyclesBySyntax)): yield None counter = ac.next() _id = id(self.garbage[i]) if _id in self._id2garbageInfo: s.append('%s:%s' % (counter, self._id2garbageInfo[_id])) if self._args.fullReport: format = '%0' + '%s' % digits + 'i:%s' s.append( '===== Referrers By Number (what is referring to garbage item?) =====' ) for i in range(numGarbage): yield None s.append(format % (i, self.referrersByNumber[i])) s.append( '===== Referents By Number (what is garbage item referring to?) =====' ) for i in range(numGarbage): yield None s.append(format % (i, self.referentsByNumber[i])) s.append( '===== Referrers (what is referring to garbage item?) =====' ) for i in range(numGarbage): yield None s.append(format % (i, self.referrersByReference[i])) s.append( '===== Referents (what is garbage item referring to?) =====' ) for i in range(numGarbage): yield None s.append(format % (i, self.referentsByReference[i])) self._report = s if self._args.log: self.printingBegin() for i in range(len(self._report)): if self.numGarbage > 0: yield None self.notify.info(self._report[i]) self.notify.info('===== Garbage Report Done =====') self.printingEnd() yield Job.Done def finished(self): if self._args.doneCallback: self._args.doneCallback(self) if self._args.autoDestroy: self.destroy() def destroy(self): #print 'GarbageReport.destroy' del self._args del self.garbage # don't get rid of these, we might need them #del self.numGarbage #del self.numCycles del self.referrersByReference del self.referrersByNumber del self.referentsByReference del self.referentsByNumber if hasattr(self, 'cycles'): del self.cycles del self._report if hasattr(self, '_reportStr'): del self._reportStr Job.destroy(self) def getNumCycles(self): # if the job hasn't run yet, we don't have a numCycles yet return self.numCycles def getDesc2numDict(self): # dict of python-syntax leak -> number of that type of leak desc2num = {} for cycleBySyntax in self.cyclesBySyntax: desc2num.setdefault(cycleBySyntax, 0) desc2num[cycleBySyntax] += 1 return desc2num def getGarbage(self): return self.garbage def getReport(self): if not hasattr(self, '_reportStr'): self._reportStr = '' for str in self._report: self._reportStr += '\n' + str return self._reportStr def _getReferrers(self, obj): # referrers (pointing to garbage) # returns two lists, first by index into gc.garbage, second by # direct reference yield None byRef = gc.get_referrers(obj) yield None # look to see if each referrer is another garbage item byNum = [] for i in range(len(byRef)): if i % 20 == 0: yield None referrer = byRef[i] num = self._id2index.get(id(referrer), None) byNum.append(num) yield byNum, byRef def _getReferents(self, obj): # referents (pointed to by garbage) # returns two lists, first by index into gc.garbage, second by # direct reference yield None byRef = gc.get_referents(obj) yield None # look to see if each referent is another garbage item byNum = [] for i in range(len(byRef)): if i % 20 == 0: yield None referent = byRef[i] num = self._id2index.get(id(referent), None) byNum.append(num) yield byNum, byRef def _getNormalizedCycle(self, cycle): # returns a representation of a cycle (list of indices) that will be # reliably derived from a unique cycle regardless of ordering # this lets us detect duplicate cycles that appear different because of # which element appears first if len(cycle) == 0: return cycle min = 1 << 30 minIndex = None for i in range(len(cycle)): elem = cycle[i] if elem < min: min = elem minIndex = i return cycle[minIndex:] + cycle[:minIndex] def _getCycles(self, index, uniqueCycleSets=None): # detect garbage cycles for a particular item of garbage assert self.notify.debugCall() # returns list of lists, sublists are garbage reference cycles cycles = [] # this lets us eliminate duplicate cycles if uniqueCycleSets is None: uniqueCycleSets = set() stateStack = Stack() rootId = index # check if the root object is one of the garbage instances (has __del__) objId = id(self.garbage[rootId]) numDelInstances = int(objId in self.garbageInstanceIds) stateStack.push(([rootId], rootId, numDelInstances, 0)) while True: yield None if len(stateStack) == 0: break candidateCycle, curId, numDelInstances, resumeIndex = stateStack.pop( ) if self.notify.getDebug(): if self._args.delOnly: print( 'restart: %s root=%s cur=%s numDelInstances=%s resume=%s' % (candidateCycle, rootId, curId, numDelInstances, resumeIndex)) else: print('restart: %s root=%s cur=%s resume=%s' % (candidateCycle, rootId, curId, resumeIndex)) for index in range(resumeIndex, len(self.referentsByNumber[curId])): yield None refId = self.referentsByNumber[curId][index] if self.notify.getDebug(): print(' : %s -> %s' % (curId, refId)) if refId == rootId: # we found a cycle! mark it down and move on to the next refId normCandidateCycle = self._getNormalizedCycle( candidateCycle) normCandidateCycleTuple = tuple(normCandidateCycle) if not normCandidateCycleTuple in uniqueCycleSets: # cycles with no instances that define __del__ will be # cleaned up by Python if (not self._args.delOnly) or numDelInstances >= 1: if self.notify.getDebug(): print( ' FOUND: ', normCandidateCycle + [ normCandidateCycle[0], ]) cycles.append(normCandidateCycle + [ normCandidateCycle[0], ]) uniqueCycleSets.add(normCandidateCycleTuple) elif refId in candidateCycle: pass elif refId is not None: # check if this object is one of the garbage instances (has __del__) objId = id(self.garbage[refId]) numDelInstances += int(objId in self.garbageInstanceIds) # this refId does not complete a cycle. Mark down # where we are in this list of referents, then # start looking through the referents of the new refId stateStack.push((list(candidateCycle), curId, numDelInstances, index + 1)) stateStack.push((list(candidateCycle) + [refId], refId, numDelInstances, 0)) break yield cycles