GDB程序啟動和斷點設置


GDB程序啟動和斷點設置

前面章節介紹了如何啟動GDB調試器,本節介紹如何在 GDB 調試器中啟動(運行)程序,啟動程序過程中的一些注意事項 以及借助 GDB 調試器在程序中的某個地方設置斷點。

程序啟動

根據不同場景的需要,GDB 調試器提供了多種方式來啟動目標程序,其中最常用的就是 run 指令,其次為 start 指令。也就是說,run 和 start 指令都可以用來在 GDB 調試器中啟動程序,它們之間的區別是:

(1)、默認情況下,run 指令會一直執行程序,直到執行結束。如果程序中手動設置有斷點,則 run 指令會執行程序至第一個斷點處;

(2)、start 指令會執行程序至 main() 主函數的起始位置,即在 main() 函數的第一行語句處停止執行。

不僅如此,在進行 run 或者 start 指令啟動目標程序之前,還可能需要做一些必要的准備工作,大致包括以下幾個方面:

(1)、如果啟動 GDB 調試器時未指定要調試的目標程序,或者由於各種原因 GDB 調試器並為找到所指定的目標程序,這種情況下就需要再次手動指定;

(2)、有些 C 或者 C++ 程序的執行,需要接收一些參數(程序中用 argc 和 argv[] 接收);

(3)、目標程序在執行過程中,可能需要臨時設置 PATH 環境變量;

(4)、默認情況下,GDB 調試器將啟動時所在的目錄作為工作目錄,但很多情況下,該目錄並不符合要求,需要在啟動程序手動為 GDB 調試器指定工作目錄。

(5)、默認情況下,GDB 調試器啟動程序后,會接收鍵盤臨時輸入的數據,並將執行結果會打印在屏幕上。但 GDB 調試器允許對執行程序的輸入和輸出進行重定向,使其從文件或其它終端接收輸入,或者將執行結果輸出到文件或其它終端。

示例程序:

#include<stdio.h>
int main(int argc,char* argv[])
{
    FILE * fp;
    if((fp = fopen(argv[1],"r")) == NULL){
        printf("file open fail");
    }
    else{
        printf("file open true");
    }
    return 0;
}

命令行打開GDB后,是位於當前目錄下,假設我們在root的家目錄啟動gdb。則在執行 main 之前,有以下幾項要做:

1) 首先,對於已啟動的 GDB 調試器,我們可以先通過 l (小寫的 L)指令驗證其是否已找到指定的目標程序文件:

可以看到,對於找不到目標程序文件的 GDB 調試器,l 指令的執行結果顯示“無法加載符號表”。這種情況下,我們就必須手動為其指定要調試的目標程序,例如:

可以看到,通過借助 file 命令,則無需重啟 GDB 調試器也能指定要調試的目標程序文件。

 2) 通過分析 main.c 中程序的邏輯不難發現,要想其正確執行,必須在執行程序的同時給它傳遞一個目標文件的文件名。總的來說,為 GDB 調試器指定的目標程序傳遞參數,常用的方法有 3 種:

(1)、啟動 GDB 調試器時,可以在指定目標調試程序的同時,使用 --args 選項指定需要傳遞給該程序的數據。仍以 main程序為例:

[root@all c]# gdb -args main a.txt

整個指令的意思是:啟動 GDB 調試器調試 main.exe 程序,並為其傳遞 "a.txt" 這個字符串(其會被 argv[] 字符串數組接收)。

(2)、GDB 調試器啟動后,可以借助 set args 命令指定目標調試程序啟動所需要的數據。仍以 main 為例:

(gdb) set args a.txt

該命令表示將 "a.txt" 傳遞給將要調試的目標程序。

(3)、還可以使用 run 或者 start 啟動目標程序時,指定其所需要的數據。例如:

(gdb) run a.txt
(gdb) start a.txt3)

3)默認情況下,GDB 調試器的工作目錄為啟動時所使用的目錄。當然,GDB 調試器提供有修改工作目錄的指令,即 cd 指令。例如,將 GDB 調試器的工作目錄修改為 /root/c,則執行指令為:

(gdb) cd /root/c

