您的位置:首页 > 编程语言 > Lua

快速掌握Lua 5.3 —— 调试库 (2)

2016-05-08 16:40 1461 查看

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