tdengine 0.1.2

game server for Rust
Documentation
--memoryd.lua
--create by wugd
--脚本内存监控机制
--table、metatable、funciton's upvalue

-- 声明模块名
MEMORY_D = {}
setmetatable(MEMORY_D, {__index = _G})
local _ENV = MEMORY_D
-- 协程
local mxc

-- 协程结束后的回调处理
local callback
local callback_arg

-- 协程挂起次数
local YIELD_TIME = 5000
local cur_times = 0

-- 定义内部接口,按照字母顺序排序


-- 检索父节点是否有自己
local function search_self(node, node_map)
    if node_map[node] then
        return true
    end
  --[[  if node_map["parent"] then
        if search_self(node, node_map["parent"]) then
            return true
        end
    end ]]
end

-- 检索产生泄漏的引用
local function search_leak(node, parent, obj, result, parent_path, search_record, check_weak)

    local current_path
    local mt, key_weak, value_weak
    local node_map = parent
    node_map[node] = true

    -- 取得该节点是否为弱表
    mt = debug.getmetatable(node)
    if mt then
        if mt["__mode"] then
            if string.find(mt["__mode"], "k") then
                key_weak = true
            end

            if string.find(mt["__mode"], "v") then
                value_weak = true
            end
        end
    end

    if not check_weak and key_weak and value_weak then
        -- 键值弱表不查找
        return
    end

    if cur_times >= YIELD_TIME then
        if type(mxc) == "thread" then
            cur_times = 0
            coroutine.yield()
        end
    else
       cur_times = cur_times + 1
    end

    for k, v in pairs(node) do

        if type(k) == "table" and (check_weak or not key_weak)then

            current_path =  parent_path .. "/" .. "key-is-table"

            if obj == k then
                result[#result + 1] = current_path
            else
                if not search_self(k, node_map) then
                    search_leak(k, node_map, obj, result, current_path, search_record, check_weak)
                end

                -- 获取对象的元表
                mt = debug.getmetatable(k)

                -- 判断是否需要查找弱表
                if mt then
                    if not search_self(mt, node_map) then
                        search_leak(mt, node_map, obj, result, current_path, search_record, check_weak)
                    end
                end
            end

        elseif is_string(k) or is_int(k) then
            current_path =  parent_path .. "/" .. k
        else
            current_path =  parent_path .. "/other type"
        end

        if type(v) == "table" and (check_weak or not value_weak)then

            if obj == v then
                result[#result + 1] = current_path
            else

                if not search_self(v, node_map) then
                    search_leak(v, node_map, obj, result, current_path, search_record, check_weak)
                end

                -- 获取对象的元表
                mt = debug.getmetatable(v)
                if mt then
                    if not search_self(mt, node_map) then
                        search_leak(mt, node_map, obj, result, current_path, search_record, check_weak)
                    end
                end
            end

        elseif type(v) == "function" then

            if not search_record[v] then
                search_record[v] = true
                -- 取得funciton's upvalue
                local fupv = get_func_upvalue(v)
                if fupv then
                    search_leak(fupv, node_map, obj, result, current_path, search_record, check_weak)
                end

                -- 取得funciton's env
                local fenv = debug.getfenv(v)
                if not search_self(fenv, node_map) then
                    search_leak(fenv, node_map, obj, result, current_path, search_record, check_weak)
                end

                -- 取得funciton's registry
                local freg = debug.getregistry(v)
                if not search_self(freg, node_map) then
                    search_leak(freg, node_map, obj, result, current_path, search_record, check_weak)
                end
                -- 获取对象的元表
                mt = debug.getmetatable(v)
                if mt then
                    if not search_self(mt, node_map) then
                        search_leak(mt, node_map, obj, result, current_path, search_record, check_weak)
                    end
                end
            end
        end
    end
end

-- 定义公共接口,按照字母顺序排序

-- 取得指定函数的upvalue
function get_func_upvalue(func)
    local tbl = {}
    local n = 1
    while true do
	local name, value = debug.getupvalue(func,n)
	if not name then
	    break
	end

	if value == nil then
	    value = {}
	end

	tbl[name] = value
	n= n + 1
    end
    return tbl
end

-- 检查单个对象当前被引用的地方
function check_leak_obj(obj, check_weak)
    local result = {}
    local search_record = {}
    local parent_path = "_G"

    -- 检索该对象泄漏的引用
    search_leak(_G, {}, obj, result, parent_path, search_record, check_weak)

    return result
end

-- 获取泄漏的对象列表
function get_leak_obj_list()

    -- 先手段执行lua回收处理
    collectgarbage("collect")

    -- 再获取已被析构对象列表
    -- 如果存在说明出现逻辑上泄漏
    return (get_all_destructed_obs())
end

-- 获得泄漏对象的引用
function check_leak_obj_refs(raiser, check_weak)
    -- 获得泄漏的对象表
    local leak_object_list = get_leak_obj_list()
    if sizeof(leak_object_list) == 0 then
        print("无内存泄漏\n")
        write_log("无内存泄漏\n")
        return
    end

    local result = {}
    local search_record = {}
    local parent_path = "_G"
    for _, obj in pairs(leak_object_list) do

        -- 检索该对象泄漏的引用
        search_leak(_G, {}, obj, result, parent_path, search_record, check_weak)
        if sizeof(result) > 0 then
            result["leak"] = watch(obj)

            if not raiser then
                -- 打印出泄漏信息
                print("%o", result)
            else
                -- 写日志
                local str_result = string.format("Error: %s\n", save_string(result))
                write_log(str_result)
            end
        end

        result = {}
        search_record= {}
    end
end

-- 创建内存检测协程
function get_leak_obj_refs(raiser, check_weak, f, f_arg)
    if not mxc then
        mxc = coroutine.create(function (val_a, val_b)
                                        check_leak_obj_refs(val_a, val_b) end)
        if type(mxc) == "thread" then

            callback = f
            callback_arg = f_arg

            -- 执行协程状态定时处理
            resume_timer(raiser, check_weak)
        end
    end
end

function resume_timer(a, b)
    if type(mxc) == "thread" then
        if coroutine.status(mxc) == "suspended" then
            coroutine.resume(mxc, a, b)
        elseif coroutine.status(mxc) == "dead" then
            mxc = nil
            cur_times = 0

            -- 执行回调
            if is_function(callback) and is_table(callback_arg) then
                callback(callback_arg[1], callback_arg[2])
            end
            return
        end
    end
    set_timer(5, resume_timer)
end

function create()
end

create()