vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:

--[[

Main.lua

This module does all the work. It hooks every OnEvent and OnUpdate function in existance.

]]

-- table setup
local mod = thismod
local me = { }
mod.main = me

me.ishooking = false

--[[
me.hookall()
Hooks all the OnUpdate() and OnEvent() methods running.

First thing is to gather all frames. We make a table, key = the frame itself, value = {onevent = <true or nil>, onupdate = <true or nil>}
]]
me.hookall = function()

        -- this method can only be run once - check!
        if me.ishooking == true then
                mod.out.print("The monitor has already been loaded, and is actively recording data.")
                return
        end

        local frame, script
        local numonevents = 0
        local numonupdates = 0
        local numframes = 0
        local numemptyframes = 0
        
        -- first let's find all the frames
        local allframes = { }
        local hasonupdate, hasonevent
                
        while true do
                
                -- get next frame
                frame = EnumerateFrames(frame)
                if frame == nil then
                        break
                end
        
                numframes = numframes + 1
                allframes[frame] = {me = frame, numchildren = 0, children = { }}
                
                -- Determine whether the frame has onupdates or onevents
                hasonupdate = frame:GetScript("OnUpdate")
                hasonevent = frame:GetScript("OnEvent")
                
                if hasonevent then
                        numonevents = numonevents + 1
                        allframes[frame].onevent = true
                end
                
                if hasonupdate then
                        numonupdates = numonupdates + 1
                        allframes[frame].onupdate = true 
                end
                
                -- add the name property
                allframes[frame].name = frame:GetName()
                
        end 
        
        -- print
        mod.out.print(string.format("Found %s frames, %s onupdates, %s onevents.", numframes, numonupdates, numonevents))
        
        -- Now, find anonymous frames whose parents have known parents (not uiparent)
        local numanonymous, numnamed, parent
        local key, value, parentvalue
        
        -- for loop as a safety limit, for now
        for x = 1, 10 do
                numanonymous = 0
                numnamed = 0
                
                for key, value in allframes do
                        if value.name == nil then
                                numanonymous = numanonymous + 1
                                
                                -- check for parent
                                parent = key:GetParent()
                                parentvalue = allframes[parent]
                                
                                if parentvalue and parentvalue.name and parentvalue.name ~= "UIParent" then
                                        -- we've found a good parent
                                        parentvalue.numchildren = parentvalue.numchildren + 1
                                        numnamed = numnamed + 1
                                        value.name = parentvalue.name .. "$child" .. parentvalue.numchildren
                                        table.insert(parentvalue.children, value)
                                        
                                elseif x == 10 then
                                        -- we just can't find the name for this frame's parents, no matter what. Better scrap it
                                        allframes[key] = nil
                                end
                        end
                end
                
                --mod.out.print(string.format("x = %d, found %d anonymous, named %d.", x, numanonymous, numnamed))
                
                if numnamed == 0 then
                        break
                end
        end
        
        local totalframes, framesdropped
        
        for x = 1, 10 do
                totalframes = 0
                framesdropped = 0
                        
                for key, value in allframes do

                        totalframes = totalframes + 1
                        
                        -- 1) check 0 children
                        if value.numchildren == 0 then
                        
                                -- check no event on onupdate
                                if value.onupdate == nil and value.onevent == nil then
                                        framesdropped = framesdropped + 1
                                        
                                        -- check for anonymous frame
                                        if key:GetName() == nil then
                                                -- remove name from parent's list
                                                parentvalue = allframes[key:GetParent()]
                                                
                                                -- parentvalue might not exist now
                                                if parentvalue then
                                                        for y = 1, parentvalue.numchildren do
                                                                if parentvalue.children[y] == value then
                                                                        table.remove(parentvalue.children, y)
                                                                        break
                                                                end
                                                        end
                                                        
                                                        parentvalue.numchildren = parentvalue.numchildren - 1
                                                end
                                        end
                                        
                                        allframes[key] = nil
                                end
                        end
                end
                
                -- print
                --mod.out.print(string.format("stage %s, found %s frames, dropped %s.", x, totalframes, framesdropped))
                
                if framesdropped == 0 then
                        break
                end
                                
        end
        
        -- Group related holdings into families
        local headers = { }
        local header
        local numfamilies = 0
        local framesleft = 0
        
        for key, value in allframes do
                
                framesleft = framesleft + 1
                
                -- somehow, value.name can be nil here
                if value.name then
                        local header = me.getnameheader(value.name)
                        
                        -- does this family already exist?
                        if headers[header] == nil then
                        
                                -- create new
                                headers[header] = { value }
                                numfamilies = numfamilies + 1
                                
                        else    
                                -- add
                                table.insert(headers[header], value)
                        end
                        
                        -- set my family ref
                        value.family = headers[header]
                end
        end
        
        -- make a file level variable
        me.families = headers
        me.frames = allframes
        
        -- print
        mod.out.print(string.format("Combined the %s active frames into %s groups.", framesleft, numfamilies))
        
        -- now let's hook them!
        numonevents = 0
        numonupdates = 0
        
        for key, value in allframes do

                -- some unknowable frames are left in - don't hook them.
                if value.name then
                
                        if value.onevent then
                                me.overridescript(key, "OnEvent", value)
                                numonevents = numonevents + 1
                        end
                        
                        if value.onupdate then
                                me.overridescript(key, "OnUpdate", value)
                                numonupdates = numonupdates + 1
                        end
                end
        end
        
        -- set as loaded
        me.ishooking = true
        
        -- print
        --mod.out.print(string.format("Found %s onevent, %s onupdate.", numonevents, numonupdates))
        
