-
Notifications
You must be signed in to change notification settings - Fork 10
/
Profiler.lua
212 lines (174 loc) · 4.7 KB
/
Profiler.lua
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
--
-- lua 代码耗时统计工具
-- 2018/8/21
-- Author LOLO
--
local sethook = debug.sethook
local getinfo = debug.getinfo
local gettime = tolua.gettime
local remove = table.remove
local tostring = tostring
local floor = math.floor
--
---@class Profiler
---
local Profiler = {}
--- 是否正在统计中
Profiler.profiling = false
local tree = {} --- 组装好的树列表 tree={ key:{ t:time, n:num, c:... }, ... }
local stack = {} --- 函数栈
local id = 0 --- 唯一ID(key)
local cache = {} --- 已获取信息的缓存列表。 cache[fn]=key
local newFn = {} --- 距离上次汇总,新增的函数信息列表 newCache = { key:{ s:source, n:name, l:linedefined }, ... }
local ignoreCache = {} --- C# 函数忽略缓存 ignoreCache[C#fn] = true
local ignoreFrameCount = 0 --- 剩余忽略帧数(在开始统计时需要忽略几帧数据)
--- 需要被忽略的 lua 函数名称列表
local ignoreList = {
"(for generator)", "trycall"
}
-- ignoreList[fnName] = true
local tmpList = ignoreList
ignoreList = {}
for i = 1, #tmpList do
ignoreList[tmpList[i]] = true
end
tmpList = nil
--
--- 代码耗时抓取函数
local function ProfilerHook(type)
-- 忽略 C# 函数(为了提高运行效率)
local fn = getinfo(2, "f").func
if ignoreCache[fn] then
return
end
-- 忽略匿名函数,和 ignoreList
local key = cache[fn]
local fnName
if key == nil then
fnName = getinfo(2, "n").name
if fnName == nil or ignoreList[fnName] then
return
end
end
local count = #stack
-- 函数结束
if type == "return" then
if count == 0 then
--print("count == 0 忽略", key)
return
end
local tail = stack[count]
while count > 1 and key ~= tail.k do
--print("移除", tail.k)
count = count - 1
tail = stack[count]
remove(stack)
end
--print("出栈", key)
tail.t = floor((gettime() - tail.t) * 1000 + 0.5) -- 得出函数耗时(微秒)
remove(stack) -- 出栈
-- 增加自身总耗时
tail.r.t = tail.r.t + tail.t
return
-- 函数开始
elseif type == "call" then
if key == nil then
local info = getinfo(2, "S")
if info.source == "=[C]" then
ignoreCache[fn] = true
return
end
id = id + 1
key = tostring(id)
newFn[key] = {
s = info.source, -- 函数所在文件路径
l = info.linedefined, -- 函数所在行号
n = fnName, -- 函数名称
}
cache[fn] = key
end
local item = {
k = key,
t = gettime(), -- 函数耗时(当前是记录函数开始时间)
}
-- 记录到树列表
local parent
if count == 0 then
parent = tree -- 没有父函数,直接加入树顶端
else
parent = stack[count].r
end
if parent.c == nil then
parent.c = {}
end
parent = parent.c
-- 在 父函数.c 中记录信息
local record = parent[key]
if record == nil then
record = { n = 1, t = 0 }
parent[key] = record
else
record.n = record.n + 1 -- 递增调用次数
end
item.r = record
-- 入栈
stack[count + 1] = item
--print("入栈", key)
end
end
--
--- 开始采样统计
---@param host string
---@param port number
---@param isUDP boolean
function Profiler.Begin(host, port, isUDP)
if Profiler.profiling then
return
end
if host ~= nil and port ~= nil then
ShibaInu.LuaProfiler.Begin(host, port, isUDP)
end
Profiler.profiling = true
if isJIT and not isUDP then
ignoreFrameCount = 15
else
ignoreFrameCount = 5
end
end
--
--- 获取采样数据(每帧汇总)
function Profiler.GetData()
local t = tree
local n = newFn
tree = {}
newFn = {}
stack = {}
if ignoreFrameCount > 0 then
ignoreFrameCount = ignoreFrameCount - 1
if ignoreFrameCount == 0 then
sethook(ProfilerHook, "cr")
end
return '{ "t":{}, "n":{} }'
end
return JSON.Stringify({ t = t, n = n })
end
--
--- 结束采样统计
function Profiler.End()
if not Profiler.profiling then
return
end
Profiler.profiling = false
ignoreFrameCount = 0
tree = {}
stack = {}
cache = {}
newFn = {}
sethook()
ShibaInu.LuaProfiler.End()
end
--
--- 显示或隐藏控制台
Profiler.Console = ShibaInu.LuaProfiler.Console
--
return Profiler