如何执行完成,然后从命令内执行另一个命令?

这是我的代码结构的简化示例:

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 icontinue )。

有没有办法告诉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在任何增量调用后立即打印?

是:

  1. 使用disas命令进行Diassemble increment例程。 在它的末尾找到ret指令(只有一个)。
  2. 使用break *0xNNNNN语法在该指令上设置断点。
  3. 将命令附加到该断点:

     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