''' Description: Simple code profiling module, which generates a hierarchy of events and saves them to disk in xml form. Author: Jason Hayes ''' import os import datetime import xml.etree.cElementTree import inspect import RS.Config from pyfbsdk import * # Profile event stack. __PROFILING_STACK = [] # Index of where we are in the stack. __CURRENT_STACK_IDX = 0 # Whether or not the profiler is enabled. ENABLED = True # Profile event history directory. PROFILE_DIR = '{0}\\profiler'.format( RS.Config.Script.Path.Root ) if not os.path.isdir( PROFILE_DIR ): os.makedirs( PROFILE_DIR ) class ProfileEvent( object ): ''' Description: Container for a profile event. Records the start and stop time. Author: Jason Hayes Arguments: context: A named string representing the profile event. Keyword Arguments: parentEvent: A parent profile event. ''' def __init__( self, context, parentEvent = None, totalTime = 0.0 ): self.__parentEvent = parentEvent self.__context = context self.__start = datetime.datetime.now() self.__end = None self.__subEvents = [] self.__totalTime = totalTime ## Properties ## @property def context( self ): return self.__context @property def subEvents( self ): return self.__subEvents @property def totalTime( self ): return self.__totalTime ## Methods ## def addSubEvent( self, event ): self.__subEvents.append( event ) def stop( self ): self.__end = datetime.datetime.now() totalTime = self.__end - self.__start self.__totalTime = '{0}.{1}'.format( totalTime.seconds, totalTime.microseconds ) def printResult( self ): print '{0}: {1} seconds'.format( self.__context, self.totalTime ) def start( context, parentEvent = None ): ''' Description: Starts a new profile event. Author: Jason Hayes Arguments: context: A named string representing the profiled event. Returns: The started ProfileEvent object. ''' global __PROFILING_STACK global __CURRENT_STACK_IDX global ENABLED if ENABLED: event = None currentFrame = inspect.currentframe() callerLineNo = currentFrame.f_back.f_lineno callerCodeFilename = currentFrame.f_back.f_code.co_filename if parentEvent: event = ProfileEvent( context, parentEvent = parentEvent ) parentEvent.addSubEvent( event ) else: event = ProfileEvent( context ) __PROFILING_STACK.append( event ) __CURRENT_STACK_IDX += 1 return event return None def stop( printResult = False ): ''' Description: Stops the current profile event on the stack. Author: Jason Hayes Keyword Arguments: printResult: Boolean of whether or not to print out the result of the profiled event. ''' global __PROFILING_STACK global __CURRENT_STACK_IDX global ENABLED if ENABLED: if __CURRENT_STACK_IDX > 0: __CURRENT_STACK_IDX -= 1 event = __PROFILING_STACK[ __CURRENT_STACK_IDX ] event.stop() if printResult: event.printResult() # Write the current profile event history to disk. if __CURRENT_STACK_IDX == 0: saveProfileEvents() __PROFILING_STACK.pop( __CURRENT_STACK_IDX ) def getTabs( tabLevel ): tabStr = '' for i in range( 0, tabLevel ): tabStr += '\t' return tabStr def recurseWriteEvents( event, xmlDoc, tabLevel ): tabLevel += 1 for event in event.subEvents: xmlDoc.write( '{0}\n'.format( getTabs( tabLevel ), event.context, event.totalTime ) ) if event.subEvents: recurseWriteEvents( event, xmlDoc, tabLevel ) xmlDoc.write( '{0}\n'.format( getTabs( tabLevel ) ) ) tabLevel -= 1 def saveProfileEvents(): global __PROFILING_STACK global PROFILE_DIR global LOG_AS_MICROSECONDS tabLevel = 0 topEvent = __PROFILING_STACK[ 0 ] writeTime = datetime.datetime.now() doc = open( '{0}\\{1}_{2}{3}{4}{5}{6}{7}{8}.xml'.format( PROFILE_DIR, topEvent.context, writeTime.day, writeTime.month, writeTime.year, writeTime.hour, writeTime.minute, writeTime.second, writeTime.microsecond ), 'w' ) doc.write( '\n' ) doc.write( '\n' ) tabLevel += 1 doc.write( '{0}\n'.format( getTabs( tabLevel ) ) ) tabLevel += 1 doc.write( '{0}{1}\n'.format( getTabs( tabLevel ), FBApplication().FBXFileName ) ) doc.write( '{0}{1}\n'.format( getTabs( tabLevel ), RS.Config.User.Name ) ) tabLevel -= 1 doc.write( '{0}\n'.format( getTabs( tabLevel ) ) ) doc.write( '{0}\n'.format( getTabs( tabLevel ), topEvent.context, topEvent.totalTime ) ) recurseWriteEvents( topEvent, doc, tabLevel ) doc.write( '{0}\n'.format( getTabs( tabLevel ) ) ) doc.write( '\n' ) doc.close() def loadProfileEvents( filename ): events = [] def recurseReadXmlEvents( xmlRoot, parentEvent ): eventNodes = xmlRoot.findall( 'event' ) for eventNode in eventNodes: context = eventNode.get( 'context' ) totalTime = eventNode.get( 'totalTime' ) event = ProfileEvent( context, parentEvent, totalTime = totalTime ) parentEvent.addSubEvent( event ) recurseReadXmlEvents( eventNode, event ) if os.path.isfile( filename ): doc = xml.etree.cElementTree.parse( filename ) root = doc.getroot() if root: infoRoot = root.find( 'info' ) if infoRoot: fbxFilename = infoRoot.find( 'fbxFilename' ) username = infoRoot.find( 'username' ) rootEventNodes = root.findall( 'event' ) for rootEventNode in rootEventNodes: context = rootEventNode.get( 'context' ) totalTime = rootEventNode.get( 'totalTime' ) event = ProfileEvent( context, totalTime = totalTime ) events.append( event ) recurseReadXmlEvents( rootEventNode, event ) return events #events = loadProfileEvents( r'X:\wildwest\script\MotionBuilderPython\profiler\Strip Scene_432013175244593000.xml' ) #print events