vanilla-wow-addons – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | |
2 | --[[ |
||
3 | |||
4 | Main.lua |
||
5 | |||
6 | This module does all the work. It hooks every OnEvent and OnUpdate function in existance. |
||
7 | |||
8 | ]] |
||
9 | |||
10 | -- table setup |
||
11 | local mod = thismod |
||
12 | local me = { } |
||
13 | mod.main = me |
||
14 | |||
15 | me.ishooking = false |
||
16 | |||
17 | --[[ |
||
18 | me.hookall() |
||
19 | Hooks all the OnUpdate() and OnEvent() methods running. |
||
20 | |||
21 | First thing is to gather all frames. We make a table, key = the frame itself, value = {onevent = <true or nil>, onupdate = <true or nil>} |
||
22 | ]] |
||
23 | me.hookall = function() |
||
24 | |||
25 | -- this method can only be run once - check! |
||
26 | if me.ishooking == true then |
||
27 | mod.out.print("The monitor has already been loaded, and is actively recording data.") |
||
28 | return |
||
29 | end |
||
30 | |||
31 | local frame, script |
||
32 | local numonevents = 0 |
||
33 | local numonupdates = 0 |
||
34 | local numframes = 0 |
||
35 | local numemptyframes = 0 |
||
36 | |||
37 | -- first let's find all the frames |
||
38 | local allframes = { } |
||
39 | local hasonupdate, hasonevent |
||
40 | |||
41 | while true do |
||
42 | |||
43 | -- get next frame |
||
44 | frame = EnumerateFrames(frame) |
||
45 | if frame == nil then |
||
46 | break |
||
47 | end |
||
48 | |||
49 | numframes = numframes + 1 |
||
50 | allframes[frame] = {me = frame, numchildren = 0, children = { }} |
||
51 | |||
52 | -- Determine whether the frame has onupdates or onevents |
||
53 | hasonupdate = frame:GetScript("OnUpdate") |
||
54 | hasonevent = frame:GetScript("OnEvent") |
||
55 | |||
56 | if hasonevent then |
||
57 | numonevents = numonevents + 1 |
||
58 | allframes[frame].onevent = true |
||
59 | end |
||
60 | |||
61 | if hasonupdate then |
||
62 | numonupdates = numonupdates + 1 |
||
63 | allframes[frame].onupdate = true |
||
64 | end |
||
65 | |||
66 | -- add the name property |
||
67 | allframes[frame].name = frame:GetName() |
||
68 | |||
69 | end |
||
70 | |||
71 | -- print |
||
72 | mod.out.print(string.format("Found %s frames, %s onupdates, %s onevents.", numframes, numonupdates, numonevents)) |
||
73 | |||
74 | -- Now, find anonymous frames whose parents have known parents (not uiparent) |
||
75 | local numanonymous, numnamed, parent |
||
76 | local key, value, parentvalue |
||
77 | |||
78 | -- for loop as a safety limit, for now |
||
79 | for x = 1, 10 do |
||
80 | numanonymous = 0 |
||
81 | numnamed = 0 |
||
82 | |||
83 | for key, value in allframes do |
||
84 | if value.name == nil then |
||
85 | numanonymous = numanonymous + 1 |
||
86 | |||
87 | -- check for parent |
||
88 | parent = key:GetParent() |
||
89 | parentvalue = allframes[parent] |
||
90 | |||
91 | if parentvalue and parentvalue.name and parentvalue.name ~= "UIParent" then |
||
92 | -- we've found a good parent |
||
93 | parentvalue.numchildren = parentvalue.numchildren + 1 |
||
94 | numnamed = numnamed + 1 |
||
95 | value.name = parentvalue.name .. "$child" .. parentvalue.numchildren |
||
96 | table.insert(parentvalue.children, value) |
||
97 | |||
98 | elseif x == 10 then |
||
99 | -- we just can't find the name for this frame's parents, no matter what. Better scrap it |
||
100 | allframes[key] = nil |
||
101 | end |
||
102 | end |
||
103 | end |
||
104 | |||
105 | --mod.out.print(string.format("x = %d, found %d anonymous, named %d.", x, numanonymous, numnamed)) |
||
106 | |||
107 | if numnamed == 0 then |
||
108 | break |
||
109 | end |
||
110 | end |
||
111 | |||
112 | local totalframes, framesdropped |
||
113 | |||
114 | for x = 1, 10 do |
||
115 | totalframes = 0 |
||
116 | framesdropped = 0 |
||
117 | |||
118 | for key, value in allframes do |
||
119 | |||
120 | totalframes = totalframes + 1 |
||
121 | |||
122 | -- 1) check 0 children |
||
123 | if value.numchildren == 0 then |
||
124 | |||
125 | -- check no event on onupdate |
||
126 | if value.onupdate == nil and value.onevent == nil then |
||
127 | framesdropped = framesdropped + 1 |
||
128 | |||
129 | -- check for anonymous frame |
||
130 | if key:GetName() == nil then |
||
131 | -- remove name from parent's list |
||
132 | parentvalue = allframes[key:GetParent()] |
||
133 | |||
134 | -- parentvalue might not exist now |
||
135 | if parentvalue then |
||
136 | for y = 1, parentvalue.numchildren do |
||
137 | if parentvalue.children[y] == value then |
||
138 | table.remove(parentvalue.children, y) |
||
139 | break |
||
140 | end |
||
141 | end |
||
142 | |||
143 | parentvalue.numchildren = parentvalue.numchildren - 1 |
||
144 | end |
||
145 | end |
||
146 | |||
147 | allframes[key] = nil |
||
148 | end |
||
149 | end |
||
150 | end |
||
151 | |||
152 | -- print |
||
153 | --mod.out.print(string.format("stage %s, found %s frames, dropped %s.", x, totalframes, framesdropped)) |
||
154 | |||
155 | if framesdropped == 0 then |
||
156 | break |
||
157 | end |
||
158 | |||
159 | end |
||
160 | |||
161 | -- Group related holdings into families |
||
162 | local headers = { } |
||
163 | local header |
||
164 | local numfamilies = 0 |
||
165 | local framesleft = 0 |
||
166 | |||
167 | for key, value in allframes do |
||
168 | |||
169 | framesleft = framesleft + 1 |
||
170 | |||
171 | -- somehow, value.name can be nil here |
||
172 | if value.name then |
||
173 | local header = me.getnameheader(value.name) |
||
174 | |||
175 | -- does this family already exist? |
||
176 | if headers[header] == nil then |
||
177 | |||
178 | -- create new |
||
179 | headers[header] = { value } |
||
180 | numfamilies = numfamilies + 1 |
||
181 | |||
182 | else |
||
183 | -- add |
||
184 | table.insert(headers[header], value) |
||
185 | end |
||
186 | |||
187 | -- set my family ref |
||
188 | value.family = headers[header] |
||
189 | end |
||
190 | end |
||
191 | |||
192 | -- make a file level variable |
||
193 | me.families = headers |
||
194 | me.frames = allframes |
||
195 | |||
196 | -- print |
||
197 | mod.out.print(string.format("Combined the %s active frames into %s groups.", framesleft, numfamilies)) |
||
198 | |||
199 | -- now let's hook them! |
||
200 | numonevents = 0 |
||
201 | numonupdates = 0 |
||
202 | |||
203 | for key, value in allframes do |
||
204 | |||
205 | -- some unknowable frames are left in - don't hook them. |
||
206 | if value.name then |
||
207 | |||
208 | if value.onevent then |
||
209 | me.overridescript(key, "OnEvent", value) |
||
210 | numonevents = numonevents + 1 |
||
211 | end |
||
212 | |||
213 | if value.onupdate then |
||
214 | me.overridescript(key, "OnUpdate", value) |
||
215 | numonupdates = numonupdates + 1 |
||
216 | end |
||
217 | end |
||
218 | end |
||
219 | |||
220 | -- set as loaded |
||
221 | me.ishooking = true |
||
222 | |||
223 | -- print |
||
224 | --mod.out.print(string.format("Found %s onevent, %s onupdate.", numonevents, numonupdates)) |
||
225 | |||
226 | end |
||
227 | |||
228 | me.getnameheader = function(name) |
||
229 | |||
230 | -- still nil? return nil |
||
231 | if name == nil then |
||
232 | return "<nil>" |
||
233 | end |
||
234 | |||
235 | local length = string.len(name) |
||
236 | |||
237 | -- really short stuff, to stop annoying cases |
||
238 | if length < 6 then |
||
239 | return name |
||
240 | end |
||
241 | |||
242 | -- check 4th char |
||
243 | local current, previous, next_ |
||
244 | local position = 3 |
||
245 | |||
246 | previous = string.sub(name, position - 1, position - 1) |
||
247 | current = string.sub(name, position, position) |
||
248 | next_ = string.sub(name, position + 1, position + 1) |
||
249 | |||
250 | local x = 0 |
||
251 | while true do |
||
252 | |||
253 | x = x + 1 |
||
254 | if x > 20 then |
||
255 | mod.out.print("emergency stop!") |
||
256 | return name |
||
257 | end |
||
258 | |||
259 | -- iterate |
||
260 | position = position + 1 |
||
261 | previous = current |
||
262 | current = next_ |
||
263 | next_ = string.sub(name, position + 1, position + 1) |
||
264 | |||
265 | -- check for end of string |
||
266 | if next_ == "" then |
||
267 | return name |
||
268 | end |
||
269 | |||
270 | -- check for underscore |
||
271 | if current == "_" then |
||
272 | return string.sub(name, 1, position) |
||
273 | end |
||
274 | |||
275 | -- check for UUl |
||
276 | if string.find(previous, "[A-Z]") and string.find(current, "[A-Z]") then |
||
277 | -- if the string is 5 or longer, that's enough |
||
278 | if position >= 5 then |
||
279 | return string.sub(name, 1, position) |
||
280 | end |
||
281 | |||
282 | |||
283 | end |
||
284 | |||
285 | -- check for lU |
||
286 | if string.find(current, "[a-z]") and string.find(next_, "[A-Z]") then |
||
287 | return string.sub(name, 1, position) |
||
288 | end |
||
289 | |||
290 | end |
||
291 | |||
292 | end |
||
293 | |||
294 | me.print2 = function(datatype, property) |
||
295 | |||
296 | if me.ishooking == false then |
||
297 | mod.out.print(string.format("The monitor is not loaded, so there's no data to print. Run the command |cffffff00%s load|r to start the monitor.", mod.string.get("print", "console", "short"))) |
||
298 | return |
||
299 | end |
||
300 | |||
301 | -- default values |
||
302 | if datatype == nil then |
||
303 | datatype = "time" -- or "memory" |
||
304 | end |
||
305 | |||
306 | if property == nil then |
||
307 | property = "total" -- or recent |
||
308 | end |
||
309 | |||
310 | -- let's just get the total values for all the families, and print off a random few |
||
311 | |||
312 | local header, family, index, framedata |
||
313 | local total, prints |
||
314 | prints = 0 |
||
315 | local values = { } |
||
316 | |||
317 | for header, family in me.families do |
||
318 | total = 0 |
||
319 | if family == nil then |
||
320 | mod.out.print("Nil family for header = " .. header) |
||
321 | end |
||
322 | |||
323 | for index = 1, table.getn(family) do |
||
324 | framedata = family[index] |
||
325 | if framedata.onupdate then |
||
326 | total = total + framedata.onupdate[datatype][property] |
||
327 | end |
||
328 | if framedata.onevent then |
||
329 | total = total + framedata.onevent[datatype][property] |
||
330 | end |
||
331 | end |
||
332 | |||
333 | -- add to values |
||
334 | table.insert(values, {header, total}) |
||
335 | end |
||
336 | |||
337 | -- sort |
||
338 | table.sort(values, function(a, b) return a[2] > b[2] end) |
||
339 | |||
340 | for index = 1, 10 do |
||
341 | --[[ the values are |
||
342 | 1) The rank, from 1 to 10 |
||
343 | 2) The header / name of the family |
||
344 | 3) An example of a frame name in that family |
||
345 | 4) The actual value |
||
346 | ]] |
||
347 | mod.out.print(string.format("%d %s (%s): %s", index, values[index][1], |
||
348 | me.families[values[index][1]][1].name, math.floor(0.5 + values[index][2]))) |
||
349 | end |
||
350 | |||
351 | end |
||
352 | |||
353 | |||
354 | -- script = "OnUpdate" or "OnEvent" |
||
355 | me.overridescript = function(frame, script, value) |
||
356 | |||
357 | local framename = value.name |
||
358 | local varname = string.lower(script) |
||
359 | local original = frame:GetScript(script) |
||
360 | |||
361 | if original == nil then |
||
362 | mod.out.print("There's no " .. script .. " script for the frame " .. frame) |
||
363 | return |
||
364 | end |
||
365 | |||
366 | me.nexthook = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) |
||
367 | me.hookproc(frame, varname, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) |
||
368 | end |
||
369 | |||
370 | me.frames[frame][varname] = { } |
||
371 | me.frames[frame][varname].original = original |
||
372 | me.frames[frame][varname].replacement = me.nexthook |
||
373 | me.frames[frame][varname].memory = mod.dataset.createnewdataset(5.0, 6) |
||
374 | me.frames[frame][varname].time = mod.dataset.createnewdataset(5.0, 6) |
||
375 | |||
376 | -- set the new script |
||
377 | frame:SetScript(script, me.nexthook) |
||
378 | |||
379 | end |
||
380 | |||
381 | -- frame is the name of a frame. script = "onupdate" or "onevent" |
||
382 | me.hookproc = function(frame, script, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) |
||
383 | |||
384 | local gcbefore = gcinfo() |
||
385 | local timebefore = GetTime() |
||
386 | |||
387 | me.frames[frame][script].original(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) |
||
388 | |||
389 | local timetaken = 1000 * (GetTime() - timebefore) |
||
390 | local memory = gcinfo() - gcbefore |
||
391 | if memory < 0 then |
||
392 | memory = 0 |
||
393 | end |
||
394 | |||
395 | mod.dataset.adddatapoint(me.frames[frame][script].memory, memory) |
||
396 | mod.dataset.adddatapoint(me.frames[frame][script].time, timetaken) |
||
397 | end |
||
398 |