end

me.getnameheader = function(name)

        -- still nil? return nil
        if name == nil then
                return "<nil>"
        end

        local length = string.len(name)

        -- really short stuff, to stop annoying cases
        if length < 6 then
                return name
        end

        -- check 4th char
        local current, previous, next_
        local position = 3
        
        previous = string.sub(name, position - 1, position - 1)
        current = string.sub(name, position, position)
        next_ = string.sub(name, position + 1, position + 1)

        local x = 0
        while true do 
        
                x = x + 1
                if x > 20 then
                        mod.out.print("emergency stop!")
                        return name
                end
        
                -- iterate
                position = position + 1
                previous = current
                current = next_
                next_ = string.sub(name, position + 1, position + 1)
                
                -- check for end of string
                if next_ == "" then
                        return name
                end
                
                -- check for underscore
                if current == "_" then
                        return string.sub(name, 1, position)
                end
                
                -- check for UUl
                if string.find(previous, "[A-Z]") and string.find(current, "[A-Z]") then
                        -- if the string is 5 or longer, that's enough
                        if position >= 5 then
                                return string.sub(name, 1, position)
                        end
                        

                end
                
                -- check for lU
                if string.find(current, "[a-z]") and string.find(next_, "[A-Z]") then
                        return string.sub(name, 1, position)
                end

        end

end

me.print2 = function(datatype, property)

        if me.ishooking == false then
                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")))
                return
        end

        -- default values
        if datatype == nil then
                datatype = "time" -- or "memory"
        end
        
        if property == nil then
                property = "total" -- or recent
        end
        
        -- let's just get the total values for all the families, and print off a random few
        
        local header, family, index, framedata
        local total, prints
        prints = 0
        local values = { }
        
        for header, family in me.families do
                total = 0
                if family == nil then
                        mod.out.print("Nil family for header = " .. header)
                end
                
                for index = 1, table.getn(family) do
                        framedata = family[index]
                        if framedata.onupdate then
                                total = total + framedata.onupdate[datatype][property]
                        end
                        if framedata.onevent then
                                total = total + framedata.onevent[datatype][property]
                        end
                end
                
                -- add to values
                table.insert(values, {header, total})
        end
        
        -- sort
        table.sort(values, function(a, b) return a[2] > b[2] end)
        
        for index = 1, 10 do
        --[[ the values are
        1) The rank, from 1 to 10
        2) The header / name of the family
        3) An example of a frame name in that family
        4) The actual value
        ]]
                mod.out.print(string.format("%d %s (%s):  %s", index, values[index][1], 
                        me.families[values[index][1]][1].name, math.floor(0.5 + values[index][2])))
        end

end


-- script = "OnUpdate" or "OnEvent"
me.overridescript = function(frame, script, value)

        local framename = value.name
        local varname = string.lower(script)
        local original = frame:GetScript(script)
        
        if original == nil then
                mod.out.print("There's no " .. script .. " script for the frame " .. frame)
                return
        end
        
        me.nexthook = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) 
                me.hookproc(frame, varname, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
        end
                
        me.frames[frame][varname] = { }
        me.frames[frame][varname].original = original
        me.frames[frame][varname].replacement = me.nexthook
        me.frames[frame][varname].memory = mod.dataset.createnewdataset(5.0, 6)
        me.frames[frame][varname].time = mod.dataset.createnewdataset(5.0, 6)
        
        -- set the new script
        frame:SetScript(script, me.nexthook)
        
end

-- frame is the name of a frame. script = "onupdate" or "onevent"
me.hookproc = function(frame, script, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)

        local gcbefore = gcinfo()
        local timebefore = GetTime()
        
        me.frames[frame][script].original(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)

        local timetaken = 1000 * (GetTime() - timebefore)
        local memory = gcinfo() - gcbefore
        if memory < 0 then
                memory = 0
        end
        
        mod.dataset.adddatapoint(me.frames[frame][script].memory, memory)
        mod.dataset.adddatapoint(me.frames[frame][script].time, timetaken)      
end