4) 某些場景中,目標調試程序的執行還需要臨時修改 PATH 環境變量,此時就可以借助 path 指令,例如:

(gdb) path /temp/demo
Executable and object file path: /root/c:/usr/local/sbin:/usr/local/bin...

此修改方式只是臨時的,退出 GDB 調試后會失效。

5) 默認情況下,GDB 調試的程序會接收 set args 等方式指定的參數,同時會將輸出結果打印到屏幕上。而通過對輸入、輸出重定向,可以令調試程序接收指定文件或者終端提供的數據,也可以將執行結果輸出到文件或者某個終端上。

例如,將 main 文件的執行結果輸出到 a.txt 文件中,執行如下命令:

(gdb) run > a.txt

在 GDB 調試的工作目錄下就會生成一個 a.txt 文件,其中存儲的即為 main 的執行結果。

總的來說,只有將調試程序所需的運行環境搭建好后,才能使用 run 或者 start 命令開始調試。如下是一個完整的實例,演示了 GDB 調試 mian之前所做的准備工作[root@all c]# pwd <--顯示當前工作路徑

[root@all c]# ls       <-- 顯示當前路徑下的文件
a.txt  main.c  main.exe
[root@all c]# cd ~  <-- 進入 home 目錄
[root@all ~]# gdb -q      <-- 開啟 GDB 調試器
(gdb) cd /root/c            <-- 修改 GDB 調試器的工作目錄
Working directory /root/c.
(gdb) file main               <-- 指定要調試的目標文件
Reading symbols from main...
(gdb) set args a.txt               <-- 指定傳遞的數據
(gdb) run                               <-- 運行程序
Starting program: /root/c/main a.txt
file open true
Program exited normally.

 設置斷點

在 GDB 調試器中對  C/C++ 程序打斷點,最常用的就是 break 命令,有些場景中還會用到 tbreak 或者 rbreak 命令,本節將對這 3 個命令的功能和用法做詳細的講解。以一下一段C代碼演示斷點的使用:

#include<stdio.h>
int main(int argc,char* argv[])
{
    int num = 1;
    while(num<100)
    {
        num *= 2;
    }
    printf("num=%d",num);
    return 0;
}

GDB break命令

break 命令(可以用 b 代替)常用的語法格式有以下 2 種。

1、(gdb) break location     // b location
2、(gdb) break ... if cond   // b .. if cond

1) 第一種格式中,location 用於指定打斷點的具體位置,其表示方式有多種,如表 1 所示。

表 1 location 參數的表示方式
location 的值 含 義
linenum linenum 是一個整數,表示要打斷點處代碼的行號。要知道,程序中各行代碼都有對應的行號,可通過執行 l(小寫的 L)命令看到。
filename:linenum filename 表示源程序文件名;linenum 為整數,表示具體行數。整體的意思是在指令文件 filename 中的第 linenum 行打斷點。
+ offset
- offset
offset 為整數(假設值為 2),+offset 表示以當前程序暫停位置(例如第 4 行)為准,向后數 offset 行處(第 6 行)打斷點;-offset 表示以當前程序暫停位置為准,向前數 offset 行處(第 2 行)打斷點。
function function 表示程序中包含的函數的函數名,即 break 命令會在該函數內部的開頭位置打斷點,程序會執行到該函數第一行代碼處暫停。
filename:function filename 表示遠程文件名;function 表示程序中函數的函數名。整體的意思是在指定文件 filename 中 function 函數的開頭位置打斷點。

2) 第二種格式中,... 可以是表 1 中所有參數的值,用於指定打斷點的具體位置;cond 為某個表達式。整體的含義為:每次程序執行到 ... 位置時都計算 cond 的值,如果為 True,則程序在該位置暫停;反之,程序繼續執行。

如下演示了以上 2 種打斷點方式的具體用法:

[root@all c]# gdb main -q
Reading symbols from /root/c/main...done.
(gdb) l
1    #include<stdio.h>
2    int main(int argc,char* argv[])
3    {
4        int num = 1;
5        while(num<100)
6        {
7            num *= 2;
8        }
9        printf("num=%d",num);
10        return 0;
(gdb) 
11    }
(gdb) b 4                                         # 程序第4行打斷點
Breakpoint 1 at 0x4004d3: file main.c, line 4.
(gdb) r                                           # 運行程序,到第4行暫停
Starting program: /root/c/main 

