4.9 – 调试接口
Lua 没有内置的调试机制。但是它提供了一组特殊的函数接口以及 钩子。这组接口可用于构建出不同的调试器、性能剖析器、或是其它需要从解释器获取“内部信息”的工具。
lua_Debug
- typedef struct lua_Debug {
- int event;
- const char *name; /* (n) */
- const char *namewhat; /* (n) */
- const char *what; /* (S) */
- const char *source; /* (S) */
- int currentline; /* (l) */
- int linedefined; /* (S) */
- int lastlinedefined; /* (S) */
- unsigned char nups; /* (u) 上值的数量 */
- unsigned char nparams; /* (u) 参数的数量 */
- char isvararg; /* (u) */
- char istailcall; /* (t) */
- char short_src[LUA_IDSIZE]; /* (S) */
- /* 私有部分 */
- 其它域
- } lua_Debug;
这是一个携带有有关函数或活动记录的各种信息的结构。lua_getstack
只会填充结构的私有部分供后面使用。调用 lua_getinfo
可以在lua_Debug
中填充那些可被使用的信息域。
下面对 lua_Debug
的各个域做一个说明:
source
: 创建这个函数的代码块的名字。如果source
以 '@
' 打头,指这个函数定义在一个文件中,而 '@
' 之后的部分就是文件名。若source
以 '=
' 打头,剩余的部分由用户行为来决定如何表示源码。其它的情况下,这个函数定义在一个字符串中,而source
正是那个字符串。short_src
: 一个“可打印版本”的source
,用于出错信息。linedefined
: 函数定义开始处的行号。lastlinedefined
: 函数定义结束处的行号。what
: 如果函数是一个 Lua 函数,则为一个字符串"Lua"
;如果是一个 C 函数,则为"C"
;如果它是一个代码块的主体部分,则为"main"
。currentline
: 给定函数正在执行的那一行。 当提供不了行号信息的时候,currentline
被设为 -1 。name
: 给定函数的一个合理的名字。因为 Lua 中的函数是一等公民,所以它们没有固定的名字:一些函数可能是全局复合变量的值,另一些可能仅仅只是被保存在一张表的某个域中。lua_getinfo
函数会检查函数是怎样被调用的,以此来找到一个适合的名字。 如果它找不到名字,name
就被设置为NULL
。namewhat
: 用于解释name
域。namewhat
的值可以是"global"
,"local"
,"method"
,"field"
,"upvalue"
, 或是""
(空串)。这取决于函数怎样被调用。(Lua 用空串表示其它选项都不符合。)istailcall
: 如果函数以尾调用形式调用,这个值就为真。在这种情况下,当层的调用者不在栈中。nups
: 函数的上值个数。nparams
: 函数固定形参个数(对于 C 函数永远是 0 )。isvararg
: 如果函数是一个可变参数函数则为真(对于 C 函数永远为真)。
lua_gethook
[-0, +0, –]
- lua_Hook lua_gethook (lua_State *L);
返回当前的钩子函数。
lua_gethookcount
[-0, +0, –]
- int lua_gethookcount (lua_State *L);
返回当前的钩子计数。
lua_gethookmask
[-0, +0, –]
- int lua_gethookmask (lua_State *L);
返回当前的钩子掩码。
lua_getinfo
[-(0|1), +(0|1|2), e]
- int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
返回一个指定的函数或函数调用的信息。
当用于取得一次函数调用的信息时,参数 ar
必须是一个有效的活动的记录。这条记录可以是前一次调用 lua_getstack
得到的,或是一个钩子 (参见 lua_Hook
)得到的参数。
用于获取一个函数的信息时,可以把这个函数压入堆栈, 然后把 what
字符串以字符 '>
' 起头。(这会让 lua_getinfo
从栈顶上弹出函数。)例如,想知道函数 f
是在哪一行定义的,你可以使用下列代码:
- lua_Debug ar;
- lua_getglobal(L, "f"); /* 取得全局变量 'f' */
- lua_getinfo(L, ">S", &ar);
- printf("%d\n", ar.linedefined);
what
字符串中的每个字符都筛选出结构ar
结构中一些域用于填充,或是把一个值压入堆栈:
- '
n
': 填充name
及namewhat
域; - '
S
': 填充source
,short_src
,linedefined
,lastlinedefined
,以及what
域; - '
l
': 填充currentline
域; - '
t
': 填充istailcall
域; - '
u
': 填充nups
,nparams
,及isvararg
域; - '
f
': 把正在运行中指定层次处函数压栈; - '
L
': 将一张表压栈,这张表中的整数索引用于描述函数中哪些行是有效行。(有效行指有实际代码的行,即你可以置入断点的行。 无效行包括空行和只有注释的行。)
如果这个选项和选项 'f
' 同时使用,这张表在函数之后压栈。
这个函数出错会返回 0 (例如,what
中有一个无效选项)。
lua_getlocal
[-0, +(0|1), –]
- const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
从给定活动记录或从一个函数中获取一个局部变量的信息。
对于第一种情况,参数 ar
必须是一个有效的活动的记录。 这条记录可以是前一次调用 lua_getstack
得到的,或是一个钩子 (参见 lua_Hook
)的参数。索引 n
用于选择要检阅哪个局部变量;参见 debug.getlocal
中关于变量的索引和名字的介绍。
lua_getlocal
将变量的值压栈,并返回其名字。
对于第二种情况,ar
必须填 NULL
。需要探知的函数必须放在栈顶。对于这种情况,只有 Lua 函数的形参是可见的(没有关于还有哪些活动变量的信息)也不会有任何值压栈。
当索引大于活动的局部变量的数量,返回 NULL
(无任何压栈)
lua_getstack
[-0, +0, –]
- int lua_getstack (lua_State *L, int level, lua_Debug *ar);
获取解释器的运行时栈的信息。
这个函数用正在运行中的指定层次处函数的 活动记录 来填写lua_Debug
结构的一部分。0 层表示当前运行的函数,n+1 层的函数就是调用第 n 层(尾调用例外,它不算在栈层次中)函数的那一个。如果没有错误, lua_getstack
返回 1 ;当调用传入的层次大于堆栈深度的时候,返回 0 。
lua_getupvalue
[-0, +(0|1), –]
- const char *lua_getupvalue (lua_State *L, int funcindex, int n);
获取一个闭包的上值信息。(对于 Lua 函数,上值是函数需要使用的外部局部变量,因此这些变量被包含在闭包中。)lua_getupvalue
获取第 n
个上值,把这个上值的值压栈,并且返回它的名字。 funcindex
指向闭包在栈上的位置。( 因为上值在整个函数中都有效,所以它们没有特别的次序。因此,它们以字母次序来编号。)
当索引号比上值数量大的时候,返回 NULL
(而且不会压入任何东西)。对于 C 函数,所有上值的名字都是空串 ""
。
lua_Hook
- typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
用于调试的钩子函数类型。
无论何时钩子被调用,它的参数 ar
中的 event
域都被设为触发钩子的事件。Lua 把这些事件定义为以下常量:LUA_HOOKCALL
,LUA_HOOKRET
,LUA_HOOKTAILCALL
,LUA_HOOKLINE
,LUA_HOOKCOUNT
。除此之外,对于 line 事件, currentline
域也被设置。要想获得 ar
中的其他域,钩子必须调用 lua_getinfo
。
对于 call 事件,event
可以是 LUA_HOOKCALL
这个通常值,或是 LUA_HOOKTAILCALL
表示尾调用;后一种情况,没有对应的返回事件。
当 Lua 运行在一个钩子内部时,它将屏蔽掉其它对钩子的调用。也就是说,如果一个钩子函数内再调回 Lua 来执行一个函数或是一个代码块 ,这个执行操作不会触发任何的钩子。
钩子函数不能有延续点,即不能用一个非空的 k
调用lua_yieldk
,lua_pcallk
,或 lua_callk
。
钩子函数可以在满足下列条件时让出:只有行计数事件可以让出,且不能在让出时传出任何值;从钩子里让出必须用 lua_yield
来结束钩子的运行,且 nresults
必须为零。
lua_sethook
[-0, +0, –]
- void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);
设置一个调试用钩子函数。
参数 f
是钩子函数。mask
指定在哪些事件时会调用:它由下列一组位常量构成LUA_MASKCALL
,LUA_MASKRET
,LUA_MASKLINE
,LUA_MASKCOUNT
。参数 count
只在掩码中包含有 LUA_MASKCOUNT
才有意义。对于每个事件,钩子被调用的情况解释如下:
- call hook: 在解释器调用一个函数时被调用。钩子将于 Lua 进入一个新函数后,函数获取参数前被调用。
- return hook: 在解释器从一个函数中返回时调用。钩子将于 Lua 离开函数之前的那一刻被调用。没有标准方法来访问被函数返回的那些值。
- line hook: 在解释器准备开始执行新的一行代码时,或是跳转到这行代码中时(即使在同一行内跳转)被调用。(这个事件仅仅在 Lua 执行一个 Lua 函数时发生。)
- count hook: 在解释器每执行
count
条指令后被调用。(这个事件仅仅在 Lua 执行一个 Lua 函数时发生。)
钩子可以通过设置mask
为零屏蔽。
lua_setlocal
[-(0|1), +0, –]
- const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
设置给定活动记录中的局部变量的值。参数 ar
与 n
和lua_getlocal
中的一样 (参见 lua_getlocal
)。lua_setlocal
把栈顶的值赋给变量然后返回变量的名字。它会将值从栈顶弹出。
当索引大于活动局部变量的数量时,返回 NULL
(什么也不弹出)。
lua_setupvalue
[-(0|1), +0, –]
- const char *lua_setupvalue (lua_State *L, int funcindex, int n);
设置闭包上值的值。它把栈顶的值弹出并赋于上值并返回上值的名字。参数 funcindex
与 n
和lua_getupvalue
中的一样(参见 lua_getupvalue
)。
当索引大于上值的数量时,返回 NULL
(什么也不弹出)。
lua_upvalueid
[-0, +0, –]
- void *lua_upvalueid (lua_State *L, int funcindex, int n);
返回索引 funcindex
处的闭包中编号为 n
的上值的一个唯一标识符。参数 funcindex
与 n
和lua_getupvalue
中的一样(参见 lua_getupvalue
)。(但 n
不可以大于上值的数量)。
这些唯一标识符可用于检测不同的闭包是否共享了相同的上值。共享同一个上值的 Lua 闭包(即它们指的同一个外部局部变量)会针对这个上值返回相同的标识。
lua_upvaluejoin
[-0, +0, –]
- void lua_upvaluejoin (lua_State *L, int funcindex1, int n1,
- int funcindex2, int n2);
让索引 funcindex1
处的 Lua 闭包的第 n1
个上值引用索引 funcindex2
处的 Lua 闭包的第 n2
个上值。