我希望能够在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 ,最后输入的命令(步骤或下一个)将重复(在第二种情况下留下更长的会话,以显示'next'保持在同一级别,'step'步骤称为)。但是,正如可以看到的,取决于步骤或下一步是否运行,可能需要一段时间才能达到结果 - 所以,我不想坐在10分钟,我的手卡在Enter按钮上:)
所以,我的问题是 - 我可以以某种方式指示gdb
在没有进一步用户干预的情况下运行到'断点2' - 同时打印出它经过的行,就好像按下了步骤(或下一步)一样? / p>
答案 0 :(得分:19)
嗯,这并不容易 - 但我想我有点得到了:)我经历了一系列失败的尝试(发布here);相关代码如下。
基本上,“下一步/直到断点”的问题是如果调试器停止(步骤),如何确定是否“断开”断点。另请注意,我使用GDB 7.2-1ubuntu11(Ubuntu 11.04的当前版本)。所以,它是这样的:
gdb.execute("CMDSTR", toString=True)
命令 - 这似乎是捕获输出所需要的:“默认情况下,命令产生的任何输出发送到gdb的标准输出。如果to_string参数为True,则输出将由gdb.execute收集并作为字符串[1] 返回!
gdb.execute
的脚本(pygdb-nub.py,gdbwrap);失败了 - 因为这个:
subprocess.Popen
GDB程序,同时替换它的stdin和stdout;然后从那里继续控制GDB(pygdb-sub.py) - 那也失败了......(显然,因为我没有重定向stdin / out right )source
),只要调用gdb.execute
,就会在内部分叉到pty,以便捕获它的输出( pygdb-fork.gdb,pygdb-fork.py)... 几乎工作 - 因为返回了字符串;然而,GDB注意到有些事情是不对的:“ [tcsetpgrp在terminal_inferior中失败:操作不允许] ”,后续的返回字符串似乎没有改变。最后,有效的方法是:暂时将GDB输出从gdb.execute
重定向到RAM中的日志文件(Linux:/dev/shm
);然后读回来,解析它并从python中打印 - python也处理一个简单的while循环,直到达到断点为止。
具有讽刺意味的是 - 大多数这些错误,通过重定向日志文件引起了这个解决方案,实际上最近修复了SVN;这意味着那些将在不久的将来传播到发行版,并且可以直接使用gdb.execute("CMDSTR", toString=True)
:/但是,因为我现在不能冒险从源代码构建GDB(并且可能碰到可能的新的不兼容性),这对我来说也足够好了:))
以下是相关文件(部分也在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脚本,为nub
设置别名nextUntilBreakpoint
,并初始化会话 - 其他一切都由python脚本处理。这是一个示例会话 - 关于OP中的测试源:
$ 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
中使用,这就是我需要的:)编辑:Ubuntu 11.04中的avr-gdb版本目前是6.4,它无法识别python命令:()
嗯,希望这有助于某人,
干杯!
这里有一些参考文献:
parse_and_eval
gone? - Stack Overflow 答案 1 :(得分:4)
使用命令文件在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
答案 2 :(得分:3)
根据@ sdaau的答案(http://www.mail-archive.com/gdb@gnu.org/msg00031.html)中的链接,我创建了自己的脚本,只需继续发送's'并连续读取gdb的输出,同时将输出打印到textfile和终端,当然,我的脚本可以修改以满足其他人的需求,但是,我希望我所做的修改应该适合大多数人的需要。
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]
答案 3 :(得分:1)
作为一个新的答案,因为前一个已经陷入困境:)基本上,如果重点是观察执行源(和/或汇编)代码行作为程序运行 - 因为在寻找时通常对我有动力进入“自动打印输出” - 那么,基本上,一种非常快捷的方法是使用GDB TUI模式;我引用:
c - gdb behavior : value optimized out - Stack Overflow #1354762
使用GDB TUI模式。当我键入减号和Enter时,我的GDB副本启用它。然后键入C-x 2(按住Control并按X,释放两个然后按2)。这将把它放入拆分源和反汇编显示中。然后使用stepi和nexti一次移动一台机器指令。使用C-x o在TUI窗口之间切换。
这里的诀窍是,即使你点击continue
- 这个时间源也会在TUI上显示和显示;程序运行后跟随:
...这对我来说避免了很多情况,我必须在“自动步进上下文”中编写断点脚本(虽然仍然存在这种情况)。关于TUI的文档:TUI - Debugging with GDB
干杯!
答案 4 :(得分:0)
当前接受的答案包括很多文件 io 并且只在断点处停止,但忽略观察点、信号甚至程序结束。
使用 python api 可以很好地处理:
stop
事件处理程序,用于检查停止原因并将步骤类型存储在那里以下代码可以放在 gdb-auto-step.py 中,您可以随时使用 source gdb-auto-step.py
激活它(或包含在 .gdbinit 文件中以使其始终可用):>
import gdb
import time
class CmdAutoStep (gdb.Command):
"""Auto-Step through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (1-19, default 5)."""
def __init__(self):
print('Registering command auto-step')
super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
gdb.events.stop.connect(stop_handler_auto_step)
def invoke(self, argument, from_tty):
number = 5 # optional: use a parameter for the default
if argument:
if not argument.isdigit():
raise gdb.GdbError("argument must be a digit, not " + argument)
number = int(argument)
if number == 0 or number > 19:
raise gdb.GdbError("argument must be a digit between 1 and 19")
sleep_time = 3.0 / (number * 1.4)
try:
frame = gdb.newest_frame()
except gdb.error:
raise gdb.GdbError("No stack.")
global last_stop_was_simple
last_stop_was_simple = True
pagination = gdb.execute("show pagination", False, True).find("on")
if pagination:
gdb.execute("set pagination off", False, False)
try:
while (last_stop_was_simple):
gdb.execute ("step")
time.sleep(sleep_time)
except KeyboardInterrupt as ki:
if pagination:
gdb.execute("set pagination on", False, False)
except gdb.GdbError as user_error:
if pagination:
gdb.execute("set pagination on", False, False)
# pass user errors unchanged
raise user_error
except:
if pagination:
gdb.execute("set pagination on", False, False)
traceback.print_exc()
def stop_handler_auto_step(event):
# check the type of stop, the following is the common one after step/next,
# a more complex one would be a subclass (for example breakpoint or signal)
global last_stop_was_simple
last_stop_was_simple = type(event) is gdb.StopEvent
CmdAutoStep()
注意:如果有人有更好的检查分页值的方法,请发表评论。