gdb高級技巧


注意: 這里是講gdb的高級技巧。如果沒有接觸過gdb,請看這篇:點這里

gdb是一個功能極其強大的命令行調試器。其實,除了我們常用的 file b s n q disp p 等命令,也有很多高級技巧。雖然有的功能是為系統級調試提供的,但還是有方便之處。

接下來,我將介紹一些高級技巧,希望可以幫助大家。

(溫馨提醒:多用help命令!請提前用 -g 參數編譯)

GDB版本:9.1;系統版本:Arch Linux 245-1

示例代碼:(以下示例均以此代碼為准)

#include <iostream>
#include <cstdio>
using namespace std;
int f(int n){
    if(n==0) return 1;
    return f(n-1)*n;
}

int main(){
    int n;
    scanf("%d",&n);
    printf("%d\n",f(n));
    return 0;
}

1. backtrace

backtrace(簡寫為bt)可以讓你查看棧幀信息。這對調試遞歸的函數很有幫助。

配套命令:

up/down [num] 往棧頂/棧底移動num幀。num默認為1。
frame [num] 切換到第num幀。frame簡寫為f。

Example:

當抵達某個斷點停下后,輸入bt,類似於這樣:

(gdb) bt
#0  f (n=3) at example.cpp:6
#1  0x000055555555519e in f (n=4) at example.cpp:6
#2  0x000055555555519e in f (n=5) at example.cpp:6
#3  0x000055555555519e in f (n=6) at example.cpp:6
#4  0x000055555555519e in f (n=7) at example.cpp:6
#5  0x000055555555519e in f (n=8) at example.cpp:6
#6  0x000055555555519e in f (n=9) at example.cpp:6
#7  0x000055555555519e in f (n=10) at example.cpp:6
#8  0x00005555555551dd in main () at example.cpp:12
(gdb)

幾個示例:

(gdb) up 1
#1  0x000055555555519e in f (n=4) at example.cpp:6
6           return f(n-1)*n;
(gdb) down 1
#0  f (n=3) at example.cpp:6
6           return f(n-1)*n;
(gdb) frame 5 //前面bt輸出的結果中,第一項是序號,frame即切換到對應序號的幀
#5  0x000055555555519e in f (n=8) at example.cpp:6
6           return f(n-1)*n;
(gdb) 

2. commands

commands(簡寫為comm)可以在觸發某個(或多個)斷點的時候運行指定gdb命令。

用法:commands [斷點編號1] [斷點編號2] ...

之后,它會讓你逐行輸入要指定的gdb命令。

效果嗎...在到你指定的斷點時,他都會逐行運行你之前輸入的命令。

Example:

(gdb) b 6
Breakpoint 1 at 0x1191: file example.cpp, line 6.
(gdb) comm 1 
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>p "Command test" //指定到達1號斷點時打印這段字符串
>end //以end結束
(gdb) r
Starting program: /run/media/acceptedzhs/SimpleDisk/編程/洛谷/example 
10

Breakpoint 1, f (n=10) at example.cpp:6
6           return f(n-1)*n;
$1 = "Command test"
(gdb) 

順便提一句,怎么查看斷點編號?運行 info b 即可。

輸出類似這樣:(num是編號)

Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000000129e in pre() at UVA10140 Prime Distance.cpp:12
2       breakpoint     keep y   0x00000000000012c7 in pre() at UVA10140 Prime Distance.cpp:13
3       breakpoint     keep y   0x00000000000012de in pre() at UVA10140 Prime Distance.cpp:14

3. ignore

用法:ignore [斷點編號] [num]。ignore可縮寫為ig。

效果:在前num次觸發指定斷點時都不停止(即到了第num+1次觸發斷點才停下)

這在調一些循環結構的代碼時比較有用。

Example:

(gdb) b 6
Breakpoint 1 at 0x1191: file example.cpp, line 6.
(gdb) ig 1 4
Will ignore next 4 crossings of breakpoint 1.
(gdb) r
Starting program: /run/media/acceptedzhs/SimpleDisk/編程/洛谷/example 
10