Breakpoint 1, main (argc=1, argv=0x7fffffffe278) at main.c:4
4        int num = 1;
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6.x86_64
(gdb) b +1                                        # 在第4行的基礎上,在第5行代碼處打斷點
Breakpoint 2 at 0x4004da: file main.c, line 5.
(gdb) c                                           # 繼續執行程序,到第5行暫停
Continuing.

Breakpoint 2, main (argc=1, argv=0x7fffffffe278) at main.c:5
5        while(num<100)
(gdb) b 7 if num>10                               # 如果 num > 10 ,在第7行打斷點
Breakpoint 3 at 0x4004dc: file main.c, line 7.
(gdb) c                                           # 繼續執行
Continuing.

Breakpoint 3, main (argc=1, argv=0x7fffffffe278) at main.c:7
7            num *= 2;                            # 程序在第7行暫停
(gdb) p num                                       # p 命令查看 num 的當前值
$1 = 16

 GDB tbreak命令

tbreak 命令可以看到是 break 命令的另一個版本,tbreak 和 break 命令的用法和功能都非常相似,唯一的不同在於,使用 tbreak 命令打的斷點僅會作用 1 次,即使程序暫停之后,該斷點就會自動消失。

tbreak 命令的使用格式和 break 完全相同,有以下 2 種:

1、(gdb) tbreak location
2、(gdb) tbreak ... if cond

仍以 main 為例,如下演示了 tbreak 命令的用法:

[root@all c]# gdb main -q
Reading symbols from /root/c/main...done.
(gdb) tbreak 7 if num>10
Temporary breakpoint 1 at 0x4004dc: file main.c, line 7.
(gdb) r
Starting program: /root/c/main 

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe278) at main.c:7
7            num *= 2;
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6.x86_64
(gdb) p num
$1 = 16
(gdb) c
Continuing.
num=128
Program exited normally.
(gdb)

可以看到,自num=16開始,后續循環過程中 num 的值始終大於 10,則num>10表達式的值永遠為 True,理應在第 7 行暫停多次。但由於打斷點采用的是 tbreak 命令,因此斷點的作用只起 1 次。

GDB rbreak命令

和 break 和 tbreak 命令不同,rbreak 命令的作用對象是 C、C++ 程序中的函數,它會在指定函數的開頭位置打斷點。

tbreak 命令的使用語法格式為:

(gdb) tbreak regex

其中 regex 為一個正則表達式,程序中函數的函數名只要滿足 regex 條件,tbreak 命令就會其內部的開頭位置打斷點。tbreak 命令打的斷點和 break 命令打斷點的效果是一樣的,會一直存在,不會自動消失。

這里我們對 main.c 源文件的程序做如下修改:

(gdb) l      <-- 顯示源碼
1 #include<stdio.h>
2 void rb_one(){
3    printf("rb_one\n");
4 }
5 void rb_second(){
6   printf("rb_second");
7 }
8 int main(int argc,char* argv[])
9 {
10     rb_one();
(gdb)
11     rb_second();
12     return 0;
13 }
(gdb) rbreak rb_*       <--匹配所有以 rb_ 開頭的函數
Breakpoint 1 at 0x1169: file main.c, line 2.
void rb_one();
Breakpoint 2 at 0x1180: file main.c, line 5.
void rb_second();
(gdb) r
Starting program: /home/ubuntu64/demo/main.exe

Breakpoint 1, rb_one () at main.c:2
2 void rb_one(){
(gdb) c
Continuing.
rb_one

Breakpoint 2, rb_second () at main.c:5
5 void rb_second(){
(gdb) c
Continuing.
rb_second[Inferior 1 (process 7882) exited normally]
(gdb)

可以看到,通過執行rbreak rb_*指令,找到了程序中所有以 tb_* 開頭的函數,並在這些函數內部的開頭位置打上了斷點(如上所示,分別為第 2 行和第 5 行)。


免責聲明!

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



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