文章目錄
一、介紹
GDB是GNU開源組織發佈的一個強大的UNIX下的程序調試工具
注意:利用gdb調試,需要在gcc編譯過程中加上-g選項,這樣編譯生成的可執行文件纔可以利用gdb進行源碼調試。-g 選項的作用是在可執行文件中加入源代碼的信息,比如可執行文件中第幾條機器指令對應源代碼的第幾行,但並不是把整個源文件嵌入到可執行文件中,所以在調試時必須保證gdb能找到源文件。如果把當前的gdb.c改名爲g.c或者將gdb.c移動到其他地方,則gdb無法進行調試。
二、簡單操作
1)啓動gdb
gdb hello
或者
gdb
file hello
2)顯示程序
list 或者 l
3)打斷點
break(b) main() //函數的入口處
break(b) 11 //行號
break(b) hello.c:14 //特定文件的行號
break(b) 12 if i=10 //if語句滿足後在行號處打斷點
4)獲取斷點信息
info break
5)刪除斷點
delete 13(斷點編號)
6)運行程序
run(r)
7)單步(不進入子函數)
next(n)
8)單步(進入子函數)
step(s)
9)繼續運行
continue(c)
10)查看變量
print(p) i(變量名)
11)運行程序到當前函數結束
finish
12)監控變量
watch i(變量名)
13)退出gdb
quit(q)
三、用gdb調試多進程程序
如果一個進程通過fork系統調用創建了子進程,gdb會繼續調試原來的進程,子進程則正常運行。
那麼如何調試子進程呢?
1、單獨調試子進程
子進程本質也是一個進程,因此也可通過gdb來調試,首先找到目標子進程的PID,再將其附加(attach)到
gdb調試器上,具體操作如下:
$ps -ef|grep 進程名 //找到待調試進程的PID
$gdb
(gdb) attach "PID"
2、使用調試器選項follow-fork-mode
gdb調試器的選項follow-fork-mode允許我們
選擇程序在執行fork系統調用後是繼續調試父進程還是調試子進程。
用法:
(gdb)set follow-fork-mode m
m 可以選parent 或 child, 分別表示調試父進程和子進程
舉例:
$gdb XX
(gdb) set follow-fork-mode child
(gdb) b process.h:264
//打斷點
四、用gdb調試多線程程序
gdb有一組命令可輔助多線程程序的調試。
info threads
顯示當前可調試的所有線程。gdb會爲每個線程分配一個ID,我們可以使用這個ID來操作對應的線程。
ID前面有“*”的線程是當前被調試的線程。
thread ID
調試目標ID指定的線程。
查看所有線程的棧信息:thread apply all bt
調試多線程程序時,默認除了被調試的線程在執行外,其他線程也在繼續執行,
但有的時候我們希望只讓被調試的線程運行。這可以通過這個命令來實現。
set scheduler-locking [off|on|step]
該命令設置sceduler-locking的值:
- off表示不鎖定任何線程,即所有線程都可以繼續執行,這是默認值。
- on表示只有當前被調試的線程會繼續執行。
- step表示在單步執行的時候,只有當前線程會執行。
五、用gdb工具分析core文件
在Unix系統下,應用程序崩潰,一般會產生core文件,如何根據core文件查找問題的所在,
並做相應的分析和調試,是非常重要的。
許多程序出錯時都會產生一個Core 文件,通過工具分析這個文件,
我們可以定位到程序異常退出時對應的堆棧調用等信息,找出問題所在並進行及時解決。
段錯誤,就是大名鼎鼎的Segmentation Fault,這通常是由指針錯誤引起的。
簡而言之,產生段錯誤就是訪問了錯誤的內存段,一般是你沒有權限,
或者根本就不存在對應的物理內存,尤其常見的是訪問0 地址。
在編程中有以下幾種做法容易導致段錯誤,基本都是錯誤地使用指針引起的:
- 訪問系統數據區,尤其是往系統保護的內存地址寫數據,最常見的就是給指針以0地址
- 內存越界(數據越界、變量類型不一致等)訪問到不屬於你的內存區域
程序在運行過程中如果出現段錯誤,那麼就會收到SIGSEGV 信號,
SIGSEGV 默認handler 的動作是打印“段錯誤”的出錯信息,併產生Core 文件。
1、core文件開關
ulimit -c 查看core開關,如果爲0表示關閉,不會生成core文件;
使用 ulimit -c [filesize]
設置core文件大小,當最小設置爲4之後纔會生成core文件;
使用 ulimit -c unlimited 設置core文件大小爲不限制,這是常用的做法;
如果需要開機就執行,則需要將這句命令寫到 /etc/profile 等文件。
core文件路徑一般在可執行文件同一目錄
2、core文件命名和保存路徑
core文件有默認的名稱和路徑,但爲了方便,我們通常會自己命名和指定保存路徑;
可以通過 /proc/sys/kernel/core_pattern 設置 core 文件名和保存路徑,方法如下:
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
命名的參數列表:
- %p - insert pid into filename 添加pid
- %u - insert current uid into filename 添加當前uid
- %g - insert current gid into filename 添加當前gid
- %s - insert signal that caused the coredump into the filename 添加導致產生core的信號
- %t - insert UNIX time that the coredump occurred into filename 添加core文件生成時的unix時間
- %h - insert hostname where the coredump happened into filename 添加主機名
- %e - insert coredumping executable name into filename 添加命令名。
3、分析Core 文件
設置Core文件大小,運行程序生成Core文件
執行ulimit -c unlimited表示不限制生成的Core 文件的大小,
4、運行這個有bug 的程序,可以在當前目錄下生成core文件
執行 gdb [exec file] [core file]啓動gdb後,
調用gdb的where或bt命令可以查看當時的調用棧信息,
進入某個棧:f N,f是frame的縮寫,N是棧號,如0、1、2、3…
進入到某個棧後,才能通過p命令查看這個棧的臨時變量,否則只能查看全局變量。
六、調試一個正在運行的程序
使用gdb -p PID命令,PID即程序的pid。
需要注意的是,gdb調試正在運行的程序會導致程序掛起,因此請記住不要gdb調試正在運行的在線服務。
七、設置斷點的方式
有很多種,最常見的有兩種:一是設置程序運行到源代碼的某一行,二是設置程序運行到某個函數。
設置程序運行到某一行,通過“文件名:行號”的形式:
b test.cpp:100
設置程序運行到某個函數,通過“名字空間::函數名”的形式:
b namespace_a::func
八、斷點操作
查看斷點:info b
刪除斷點:d N,d是delete的縮寫,N是斷點的編號,可以通過info b查看。
無論哪種方式設置斷點,都要執行c命令(continue),讓程序繼續運行。
九、在調試程序時,gdb常用命令總結
常用:n、s、p
- n即next,單步執行,執行下一步的意思,遇到函數會調用函數。
- s即step,也是單步執行,但是會進入函數內部,然後結合n命令來調試函數。
- p即print,打印變量,最常用的命令。p可以打印普通變量、std::string字符串、指針、數組等。gdb打印字符串支持c_str()、length()等
std::string str;
p str,p str.c_str()查看字符串內容,p str.length(),查看字符串長度
有時會遇到字符串太長不能顯示全,最後顯示"…",可以通過命令取消長度限制:
set print elements 0
這樣就能打印完整的字符串。
命令 | 描述 |
---|---|
backtrace(或bt) | 查看各級函數調用及參數 |
finish | 連續運行到當前函數返回爲止,然後停下來等待命令 |
frame(或f) | 幀編號 選擇棧幀 |
info(或i) | locals 查看當前棧幀局部變量的值 |
list(或l) | 列出源代碼,接着上次的位置往下列,每次列10行 |
list 行號 | 列出從第幾行開始的源代碼 |
list 函數名 | 列出某個函數的源代碼 |
next(或n) | 執行下一行語句 |
print(或p) | 打印表達式的值,通過表達式可以修改變量的值或者調用函數 |
quit(或q) | 退出gdb調試環境 |
set var | 修改變量的值 |
start | 開始執行程序,停在main函數第一行語句前面等待命令 |
step(或s) | 執行下一行語句,如果有函數調用則進入到函數中 |
最後說一下gdb中如何打印STL的vector和map,gdb默認不支持STL,
需要從網上下載一個txt文件,然後將其內容追加到.gdbinit文件中,就可以使用pvector命令查看vector容器數據。