Breakpoint 1, f (n=6) at example.cpp:6
6           return f(n-1)*n;
(gdb) //前面4次經過斷點,分別為f(10)、f(9)、f(8)、f(7),都跳過了
//因此f(6)才觸發

4. condition

用法:condition [斷點編號] [條件]。condition可縮寫為cond。

效果:觸發斷點時,只有指定的條件為真時才停下。

Example:

(gdb) b 6
Breakpoint 1 at 0x1191: file example.cpp, line 6.
(gdb) cond 1 n==5 //只有n等於5時才觸發斷點
(gdb) r
Starting program: /run/media/acceptedzhs/SimpleDisk/編程/洛谷/example 
10

Breakpoint 1, f (n=5) at example.cpp:6
6           return f(n-1)*n;
(gdb) 

5. 各種breakpoint

什么?斷點還有類型?這里介紹下:

  • break(簡寫b)是我們最熟悉的。
  • tbreak(簡寫tb):臨時斷點,也就是觸發一次后自動消失。與break用法相同。
  • hbreak(簡寫hb):硬件斷點。對我們來說沒什么用。
  • rbreak(簡寫rb):根據正則表達式設置斷點。用法:rbreak [正則表達式]

說明一下rbreak。舉個例子,我程序里有兩個函數,dfs1與dfs2。如果我運行 rbreak dfs* ,由於dfs1與dfs2均匹配,所以這兩個函數均會被加上斷點。

Example1:(tbreak)(看到沒,第一次觸發在f函數處的斷點,繼續運行便不會再觸發該斷點了)

(gdb) tb f
Temporary breakpoint 1 at 0x1184: file example.cpp, line 5.
(gdb) r
Starting program: /run/media/acceptedzhs/SimpleDisk/編程/洛谷/example 
10 //這里是輸入,10!=3628800

Temporary breakpoint 1, f (n=10) at example.cpp:5
5           if(n==0) return 1; //臨時斷點,只停了一次
(gdb) c
Continuing. //繼續,就不會再停了
3628800
[Inferior 1 (process 31859) exited normally]
(gdb) 

Example2:(rbreak)(main符合mai*的條件,因此被加了斷點)

(gdb) rb mai*
Breakpoint 1 at 0x11ac: file example.cpp, line 9.
int main();
(gdb) 

6. print/display命令輸出格式(可以用簡寫)

用法:print/[format] [變量1] [變量2] ...

當然,如果是display命令,則要換成display/[format] [變量1] [變量2] ...

其中,format是一個小寫字母,指定打印變量值的格式。

format字母 對應格式
x 按十六進制格式顯示變量
d 按十進制格式顯示變量
u 按十進制格式顯示無符號整型
o 按八進制格式顯示變量
t 按二進制格式顯示變量
a 按十六進制格式顯示變量
c 按字符格式顯示變量
f 按浮點數格式顯示變量

比如說,調試狀壓DP的程序時,就可以 p/t [變量名] 來以二進制形式查看變量了。disp同理。

Example:

(gdb) p/x 100 
$1 = 0x64
(gdb) p/d 100
$2 = 100
(gdb) p/u 100
$3 = 100
(gdb) p/o 100
$4 = 0144
(gdb) p/t 100
$5 = 1100100
(gdb) p/a 100
$6 = 0x64
(gdb) p/c 100
$7 = 100 'd'
(gdb) p/f 100
$8 = 1.40129846e-43 //浮點數格式不一樣
(gdb) 

7. save

其實斷點是可以保存的!比如說,我臨時要重啟一下,又不想丟失當前調試的斷點信息。那么,我們可以將當前的斷點信息保存到一個文件里,到時候再導入。

用法:save breakpoints [文件名]

效果:將當前所有的斷點信息保存到一個指定的文件里。

有人有疑問了,怎么導入斷點信息呢?那就是source命令!

用法:source [文件名]

效果:從指定的文件里導入斷點信息。

Example:

(gdb) b 5
Breakpoint 1 at 0x1184: file example.cpp, line 5.
(gdb) b 6
Breakpoint 2 at 0x1191: file example.cpp, line 6.
(gdb) b 7
Breakpoint 3 at 0x11a2: file example.cpp, line 7.
(gdb) save breakpoints 123 //保存斷點信息
Saved to file '123'.
(gdb) q

