stdin:2: attempt to index a nil value (global 'a')
stack traceback:
stdin:2: in function 'traceBackFunc'
stdin:2: in function 'errFunc'
[C]: in function 'xpcall'
stdin:1: in main chunk
[C]: in ?
source
该函数定义的位置,如果该函数通过 loadstring 定义,source 就是那个字符串。如果该函数定义在一个文件中,source 就是该文件名,并且以一个@符号开头
short_src
最多60个字符的 source,对于错误信息很有用
linedefined
该函数被定义的行数
该函数是什么,可能是”Lua”——一个普通的 Lua 函数,可能是”C”——一个 C 函数,也可能是”main”——一个 Lua 代码块的 main 部分
该函数的名称
namewhat
函数名称的含义,这个字段可能是”global” “local” “method” “field”或者为空字符串”“,为空字符串时,意味着 Lua 没有找到叫这个名称的函数
该函数的 upvalues 的个数
该函数本身
while true do
local info = debug.getinfo(level, "Sl")
if not info then break end
if info.what == "C" then
print(level, "C function")
print(string.format("[%s]:%d",
info.short_src, info.currentline))
level = level + 1
2. debug.getlocal/debug.setlocal
使用 debug.getlocal 函数,可以获得任何活动的函数中的局部变量,该函数有两个入参:所查看的函数的堆栈级别、变量的索引,函数会返回两个值:变量的名称和值。如果变量的索引超出了变量的个数,则返回 nil;如果函数的堆栈级别无效,则会引发一个错误
同样,这里附上书中的栗子:
function foo (a,b)
local x
do local c = a - b end
local a = 1
while true do
local name, value = debug.getlocal(1, a)
if not name then break end
print(name, value)
a = a + 1
foo(10, 20)
执行的结果为:
a 10
b 20
x nil
a 4
函数 foo 的入参为最先的变量,其次是函数内部的局部变量,变量 c 和 getlocal 不在同一个作用域,所以并没有输出
除此之外,还可以使用 debug.setlocal 函数修改局部变量,前两个入参的含义和 debug.getlocal 相同,第三个入参是想要设置的新值,该函数返回对应变量的名称,如果索引超出,则返回nil
稍微修改一下上面的栗子:
function foo (a,b)
local x
local index = 1
while true do
local name, value = debug.getlocal(1, index)
if not name then break end
print("before change:",name,value)
--修改变量的值为对应索引值
debug.setlocal(1,index,index)
name, value = debug.getlocal(1, index)
print("after change:",name,value)
index = index + 1
foo(10, 20)
此时可以看到局部变量会被修改:
before change: a 10
after change: a 1
before change: b 20
after change: b 2
before change: x nil
after change: x 3
before change: index 4
after change: index 4
3. debug.getupvalue/debug.setupvalue
debug 库还提供了访问 upvalue 的方法,就是 debug.getupvalue 函数,这个函数的第一个参数是一个函数,更确切的说是一个闭包(闭包这个概念一直不是非常清楚,之前做 javascript 的时候也没了解过,先留个坑,下一篇学学闭包这个东西),第二个参数是 upvalue 的索引
关于这个 upvalue 的翻译,从官方解释来看,就是内部函数所访问的(外部)变量,对内部函数而言,称之为 upvalue 或外部局部变量(external local variable)
从这篇博客里有看到了一句觉得更白话(准确不准确暂时不知)的解释:函数里用到的定义在该函数之前的local变量,就成为了该函数的upvalue
这里有一个使用 debug.getupvalue 的栗子:
function newCounter()
local n = 0
return function()
n = n + 1
return n
c = newCounter()
local i = 1
repeat
name, val = debug.getupvalue(c, i)
if name then
print ("index", i, name, "=", val)
i = i + 1
until not name
这段代码输出的结果是:
index 1 n = 2
getupvalue 函数输出了函数 c 的 upvalue n
setupvalue 函数和上面的 setlocal 函数类似,前两个参数和 getupvalue 函数一致,第三个参数是要设置的新值,这里就不再多解释了
4. hook
Lua 的 hook 机制允许我们注册一个在程序运行过程中在特定事件下调用的函数,有四种类型的事件可以触发钩子:
当 Lua 每调用一个函数时触发
return
每当一个函数返回时触发
当 Lua 开始执行新的一行代码时触发
count
当 Lua 执行了指定次数的指令后触发
设置钩子可以使用 debug.sethook 函数,该函数的第一个入参就是钩子函数;第二个入参是一个字符串,用来表示我们想监控哪些事件, 对于 call/return/line 事件,我们使用它们的第一个字母(`c`/`r`/`l`)来代替;如果是监控 count 事件,则只需要传递计数次数作为第三个入参即可
另外,如果想停掉钩子,一样是使用 sethook 函数,不传递任何参数就可以了
一个简单的栗子:
function test()
local a = 0
for i = 0, 100 do
a = a + 1
print("a="..a)
function hook(why)
error("hook reached: " .. why)
debug.sethook (hook, "", 100)
test()
我自己的环境测试时,a 大概打印到13时就触发 hook 函数,所以这个 count 事件所谓的指令执行,就是指各种函数执行、赋值、加减、打印等操作
另一个简单的栗子:
function f()
function g() end
function hook (why)
print ("hook reached: ", why)
print ("function =", debug.getinfo(2, "n").name)
debug.sethook(hook, "c", 0)
这个栗子会打印出执行的函数: