lua 调试器制作注意
前两年写过一个 lua 的调试器,blog 上有截图
不过调试器设计的关键不在于界面,在于调试协议。前两年的那个是设计的不完整的。
最近同事强烈要求引擎提供一个强力的调试工具,虽然我个人不太依赖调试去写代码。甚至认为,经过反复调试才正确工作的代码不是好代码。不过周末还是花了点时间重新制作了一个 lua 调试器。
中间发现一些问题,非常让人吐血。列在这里,做个记录。
一开始我比较担心 lua 的 debug hook 会降低运行性能。所以考虑的优化比较多。
都说提前优化是万恶之源,此话绝对不假。可挡不住的诱惑是,制作一个几乎不影响运行性能的调试器,更为实用。而且这个东西我构思了好几年,优化策略也思考再三,不算提前优化吧。
基本优化策略是把被调试程序分成三种调试状态:
无须 hook 的状态。这个状态下,没有设置断点,lua 程序可以全速去跑。只要定期查询一下,调试器有没有指令输入即可。( 和 gdb 一样,这个调试器支持且暂时仅支持远程调试)这个轮询,可以放在程序主循环内,定期跑一下即可。几乎不会影响正常的程序运行。这样,我可以随时 attach 到正在工作的 lua 进程上。
高密度的 hook 状态,使用 lua debug hook 的 line 模式。每条 lua 指令都被 hook ,用来检测断点的工作。
低密度的 hook 状态。仅使用 lua debug hook 的 call/return 模式。这个模式下,仅仅返回进入和返回被 hook ,可以做一些粗略的判断。
实现的时候发现几个问题。
我原来的计划中,使用 call/return 的 hook 消息,监视 lua 运行所在的源文件/ 函数。 一旦发现没有断点,则可以使用第三模式运行,提高被监控状态的运行效率。
当然,如果所有断点都被 disable ,则切换到第一状态。
事实上,简单的在 call/return 的 hook 内获取上级函数的 source 是不够的。因为,lua 的 call hook 发生的时候,已经进入被调用函数;而 lua 的 return hook 发生,尚还停留在调用函数的最后一行。也就是说,可能没有机会回到被调用者。
比如在 a.lua 中写上两个函数调用 :
foo1()
foo2()
而把 foo1 和 foo2 定义在 b.lua 中。如果仅仅 hook call/return 。会发现 foo1() 和 foo2() 调用期间,从 hook 中,没有回到 a.lua 。假设在 foo2() 上设置断点,就没有机会断下来。
想来想去,补救的方法是:当发生 return 或 tail return 时,多看一级堆栈,获得调用者的 source 名。不过这样还是有点问题,有可能调用者是从 C 或是一段运行期产生的代码中过来,依然回溯不到正确的位置。正确的做法是一直回溯到可识别的源代码文件名。( @ 开头的字符串)
最后再查表判定是否需要切换 hook 的 mask 状态。
另一点是关于 lua 的 tail return 的,return 和 tail return 是分开的事件,在实现 step over 的功能时务必小心。
关于无效代码行的判定没有想到特别好的方案。即设置断点时,如果设置在无效行上,应该向上移动到有效位置。虽然 lua 的 debug 模块提供了一些相关的支持,但是比较有限。靠监控运行的当前函数来反复匹配合适的断点位置,代码写起来会过于繁琐。
在调试器中查看变量的值,最好注意一下潜在的副作用。主要是 metatable 造成的。用 rawget 去取 table 里的数据更靠谱一些,需要小心的是是 tostring 的 meta 方法调用。
lua 的高阶用法中,往往会由一段代码生成新的代码运行。让调试器识别这种情况,并给予支持,会带来许多方便。(主要是格式化源码,并对 unix 和 dos 的回车做兼容比较麻烦)
周六一天本来把调试器已经写完,后来发现一些隐藏的 bug 。越看代码越不顺眼,然后周日推翻重写了一次。
新的版本主要是建立起一个调试状态机。把调试器的各种状态严格区分。比如运行态和阻塞交互状态。以及大状态下的小状态划分。
因为后面会做一个图形交互前端,对调试指令的协议定义要求比较严格。文档也仔细研究过,参考了 gdb 的协议。
做完的感觉就是累。没想到后面有没完没了的小需求加进来。幸亏中途一咬牙重写了。否则肯定在周末是完成不了的。不算 socket 通讯部分(以前自己实现的一个模块),暂时有 1000 行 lua 代码吧,比第一个版本简洁很多,但比我预期代码规模大一些。也不能算太好看。
btw,我在选择调试器默认端口时,选了个 3563 。16 进制为 0xdeb 。 昨天 Sean 同学告诉我,这个是个知名端口: 乃 Wacom C 的调试器端口。吼吼,真是英雄所见略同。
Comments
Posted by: 黑衣人 | (26) January 4, 2020 09:54 PM
Posted by: bear-ear | (25) June 26, 2019 04:17 PM
Posted by: 陈金星 | (24) February 5, 2018 03:03 PM
Posted by: unknow | (23) December 18, 2014 07:35 PM
Posted by: roboq | (22) May 4, 2014 07:42 PM
Posted by: zxcvbnm321321 | (21) April 5, 2013 10:50 AM
Posted by: zhujnt | (20) June 28, 2011 10:41 AM
Posted by: eastcowboy | (19) March 8, 2010 01:47 PM
Posted by: zz | (18) August 9, 2009 10:12 PM
Posted by: Anonymous | (17) July 12, 2009 02:50 PM
Posted by: zz | (16) July 12, 2009 02:47 PM
Posted by: 小顺 | (15) June 26, 2009 03:03 PM
Posted by: Cloud | (14) June 19, 2009 12:18 AM
Posted by: 鍏跺疄涓嶆槸鎴 | (13) June 18, 2009 09:25 AM
Posted by: Albert | (12) June 4, 2009 10:20 AM
Posted by: Anonymous | (11) May 29, 2009 04:09 PM
Posted by: moon | (10) May 28, 2009 12:53 PM
Posted by: LvYou | (9) May 27, 2009 12:13 AM
Posted by: ix | (8) May 26, 2009 07:44 PM
Posted by: sunzhuo | (7) May 26, 2009 06:22 PM
Posted by: sail tsao | (6) May 26, 2009 02:04 PM
Posted by: archerk | (5) May 26, 2009 10:52 AM
Posted by: thejinchao | (4) May 26, 2009 10:12 AM
Posted by: chu | (3) May 26, 2009 08:59 AM
Posted by: remdebug | (2) May 26, 2009 12:22 AM
Posted by: hello world | (1) May 25, 2009 10:35 PM