---中間退出gdb,再重新進---

(gdb) source 123 //從這個文件中引入斷點信息
Breakpoint 1 at 0x1184: file example.cpp, line 5.
Breakpoint 2 at 0x1191: file example.cpp, line 6.
Breakpoint 3 at 0x11a2: file example.cpp, line 7.
(gdb) 

8. call

用法:call [調用語句]

效果:調用指定函數。

比如說,我的程序有一個min函數,我就可以通過 call min(a,b) 來獲取變量a、b的最小值了。

Example:

(gdb) call f(10) //也就是10!
$1 = 3628800
(gdb) 

9. finish(縮寫fin)

用法:無參數。

效果:繼續運行,直到當前函數返回。

Example:

Breakpoint 1, main() at example.cpp:10
10    int n;
(gdb) fin
Run till exit from #0 main() at example.cpp:9 //main函數執行完畢,返回了0

[Inferior 1 (process 32243) exited normally]
(gdb) 

10. watchpoint

其實,斷點還有一種特殊的類型——watchpoint。(簡寫為wa——似乎不太吉祥)

用法:watch/rwatch/awatch [變量名]

作用:監視指定變量。

  • watch(簡寫wa):當指定變量被寫時停下。
  • rwatch(簡寫rwa):當指定變量被讀時停下。
  • awatch(簡寫awa):當指定變量被讀/寫時停下。

Example:

(gdb) wa n
Hardware watchpoint 2: n
(gdb) c
Continuing.
10

Hardware watchpoint 2: n

Old value = 32767 //此處由於scanf讀入修改了n的值,因此停下
New value = 10
0x00007ffff7ac43a9 in __vfscanf_internal () from /usr/lib/libc.so.6
(gdb) 

11. checkpoint

有時候,我們要復現某個bug,這個時候,我們可以創建一個快照,即checkpoint。

命令:checkpoint(可簡寫為ch)

用法:無參數。

效果:創建一個快照,包含當前調試的所有信息。同時會輸出這個checkpoint的信息,就像這樣:

checkpoint 1: fork returned pid 25776.

其中,數字1便是這個checkpoint的編號。

那么,如何回滾到以前的快照呢?那就是restart命令啦!

用法:restart [checkpoint編號]

效果:回退到指定checkpoint的快照。

Example:

Breakpoint 1, f (n=10) at example.cpp:5
5           if(n==0) return 1;
(gdb) ch //創建了編號為1的快照
checkpoint 1: fork returned pid 32213.
(gdb) c
Continuing.

Breakpoint 1, f (n=9) at example.cpp:5
5           if(n==0) return 1;
(gdb) restart 1 //恢復到編號為1的快照
Switching to process 32213
#0  f (n=10) at example.cpp:5
5           if(n==0) return 1;
(gdb) 

12. jump

用法:jump [num]

作用:強制使跳轉至第num行。(中間的行都跳過了)

注意,這個不能跨函數跳轉,否則會出錯。

Example:

(gdb) b 10
Breakpoint 1 at 0x11bb: file example.cpp, line 11.
(gdb) r
Starting program: /run/media/acceptedzhs/SimpleDisk/編程/洛谷/example 

Breakpoint 1, main () at example.cpp:11
11          scanf("%d",&n);
(gdb) jump 13
Continuing at 0x5555555551f0.
[Inferior 1 (process 32243) exited normally] //跳到了第13行,main函數已經結束了,因此直接退出程序
(gdb) 

13. return

用法:return [argu]

作用:強制使當前函數退出,並返回argu值。(如果該函數本來就沒有返回值,則argu可以省略)

Breakpoint 1, f (n=10) at example.cpp:5
5           if(n==0) return 1;
(gdb) return 15
Make f(int) return now? (y or n) y
#0  0x00005555555551dd in main () at example.cpp:12
12          printf("%d\n",f(n));
(gdb) n
15  //因為前面設定返回15,所以這里輸出15

蒟蒻寫博客不易,懇請大佬點個贊!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM