NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

profiling.py (13295B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\util\profiling.py
      2 from __future__ import print_function
      3 import blue
      4 import stackless
      5 import uthread
      6 from carbon.common.script.sys.service import Service
      7 from carbon.common.script.sys.serviceConst import ROLE_ANY
      8 from carbon.common.script.util import timerstuff
      9 from carbon.common.script.util.format import FmtDate
     10 from eve.common.lib import appConst as const
     11 from eveprefs import boot
     12 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     13 
     14 class TotalProfiler:
     15 
     16     def __init__(self):
     17         self.file = None
     18         self.timer = None
     19         self.startMem = None
     20         self.snapshotDict = {}
     21 
     22     def Start(self):
     23         now = blue.os.GetWallclockTime()
     24         datestr = FmtDate(now, 'sl')
     25         datestr = datestr.replace('.', '')
     26         datestr = datestr.replace(':', '')
     27         datestr = datestr.replace(' ', '')
     28         import os
     29         self.file = file(os.path.join(blue.paths.ResolvePath(u'cache:/'), 'prfl' + datestr + '.txt'), 'w')
     30         self.file.write('<profiling>\r\n')
     31         self.file.write('<data name="time" type="int">' + datestr[-6:] + '</data>\r\n')
     32         self.file.write('<data name="date" type="int">' + datestr[:-6] + '</data>\r\n')
     33         self.file.write('<data name="build" type="int">' + str(boot.build) + '</data>\r\n')
     34         self.startMem = blue.sysinfo.GetMemory()
     35 
     36     def StartLocation(self, location):
     37         self.file.write('<locations type="list" name="location">\r\n')
     38         self.file.write('<location id="TheLocation">\r\n')
     39 
     40     def EndLocation(self):
     41         self.file.write('</location>\r\n')
     42         self.file.write('</locations>\r\n')
     43 
     44     def Finish(self):
     45         endMem = blue.sysinfo.GetMemory()
     46         self.file.write('<data name="NettoMemory" type="int" signifier="Kb">' + str((endMem.pageFile - self.startMem.pageFile) / 1024) + '</data>\r\n')
     47         self.file.write('</profiling>\r\n')
     48         self.file.close()
     49 
     50     def BeginSnapshot(self, name):
     51         key = uthread.uniqueId()
     52         snapshot = self.snapshotDict[key] = Snapshot(name)
     53         tasklets = blue.pyos.taskletTimer.GetTasklets().values()
     54         snapshot.taskletStart = map(lambda i: (str(i.context), i.switches, i.time), filter(lambda i: i.context != 'Idle Thread' and i.time, tasklets))
     55         snapshot.memoryStart = blue.sysinfo.GetMemory().pageFile
     56         return key
     57 
     58     def EndSnapshot(self, key):
     59         snapshot = self.snapshotDict[key]
     60         snapshot.end = blue.os.GetWallclockTimeNow()
     61         tasklets = blue.pyos.taskletTimer.GetTasklets().values()
     62         snapshot.taskletEnd = map(lambda i: (str(i.context), i.switches, i.time), filter(lambda i: i.context != 'Idle Thread' and i.time, tasklets))
     63         snapshot.memoryEnd = blue.sysinfo.GetMemory().pageFile
     64 
     65     def ProcessSnapshots(self):
     66         self.file.write('<snapshots type="list" name="snapshot">\r\n')
     67         for key, snapshot in self.snapshotDict.iteritems():
     68             tasklets = snapshot.ProcessTasklets()
     69             self.file.write('<snapshot name="' + snapshot.name + '" id="' + str(key) + '">\r\n')
     70             self.file.write('<data name="tasklets" type="list">\r\n')
     71             for each in tasklets:
     72                 self.file.write(str(each) + '\r\n')
     73 
     74             self.file.write('</data>\r\n')
     75             self.file.write('<data name="ms" type="int" signifier="ms">' + str(blue.os.TimeDiffInMs(snapshot.start, snapshot.end)) + '</data>\r\n')
     76             self.file.write('<Memory>\r\n')
     77             self.file.write('<data name="usage" type="int" signifier="Kb">' + str((snapshot.memoryEnd - snapshot.memoryStart) / 1024) + '</data>\r\n')
     78             self.file.write('</Memory>\r\n')
     79             self.file.write('</snapshot>\r\n')
     80 
     81         self.file.write('</snapshots>\r\n')
     82 
     83 
     84 class FPSProfiler:
     85 
     86     def Start(self, file, ms = 10000):
     87         self.minValue = 10000000.0
     88         self.maxValue = -1.0
     89         self.num = 0
     90         self.accum = 0.0
     91         self.run = 1
     92         self.start = blue.os.GetWallclockTimeNow()
     93         self.endtimer = timerstuff.AutoTimer(ms, self.Finish, file)
     94         uthread.new(self.Start_thread).context = 'profiling.FPSProfiler.Start'
     95 
     96     def Start_thread(self):
     97         while self.run:
     98             self.num += 1
     99             fps = blue.os.fps
    100             if fps > self.maxValue:
    101                 self.maxValue = fps
    102             elif fps < self.minValue:
    103                 self.minValue = fps
    104             blue.pyos.synchro.Yield()
    105 
    106     def Finish(self, file):
    107         self.endtimer = None
    108         self.run = 0
    109         end = blue.os.GetWallclockTimeNow()
    110         totTime = blue.os.TimeDiffInMs(self.start, end)
    111         FPS = self.num * 1000.0 / totTime
    112         file.write('<FPS>\r\n')
    113         file.write('<data name="average" type="float">' + str(FPS) + '</data>\r\n')
    114         file.write('<data name="max" type="float">' + str(self.maxValue) + '</data>\r\n')
    115         file.write('<data name="min" type="float">' + str(self.minValue) + '</data>\r\n')
    116         file.write('</FPS>\r\n')
    117 
    118 
    119 class DispatchProfiler:
    120 
    121     def __init__(self):
    122         self.startMem = None
    123         self.startNet = None
    124         self.started = 0
    125 
    126     def Start(self):
    127         self.started = 1
    128         self.startMem = blue.sysinfo.GetMemory()
    129         self.startNet = sm.GetService('machoNet').GetConnectionProperties()
    130         blue.pyos.taskletTimer.Reset()
    131 
    132     def Finish(self, file):
    133         if not self.started:
    134             raise RuntimeError('Flush before start')
    135         self.started = 0
    136         tasklets = blue.pyos.taskletTimer.GetTasklets().values()
    137         tasklets = map(lambda i: (str(i.context), i.switches, i.time), filter(lambda i: i.context != 'Idle Thread' and i.time, tasklets))
    138         blue.pyos.taskletTimer.Reset()
    139         self.endNet = sm.GetService('machoNet').GetConnectionProperties()
    140         file.write('<data name="ConnectionProperties" type="dict">\r\n')
    141         for key in self.startNet.iterkeys():
    142             file.write(key + ': ' + str(self.endNet[key] - self.startNet[key]) + '\r\n')
    143 
    144         file.write('</data>\r\n')
    145         file.write('\r\n<data name="tasklets" type="list">\r\n')
    146         for each in tasklets:
    147             file.write(str(each) + '\r\n')
    148 
    149         file.write('</data>\r\n')
    150         tasklets = None
    151         endMem = blue.sysinfo.GetMemory()
    152         file.write('<Memory>\r\n')
    153         file.write('<data name="usage" type="int" signifier="Kb">' + str((endMem.pageFile - self.startMem.pageFile) / 1024) + '</data>\r\n')
    154         file.write('</Memory>\r\n')
    155 
    156 
    157 class Snapshot:
    158 
    159     def __init__(self, name = ''):
    160         self.taskletStart = None
    161         self.taskletEnd = None
    162         self.memoryStart = 0
    163         self.memoryEnd = 0
    164         self.start = blue.os.GetWallclockTimeNow()
    165         self.end = self.start
    166         self.name = name
    167 
    168     def ProcessTasklets(self):
    169         aDict = {}
    170         for each in self.taskletEnd:
    171             aDict[each[0]] = list(each)
    172 
    173         for each in self.taskletStart:
    174             if each[0] in aDict:
    175                 aDict[each[0]][1] = aDict[each[0]][1] - each[1]
    176                 aDict[each[0]][2] = aDict[each[0]][2] - each[2]
    177 
    178         return aDict.values()
    179 
    180 
    181 class ProfilingScheduler(Service):
    182     __guid__ = 'svc.profiling'
    183     __exportedcalls__ = {'StartDispatching': [ROLE_ANY],
    184      'StartProfiling': [ROLE_ANY],
    185      'BeginSnapshot': [ROLE_ANY],
    186      'EndSnapshot': [ROLE_ANY]}
    187     __notifyevents__ = ['OnClientEvent_WarpFinished', 'OnSessionChanged']
    188     __dependencies__ = ['michelle']
    189     running = 0
    190 
    191     def __init__(self):
    192         Service.__init__(self)
    193         self.channel = stackless.channel()
    194         self.keepAlive = NonniEvent(2000)
    195         self.totalProfiler = TotalProfiler()
    196         self.FPSProfiler = FPSProfiler()
    197         self.dispatchProfiler = DispatchProfiler()
    198         self.timer = None
    199         self.snapshotDict = {}
    200 
    201     def Run(self, memstream = None):
    202         Service.Run(self, memstream)
    203         if not self.SetUpEnvironment():
    204             raise RuntimeError('environment not suitable for profiling')
    205 
    206     def StartDispatching(self):
    207         self.dispatchProfiler.Start()
    208 
    209     def SetUpEnvironment(self):
    210         if settings.public.generic.Get('memoryMapping', 1):
    211             print('memory mapping should be turned off for normal profiling')
    212             print('in prefs.ini set')
    213             print('memoryMapping=0')
    214             print('and restart EVE')
    215             return 0
    216         if not session.charid:
    217             prompt_player_fsd_dialog('CustomInfo', {'info': 'You have to be in the game to use the profiler'})
    218             return 0
    219         return 1
    220 
    221     def StartProfiling(self, start, locationList):
    222         itemInPark = sm.GetService('michelle').GetBall(start)
    223         haveToWait = 1
    224         if itemInPark:
    225             from eve.client.script.ui.util import uix
    226             if uix.GetDistanceBetweenBalls(itemInPark, sm.GetService('michelle').GetBall(session.shipid)) > const.minWarpDistance:
    227                 self.WarpTo(start)
    228             else:
    229                 haveToWait = 0
    230         else:
    231             cmdstring = '/tr me %s' % start
    232             slashRes = sm.RemoteSvc('slash').SlashCmd(cmdstring)
    233         if haveToWait:
    234             self.LogInfo('Waiting until in place')
    235             self.channel.receive()
    236         self.LogInfo('Ready to run')
    237         self.LogInfo('Verifying locations')
    238         blue.pyos.synchro.SleepWallclock(2000)
    239         for location in locationList:
    240             inPark = sm.GetService('michelle').GetBall(location)
    241             if not inPark:
    242                 self.LogError('Location ', location, ' not in ballPark')
    243                 raise RuntimeError('location not found in ballpark')
    244 
    245         self.totalProfiler.Start()
    246         self.running = 1
    247         self.LoopThrough(locationList)
    248         self.WarpTo(start)
    249         self.channel.receive()
    250         self.totalProfiler.Finish()
    251         self.LogInfo('Finished profiling')
    252         self.running = 0
    253 
    254     def LoopThrough(self, locationList):
    255         for location in locationList:
    256             self.dispatchProfiler.Prime()
    257             self.LogInfo('Trying to warp to ', location)
    258             self.WarpTo(location)
    259             self.LogInfo('Waiting for warp to finish')
    260             self.channel.receive()
    261             self.LogInfo('Warping finished')
    262             self.LogInfo('Wait while we gather information, and allow for interupts')
    263             start = blue.os.GetWallclockTimeNow()
    264             blue.pyos.synchro.SleepWallclock(1000)
    265             timer = timerstuff.AutoTimer(60000, self.ShuntEvents)
    266             self.keepAlive.Wait()
    267             timer = None
    268             end = blue.os.GetWallclockTimeNow()
    269             self.LogInfo('We waited for ', blue.os.TimeDiffInMs(start, end), ' ms')
    270             self.totalProfiler.ProcessSnapshots()
    271             self.dispatchProfiler.Flush(self.totalProfiler.file)
    272             self.LogInfo('Sleeping for good measure')
    273             blue.pyos.synchro.SleepWallclock(2000)
    274             FPStime = 10000
    275             self.LogInfo('FPS profiling for ', FPStime, ' ms')
    276             self.FPSProfiler.Start(self.totalProfiler.file, FPStime)
    277             blue.pyos.synchro.SleepWallclock(FPStime + 500)
    278             self.LogInfo(location, ' done.')
    279 
    280     def WarpTo(self, location):
    281         uthread.new(self.WarpTo_thread, location).context = 'svc.profiling.WarpTo'
    282 
    283     def WarpTo_thread(self, location):
    284         bp = sm.GetService('michelle').GetRemotePark()
    285         if bp is None:
    286             raise RuntimeError('No park found')
    287         self.LogInfo('Warping to ', location)
    288         bp.CmdWarpToStuff('item', location)
    289 
    290     def BeginSnapshot(self, name):
    291         if not self.dispatchProfiler.primed:
    292             return
    293         self.keepAlive.Set()
    294         return self.totalProfiler.BeginSnapshot(name)
    295 
    296     def EndSnapshot(self, key):
    297         self.totalProfiler.BeginSnapshot(key)
    298         self.keepAlive.Release()
    299 
    300     def OnClientEvent_WarpFinished(self, *args):
    301         self.channel.send(None)
    302 
    303     def OnSessionChanged(self, *args):
    304         self.channel.send(None)
    305 
    306     def ShuntEvents(self):
    307         self.LogWarn('We had to kill some events')
    308         ret = self.keepAlive.ReleaseAll()
    309         if ret > 0:
    310             self.LogWarn(ret, ' sends without releases')
    311         if ret < 0:
    312             self.LogError(-ret, ' receives without sends?!?')
    313 
    314 
    315 def Get():
    316     return ProfilingScheduler.running
    317 
    318 
    319 def Start():
    320     if ProfilingScheduler.running:
    321         sm.GetService('profiling').StartDispatching()
    322 
    323 
    324 class NonniEvent:
    325 
    326     def __init__(self, sleepingTime = 0):
    327         self.channel = stackless.channel()
    328         self.primed = 0
    329         self.receiving = 0
    330         self.sleepingTime = sleepingTime
    331 
    332     def Set(self):
    333         self.primed += 1
    334         return 1
    335 
    336     def Release(self):
    337         self.channel.send(None)
    338 
    339     def Wait(self):
    340         if not self.primed:
    341             return
    342         while self.primed:
    343             self.primed -= 1
    344             self.channel.receive()
    345 
    346         blue.pyos.synchro.SleepWallclock(self.sleepingTime)
    347         self.Wait()
    348 
    349     def ReleaseAll(self):
    350         ret = self.channel.balance
    351         for i in range(self.channel.balance):
    352             self.channel.receive()
    353 
    354         for i in range(-self.channel.balance):
    355             self.channel.send(None)
    356 
    357         self.primed = 0
    358         return ret