回答
不帶參數的break在當前所選棧幀的下一條指令處停止執行。您可以通過frame
或up
和down
命令選擇散架。如果你想調試點實際上離開當前功能,選擇下一個外框並在那裏打破。
斷點在當前指令處設置,而不是下一個。如果你正在執行一個命令,執行已經停止。當前函數處於活動狀態時,調用函數中的任何斷點都不會發生,除非它是遞歸的,在這種情況下,此類調試會變得令人困惑。 – Potatoswatter 2010-09-06 06:48:09
你如何「選擇下一個外框並在那裏打破」?你能澄清嗎? (請注意,目標是在函數內部有一個斷點*(例如能夠看到它的本地語言),但在它返回之前)。 – ShreevatsaR 2017-09-06 23:13:54
無參數斷點在當前行設置斷點。
單個斷點無法捕獲所有返回路徑。在返回後立即在調用者處設置斷點,或在所有return
語句處中斷。因爲這是C++,我想你可以創建一個本地的崗位對象,並打破它的析構函數,儘管如此。
您可以使用reverse debugging找出函數實際返回的位置。完成執行當前幀,然後執行反向步驟,然後您應該停止返回語句。
(gdb) record
(gdb) fin
(gdb) reverse-step
根據該頁面,這需要Linux-x86,它可能有一個強大的表現懲罰。無論如何+1,因爲它太酷了。 – Potatoswatter 2010-09-07 03:13:40
http://rr-project.org/上的'rr'工具可以通過Linux上的重放進行反向調試,而只會產生大約1.2倍的放緩(根據其網站,至少)。它使更酷的事情變得更酷。 :) – pnkfelix 2015-08-05 10:41:49
@Potatoswatter最重要的是,它在7.11中完全崩潰,如果你因爲缺乏AVX支持而進行大多數庫調用... https://stackoverflow.com/questions/2528918/gdb-reverse-debugging-fails -with-process-record-does-not-support-instruction-0x/46113472 – 2017-09-08 09:49:21
相反的答案,到目前爲止,大多數編譯器會創建一個單一的回報彙編指令,不管return
陳述有多少的功能(方便編譯器要做到這一點,所以只有一個地方執行所有的堆棧幀清理)。
如果你想停止該指令,你所要做的就是disas
並尋找retq
(或任何你的處理器的返回指令),並在其上設置一個斷點。例如:
int foo(int x)
{
switch(x) {
case 1: return 2;
case 2: return 3;
default: return 42;
}
}
int main()
{
return foo(0);
}
(gdb) disas foo
Dump of assembler code for function foo:
0x0000000000400448 <+0>: push %rbp
0x0000000000400449 <+1>: mov %rsp,%rbp
0x000000000040044c <+4>: mov %edi,-0x4(%rbp)
0x000000000040044f <+7>: mov -0x4(%rbp),%eax
0x0000000000400452 <+10>: mov %eax,-0xc(%rbp)
0x0000000000400455 <+13>: cmpl $0x1,-0xc(%rbp)
0x0000000000400459 <+17>: je 0x400463 <foo+27>
0x000000000040045b <+19>: cmpl $0x2,-0xc(%rbp)
0x000000000040045f <+23>: je 0x40046c <foo+36>
0x0000000000400461 <+25>: jmp 0x400475 <foo+45>
0x0000000000400463 <+27>: movl $0x2,-0x8(%rbp)
0x000000000040046a <+34>: jmp 0x40047c <foo+52>
0x000000000040046c <+36>: movl $0x3,-0x8(%rbp)
0x0000000000400473 <+43>: jmp 0x40047c <foo+52>
0x0000000000400475 <+45>: movl $0x2a,-0x8(%rbp)
0x000000000040047c <+52>: mov -0x8(%rbp),%eax
0x000000000040047f <+55>: leaveq
0x0000000000400480 <+56>: retq
End of assembler dump.
(gdb) b *0x0000000000400480
Breakpoint 1 at 0x400480
(gdb) r
Breakpoint 1, 0x0000000000400480 in foo()
(gdb) p $rax
$1 = 42
中斷當前函數的所有retq
這段Python命令把一個斷點當前函數的每retq
指令:
class BreakReturn(gdb.Command):
def __init__(self):
super().__init__(
'break-return',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
frame = gdb.selected_frame()
# TODO make this work if there is no debugging information, where .block() fails.
block = frame.block()
# Find the function block in case we are in an inner block.
while block:
if block.function:
break
block = block.superblock
start = block.start
end = block.end
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instructions = arch.disassemble(start, end - 1)
for instruction in instructions:
if instruction['asm'].startswith('retq '):
gdb.Breakpoint('*{}'.format(instruction['addr']))
BreakReturn()
與來源是:
source gdb.py
和請使用以下命令:
break-return
continue
您現在應該在retq
。
步驟,直到retq
只是爲了好玩,當retq
發現(效率較低,因爲沒有硬件支持),該站另一種實現方式:
class ContinueReturn(gdb.Command):
def __init__(self):
super().__init__(
'continue-return',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
thread = gdb.inferiors()[0].threads()[0]
while thread.is_valid():
gdb.execute('ni', to_string=True)
frame = gdb.selected_frame()
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instruction = arch.disassemble(pc)[0]['asm']
if instruction.startswith('retq '):
break
ContinueReturn()
這會忽略你的其他斷點。 TODO:可以避免嗎?不知道是否比reverse-step
更快或更慢。
停在一個給定的操作碼的版本,可以發現在:https://stackoverflow.com/a/31249378/895245
不知何故,使用多次調用的遞歸函數,干擾,並且應該在返回時運行的每個斷點都稱爲多次。 (實際上還沒有用一個簡單的函數嘗試過它......)(另一方面,這實際上起作用,即使多次調用斷點,所以謝謝。) – ShreevatsaR 2017-08-24 14:12:57
@ShreevatsaR很奇怪。如果可以的話,鏈接到一個最小可重現的例子。 – 2017-08-24 15:51:51
如果你可以改變的源代碼,你可以使用一些骯髒的伎倆與預處理:
void on_return() {
}
#define return return on_return(), /* If the function has a return value != void */
#define return return on_return() /* If the function has a return value == void */
/* <<<-- Insert your function here -->>> */
#undef return
然後設置一個斷點到on_return
並去一幀up
。
注意:如果函數不通過return
語句返回,則這不起作用。所以請確保它的最後一行是return
。
實施例(無恥地從C代碼複製,但也將工作在C++):
#include <stdio.h>
/* Dummy function to place the breakpoint */
void on_return(void) {
}
#define return return on_return()
void myfun1(int a) {
if (a > 10) return;
printf("<10\n");
return;
}
#undef return
#define return return on_return(),
int myfun2(int a) {
if (a < 0) return -1;
if (a > 0) return 1;
return 0;
}
#undef return
int main(void)
{
myfun1(1);
myfun2(2);
}
第一個宏將改變
return;
到
return on_return();
哪個是有效,因爲on_return
也返回void
。
第二個宏將改變
return -1;
到
return on_return(), -1;
它將調用on_return()
,然後返回-1(感謝,
- 運算符)。
這是一個非常髒的技巧,但儘管使用向後步進,它也可以在多線程環境和內聯函數中工作。
rr
反向調試
類似於在https://stackoverflow.com/a/3649698/895245提到GDB record
,但更實用的GDB 7.11的VS rr
4.1.0在Ubuntu 16.04。
值得注意的是,它涉及AVX正確:
- gdb reverse debugging fails with "Process record does not support instruction 0xf0d at address"
- "target record-full" in gdb makes "n" command fail on printf with "Process record does not support instruction 0xc5 at address 0x7ffff7dee6e7"?
它禁止使用默認標準庫調用工作。
安裝Ubuntu 16.04。
sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
但也考慮從源代碼編譯來獲取最新的更新,這並不難。
測試程序:
int where_return(int i) {
if (i)
return 1;
else
return 0;
}
int main(void) {
where_return(0);
where_return(1);
}
編譯和運行:
gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay
現在你留下了GDB會話裏面,可以適當反向調試:
(rr) break main
Breakpoint 1 at 0x56057c458619: file a.c, line 9.
(rr) continue
Continuing.
Breakpoint 1, main() at a.c:9
9 where_return(0);
(rr) step
where_return (i=0) at a.c:2
2 if (i)
(rr) finish
Run till exit from #0 where_return (i=0) at a.c:2
main() at a.c:10
10 where_return(1);
Value returned is $1 = 0
(rr) reverse-step
where_return (i=0) at a.c:6
6 }
(rr) reverse-step
5 return 0;
我們現在在正確的回程線上。
- 1. 在GDB中設置斷點
- 2. 在C文件中設置GDB斷點
- 3. 使用gdb在LibC中設置斷點
- 4. GDB無法設置斷點
- 5. gdb:通過名稱搜索功能來設置斷點
- 6. 在GDB的每一行設置斷點
- 7. 退出信號設置GDB中斷點
- 8. GDB:設置在非源文件斷點
- 9. gdb掛鉤設置中斷
- 10. 關閉確認上設置斷點GDB
- 11. 它能夠在返回值上設置「數據斷點」
- 12. 不能在gdb中放置斷點在emacs中
- 13. gdb,在文件中的所有函數上設置斷點
- 14. 如何在gdb中爲C++設置operator()的斷點?
- 15. gdb:如何在帶空格的文件中設置斷點
- 16. GDB:函數返回後中斷
- 17. 如何在方法返回特定值時在Xcode中設置「智能」斷點?
- 18. 在GDB中命名斷點
- 19. 在gdb中移動斷點
- 20. GDB從whatis返回設置變量
- 21. 當數組值在cuda-gdb中更改時設置斷點
- 22. 在gdb中設置文件寬度斷點
- 23. 如何使用共享庫函數在gdb中設置斷點
- 24. 在gdb中設置一個隨機地址斷點
- 25. 在共享對象文件中設置gdb斷點
- 26. 在emacs(或DDD)中使用gdb時無法設置斷點
- 27. 在gdb-many-window中設置斷點沒有可視光標
- 28. GDB:僅在命中不同斷點後才斷點的斷點
- 29. 如何設置gdb中的python函數的斷點
- 30. gdb和GPS:不能在被保護類型Ada對象的一部分功能或過程上設置斷點
您是否探索過rbr? – Jack 2010-09-06 06:54:15