如何在GDB中自动打印每个执行的行,直到达到给定的断点?
我希望能够在GDB中设置一个断点,并让它运行到这一点 – 并在这个过程中,打印出它已经“通过”的行。
下面是一个例子,基于这个带有main
函数和函数的简单文件,以及两个断点:
$ cat > test.c <<EOF #include "stdio.h" int count=0; void doFunction(void) { // two steps forward count += 2; // one step back count--; } int main(void) { // some pointless init commands; count = 1; count += 2; count = 0; //main loop while(1) { doFunction(); printf("%d\n", count); } } EOF $ gcc -g -Wall test.c -o test.exe $ chmod +x test.exe $ gdb -se test.exe ... Reading symbols from /path/to/test.exe...done. (gdb) b main Breakpoint 1 at 0x80483ec: file test.c, line 14. (gdb) b doFunction Breakpoint 2 at 0x80483c7: file test.c, line 7.
要开始会话,我需要运行( r
)程序,然后在第一个断点( main
)停止:
(gdb) r Starting program: /path/to/test.exe Breakpoint 1, main () at test.c:14 14 count = 1; (gdb)
在这一点上 – 例如,我可以继续下去( c
); 并且该进程将运行,不输出任何内容,并在请求的行中断开:
(gdb) c Continuing. Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb)
另一方面,而不是继续 – 我可以一行一行,要么使用步骤( s
)或下一步( n
); 例如:
14 count = 1; (gdb) n 15 count += 2; (gdb) s 16 count = 0; (gdb) s 19 doFunction(); (gdb) s Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb) s 9 count--; (gdb) s 10 } (gdb) s main () at test.c:20 20 printf("%d\n", count); (gdb) s ... (gdb) s _IO_vfprintf_internal (s=Cannot access memory at address 0xe5853361 ) at vfprintf.c:210 210 vfprintf.c: No such file or directory. in vfprintf.c (gdb) s 245 in vfprintf.c (gdb) s 210 in vfprintf.c (gdb) n 245 in vfprintf.c ... (gdb) n 2006 in vfprintf.c (gdb) n __printf (format=0x80484f0 "%d\n") at printf.c:39 39 printf.c: No such file or directory. in printf.c (gdb) n main () at test.c:21 21 } (gdb) n 19 doFunction(); (gdb) n Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb)
无论如何,我知道我可以保持Enter键被按下,并且最后input的命令(步或下一个)将会重复( 在第二种情况下保留稍长一点的会话,以显示“next”保持在同一级别上,“步”步骤在被调用的函数内部 )。 但是,正如我们所看到的,取决于步骤还是下一步运行,可能需要一段时间,直到达到一个结果 – 所以,我不想坐在inputbutton上,我只需要坐10分钟:)
所以,我的问题是 – 我可以以某种方式指示gdb
运行到“断点2”,而无需进一步的用户干预 – 打印出它所经过的线,就好像按下了(或下一步)button?
好吧,这不是件容易的事情 – 但我想我已经有点了:)我经历了一系列失败的尝试(贴在这里 ); 相关代码如下。
基本上,“下一步/直到断点”的问题是,如果debugging器停止(在一个步骤),如何判断你是否在断点上。 还要注意我使用GDB 7.2-1ubuntu11(Ubuntu 11.04的最新版本)。 所以,它是这样的:
- 我首先发现了便捷variables ,并且认为 – 由于有程序计数器等可用,所以必须有一些GDB便利variables给出了“断点”状态,并且可以直接在GDB脚本中使用。 但是,在查看GDB引用索引一段时间之后,我根本找不到任何这样的variables(我的尝试在nub.gdb中 )
- 缺乏这种“断点状态”的内部variables – 唯一需要做的事情就是将GDB的('stdout')命令行输出(作为对命令的响应)捕获为一个string,并parsing它(寻找“断点“)
- 然后,我发现了GDB的Python API以及
gdb.execute("CMDSTR", toString=True)
命令 – 这似乎正是捕获输出所需要的:“ 默认情况下,由命令产生的任何输出被发送到gdb的标准输出,如果to_string参数是True,那么输出将被gdb.execute收集并作为string[ 1 ]返回 !- 所以,我首先尝试使用build议的方式使用
gdb.execute
的脚本( pygdb-nub.py , gdbwrap ) 在这里失败 – 因为这个:- 错误627506 – python:gdb.execute([…],to_string = True)部分打印到stdout / stderr
- 错误10808 – 允许GDB / Python API捕获和存储GDB输出
- 然后,我想我会使用python脚本进行
subprocess.Popen
打开GDB程序,同时replace它的标准input和标准输出; 然后从那里继续控制GDB( pygdb-sub.py ) – 也失败了…( 显然,因为我没有redirect标准input/输出权 ) - 然后,我想我会使用python脚本从GDB(通过
source
)调用,这将内部fork到一个pty,每当gdb.execute
应该被调用,以捕获其输出( pygdb-fork.gdb , pygdb-fork .py )…这几乎工作 – 因为有返回的string; 然而GDB注意到一些事情是不正确的:“ [tcsetpgrp失败,在terminal_inferior:操作不允许] ”,后续的返回string似乎没有改变。
- 所以,我首先尝试使用build议的方式使用
最后,有效的方法是:暂时将GDB输出从gdb.execute
redirect到RAM(Linux: /dev/shm
)中的日志文件; 然后读回来,parsing它,并从Python打印 – python也处理一个简单的while循环,直到达到断点。
具有讽刺意味的是 – 这些错误中的大多数,通过redirect日志文件导致了这个解决scheme,实际上最近已经在SVN中修复了; 这意味着它们会在不久的将来传播到发行版,并且可以直接使用gdb.execute("CMDSTR", toString=True)
:/但是,现在我不能冒险从源代码构buildGDB(可能会碰撞变成可能的新的不兼容),这对我来说也够用了:)
这里是相关的文件(部分也在pygdb-fork.gdb , pygdb-fork.py中 ):
pygdb-logg.gdb
是:
# gdb script: pygdb-logg.gdb # easier interface for pygdb-logg.py stuff # from within gdb: (gdb) source -v pygdb-logg.gdb # from cdmline: gdb -x pygdb-logg.gdb -se test.exe # first, "include" the python file: source -v pygdb-logg.py # define shorthand for nextUntilBreakpoint(): define nub python nextUntilBreakpoint() end # set up breakpoints for test.exe: b main b doFunction # go to main breakpoint run
pygdb-logg.py
是:
# gdb will 'recognize' this as python # upon 'source pygdb-logg.py' # however, from gdb functions still have # to be called like: # (gdb) python print logExecCapture("bt") import sys import gdb import os def logExecCapture(instr): # /dev/shm - save file in RAM ltxname="/dev/shm/c.log" gdb.execute("set logging file "+ltxname) # lpfname gdb.execute("set logging redirect on") gdb.execute("set logging overwrite on") gdb.execute("set logging on") gdb.execute(instr) gdb.execute("set logging off") replyContents = open(ltxname, 'r').read() # read entire file return replyContents # next until breakpoint def nextUntilBreakpoint(): isInBreakpoint = -1; # as long as we don't find "Breakpoint" in report: while isInBreakpoint == -1: REP=logExecCapture("n") isInBreakpoint = REP.find("Breakpoint") print "LOOP:: ", isInBreakpoint, "\n", REP
基本上, pygdb-logg.gdb
加载pygdb-logg.py
python脚本,设置pygdb-logg.py
的别名nextUntilBreakpoint
,并初始化会话 – 其他任何事情都由python脚本处理。 这里是一个示例会话 – 关于OP中的testing源:
$ gdb -x pygdb-logg.gdb -se test.exe ... Reading symbols from /path/to/test.exe...done. Breakpoint 1 at 0x80483ec: file test.c, line 14. Breakpoint 2 at 0x80483c7: file test.c, line 7. Breakpoint 1, main () at test.c:14 14 count = 1; (gdb) nub LOOP:: -1 15 count += 2; LOOP:: -1 16 count = 0; LOOP:: -1 19 doFunction(); LOOP:: 1 Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb) nub LOOP:: -1 9 count--; LOOP:: -1 10 } LOOP:: -1 main () at test.c:20 20 printf("%d\n", count); 1 LOOP:: -1 21 } LOOP:: -1 19 doFunction(); LOOP:: 1 Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb)
…就像我想要的那样:P只是不知道它是多么可靠( 以及是否可以在avr-gdb
,这是我需要的:)编辑:avr-gdb的版本Ubuntu 11.04目前是6.4,不能识别python命令:( )
那么,希望这可以帮助别人,
干杯!
这里有一些参考:
- GDB:在stdin上检测到错误
- GDB在将命令传递给STDIN时遇到了问题
- Re:[Gdb]我如何使用GDB的其他input?
- gdb不接受stdin的input
- 在IDE中使用gdb – comp.os.linux.development.apps | Google网上论坛
- rmathew:terminal疾病
- [教程]在C(Linux)中调用外部程序 – GIDForums
- 壳 – 如何使用shebang(即#!)多个参数? – 堆栈溢出
- redirect/存储shell的输出到GDBvariables? – 堆栈溢出
- Corey Goldberg:Python – redirect或closuresSTDOUT和STDERR
- 9,脚本gdb
- gdb的python脚本:哪里有
parse_and_eval
了? – 堆栈溢出 - shell – 调用gdb自动将parameter passing给被debugging的程序 – Stack Overflow
- 使用tmpfs |将文件/目录存储在内存中 HowtoForge – Linux Howtos和教程
- 简单的方法来触摸文件,如果它不存在| Python | python
- os.fork()不同的CGI脚本? – Python
- java – 编写使用GDB的testing – 如何捕获输出? – 堆栈溢出
- 使用GDB进行debugging:如何在Python中创buildGDB命令 – Wiki
- GDB参考卡
基于@ sdaau答案中的链接( http://www.mail-archive.com/gdb@gnu.org/msg00031.html ),我创build了自己的脚本,只是继续发送's'并读取gdb的输出不断地,当输出到文本文件和terminal,当然,我的脚本可以修改,以适应任何人的需求,但是,我希望我所做的修改应该适合大多数人的需要。
http://www.codeground.net/coding/gdb-step-into-all-lines-to-get-full-application-flow/
wget http://www.codeground.net/downloads/gdbwalkthrough.c gcc gdbwalkthrough.c -o gdbwalkthrough ./gdbwalkthrough <application full path> [application arguments]
怎么样在gdb中这样做,使用命令文件。 更改文件参数,并根据需要循环计数。
gdb -x run.gdb
run.gdb:
set pagination off set logging file gdb.log set logging on set $i = 0 file main break main break WriteData # sadly, commands not getting executed on reaching breakpoint 2 commands 2 set $i=1000 print "commands 2 : %d",$i end run while ( $i < 1000 ) step # next # continue set $i = $i + 1 end
作为一个新的答案,因为前一个已经被占用了:)基本上,如果重点是观察源程序(和/或程序集)的代码行作为运行程序的执行 – 因为当看着“ 自动打印输出 “ – 然后,基本上,一个非常快捷的方法是使用GDB TUI模式; 我引用:
c – gdb的行为:值优化出来 – Stack Overflow#1354762
使用GDB TUI模式。 当我input减号和回车时,我的GDB副本可以启用它。 然后键入Cx 2(即按住Control并按下X,释放两者,然后按下2)。 这将把它分解源和反汇编显示。 然后使用stepi和nexti一次移动一条机器指令。 使用Cx o在TUI窗口之间切换。
这里的诀窍是,即使你continue
– 这个时间源将在TUI上显示和指示; 并随着程序运行:
…这对我来说避免了许多情况下,我必须脚本在“自动步进上下文”的断点(虽然仍然有这样的情况)..关于TUI文档: TUI – debugging与GDB
干杯!