快速掌握Lua 5.3 —— 调试库
Posted VermillionTear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速掌握Lua 5.3 —— 调试库相关的知识,希望对你有一定的参考价值。
Q:如何调试”Closure”的”upvalue”信息?
A:
--[[ debug.getupvalue(f, up)
返回函数("Closure")"f"的第"up"个"upvalue"的名字和值。
Lua按照"upvalues"在匿名函数中出现的顺序对其编号。如果指定的"up"索引越界,则返回"nil"。
以'('开头的变量名表示没有名字的变量(比如是循环控制用到的控制变量,或是去除了调试信息的代码块)。
debug.setupvalue(f, up, value)
与"debug.setupvalue()"的功能相对,将函数"f"("Closure")的第"up"个"upvalue"的值设置为"value"。
函数返回被设置的"upvalue"的名字。如果指定的"up"索引越界,则返回"nil"。
注:获取与设置"upvalue"与"Closure"是否被调用(是否在调用栈上)无关。]]
-- "Closure"。
function newCounter ()
local n = 0
local k = 0
return function ()
k = n
n = n + 1
return n
end
end
counter = newCounter()
print(counter())
print(counter())
-- 此时"k"是1,"n"是2。
local i = 1
repeat
name, val = debug.getupvalue(counter, i)
if name then
print ("index", i, name, "=", val) -- 依次输出两个"upvalues"的名字和值。
if(name == "n") then
debug.setupvalue (counter, 2, 10) -- 设置"n"的值为10。
end
i = i + 1
end
until not name
-- 此时"n"的值被设置为10。
print(counter())
-- 在此调用后"n"的值被加1,变为11。
--[[ results:
1
2
index 1 k = 1
index 2 n = 2
11
]]
--[[ debug.upvaluejoin(f1, n1, f2, n2)
让"Closure""f1"的第"n1"个"upvalue"引用"Closure""f2"的第"n2"个"upvalue"。
debug.upvalueid(f, n)
返回指定"Closure""f"的第"n"个"upvalue"的标识符
(一个轻量用户数据,每个"upvalue"的标识符唯一)。
这个标识符可以让程序检查两个不同的"Closure"是否共享了相同的"upvalue(s)"。 ]]
function newCounter()
local n = 0
local k = 0
return function ()
k = n
n = n + 1
return n
end
end
counter = newCounter()
function newCounter1()
local n = 0
local k = 0
return function ()
k = n
n = n + 1
return n
end
end
counter1 = newCounter1()
-- 每个"upvalue"都有自己独有的ID。
print(debug.upvalueid(counter, 1)) --> userdata: 00559300
print(debug.upvalueid(counter, 2)) --> userdata: 00559348
print(debug.upvalueid(counter1, 1)) --> userdata: 005593D8
print(debug.upvalueid(counter1, 2)) --> userdata: 00559420
-- 让"counter"的第一个"upvalue"引用"counter1"的第二个"upvalue"。
debug.upvaluejoin(counter, 1, counter1, 2)
-- "counter"的第一个"upvalue"与"counter1"的第二个"upvalue"的ID相同。
print(debug.upvalueid(counter, 1)) --> userdata: 00559420
print(debug.upvalueid(counter, 2)) --> userdata: 00559348
print(debug.upvalueid(counter1, 1)) --> userdata: 005593D8
print(debug.upvalueid(counter1, 2)) --> userdata: 00559420
Q:如何追踪程序的运行?
A:
--[[ debug.sethook([thread,] hook, mask [, count])
将函数"hook"设置为线程"thread"的钩子函数。
"mask"决定钩子函数何时被触发,"count"决定何时额外的调用一次钩子函数。
"thread"默认为当前线程。"count"默认为0,
钩子函数将在每运行"count"条指令时额外的调用一次钩子函数,向钩子函数传递事件"count"。
"mask"可以指定为如下值的一个或多个:
'c': 每当Lua调用一个函数时,调用钩子函数,向钩子函数传递事件"call"或"tail call";
'r': 每当Lua从一个函数内返回时,调用钩子函数,向钩子函数传递事件"return";
'l': 每当Lua进入新的一行时,调用钩子函数,向钩子函数传递事件"line"。
当钩子函数被调用时,第一个参数是触发这次调用的事件。对于"line"事件,有第二个参数,为当前行号。
函数不传参,则为关闭钩子函数。]]
debug.sethook(print, "crl")
function foo()
local a = 1
end
local x = 1
foo()
local y = 1
--[[ results:
return nil
line 5
line 3
line 7
line 8
call nil
line 4
line 5
return nil
line 9
return nil
return nil
]]
--[[ debug.gethook([thread])
返回钩子函数的内存地址,钩子函数的掩码,"debug.sethook()"为钩子函数设置的"count"。]]
debug.sethook(print, "l", 9)
print(debug.gethook())
debug.sethook() -- 关闭钩子函数。
print(debug.gethook()) -- 没有钩子函数就什么都获取不到了。
--[[ results:
line 2
function: 013D1A70 l 9
line 3
nil 0
]]
Q:如何查看Lua的注册表信息?
A:
--[[ debug.getregistry()
函数返回Lua的"registry"。]]
Q:如何创建一个程序分析器?
A:调式库除了用于调式以外还可以用于完成其他任务,这种常见的任务就是分析。对于一个实时的分析来说,最好使用C接口来完成。对于每一个钩子函数其使用的Lua调用代价太大,并且通常会导致测量的结果不准确。然而,对于计数分析来说,Lua可以很好的胜任。
-- 一个记录程序中函数被调用次数的小型基本分析器。
local Counters = -- key-value: 函数-计数
local Names = -- key-value:函数-函数名
local function hook()
local f = debug.getinfo(2, "f").func -- 获取被调用的函数本身。
if Counters[f] == nil then -- 如果是第一次被调用。
Counters[f] = 1
Names[f] = debug.getinfo(2, "Sn") -- 获取函数信息。
else -- 如果之前被记录过,这里只是增加其计数。
Counters[f] = Counters[f] + 1
end
end
local f = assert(load("print('Hello World!')"))
debug.sethook(hook, "c") -- 当函数被调用时调用钩子函数。
f()
debug.sethook() -- 关闭钩子函数。
-- 获取结果。
function getname(func)
local n = Names[func]
if n.what == "C" then -- 如果是C函数,只返回其名字。
return n.name
end
-- 如果不是C函数,返回"[file]:line"的形式。
local loc = string.format("[%s]:%s", n.short_src, n.linedefined)
if n.namewhat ~= "" then -- 如果不是匿名函数,返回一个合理的名字,"[file]:line (name)"。
return string.format("%s (%s)", loc, n.name)
else -- 否则只返回"[file]:line"的形式。
return string.format("%s", loc)
end
end
for func, count in pairs(Counters) do
print(getname(func), count)
end
--[[ results:
Hello World!
[[string "print('Hello World!')"]]:0 (f) 1
print 1
sethook 1
nil 1 <-- 这个不知道是什么函数。
]]
附加:
1、在钩子函数内,你可以调用”debug.getinfo()”,指定栈级别为2, 来获得正在运行的函数的详细信息(”debug.getinfo()”的栈级别为0,钩子函数的栈级别为1)。
2、一个打印文件名及行号的精致的追踪器,
function trace(event, line)
local s = debug.getinfo(2).short_src
print(s .. ":" .. line)
end
debug.sethook(trace, "l")
以上是关于快速掌握Lua 5.3 —— 调试库的主要内容,如果未能解决你的问题,请参考以下文章
快速掌握Lua 5.3 —— 编写提供给Lua使用的C库函数的技巧