gdb除錯多程序與多執行緒

一,gdb的基礎知識

1>介紹: gdb是Linux環境下的程式碼除錯工具。
2>使用:需要在原始碼生成的時候加上 -g 選項.
3>開始使用: gdb binFile
4>退出:ctrl d 或 quit
5>除錯過程中的常用命令:

   list/l 行號:顯示binFile原始碼,接著上次的位置往下列,每次列10行。
list/l 函式名:列出某個函式的原始碼。
r或run:執行程式。
s或step:進入函式呼叫
breaktrace(bt):檢視各級函式呼叫及引數
info(i) locals:檢視當前棧幀區域性變數的值
info break :檢視斷點資訊。
finish:執行到當前函式返回,然後挺下來等待命令
print(p):列印表示式的值,通過表示式可以修改變數的值或者呼叫函式
set var:修改變數的值
quit:退出gdb
break(b) 行號:在某一行設定斷點
break 函式名:在某個函式開頭設定斷點
continue(或c):從當前位置開始連續而非單步執行程式
run(或r):從開始連續而非單步執行程式
delete breakpoints:刪除所有斷點
delete breakpoints n:刪除序號為n的斷點
disable breakpoints:禁用斷點
enable breakpoints:啟用斷點
info(或i) breakpoints:參看當前設定了哪些斷點
display 變數名:跟蹤檢視一個變數,每次停下來都顯示它的值
undisplay:取消對先前設定的那些變數的跟蹤
until X行號:跳至X行
p 變數:列印變數值
n 或 next:單條執行

二,使用gdb除錯多程序

1 ,除錯程式碼

  1 /**************************************
2 *檔案說明:process.c
3 *作者:段曉雪
4 *建立時間:2017年06月10日 星期六 10時59分14秒
5 *開發環境:Kali Linux/g   v6.3.0
6 ****************************************/
7 
8 #include<stdio.h>
8 #include<stdio.h>
9 #include<unistd.h>
10 #include<sys/types.h>
11 #include<sys/wait.h>
12 
13 int main()
14 {
15     pid_t pid = fork();//建立子程序
16 
17     if(pid == -1)
18     {
19         perror("fork error");
20         return -1;
21     }
22     else if(pid == 0)//child
23     {
24         printf("i am a child:my pid is %d,my father is %d\n",getpid(),getppid());
25     }
26     else//father
27     {
28         printf("i am a father:my pid is %d\n",getpid());
29         wait(NULL);//等待子程序
30     }
31 
32     return 0;
33 
34 }

2,預設設定下,在除錯多程序程式時GDB只會除錯主程序。但是GDB(>V7.0)支援多程序的分別以及同時除錯,換句話說,GDB可以同時除錯多個程式。只需要設定follow-fork-mode(預設值:parent)和detach-on-fork(預設值:on)即可。

follow-fork-mode detach-on-fork 說明:

parent                   on               只除錯主程序(GDB預設)
child                    on               只除錯子程序
parent                   off              同時除錯兩個程序,gdb跟主程序,子程序block在fork位置
child                    off              同時除錯兩個程序,gdb跟子程序,主程序block在fork位置

1>進入gdb除錯模式:
這裡寫圖片描述

2>檢視系統預設的follow-fork-mode 和 detach-on-fork:

show follow-fork-mode
show detach-on-fork

這裡寫圖片描述

3>設定follow-fork-mode 和 detach-on-fork:

set follow-fork-mode [parent|child]   
set detach-on-fork [on|off]

這裡寫圖片描述

4>用l/list命令檢視原始碼(按enter翻頁),分別在子程序和父程序相應位置下斷點:
這裡寫圖片描述
下斷點:
這裡寫圖片描述

5>執行程式,查詢正在除錯的程序:
顯示GDB除錯的所有inferior,GDB會為他們分配ID。其中帶有*的程序是正在除錯的inferior。( GDB將每一個被除錯程式的執行狀態記錄在一個名為inferior的結構中。一般情況下一個inferior對應一個程序,每個不同的inferior有不同的地址空間。inferior有時候會在程序沒有啟動的時候就存在。)

run
info inferiors

這裡寫圖片描述

6> 切換除錯的程序:

 inferior <infer number>

這裡寫圖片描述

7>其他

(1)add-inferior [-copies n] [-exec executable]

新增新的除錯程序,可以用file executable來分配給inferior可執行檔案。增加n個inferior並執行程式為executable。如果不指定n只增加一個inferior。如果不指定executable,則執行程式留空,增加後可使用file命令重新指定執行程式。這時候建立的inferior其關聯的程序並沒啟動。

(2)remove-inferiors infno

刪除一個infno號的inferior。如果inferior正在執行,則不能刪除,所以刪除前需要先kill或者detach這個inferior。

(3)clone-inferior [-copies n] [infno]

複製n個編號是infno的inferior。如果不指定n的話,就只複製一個inferior。如果不指定infno,則就複製正在除錯的inferior。

 (4)detach inferior

