探究Lua Debug库实现简单的本地调试

简介

在网上有各种各样的调试库,比如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).
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342