如何执行完成,然后从命令内执行另一个命令?
这是我的代码结构的简化示例:
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
调用后立即打印?
您可以通过将所有命令包装在单个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
)也会停止执行固定命令序列。
更新:
另请参阅此GDB错误报告 。
有没有办法告诉GDB在任何增量调用后立即打印?
是:
- 使用
disas
命令进行Diassembleincrement
例程。 在它的末尾找到ret
指令(只有一个)。 - 使用
break *0xNNNNN
语法在该指令上设置断点。 -
将命令附加到该断点:
command N print $rax # or $eax if you are on 32-bit x86 platform continue end
Voila:你应该从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
(以及文档的链接)
你真的试过编译吗? 您的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) pi $2 = 1 (gdb)
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 "%lu\n", $rax end
包含返回值的寄存器取决于调用约定,并且取决于体系结构。 在x86-64上它是 $rax
。 其他选择是x86-32上的$eax
,SPARC上的$o0
,ARM上的$r0
等。
可以使用其脚本支持在GDB中自动创建其他断点。
python
最近的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一个)
为了自动创建打印返回值的断点(即register $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和/或有一个禁用了Python支持的GDB – 但启用了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 command\n\npret 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