一,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 ,調試代碼
/**************************************
*文件說明:process.c
****************************************/
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t pid = fork();//創建子進程
if(pid == -1)
{
perror("fork error");
return -1;
}
else if(pid == 0)//child
{
printf("i am a child:my pid is %d,my father is %d\n",getpid(),getppid());
}
else//father
{
printf("i am a father:my pid is %d\n",getpid());
wait(NULL);//等待子進程
}
return 0;
}
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位置
編譯(需要加上-g選項):
gcc -o process prcess.c -g
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,多線程程序舉例
/**************************************
*文件說明:thread.c
*
*
*開發環境:Ubuntu
****************************************/
#include<stdio.h>
#include<pthread.h>
void* thread1(void* arg)
{
printf("i am thread1,my tid is %u\n",pthread_self());
return NULL;
}
void* thread2(void* arg)
{
printf("i am thread2,my tid is %u\n",pthread_self());
return NULL;
}
int main()
{
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,thread1,NULL);//創建線程1
pthread_create(&tid2,NULL,thread2,NULL);//創建線程2
pthread_join(tid1,NULL);//等待線程1
pthread_join(tid2,NULL);//等待線程2
return 0;
}
以上代碼中,主線程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:
查看當前鎖定線程的模式