这是我的代码结构的简化示例:
void increment(int j);
int main()
{
int i = 0;
while(1) {
i = increment(i);
}
return 0;
}
int increment(int j)
{
return j + 1;
}
这是相应的 GDB 脚本:
b increment
command 1
finish
print i
continue
end
问题是finish
命令阻止它后面的命令(即print i
和continue
(不被调用。
有没有办法告诉GDB在任何increment
呼叫后立即打印i
?
您显然可以通过将所有命令包装在单个 python 调用中来解决此错误,例如
(gdb) break doSomething
Breakpoint 1 at 0x400478: file iter.c, line 5.
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>python gdb.execute("print i"); gdb.execute("finish"); gdb.execute("print i");
>end
Breakpoint 1, doSomething () at iter.c:5
5 while (i < 5)
$1 = 0
main (argc=1, argv=0x7fffffffe178) at iter.c:13
13 return 0;
$2 = 5
编辑:不需要 Python 的第二个解决方法似乎是定义一个新的 gdb 命令并在命令中运行它:
define foo
print *i
set $addrOfI = i
finish
print *$addrOfI
end
break doSomething
commands
foo
end
问题是 finish 似乎停止中止为其之后的第一个断点设置的命令。
这是预期行为:任何恢复劣质(正在调试(进程(如finish
(的命令也会停止执行固定命令序列。
更新:
另请参阅此 GDB 错误报告。
有没有办法告诉GDB在任何增量调用后立即打印i?
是的:
- 使用
disas
命令组装increment
例程。在最后找到ret
指令(只有一个(。 - 使用
break *0xNNNNN
语法在该指令上设置断点。 -
将命令附加到该断点:
command N print $rax # or $eax if you are on 32-bit x86 platform continue end
瞧:你应该从打印increment()
(就在返回之前(获得返回的值。
或者@Matt答案,如果你使用 GDB 7.4,你可以使用 FinishBreakpoints,如下所示(未经测试 - 我不确定这里是否接受评论(:
(gdb) python #first defined the class
class MyFinishBreakpoint (gdb.FinishBreakpoint):
def stop (self):
print "%s" % gdb.parse_and_eval("i")
return False # don't want to stop
end
(gdb) break doSomething
(gdb) commands
# then set the FinishBreakpoint silently
silent
py MyFinishBreakpoint()
continue
(以及文档链接(
GDB 断点命令列表受到限制,因为它们忽略第一个步进/继续命令之后的任何命令(截至 2017 年 3 月,GDB 7.12(。这记录在 GDB 手册中,其中的动机是当前实现无法同时执行两个命令列表(参见 GDB #10852 - 命令序列意外中断(。
此限制仅通过命令列表中直接存在的单步执行/继续命令强制执行。因此,人们可以解决这个问题 - 但限制仍然适用,例如 GDB 手册在 Python API 部分中警告:"你不应该改变下级的执行状态(即步骤、下一步等(">
因此,当需要在函数进入和函数退出时执行 GDB 命令时,可靠的解决方案是使用多个断点并拆分命令列表。这意味着需要为所调查函数的每个返回指令设置额外的断点。
这可以类似于:
(gdb) b my_function
(gdb) commands
silent
printf "my_function: %d -> ", j
end
(gdb) set pagination off
(gdb) set logging file gdb.log
(gdb) set logging overwrite on
(gdb) set logging on
(gdb) disas my_function
(gdb) set logging off
(gdb) shell grep ret gdb.log
0x00007ffff76ad095 <+245>: retq
(gdb) b *0x00007ffff76ad095
(gdb) commands
silent
printf "%lun", $rax
end
哪个寄存器包含返回值取决于调用约定,并且取决于体系结构。在 x86-64 上,它在 $rax
.其他选择在x86-32上$eax
,在SPARC上$o0
,在ARM上$r0
等。
其他断点的创建可以在 GDB 中使用其脚本支持自动创建。
蟒
最近的 GDB 版本带有一个非常适合这种自动化的 Python API。默认情况下,发行版提供的 GDB 包通常启用 Python 支持。
作为第一个示例,在给定函数的每个 ret 指令上自动设置断点:
(gdb) py fn='myfunc'; list(map(lambda l: gdb.execute('b *{}'.format(l[0])),
filter(lambda l : l[2].startswith('ret'), map(lambda s : s.split(),
gdb.execute('disas '+fn, to_string=True).splitlines()))))
(假设GDB是在Python3支持下编译的,例如Fedora 25(
为了自动创建打印返回值(即寄存器$rax
的值(然后继续的断点,gdb.Breakpoint
类需要子类化:
py
class RBP(gdb.Breakpoint):
def stop(self):
print(gdb.parse_and_eval('$rax'))
return False
end
然后可以像这样创建一个断点:
py RBP('*0x000055555555894e')
结合这两个步骤来创建新的自定义命令:
py
class Pret_Cmd(gdb.Command):
'''print return value via breakpoint command
pret FUNCTION
'''
def __init__(self):
super().__init__('pret', gdb.COMMAND_BREAKPOINTS)
def install(self, fn):
for l in filter(lambda l : l[2].startswith('ret'),
map(lambda s : s.split(),
gdb.execute('disas '+fn, to_string=True).splitlines())):
RBP('*{}'.format(l[0]))
def invoke(self, arg, from_tty):
self.install(arg)
Pret_Cmd()
end
使用此新命令的示例:
(gdb) help breakpoints
(gdb) help pret
(gdb) pret myfunc
诡计
如果您不喜欢 Python 和/或 GDB 禁用了 Python 支持 - 但启用了 Guile 支持 - 也可以通过 Guile 自动设置断点。
Guile中的自定义命令定义:
(gdb) gu (use-modules (gdb))
(gdb) gu
(register-command!
(make-command "pret" #:command-class COMMAND_BREAKPOINTS #:doc
"print return value via breakpoint commandnnpret FUNCTION"
#:invoke
(lambda (fn)
(map (lambda (x)
(let ((bp (make-breakpoint (string-append "*" x))))
(register-breakpoint! bp)
(set-breakpoint-stop!
bp
(lambda (x)
(display (parse-and-eval "$rax"))
(newline)
#f))
bp))
(map (lambda (x) (list-ref x 0))
(filter
(lambda (x)
(and (not (null? x))
(string-prefix? "ret" (list-ref x 2))))
(map (lambda (x) (string-tokenize x))
(string-split
(execute
(string-append "disas " fn)
#:to-string
#t)
#newline))))))))
end
你真的尝试过编译这个吗?您的increment()
函数被声明为 void
,但需要int
。更改后,它对我来说效果很好:
% gdb test
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
[...]
Reading symbols from test...done.
(gdb) b increment
Breakpoint 1 at 0x4004bb: file test.c, line 5.
(gdb) r
Starting program: test
Breakpoint 1, increment (j=0) at test.c:5
5 return j+1;
(gdb) fin
Run till exit from #0 increment (j=0) at test.c:5
0x00000000004004dc in main () at test.c:11
11 i = increment(i);
Value returned is $1 = 1
(gdb) n
12 }
(gdb) p i
$2 = 1
(gdb)