summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server-beta/src/brave/brave.lua10
-rw-r--r--server-beta/src/doctor.lua336
-rw-r--r--server-beta/src/pub/init.lua4
-rw-r--r--server-beta/src/pub/pub.lua11
-rw-r--r--server-beta/src/service/service.lua38
5 files changed, 387 insertions, 12 deletions
diff --git a/server-beta/src/brave/brave.lua b/server-beta/src/brave/brave.lua
index bf6cb559..93a93142 100644
--- a/server-beta/src/brave/brave.lua
+++ b/server-beta/src/brave/brave.lua
@@ -25,6 +25,7 @@ end
--- 开始找工作
function m.start()
+ m.push('mem', collectgarbage 'count')
while true do
local suc, name, id, params = m.taskpad:pop()
if not suc then
@@ -39,12 +40,13 @@ function m.start()
log.error('Brave can not handle this work: ' .. name)
goto CONTINUE
end
- local suc, res = xpcall(ability, log.error, params)
- if not suc then
+ local ok, res = xpcall(ability, log.error, params)
+ if ok then
+ m.waiter:push(id, res)
+ else
m.waiter:push(id)
- goto CONTINUE
end
- m.waiter:push(id, res)
+ m.push('mem', collectgarbage 'count')
::CONTINUE::
end
end
diff --git a/server-beta/src/doctor.lua b/server-beta/src/doctor.lua
new file mode 100644
index 00000000..48c3ebbe
--- /dev/null
+++ b/server-beta/src/doctor.lua
@@ -0,0 +1,336 @@
+local ac = ac
+local type = type
+local next = next
+local ipairs = ipairs
+local rawget = rawget
+local pcall = pcall
+local collectgarbage = collectgarbage
+local getregistry = debug.getregistry
+local getmetatable = debug.getmetatable
+local getupvalue = debug.getupvalue
+local getuservalue = debug.getuservalue
+local getlocal = debug.getlocal
+local getinfo = debug.getinfo
+local maxinterger = math.maxinteger
+local mathType = math.type
+local tableConcat = table.concat
+local _G = _G
+local registry = getregistry()
+
+_ENV = nil
+
+local m = {}
+
+--- 内存快照
+---@return table
+function m.snapshot()
+ local mark = {}
+ local find
+
+ local function getTostring(obj)
+ local mt = getmetatable(obj)
+ if not mt then
+ return nil
+ end
+ local toString = rawget(mt, '__tostring')
+ if not toString then
+ return nil
+ end
+ local suc, str = pcall(toString, obj)
+ if not suc then
+ return nil
+ end
+ if type(str) ~= 'string' then
+ return nil
+ end
+ return str
+ end
+
+ local function formatName(obj)
+ local tp = type(obj)
+ if tp == 'nil' then
+ return 'nil:nil'
+ elseif tp == 'boolean' then
+ if obj == true then
+ return 'boolean:true'
+ else
+ return 'boolean:false'
+ end
+ elseif tp == 'number' then
+ if mathType(obj) == 'integer' then
+ return ('number:%d'):format(obj)
+ else
+ -- 如果浮点数可以完全表示为整数,那么就转换为整数
+ local str = ('%.10f'):format(obj):gsub('%.?[0]+$', '')
+ if str:find('.', 1, true) then
+ -- 如果浮点数不能表示为整数,那么再加上它的精确表示法
+ str = ('%s(%q)'):format(str, obj)
+ end
+ return 'number:' .. str
+ end
+ elseif tp == 'string' then
+ local str = ('%q'):format(obj)
+ if #str > 100 then
+ local new = ('%s...(len=%d)'):format(str:sub(1, 100), #str)
+ if #new < #str then
+ str = new
+ end
+ end
+ return 'string:' .. str
+ elseif tp == 'function' then
+ local info = getinfo(obj, 'S')
+ if info.what == 'c' then
+ return ('function:%p(C)'):format(obj)
+ elseif info.what == 'main' then
+ return ('function:%p(main)'):format(obj)
+ else
+ return ('function:%p(%s:%d-%d)'):format(obj, info.source, info.linedefined, info.lastlinedefined)
+ end
+ elseif tp == 'table' then
+ local id = getTostring(obj)
+ if not id then
+ if obj == _G then
+ id = '_G'
+ elseif obj == registry then
+ id = 'registry'
+ end
+ end
+ if id then
+ return ('table:%p(%s)'):format(obj, id)
+ else
+ return ('table:%p'):format(obj)
+ end
+ elseif tp == 'userdata' then
+ local id = getTostring(obj)
+ if id then
+ return ('userdata:%p(%s)'):format(obj, id)
+ else
+ return ('userdata:%p'):format(obj)
+ end
+ else
+ return ('%s:%p'):format(tp, obj)
+ end
+ end
+
+ local function findTable(t, result)
+ result = result or {}
+ local mt = getmetatable(t)
+ local wk, wv
+ if mt then
+ local mode = rawget(mt, '__mode')
+ if type(mode) == 'string' then
+ if mode:find('k', 1, true) then
+ wk = true
+ end
+ if mode:find('v', 1, true) then
+ wv = true
+ end
+ end
+ end
+ for k, v in next, t do
+ if not wk then
+ local keyInfo = find(k)
+ if keyInfo then
+ result[#result+1] = {
+ type = 'key',
+ name = formatName(k),
+ info = keyInfo,
+ }
+ end
+ end
+ if not wv then
+ local valueInfo = find(v)
+ if valueInfo then
+ result[#result+1] = {
+ type = 'field',
+ name = formatName(k) .. '|' .. formatName(v),
+ info = valueInfo,
+ }
+ end
+ end
+ end
+ local MTInfo = find(getmetatable(t))
+ if MTInfo then
+ result[#result+1] = {
+ type = 'metatable',
+ name = '',
+ info = MTInfo,
+ }
+ end
+ if #result == 0 then
+ return nil
+ end
+ return result
+ end
+
+ local function findFunction(f, result, trd, stack)
+ result = result or {}
+ for i = 1, maxinterger do
+ local n, v = getupvalue(f, i)
+ if not n then
+ break
+ end
+ local valueInfo = find(v)
+ if valueInfo then
+ result[#result+1] = {
+ type = 'upvalue',
+ name = n,
+ info = valueInfo,
+ }
+ end
+ end
+ if trd then
+ for i = 1, maxinterger do
+ local n, l = getlocal(trd, stack, i)
+ if not n then
+ break
+ end
+ local valueInfo = find(l)
+ if valueInfo then
+ result[#result+1] = {
+ type = 'local',
+ name = n,
+ info = valueInfo,
+ }
+ end
+ end
+ end
+ if #result == 0 then
+ return nil
+ end
+ return result
+ end
+
+ local function findUserData(u, result)
+ result = result or {}
+ for i = 1, maxinterger do
+ local v, b = getuservalue(u, i)
+ if not b then
+ break
+ end
+ local valueInfo = find(v)
+ if valueInfo then
+ result[#result+1] = {
+ type = 'uservalue',
+ name = formatName(i),
+ info = valueInfo,
+ }
+ end
+ end
+ local MTInfo = find(getmetatable(u))
+ if MTInfo then
+ result[#result+1] = {
+ type = 'metatable',
+ name = '',
+ info = MTInfo,
+ }
+ end
+ if #result == 0 then
+ return nil
+ end
+ return result
+ end
+
+ local function findThread(trd, result)
+ -- 不查找主线程,主线程一定是临时的(视为弱引用)
+ if trd == registry[1] then
+ return nil
+ end
+ result = result or {}
+
+ for i = 1, maxinterger do
+ local info = getinfo(trd, i, 'Sf')
+ if not info then
+ break
+ end
+ local funcInfo = find(info.func, trd, i)
+ if funcInfo then
+ result[#result+1] = {
+ type = 'stack',
+ name = i .. '@' .. formatName(info.func),
+ info = funcInfo,
+ }
+ end
+ end
+
+ if #result == 0 then
+ return nil
+ end
+ return result
+ end
+
+ function find(obj, trd, stack)
+ if mark[obj] then
+ return mark[obj]
+ end
+ local tp = type(obj)
+ if tp == 'table' then
+ mark[obj] = {}
+ mark[obj] = findTable(obj, mark[obj])
+ elseif tp == 'function' then
+ mark[obj] = {}
+ mark[obj] = findFunction(obj, mark[obj], trd, stack)
+ elseif tp == 'userdata' then
+ mark[obj] = {}
+ mark[obj] = findUserData(obj, mark[obj])
+ elseif tp == 'thread' then
+ mark[obj] = {}
+ mark[obj] = findThread(obj, mark[obj])
+ else
+ return nil
+ end
+ if mark[obj] then
+ mark[obj].object = obj
+ end
+ return mark[obj]
+ end
+
+ return {
+ name = formatName(registry),
+ type = 'root',
+ info = find(registry),
+ }
+end
+
+--- 寻找对象的引用
+---@return string
+function m.catch(...)
+ local targets = {}
+ for _, target in ipairs {...} do
+ targets[target] = true
+ end
+ local report = m.snapshot()
+ local path = {}
+ local result = {}
+ local mark = {}
+
+ local function push()
+ result[#result+1] = tableConcat(path, ' => ')
+ end
+
+ local function search(t)
+ path[#path+1] = ('(%s)%s'):format(t.type, t.name)
+ local addTarget
+ if targets[t.info.object] then
+ targets[t.info.object] = nil
+ addTarget = t.info.object
+ push(t)
+ end
+ if not mark[t.info] then
+ mark[t.info] = true
+ for _, obj in ipairs(t.info) do
+ search(obj)
+ end
+ end
+ path[#path] = nil
+ if addTarget then
+ targets[addTarget] = true
+ end
+ end
+
+ search(report)
+
+ return result
+end
+
+return m
diff --git a/server-beta/src/pub/init.lua b/server-beta/src/pub/init.lua
index f86a42b6..bf188e8d 100644
--- a/server-beta/src/pub/init.lua
+++ b/server-beta/src/pub/init.lua
@@ -4,4 +4,8 @@ pub.on('log', function (params, brave)
log.raw(brave.id, params.level, params.msg, params.src, params.line)
end)
+pub.on('mem', function (count, brave)
+ brave.memory = count
+end)
+
return pub
diff --git a/server-beta/src/pub/pub.lua b/server-beta/src/pub/pub.lua
index 69331357..ac4c779c 100644
--- a/server-beta/src/pub/pub.lua
+++ b/server-beta/src/pub/pub.lua
@@ -46,6 +46,7 @@ function m.recruitBraves(num)
taskList = {},
counter = utility.counter(),
currentTask = nil,
+ memory = 0,
}
end
end
@@ -145,4 +146,14 @@ function m.checkDead()
end
end
+function m.listen()
+ task.create(function ()
+ while true do
+ m.checkDead()
+ m.recieve()
+ task.sleep(0)
+ end
+ end)
+end
+
return m
diff --git a/server-beta/src/service/service.lua b/server-beta/src/service/service.lua
index 3712b7b8..fb7200b8 100644
--- a/server-beta/src/service/service.lua
+++ b/server-beta/src/service/service.lua
@@ -7,14 +7,35 @@ local proto = require 'proto'
local m = {}
m.type = 'service'
-function m.listenPub()
- task.create(function ()
- while true do
- pub.checkDead()
- pub.recieve()
- task.sleep(0)
- end
+function m.reportMemory()
+ local mems = {}
+ local totalMem = 0
+ mems[0] = collectgarbage 'count'
+ totalMem = totalMem + collectgarbage 'count'
+ for id, brave in ipairs(pub.braves) do
+ mems[id] = brave.memory
+ totalMem = totalMem + brave.memory
+ end
+
+ local lines = {}
+ lines[#lines+1] = ' --------------- Memory ---------------'
+ lines[#lines+1] = (' Total: %.3f MB'):format(totalMem / 1000.0)
+ for i = 0, #mems do
+ lines[#lines+1] = (' # %02d : %.3f MB'):format(i, mems[i] / 1000.0)
+ end
+ return table.concat(lines, '\n')
+end
+
+function m.report()
+ local t = timer.loop(60.0, function ()
+ local lines = {}
+ lines[#lines+1] = '========= Medical Examination Report ========='
+ lines[#lines+1] = m.reportMemory()
+ lines[#lines+1] = '=============================================='
+
+ log.debug(table.concat(lines, '\n'))
end)
+ t:onTimer()
end
function m.startTimer()
@@ -32,7 +53,8 @@ function m.start()
pub.recruitBraves(4)
task.setErrorHandle(log.error)
proto.listen()
- m.listenPub()
+ pub.listen()
+ m.report()
m.startTimer()
end