4.9 – 调试接口

Lua 没有内置的调试机制。但是它提供了一组特殊的函数接口以及 钩子。这组接口可用于构建出不同的调试器、性能剖析器、或是其它需要从解释器获取“内部信息”的工具。

lua_Debug

  1. typedef struct lua_Debug {
  2. int event;
  3. const char *name; /* (n) */
  4. const char *namewhat; /* (n) */
  5. const char *what; /* (S) */
  6. const char *source; /* (S) */
  7. int currentline; /* (l) */
  8. int linedefined; /* (S) */
  9. int lastlinedefined; /* (S) */
  10. unsigned char nups; /* (u) 上值的数量 */
  11. unsigned char nparams; /* (u) 参数的数量 */
  12. char isvararg; /* (u) */
  13. char istailcall; /* (t) */
  14. char short_src[LUA_IDSIZE]; /* (S) */
  15. /* 私有部分 */
  16. 其它域
  17. } 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, –]

  1. lua_Hook lua_gethook (lua_State *L);

返回当前的钩子函数。

lua_gethookcount

[-0, +0, –]

  1. int lua_gethookcount (lua_State *L);

返回当前的钩子计数。

lua_gethookmask

[-0, +0, –]

  1. int lua_gethookmask (lua_State *L);

返回当前的钩子掩码。

lua_getinfo

[-(0|1), +(0|1|2), e]

  1. int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);

返回一个指定的函数或函数调用的信息。

当用于取得一次函数调用的信息时,参数 ar 必须是一个有效的活动的记录。这条记录可以是前一次调用 lua_getstack 得到的,或是一个钩子 (参见 lua_Hook )得到的参数。

用于获取一个函数的信息时,可以把这个函数压入堆栈, 然后把 what 字符串以字符 '>' 起头。(这会让 lua_getinfo 从栈顶上弹出函数。)例如,想知道函数 f 是在哪一行定义的,你可以使用下列代码:

  1. lua_Debug ar;
  2. lua_getglobal(L, "f"); /* 取得全局变量 'f' */
  3. lua_getinfo(L, ">S", &ar);
  4. printf("%d\n", ar.linedefined);

what 字符串中的每个字符都筛选出结构ar 结构中一些域用于填充,或是把一个值压入堆栈:

  • 'n': 填充 namenamewhat 域;
  • 'S': 填充 sourceshort_srclinedefinedlastlinedefined ,以及 what 域;
  • 'l': 填充 currentline 域;
  • 't': 填充 istailcall 域;
  • 'u': 填充 nupsnparams,及 isvararg 域;
  • 'f': 把正在运行中指定层次处函数压栈;
  • 'L': 将一张表压栈,这张表中的整数索引用于描述函数中哪些行是有效行。(有效行指有实际代码的行,即你可以置入断点的行。 无效行包括空行和只有注释的行。)
    如果这个选项和选项 'f' 同时使用,这张表在函数之后压栈。

这个函数出错会返回 0 (例如,what 中有一个无效选项)。

lua_getlocal

[-0, +(0|1), –]

  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, –]

  1. 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), –]

  1. const char *lua_getupvalue (lua_State *L, int funcindex, int n);

获取一个闭包的上值信息。(对于 Lua 函数,上值是函数需要使用的外部局部变量,因此这些变量被包含在闭包中。)lua_getupvalue 获取第 n 个上值,把这个上值的值压栈,并且返回它的名字。 funcindex 指向闭包在栈上的位置。( 因为上值在整个函数中都有效,所以它们没有特别的次序。因此,它们以字母次序来编号。)

当索引号比上值数量大的时候,返回 NULL(而且不会压入任何东西)。对于 C 函数,所有上值的名字都是空串 ""

lua_Hook

  1. typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);

用于调试的钩子函数类型。

无论何时钩子被调用,它的参数 ar 中的 event 域都被设为触发钩子的事件。Lua 把这些事件定义为以下常量:LUA_HOOKCALLLUA_HOOKRETLUA_HOOKTAILCALLLUA_HOOKLINELUA_HOOKCOUNT。除此之外,对于 line 事件, currentline 域也被设置。要想获得 ar 中的其他域,钩子必须调用 lua_getinfo

对于 call 事件,event 可以是 LUA_HOOKCALL 这个通常值,或是 LUA_HOOKTAILCALL 表示尾调用;后一种情况,没有对应的返回事件。

当 Lua 运行在一个钩子内部时,它将屏蔽掉其它对钩子的调用。也就是说,如果一个钩子函数内再调回 Lua 来执行一个函数或是一个代码块 ,这个执行操作不会触发任何的钩子。

钩子函数不能有延续点,即不能用一个非空的 k 调用lua_yieldklua_pcallk,或 lua_callk

钩子函数可以在满足下列条件时让出:只有行计数事件可以让出,且不能在让出时传出任何值;从钩子里让出必须用 lua_yield来结束钩子的运行,且 nresults 必须为零。

lua_sethook

[-0, +0, –]

  1. void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);

设置一个调试用钩子函数。

参数 f 是钩子函数。mask 指定在哪些事件时会调用:它由下列一组位常量构成LUA_MASKCALLLUA_MASKRETLUA_MASKLINELUA_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, –]

  1. const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);

设置给定活动记录中的局部变量的值。参数 arnlua_getlocal 中的一样 (参见 lua_getlocal )。lua_setlocal 把栈顶的值赋给变量然后返回变量的名字。它会将值从栈顶弹出。

当索引大于活动局部变量的数量时,返回 NULL (什么也不弹出)。

lua_setupvalue

[-(0|1), +0, –]

  1. const char *lua_setupvalue (lua_State *L, int funcindex, int n);

设置闭包上值的值。它把栈顶的值弹出并赋于上值并返回上值的名字。参数 funcindexnlua_getupvalue 中的一样(参见 lua_getupvalue )。

当索引大于上值的数量时,返回 NULL (什么也不弹出)。

lua_upvalueid

[-0, +0, –]

  1. void *lua_upvalueid (lua_State *L, int funcindex, int n);

返回索引 funcindex 处的闭包中编号为 n 的上值的一个唯一标识符。参数 funcindexnlua_getupvalue 中的一样(参见 lua_getupvalue )。(但 n 不可以大于上值的数量)。

这些唯一标识符可用于检测不同的闭包是否共享了相同的上值。共享同一个上值的 Lua 闭包(即它们指的同一个外部局部变量)会针对这个上值返回相同的标识。

lua_upvaluejoin

[-0, +0, –]

  1. void lua_upvaluejoin (lua_State *L, int funcindex1, int n1,
  2. int funcindex2, int n2);

让索引 funcindex1 处的 Lua 闭包的第 n1 个上值引用索引 funcindex2 处的 Lua 闭包的第 n2 个上值。