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