Lua的hook机制及内存泄漏检测

2017-06-23  本文已影响0人  XavierCHN

最近突然有了对lua内存泄漏进行检测的需求,去研究了一下lua的说明书,发现lua的hook机制可以很好地满足要求。

1 hook机制

lua的hook属于debug库,可以在你的程序触发了某些事件的时候,调用你所注册的一个函数。
可以触发hook函数的事件有四种:

  1. call事件: lua调用了一个函数的时候
  2. return事件:lua的函数返回了
  3. line事件:当lua开始执行新的一行代码
  4. count事件:在指定的数字后执行?
    lua在调用hook函数的时候,将会带有一个参数,一个字符串来表示生成这次调用的类型,也就是"call", "return", "line", or "count"中的一种。特殊的是,对于line事件,他还会带第二个参数,行号。 当然,我们可以用debug.getinfo来获得hook中的更多信息。
    注册一个hook需要使用debug.sethook函数,带两个或者三个参数,第一个是hook的回调函数,也就是事件触发的时候需要调用的函数,第二个参数是一个用来标识我们需要监听的函数类型的字符串,还有一个可选的参数标识调用的频率。
    第二个参数,可以是c´,r´, or `l´来分别代表四个事件。
    如果sethook函数没有任何参数,就代表停用hook。

2 内存检测

核心在于

  1. collectgabage("count")将会返回当前所占用的内存,并进行一次GC
  2. debug.getinfo() 函数可以在调用的时候获取行号

使用方法:
将代码放到你的文件夹中,在addon_game_mode.lua或者你的入口处的文件中require对应的文件,之后你可以使用utilsMemoryLeakDetector:ShowRecord(count)函数,或者使用debug_dump_lua_memory_detail的控制台指令来查看当前的状态

local memory_state = {} -- 储存所有的内存调用
local current_memory = 0 -- 储存总内存状态

local function recordAlloc(event, line_no)
    local memory_increased = collectgarbage("count") - current_memory
    if (memory_increased < 1e-6) then return end

    local info = debug.getinfo(2, "S").source
    info = string.format("%s@%s", info, line_no - 1)

    local item = memory_state[info]
    if not item then
        memory_state[info] = {info, 1, memory_increased}
    else
        item[2] = item[2] + 1
        item[3] = item[3] + memory_increased
    end
    current_memory = collectgarbage("count")
end

utilsMemoryLeakDetector = {}

function utilsMemoryLeakDetector:StartRecord()
    if debug.gethook() then
        self:StopRecord()
        return
    end

    memory_state = {}
    current_memory = collectgarbage("count")
    debug.sethook(recordAlloc, "l")
end

function utilsMemoryLeakDetector:ShowRecord(count)
    if not memory_state then return end
    local sorted = {}

    for k, v in pairs(memory_state) do
        table.insert(sorted, v)
    end

    table.sort(sorted, function(a, b) return a[3] > b[3] end)

    for i = 1, count do
        local v = sorted[i]
        print(string.format("MemoryDump [MEM: %sK] [COUNT: %s ] [AVG: %s k] %s:", v[3], v[2], v[3] / v[2], v[1]))
    end
end

function utilsMemoryLeakDetector:EndRecord()
    debug.sethook()
end

utilsMemoryLeakDetector:StartRecord()

if IsInToolsMode() then
    Convars:RegisterCommand("debug_dump_lua_memory_detail",function(_, top_count)
        count = tonumber(top_count) or 30
        utilsMemoryLeakDetector:ShowRecord(count)
    end,"Dump lua usage",FCVAR_CHEAT)
end
上一篇下一篇

猜你喜欢

热点阅读