[英]How to execute finish and then another command from inside commands?
這是我的代碼結構的簡化示例:
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打印i
的任何之后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
命令進行Diassemble increment
例程。 在它的末尾找到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) p i
$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中自動創建其他斷點。
最近的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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.