Lua 调试

Lua 提供一个调试库,这个库中提供了创建自己的调试器所需的所有原语函数。虽然,Lua 没有内置调试器,但是开发者们为 Lua 开发了许多的开源调试器。

Lua 调试库包括的函数如下表所示。

S.N. 方法和描述
1 debug():进入交互式调试模式,在此模式下用户可以用其它函数查看变量的值。
2 getfenv(object):返回对象的环境。
3 gethook(optional thread):返回线程当前的钩子设置,总共三个值:当前钩子函数、当前的钩子掩码与当前的钩子计数。
4 getinfo(optional thread,function or stack leve,optional flag):返回保存函数信息的一个表。你可以直接指定函数,或者你也可以通过一个值指定函数,该值为函数在当前线程的函数调用栈的层次。其中,0 表示当前函数(getinfo 本身);层次 1 表示调用 getinfo 的函数,依次类推。如果数值大于活跃函数的总量,getinfo 则返回 nil。
5 getlocal(optional thread,stack level,local index):此函数返回在 level 层次的函数中指定索引位置处的局部变量和对应的值。如果指定的索引处不存在局部变量,则返回 nil。当 level 超出范围时,则抛出错误。
6 getmetatable(value):返回指定对象的元表,如果不存在则返回 nil。
7 getregistry():返回寄存器表。寄存器表是一个预定义的用于 C 代码存储 Lua 值的表。
8 getupvalue(func function,upvalue index):根据指定索引返回函数 func 的 upvalue 值(译注:upvalue 值与函数局部变量的区别在于,即使函数并非活跃状态也可能有 upvalue 值,而非活跃函数则不存在局部变量,所以其第一个参数不是栈的层次而是函数)。如果不存在,则返回 nil。
9 setfenv(function or thread or userdata,environment table):将指定的对象的环境设置为 table,即改变对象的作用域。
10 sethook(optional thread,hook function,hook mask string with "c" and/or "r" and/or "l",optional instruction count):把指定函数设置为钩子。字符串掩码和计数值表示钩子被调用的时机。这里,c 表示每次调用函数时都会执行钩子;r 表示每次从函数中返回时都调用钩子;l 表示每进入新的一行调用钩子。
11 setlocal(optional thread,stack level,local index,value):在指定的栈深度的函数中,为 index 指定的局部变量赋予值。如果局部变量不存在,则返回 nil。若 level 超出范围则抛出错误;否则返回局部变量的名称。
12 setmetatable(value,metatable):为指定的对象设置元表,元表可以为 nil。
13 setupvalue(function,upvalue index,value):为指定函数中索引指定的 upvalue 变量赋值。如果 upvalue 不存在,则返回 nil。否则返回此 upvalue 的名称。
14 traceback(optional thread,optional meesage string,opitona level argument):用 traceback 构建扩展错误消息。

上面的表中列出了 Lua 的全部调试函数,我们经常用到的调试库都会用到上面的函数,它让调试变得非常容易。虽然提供了便捷的接口,但是想要用上面的函数创建一个自己的调试器并不是件容易的事。无论怎样,我们可以看一下下面这个例子中怎么使用这些调试函数的。

function myfunction ()
print(debug.traceback("Stack trace"))
print(debug.getinfo(1))
print("Stack trace end")
	return 10
end
myfunction ()
print(debug.getinfo(1))

执行上面的程序,我们可以得到如下的栈轨迹信息:

Stack trace
stack traceback:
	test2.lua:2: in function 'myfunction'
	test2.lua:8: in main chunk
	[C]: ?
table: 0054C6C8
Stack trace end

上面的例子中,我们使用 debug.trace 函数输出了栈轨迹。 debug.getinfo 函数获得函数的当前表。

示例二

在调试过程中,我们常常需要查看或修改函数局部变量的值。因此,我们可以用 getupvalue 获得变量的值,用 setupvalue 修改变量的值。示例如下:

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())

local i = 1

repeat
  name, val = debug.getupvalue(counter, i)
  if name then
    print ("index", i, name, "=", val)
	if(name == "n") then
		debug.setupvalue (counter,2,10)
	end
    i = i + 1
  end -- if
until not name

print(counter())

运行上面的程序,我们可以得到如下面的输出结果:

1
2
index	1	k	=	1
index	2	n	=	2
11

在这个例子中,每次调用 counter 都会更新该闭包函数。我们可以通过 getupvalue 查看其当前的局部变量值。随后,我们更新局部变量的值。在为 n 设置新值之前,其值为 2。调用 setupvalue 后,n 被设置为 10。再调用 counter 时,它就会返回值 11 而不再是 3。

调试类型

  • 命令行调试
  • 图形界面调试

命令行调试工具

命令行调试就是使用命令行命令和 print 语句来调试程序。已经有许多现成的 Lua 命令行调试工具,下面列出了其中的一部分:

  • RemDebug:RemDebug 是一个远程的调试器,它支持 Lua 5.0 和 5.1 版本。允许远程调试 Lua 程序,设置断点以及查看程序的当前状态。同时,它还能调试 CGILua 脚本。

  • clidebugger:此调试器是用纯 Lua 脚本开发的命令行调试工具,支持 Lua 5.1。除了 Lua 5.1 标准库以外,它不依赖于任何其它的 Lua 库。虽然它受到了 RemDebug 影响而产生的,但是它没有远程调试的功能。

  • ctrace:跟踪 Lua API 调用的小工具。

  • xdbLua:windows 平台下的 Lua 命令行调试工具。

  • LuaInterface - Debuger:这个项目是 LuaInterface 的扩展,它对 Lua 调试接口进行进一步的抽象,允许通过事件和方法调用的方式调试程序。

  • RIdb:使用套接字的远程 Lua 调试器,支持 Linux 和 Windows 平台。它的特性比任何其它调试器都丰富。

  • ModDebug:允许远程控制另外一个 Lua 程序的执行、设置断点以及查看程序的当前状态。

图形界面调试工具

图形界面的调试工具往往和集成开发环境(IDE)打包在一起。它允许在可视环境下进行调试,比如查看变量值,栈跟踪等。通过 IDE 的图形界面,你可以设置断点单步执行程序。

下面列出了几种图形界面的调试工具。

  • SciTE:Windows 系统上默认的 Lua 集成开发环境,它提供了丰富的调试功能,比如,断点、单步、跳过、查看变量等等。
  • Decoda:一个允许远程调试的图形界面调试工具。
  • ZeroBrane Studio:一个 Lua 的集成开发环境,它集成了远程调试器、栈视图、远程控制终端、静态分析等诸多功能。它兼容各类 Lua 引擎,例如 LuaJIT,Love2d,Moai等。支持 Windows, OSX, Linux;开源。
  • akdebugger:eclipse 的 Lua 调试器和编辑器插件。
  • luaedit:支持运程调试、本地调试、语法高亮、自动补完、高级断点管理(包括有条件地触发断和断点计数)、函数列表、全局和本地变量列表、面对方案的管理等。