如何在C或Python代码中控制gdb而不使用GDB Python API?
我正在尝试用Python或C语言写一个程序,能够通过gdb来调试C代码。
我看过Tom的解决方案和从Python调用和控制GDB。不过这些方案基本上都是关于如何在Python中脚本化gdb的。因为我打算用arm-gdb来调试嵌入式程序,所以我不能在我的gdb中启用Python脚本功能。
我的目标是创建一个gdb的高级抽象。比如说,启动gdb,设置一些断点,然后在我的代码中继续执行。我也看了一些关于gdb/mi接口的资料。但是,有人能告诉我如何使用gdb/mi接口来创建一个gdb进程,并通过C或Python代码与gdb进行通信吗?(幸运的是,我的arm-gdb支持gdb/mi接口)。
3 个回答
那用一下 http://www.noah.org/python/pexpect/ 呢?这个是 Python 版本的 http://en.wikipedia.org/wiki/Expect,它非常适合用来自动化处理一些外部命令的任务。
你提到的链接主要是关于“如何从GDB调用Python”,但你其实是在问“如何从Python或C调用GDB”。这里推荐使用GDB/MI接口,这个是最合适的选择。Eclipse、Emacs和KDevelop等工具都使用GDB/MI来简化调试界面。我个人在使用KDevelop时,曾经用过三种不同的交叉编译的gdb版本,分别针对ARM、AVR和H8S。MI协议的设计是为了让软件能够解析,所以它的语法非常规范。
在谷歌搜索中,我找到了一个Python GDB封装工具,这个可以帮助你入门。
如上面评论中所承诺的,我把我的(早期、不完整、几乎肯定有bug的)Ruby项目发布到了http://github.com/mcarpenter/rubug。
这里有个例子(你可以在examples/breakpoint
找到)。函数check_for_crash
是一个回调函数,可能在程序调用factorial
后被调用。这个断点需要一个函数名(fac
;前面的冒号表示这是一个Ruby符号,在这里可以理解为一种轻量级的字符串)。
EXE = 'factorial'
def check_for_crash(gdb, event)
case event.type
when :command_response
raise RuntimeError, 'oops' unless
[ :done, :running ].include? event.response.result
when :breakpoint
puts 'Breakpoint reached'
pp event
gdb.continue
when :exit
puts 'Exit'
gdb.stop_event_loop
exit
end
end
gdb = Rubug::Gdb.new
resp = gdb.file EXE
gdb.register_callback(method :check_for_crash)
gdb.break(:fac)
gdb.run '5 > /dev/null'
gdb.start_event_loop
需要提醒你的是,代码可能有点...乱。目前(这是我停下来的地方)大部分功能都不能正常工作(因为我在工作中途更新了gdb,见下面的语法部分)。
不过,目录中有一堆示例可能会对你有帮助。要尝试运行它们,你需要做类似这样的事情:
rake clean
rake grammar
rake make
cd examples/simple_fuzzer
ruby -I ../../lib -r rubygems simple_fuzzer.rb
考虑到写这段代码时的情况,如果你有选择的话,建议使用ruby1.8(当时我不太喜欢1.9,可能在1.9下会有字符串编码的问题)。
响应的解析是通过treetop完成的,http://treetop.rubyforge.org,这是一种PEG解析器。看着语法,我相信可以简化。你需要使用gem install ...
来安装这个(以及其他需要的gem)。
如果你想用Python的方式来做,以下是一些额外的建议:
文档
除了“使用GDB调试”(第22章)外,几乎没有其他文档。我把这个PDF和第22章作为单独文件放到了docs部分。
异步
这个协议是异步的(起初我以为这是命令/响应类型的协议,这个想法是错误的)。如果我重新实现这个,我可能会使用像event machine或libevent这样的东西,而不是自己写select()
循环。
语法
语法有点...混乱。虽然文档(27.2.2)说响应“由零个或多个带外记录组成,后面可选地跟一个结果记录”:
`output -> ( out-of-band-record )* [ result-record ] "(gdb)" nl`
你需要知道,由于任何东西都可以随时到达,read()
在套接字上显然可以返回异步/结果/更多异步/终止符(!)。例如,我在当前的gdb中看到:
=thread-group-started,id="i1",pid="1086"
=thread-created,id="1",group-id="i1"
^running
*running,thread-id="all"
(gdb)
以^
开头的行是结果记录,其他的都是异步(然后是终止符)。这似乎是规范中的一个相当重要的缺陷。
速度
我的主要关注点是安全性,我对MI(机器接口)感兴趣,用于自动模糊测试、二进制检查等。出于这个目的,GDB/MI太慢了(在调试器中启动程序的成本)。你的体验可能会有所不同。
MI / CLI映射
在标准的gdb CLI命令集中,有些东西我无法用MI命令实现。我有一些类似这样的骨架代码:
gdb = Gdb::MI.new
gdb.cli(:file, '/bin/ls')
gdb.cli(:set, :args, '> /dev/null')
gdb.cli(:run)
gdb.cli(:quit)
(我觉得这对我们这些不精通MI但对gdb有了解的用户来说很清晰)。我现在记不清那些有问题的东西是什么了(自从我看这个已经过去一年了),但如果我想起来了,我会回来更新这个。
替代方案
当我刚开始这条路时,我发现了Jamis Buck的一篇博客文章:http://weblog.jamisbuck.org/2006/9/25/gdb-wrapper-for-ruby。这个文章把gdb命令行会话包装在popen()中,这让我有点不安。特别是人们可能会认为它不够稳定,因为gdb对CLI输出的稳定性没有保证。你可能会(或可能不会)更喜欢这种方法。
如果你在Windows上,PyDbg / PeiMei可能会引起你的兴趣:http://code.google.com/p/paimei/
你可能还会喜欢这本书《灰帽Python:黑客的Python编程》(Seitz)。同样,这本书主要是针对Windows的,但可能会给你带来灵感。