简介
在网上有各种各样的调试库,比如MobDebug或者RemDebug等等,自己也想实现类似的功能,以便更好的自定义改造。
工具准备:
1、ZeroBraneSutdio
debug库介绍
这里将介绍常见的debug命令:
函数 | 简介 |
---|---|
debug() | 进入一个用户交互模式,简单的来讲就是会等待用户的输入,lua程序处于阻塞状态,在这个时候可以执行一些lua的语法,比如调用方法,和运算等等,当输入"cont"后才会结束阻塞状态。 |
sethook ([thread,] hook, mask [, count]) | 将一个函数作为钩子函数设入。 字符串 mask 以及数字 count 决定了钩子将在何时调用。 掩码是由下列字符组合成的字符串,每个字符有其含义: 'c': 每当 Lua 调用一个函数时,调用钩子; 'r': 每当 Lua 从一个函数内返回时,调用钩子; 'l': 每当 Lua 进入新的一行时,调用钩子。 |
getinfo ([thread,] f [, what]) | 返回关于一个函数信息的表。 你可以直接提供该函数, 也可以用一个数字 f 表示该函数。 数字 f 表示运行在指定线程的调用栈对应层次上的函数: 0 层表示当前函数(getinfo 自身); 1 层表示调用 getinfo 的函数 (除非是尾调用,这种情况不计入栈);等等。 如果 f 是一个比活动函数数量还大的数字, getinfo 返回 nil。 |
getlocal ([thread,] f, local) | 此函数返回在栈的 f 层处函数的索引为 local 的局部变量 的名字和值。 这个函数不仅用于访问显式定义的局部变量,也包括形参、临时变量等。 |
更多的命令详情可以查看菜鸟教程:https://www.runoob.com/lua/lua-debug.html
编码-API的使用
debug库的api使用
1. debug()
利用debug()函数可以实程序的阻塞
print("aaa")
debug.debug() --进入用户交互状态,阻塞
print("bbb")
function add(a,b)
return a+b
end
运行的结果:
> aaa
>
在交互模式下,可以执行lua语句,比如方法调用
> add(1,2)
> 3
使用"cont"命令,可以让程序继续往下运行
> cont
> bbb
2. sethook ([thread,] hook, mask [, count])
sethook函数可以实现程序运行的监听:
'c': 每当 Lua 调用一个函数时,调用钩子;
'r': 每当 Lua 从一个函数内返回时,调用钩子;
'l': 每当 Lua 进入新的一行时,调用钩子。
1 function hook(event,line)
2 print(event,line) --打印行号
3 end
4 debug.sethook(hook,"l")
5 local a = 10
6 local b = 20
7 print(a+b)
运行结果:hook函数打印了每个被执行代码的行号。"l"参数每运行一段新的代码时会被hook,从而调用hook函数
> line 5
> line 6
> line 7
> 30
其余的参数 "c"、"r"不再进行演示了,详细的介绍如上
3. getinfo ([thread,] f [, what])
这个函数用处很多,可以获取到一些信息,方法信息、文件信息等,这里较为复杂的可能是 f 这个参数了, 0 层表示当前函数(getinfo 自身); 1 层表示调用 getinfo 的函数 ,以此类推,具体效果可以写个demo进行试验
local table_info = debug.getinfo(1)
for k,v in pairs(table_info) do
print(k,v)
end
输出结果:
linedefined 0 --函数定义的起始和结束行号
currentline 1 --上级(level - 1)函数被调用的行号
func function: 0x00021cd0 --函数本身
isvararg true --参数是否有可变长参数
namewhat --函数类型(field, upvalue, global)
lastlinedefined 5 --函数定义的起始和结束行号
source @D:\Work_Home\ZB_Work\myTest.lua --指代函数定义的位置,如果函数是在文件中定义的,source就是这个文件路径前加"@"
nups 0 -- 函数的upvalue值的个数
what main --函数类型(Lua, C, main)
nparams 0 --函数参数个数
short_src --D:\Work_Home\ZB_Work\myTest.lua -- 是source的短版本,不超过60字符
4. getlocal ([thread,] f, local)
这个方法可以用于获取栈中变量的值, f 层处函数的索引为 local 的局部变量的名字和值
local a = 10
local b = 20
local i = 1
repeat
local k,v = debug.getlocal(1,i) --获取值
print(k,v)
i = i + 1
until not k
输出结果
> a 10
> b 20
> i 3
> nil nil
编码-整合
使用上面介绍的api可以实现一个简单的单步调试功能的调试器
1 local model_step = 1
2 local model_over = 2
3 local model = model_step
4 local breakList={}
5
6 local vkList={}
7
8 function saveValues() --保存值
9 local i = 5
10 repeat
11 local k,v = debug.getlocal(3,i)
12 if k ~= "(*temporary)" and k~=nil then
13 vkList[k] = v
14 end
15 i = i+1
16 until not k
17 end
18
19 function watch(key) --查看值
20 print(vkList[key])
21 end
22
23 function hook(event,line)
24 print("current at line:",line)
25 saveValues(line) --保存输出当前值
26 if(model == model_step) then
27 --单步
28 debug.debug() --实现单步调试效果,进入交互状态(阻塞)
29 else
30 --步过
31 if(breakList[line] ~= nil) then
32 debug.debug() --实现单步调试效果,进入交互状态(阻塞)
33 end
34 end
35 end
36
37 function addBreakpoint(line) --添加断点
38 breakList[line] = line
39 end
40
41 function removeBreakpoint(line) --移除断点
42 breakList[line] = nil
43 end
44
45 function step() --单步
46 model = model_step
47 end
48
49 function over() --步过
50 model = model_over
51 end
52 debug.sethook(hook,"l")
53
54 local a = 10 --被调试程序
55 local b = 20
56
57 function add(a,b)
58 return a+b
59 end
60
61 local result = add(a,b)
62 print(result)
运行结果:
> current at line: 54
> lua_debug> addBreakpoint(58) --58行添加断点
> lua_debug> cont --结束debug()函数的阻塞
> current at line: 55
> lua_debug> watch("a") --查看值
> 10
> lua_debug> over() --进入步过模式
> lua_debug> cont
> current at line: 59
> current at line: 57
> current at line: 61
> current at line: 58
> lua_debug> step() --进入单步模式
> lua_debug> cont
> current at line: 62
> lua_debug> cont
> 30
> Program completed in 28.95 seconds (pid: 11404).