|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
--[[This is a reference implementation of a profile reporter using the ScriptProfiler API.You can use it as an example of you how you can get useful results from the API, but alternate reportersshould be implemented in terms of the API, and not the functions defined here.Usage:to start collecting data: StartScriptProfiler()to stop: StopScriptProfiler()To print a report out to chat: ZO_ScriptProfiler_GenerateReport()--]]local g_generatingReport = false if g_generatingReport then return end g_generatingReport = true local numRecords = 0 local timeSpent = 0 local recordDataByRecordDataType = { [SCRIPT_PROFILER_RECORD_DATA_TYPE_CLOSURE] = {}, [SCRIPT_PROFILER_RECORD_DATA_TYPE_CFUNCTION] = {}, [SCRIPT_PROFILER_RECORD_DATA_TYPE_GARBAGE_COLLECTION] = {}, [SCRIPT_PROFILER_RECORD_DATA_TYPE_USER_EVENT] = {}, } if not recordDataByRecordDataType[recordDataType][recordDataIndex] then { dataType = recordDataType, includeTime = 0, excludeTime = 0, } if recordDataType == SCRIPT_PROFILER_RECORD_DATA_TYPE_CLOSURE then -- Closures are functions defined in Lua. Functions defined in the same file, on the same line, are considered the same function by the profiler. elseif recordDataType == SCRIPT_PROFILER_RECORD_DATA_TYPE_CFUNCTION then -- C Functions are functions defined by ZOS as part of the game's API. elseif recordDataType == SCRIPT_PROFILER_RECORD_DATA_TYPE_GARBAGE_COLLECTION then -- At arbitrary times, the lua intepreter will automatically try to reclaim memory you are no longer using. When it does this we generate a GC event to track it. data.name = GetScriptProfilerGarbageCollectionInfo(recordDataIndex) == SCRIPT_PROFILER_GARBAGE_COLLECTION_TYPE_AUTOMATIC and "Lua GC Step" or "Manual collectgarbage() GC step" elseif recordDataType == SCRIPT_PROFILER_RECORD_DATA_TYPE_USER_EVENT then -- You can fire off your own custom events using RecordScriptProfilerUserEvent(myEventString). Events with the same eventString will share a recordDataIndex. else end end return recordDataByRecordDataType[recordDataType][recordDataIndex] end local recordDataIndex, startTimeNS, endTimeNS, calledByRecordIndex, recordDataType = GetScriptProfilerRecordInfo(frameIndex, recordIndex) local timeMS = (endTimeNS - startTimeNS) / (1000*1000) source.includeTime = source.includeTime + timeMS source.excludeTime = source.excludeTime + timeMS timeSpent = timeSpent + timeMS if calledByRecordIndex then -- get caller, and exclude the current record's time from it. By the end, the only time that will be left is time spent exclusively in the caller and not in the callees. local calledByRecordDataIndex, _, _, _, calledByRecordDataType = GetScriptProfilerRecordInfo(frameIndex, calledByRecordIndex) calledByData.excludeTime = calledByData.excludeTime - timeMS end end local sorted = {} end end return a.excludeTime > b.excludeTime end) -- Print backwards, so the first element is at the bottom of chat end local totals = { numRecords = numRecords, } g_generatingReport = false end do -- This splits up the work you would otherwise do as: -- for frameIndex = 1, GetScriptProfilerNumFrames() do -- for recordIndex = 1, GetScriptProfilerFrameNumRecords(frameIndex) do -- ... -- end -- end local frameIndex = 1 local recordIndex = 1 local RECORDS_PER_UPDATE = 100000 -- Arbitrarily chosen number, tune to workload for _ = 1, RECORDS_PER_UPDATE do if recordIndex > numFrameRecords then frameIndex = frameIndex + 1 recordIndex = 1 end if frameIndex > numFrames then return end numRecords = numRecords + 1 recordIndex = recordIndex + 1 end end) endend |