|
@@ -1,6 +1,6 @@
|
|
|
from direct.directnotify.DirectNotifyGlobal import directNotify
|
|
from direct.directnotify.DirectNotifyGlobal import directNotify
|
|
|
from direct.fsm.StatePush import FunctionCall
|
|
from direct.fsm.StatePush import FunctionCall
|
|
|
-from direct.showbase.PythonUtil import getProfileResultString, Averager
|
|
|
|
|
|
|
+from direct.showbase.PythonUtil import Averager
|
|
|
|
|
|
|
|
class TaskTracker:
|
|
class TaskTracker:
|
|
|
# call it TaskProfiler to avoid confusion for the user
|
|
# call it TaskProfiler to avoid confusion for the user
|
|
@@ -8,60 +8,72 @@ class TaskTracker:
|
|
|
def __init__(self, namePattern):
|
|
def __init__(self, namePattern):
|
|
|
self._namePattern = namePattern
|
|
self._namePattern = namePattern
|
|
|
self._durationAverager = Averager('%s-durationAverager' % namePattern)
|
|
self._durationAverager = Averager('%s-durationAverager' % namePattern)
|
|
|
- self._avgData = None
|
|
|
|
|
- self._avgDataDur = None
|
|
|
|
|
- self._maxNonSpikeData = None
|
|
|
|
|
- self._maxNonSpikeDataDur = None
|
|
|
|
|
|
|
+ self._avgSession = None
|
|
|
|
|
+ self._maxNonSpikeSession = None
|
|
|
def destroy(self):
|
|
def destroy(self):
|
|
|
|
|
+ self.flush()
|
|
|
del self._namePattern
|
|
del self._namePattern
|
|
|
del self._durationAverager
|
|
del self._durationAverager
|
|
|
def flush(self):
|
|
def flush(self):
|
|
|
self._durationAverager.reset()
|
|
self._durationAverager.reset()
|
|
|
- self._avgData = None
|
|
|
|
|
- self._avgDataDur = None
|
|
|
|
|
- self._maxNonSpikeData = None
|
|
|
|
|
- self._maxNonSpikeDataDur = None
|
|
|
|
|
|
|
+ if self._avgSession:
|
|
|
|
|
+ self._avgSession.release()
|
|
|
|
|
+ if self._maxNonSpikeSession:
|
|
|
|
|
+ self._maxNonSpikeSession.release()
|
|
|
|
|
+ self._avgSession = None
|
|
|
|
|
+ self._maxNonSpikeSession = None
|
|
|
def getNamePattern(self, namePattern):
|
|
def getNamePattern(self, namePattern):
|
|
|
return self._namePattern
|
|
return self._namePattern
|
|
|
- def addDuration(self, duration, data, isSpike):
|
|
|
|
|
|
|
+ def addProfileSession(self, session, isSpike):
|
|
|
|
|
+ duration = session.getWallClockDuration()
|
|
|
self._durationAverager.addValue(duration)
|
|
self._durationAverager.addValue(duration)
|
|
|
- storeAvgData = True
|
|
|
|
|
- storeMaxNSData = True
|
|
|
|
|
- if self._avgDataDur is not None:
|
|
|
|
|
|
|
+ storeAvg = True
|
|
|
|
|
+ storeMaxNS = True
|
|
|
|
|
+ if self._avgSession is not None:
|
|
|
avgDur = self.getAvgDuration()
|
|
avgDur = self.getAvgDuration()
|
|
|
- if abs(self._avgDataDur - avgDur) < abs(duration - avgDur):
|
|
|
|
|
|
|
+ if abs(self._avgSession.getWallClockDuration() - avgDur) < abs(duration - avgDur):
|
|
|
# current avg data is more average than this new sample, keep the data we've
|
|
# current avg data is more average than this new sample, keep the data we've
|
|
|
# already got stored
|
|
# already got stored
|
|
|
- storeAvgData = False
|
|
|
|
|
|
|
+ storeAvg = False
|
|
|
if isSpike:
|
|
if isSpike:
|
|
|
- storeMaxNSData = False
|
|
|
|
|
|
|
+ storeMaxNS = False
|
|
|
else:
|
|
else:
|
|
|
- if self._maxNonSpikeDataDur is not None and self._maxNonSpikeDataDur > duration:
|
|
|
|
|
- storeMaxNSData = False
|
|
|
|
|
- if storeAvgData:
|
|
|
|
|
- self._avgData = data
|
|
|
|
|
- self._avgDataDur = duration
|
|
|
|
|
- if storeMaxNSData:
|
|
|
|
|
- self._maxNonSpikeData = data
|
|
|
|
|
- self._maxNonSpikeDataDur = duration
|
|
|
|
|
|
|
+ if (self._maxNonSpikeSession is not None and
|
|
|
|
|
+ self._maxNonSpikeSession.getWallClockDuration() > duration):
|
|
|
|
|
+ storeMaxNS = False
|
|
|
|
|
+ if storeAvg:
|
|
|
|
|
+ if self._avgSession:
|
|
|
|
|
+ self._avgSession.release()
|
|
|
|
|
+ session.acquire()
|
|
|
|
|
+ self._avgSession = session
|
|
|
|
|
+ if storeMaxNS:
|
|
|
|
|
+ if self._maxNonSpikeSession:
|
|
|
|
|
+ self._maxNonSpikeSession.release()
|
|
|
|
|
+ session.acquire()
|
|
|
|
|
+ self._maxNonSpikeSession = session
|
|
|
def getAvgDuration(self):
|
|
def getAvgDuration(self):
|
|
|
return self._durationAverager.getAverage()
|
|
return self._durationAverager.getAverage()
|
|
|
def getNumDurationSamples(self):
|
|
def getNumDurationSamples(self):
|
|
|
return self._durationAverager.getCount()
|
|
return self._durationAverager.getCount()
|
|
|
- def getAvgData(self):
|
|
|
|
|
- # returns duration, data for closest-to-average sample
|
|
|
|
|
- return self._avgDataDur, self._avgData
|
|
|
|
|
- def getMaxNonSpikeData(self):
|
|
|
|
|
- # returns duration, data for closest-to-average sample
|
|
|
|
|
- return self._maxNonSpikeDataDur, self._maxNonSpikeData
|
|
|
|
|
|
|
+ def getAvgSession(self):
|
|
|
|
|
+ # returns profile session for closest-to-average sample
|
|
|
|
|
+ return self._avgSession
|
|
|
|
|
+ def getMaxNonSpikeSession(self):
|
|
|
|
|
+ # returns profile session for closest-to-average sample
|
|
|
|
|
+ return self._maxNonSpikeSession
|
|
|
def log(self):
|
|
def log(self):
|
|
|
- self.notify.info('task CPU profile (%s):\n'
|
|
|
|
|
- '== AVERAGE (%s wall-clock seconds)\n%s\n'
|
|
|
|
|
- '== LONGEST NON-SPIKE (%s wall-clock seconds)\n%s' % (
|
|
|
|
|
- self._namePattern,
|
|
|
|
|
- self._avgDataDur, self._avgData,
|
|
|
|
|
- self._maxNonSpikeDataDur, self._maxNonSpikeData,
|
|
|
|
|
- ))
|
|
|
|
|
|
|
+ if self._avgSession:
|
|
|
|
|
+ self.notify.info('task CPU profile (%s):\n'
|
|
|
|
|
+ '== AVERAGE (%s wall-clock seconds)\n%s\n'
|
|
|
|
|
+ '== LONGEST NON-SPIKE (%s wall-clock seconds)\n%s' % (
|
|
|
|
|
+ self._namePattern,
|
|
|
|
|
+ self._avgSession.getWallClockDuration(),
|
|
|
|
|
+ self._avgSession.getResults(),
|
|
|
|
|
+ self._maxNonSpikeSession.getWallClockDuration(),
|
|
|
|
|
+ self._maxNonSpikeSession.getResults(),
|
|
|
|
|
+ ))
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.notify.info('task CPU profile (%s): no data collected' % self._namePattern)
|
|
|
|
|
|
|
|
class TaskProfiler:
|
|
class TaskProfiler:
|
|
|
# this does intermittent profiling of tasks running on the system
|
|
# this does intermittent profiling of tasks running on the system
|
|
@@ -77,8 +89,6 @@ class TaskProfiler:
|
|
|
self._minSamples = config.GetInt('profile-task-spike-min-samples', 30)
|
|
self._minSamples = config.GetInt('profile-task-spike-min-samples', 30)
|
|
|
# defines spike as longer than this multiple of avg task duration
|
|
# defines spike as longer than this multiple of avg task duration
|
|
|
self._spikeThreshold = config.GetFloat('profile-task-spike-threshold', 10.)
|
|
self._spikeThreshold = config.GetFloat('profile-task-spike-threshold', 10.)
|
|
|
- # assign getProfileResultString to the taskMgr here, since Task.py can't import PythonUtil
|
|
|
|
|
- taskMgr._getProfileResultString = getProfileResultString
|
|
|
|
|
|
|
|
|
|
def destroy(self):
|
|
def destroy(self):
|
|
|
if taskMgr.getProfileTasks():
|
|
if taskMgr.getProfileTasks():
|
|
@@ -117,31 +127,32 @@ class TaskProfiler:
|
|
|
def _doProfileTasks(self, task=None):
|
|
def _doProfileTasks(self, task=None):
|
|
|
# gather data from the previous frame
|
|
# gather data from the previous frame
|
|
|
# set up for the next frame
|
|
# set up for the next frame
|
|
|
- profileDt = taskMgr._getTaskProfileDt()
|
|
|
|
|
- if (self._task is not None) and (profileDt is not None):
|
|
|
|
|
- lastProfileResult = taskMgr._getLastProfileResultString()
|
|
|
|
|
- if lastProfileResult:
|
|
|
|
|
- namePattern = self._task.getNamePattern()
|
|
|
|
|
- if namePattern not in self._namePattern2tracker:
|
|
|
|
|
- self._namePattern2tracker[namePattern] = TaskTracker(namePattern)
|
|
|
|
|
- tracker = self._namePattern2tracker[namePattern]
|
|
|
|
|
- isSpike = False
|
|
|
|
|
- # do we have enough samples?
|
|
|
|
|
- if tracker.getNumDurationSamples() > self._minSamples:
|
|
|
|
|
- # was this a spike?
|
|
|
|
|
- if profileDt > (tracker.getAvgDuration() * self._spikeThreshold):
|
|
|
|
|
- isSpike = True
|
|
|
|
|
- avgDur, avgResult = tracker.getAvgData()
|
|
|
|
|
- maxNSDur, maxNSResult = tracker.getMaxNonSpikeData()
|
|
|
|
|
- self.notify.info('task CPU spike profile (%s):\n'
|
|
|
|
|
- '== AVERAGE (%s wall-clock seconds)\n%s\n'
|
|
|
|
|
- '== LONGEST NON-SPIKE (%s wall-clock seconds)\n%s\n'
|
|
|
|
|
- '== SPIKE (%s wall-clock seconds)\n%s' % (
|
|
|
|
|
- namePattern,
|
|
|
|
|
- avgDur, avgResult,
|
|
|
|
|
- maxNSDur, maxNSResult,
|
|
|
|
|
- profileDt, lastProfileResult))
|
|
|
|
|
- tracker.addDuration(profileDt, lastProfileResult, isSpike)
|
|
|
|
|
|
|
+ if (self._task is not None) and taskMgr._hasProfiledDesignatedTask():
|
|
|
|
|
+ session = taskMgr._getLastProfileSession()
|
|
|
|
|
+ sessionDur = session.getWallClockDuration()
|
|
|
|
|
+ namePattern = self._task.getNamePattern()
|
|
|
|
|
+ if namePattern not in self._namePattern2tracker:
|
|
|
|
|
+ self._namePattern2tracker[namePattern] = TaskTracker(namePattern)
|
|
|
|
|
+ tracker = self._namePattern2tracker[namePattern]
|
|
|
|
|
+ isSpike = False
|
|
|
|
|
+ # do we have enough samples?
|
|
|
|
|
+ if tracker.getNumDurationSamples() > self._minSamples:
|
|
|
|
|
+ # was this a spike?
|
|
|
|
|
+ if sessionDur > (tracker.getAvgDuration() * self._spikeThreshold):
|
|
|
|
|
+ print 'sessionDur=%s' % sessionDur
|
|
|
|
|
+ print 'avgDur=%s' % tracker.getAvgDuration()
|
|
|
|
|
+ isSpike = True
|
|
|
|
|
+ avgSession = tracker.getAvgSession()
|
|
|
|
|
+ maxNSSession = tracker.getMaxNonSpikeSession()
|
|
|
|
|
+ self.notify.info('task CPU spike profile (%s):\n'
|
|
|
|
|
+ '== AVERAGE (%s wall-clock seconds)\n%s\n'
|
|
|
|
|
+ '== LONGEST NON-SPIKE (%s wall-clock seconds)\n%s\n'
|
|
|
|
|
+ '== SPIKE (%s wall-clock seconds)\n%s' % (
|
|
|
|
|
+ namePattern,
|
|
|
|
|
+ avgSession.getWallClockDuration(), avgSession.getResults(),
|
|
|
|
|
+ maxNSSession.getWallClockDuration(), maxNSSession.getResults(),
|
|
|
|
|
+ sessionDur, session.getResults()))
|
|
|
|
|
+ tracker.addProfileSession(session, isSpike)
|
|
|
|
|
|
|
|
# set up the next task
|
|
# set up the next task
|
|
|
self._task = taskMgr._getRandomTask()
|
|
self._task = taskMgr._getRandomTask()
|