detach掉編號是infno的inferior。注意這個inferior還存在,可以再次用run命令執行它。

(5)kill inferior infno: 

kill掉infno號inferior。注意這個inferior仍然存在,可以再次用run等命令執行它。

(6)set schedule-multiple on|off

設為off:只有當前inferior會執行。
設為on:全部是執行狀態的inferior都會執行。
這個選項類似於多執行緒除錯裡的set .

(7)scheduler-locking

注意:如果scheduler-locking是指為on,即使schedule-multiple設定為on,也只有當前程序的當前執行緒會執行。
show schedule-multiple: 檢視schedule-multiple的狀態。

(8)set follow-exec-mode new|same

設定same:當發生exec的時候,在執行exec的inferior上控制子程序。
設定為new:新建一個inferior給執行起來的子程序。而父程序的inferior仍然保留,當前保留的inferior的程式狀態是沒有執行。

show follow-exec-mode

檢視follow-exec-mode設定的模式。

(9)set print inferior-events on|off

用來開啟和關閉inferior狀態的提示資訊。

show print inferior-events

檢視print inferior-events設定的狀態。

(10)maint info program-spaces

用來顯示當前GDB一共管理了多少地址空間。

三,gdb 除錯多執行緒

1,多執行緒程式舉例

  1 /**************************************
2 *檔案說明:thread.c
3 *作者:段曉雪
4 *建立時間:2017年06月10日 星期六 15時24分05秒
5 *開發環境:Kali Linux/g   v6.3.0
6 ****************************************/
7 
8 #include<stdio.h>
9 #include<pthread.h>
10 
11 void* thread1(void* arg)
12 {
13     printf("i am thread1,my tid is %u\n",pthread_self());
14     return NULL;
15 }
16 
17 void* thread2(void* arg)
18 {
19     printf("i am thread2,my tid is %u\n",pthread_self());
20     return NULL;
21 }
22 
23 int main()
24 {
25     pthread_t tid1,tid2;
26     pthread_create(&tid1,NULL,thread1,NULL);//建立執行緒1
27     pthread_create(&tid2,NULL,thread2,NULL);//建立執行緒2
28 
29     pthread_join(tid1,NULL);//等待執行緒1
30     pthread_join(tid2,NULL);//等待執行緒2 
31 
32     return 0;
33 }

以上程式碼中,主執行緒main建立了兩個子執行緒分別是thread1和thread2,所以執行緒的總數為3個。

2,使用gdb對多執行緒程式進行除錯
在多執行緒程式設計時,當我們需要除錯時,有時需要控制某些執行緒停在斷點,有些執行緒繼續執行。有時需要控制執行緒的執行順序。有時需要中斷某個執行緒,切換到其他執行緒。這些都可以通過gdb實現。
GDB預設支援除錯多執行緒,跟主執行緒,子執行緒block在create thread。

gdb除錯多執行緒常用命令:

(1)info threads

顯示可以除錯的所有執行緒。gdb會為每個執行緒分配一個ID(和tid不同),編號一般從1開始。後面的ID是指這個ID。

1>在主執行緒處打斷點
這裡寫圖片描述
由於斷點在第25行,執行緒1和執行緒2還沒建立,所以可以除錯的只有一個主執行緒。

2>線上程1中打斷點
這裡寫圖片描述
斷點設定線上程1中,顯示可以除錯的執行緒有3個,正在執行的為執行緒1.

3>線上程2中打斷點
這裡寫圖片描述
斷點設定在19行(執行緒2中),由於執行緒1已經執行完畢,所以可以除錯的執行緒只有兩個,正在執行的為執行緒2。

(2)thread ID

切換當前除錯的執行緒為指定ID的執行緒。
這裡寫圖片描述

(3)其他
break FileName.cpp:LinuNum thread all:
所有執行緒都在檔案FileName.cpp的第LineNum行有斷點。

thread apply ID1 ID2 IDN command:
讓執行緒編號是ID1,ID2…等等的執行緒都執行command命令。

thread apply all command:所有執行緒都執行command命令。

set scheduler-locking off|on|step
在調式某一個執行緒時,其他執行緒是否執行。在使用step或continue命令除錯當前被除錯執行緒的時候,其他執行緒也是同時執行的,如果我們只想要被除錯的執行緒執行,而其他執行緒停止等待,那就要鎖定要除錯的執行緒,只讓他執行。

off:不鎖定任何執行緒,預設值。
on:鎖定其他執行緒,只有當前執行緒執行。

step:在step(單步)時,只有被除錯執行緒執行。

set non-stop on/off:
當調式一個執行緒時,其他執行緒是否執行。

set pagination on/off:
在使用backtrace時,在分頁時是否停止。

set target-async on/ff:
同步和非同步。同步,gdb在輸出提示符之前等待程式報告一些執行緒已經終止的資訊。而非同步的則是直接返回。

show scheduler-locking
檢視當前鎖定執行緒的模式