調試技巧備忘
準備工作
為了能讓程序更直觀的被調試,在編譯時應該添加一些選項
- -g: 添加調試選項
- -ggdb3: 調試宏定義
啟動方式
不帶參數
gdb ./a.out
帶參數
gdb ./a.out
set args -a -b -c any_argument_you_need
b main
run
調試core文件
gdb bin_name core_name
調試正在運行的程序
大致按如下步驟
ps axu | grep bin_name , 獲取進程id
gdb attach pid,啟動gdb
b somewhere,設置斷點
c,繼續運行程序
基本命令
括號裡是命令縮寫,詳細命令介紹見http://www.yolinux.com/TUTORIALS/GDB-Commands.html,這裡只列出常用的
命令 | 描述 |
---|---|
查看信息 | |
info break(b) | 查看斷點 |
info threads | 查看線程 |
info watchpoints | 查看觀察點 |
thread thread-number | 進入某個線程 |
刪除信息 | |
delete(d) | 刪除所有斷點,觀察點 |
delete(d) breakpoint-number delete(d) watchpoint |
刪除指定斷點,觀察點 |
調試 | |
step(s) | 進入函數 |
next(n) | 執行一行 |
until line-number | 執行到指定行 |
continue(c) | 執行到下一個斷點/觀察點 |
finish | 執行到函數完成 |
堆棧 | |
backtrace(bt) | 打印堆棧 |
frame(f) number | 查看某一幀 |
up/down | 查看上一幀/下一幀 |
thread apply all bt | 打印所有線程堆棧信息 |
源碼 | |
list(l) list function |
查看源碼,函數 |
directory(dir) directory-name | 添加源碼搜索路徑 |
查看變量 | |
print(p) variable-name | 打印變量 |
p *array-variable@length | 打印數組的前length個變量 |
p/format variable-name format和printf格式近似 d: 整數 u: 無符號整數 c: 字符 f: 浮點數 x: 十六進制 o: 八進制 t: 二進制 r: raw格式 |
按指定格式打印變量,如p/x variable-name代表以十六進制打印變量 |
x/nfu address nfu為可選的三個參數 n代表要打印的數據塊數量 f為打印的格式,和p/format中一致 u為打印的數據塊大小,有如下選擇 b/h/w/g: 單/雙/四/八字節 默認為4字節 |
按指定格式查看內存數據,如x/7xh address 表示從內存地址address開始打印7個雙字節,每個雙字節以十六進制顯示 |
ptype variable | 打印變量數據類型 |
運行和退出 | |
run(r) | 運行程序 |
quit(q) | 退出調試 |
設置 | |
set print pretty on/off | 默認off。格式化結構體的打印 |
set print element 0 | 打印完整字符串 |
set logging file log-file set logging on/off |
設置日誌文件,默認是gdb.txt 打開/關閉日誌 |
case說明
手動加載源代碼
當我們服務器上調試程序時,由於沒有加載源碼路徑而無法查看代碼,此時可以將源碼目錄拷貝到服務器上,然後在gdb調試時通過dir directory-name命令加載源碼,注意,這裡的directory-name一般是程序的makefile所在的路徑
打印調試信息到日誌文件
有時候需要對打印的信息進行查找分析,這種操作在gdb界面不太方便,可以將內容打印到日誌,然後通過shell腳本處理。先打開日誌調試開關set logging on,然後打印你需要的信息,再關閉開關set logging off,這期間打印的信息就會被寫入gdb.txt文件,如果不想寫入這個文件,可以在打開日誌開關前先設置日誌文件名set logging file log-file
可視化調試
gdb自帶TUI(Text User Interface)模式,詳細介紹見https://sourceware.org/gdb/onlinedocs/gdb/TUI.html
基本使用方式如下
- Ctrl-x a:啟動/結束TUI ,啟動TUI還可以使用win命令
- Ctrl-x o:切換激活窗口
- info win:查看窗口
- focus next / prev / src / asm / regs / split:激活指定窗口
- PgUp:在激活窗口上翻
- PgDown:在激活窗口下翻
- Up/Down/Left/Right:在激活窗口上移一行/下移一行/左移一列/右移一列
- layout next / prev:上一個/下一個窗口佈局
- layout src:只展示源碼窗口
- layout asm:只展示彙編窗口
- layout split:展示源碼和彙編窗口
- layout regs:展示寄存器窗口
- winheight name +count/-count:調整窗口高度(慎用,可能會讓屏幕凌亂)
需要注意的是,在cmd窗口上,原本Up/Down是在歷史命令中選擇上一條/下一條命令,若想使用該功能,必須先將焦點轉移到cmd窗口,即執行focus cmd
TUI的窗口一共有4種,src, cmd, asm, regs, 默認是打開src和cmd窗口,可以通過layout選擇不同的窗口佈局。最終的效果圖是這樣的
可以看到上面是代碼區(src),可以查看當前執行的代碼和斷點信息,當前執行的代碼被高亮顯示,並且在代碼最左邊有一個符號>,設置了斷點的行最左邊的符號是B,下面是命令區(cmd),可以鍵入gdb調試命令
這樣調試的時候
打印STL和boost數據結構
當我們要查看某種數據結構的變量,如果gdb不認識該數據結構,它會按照p/r variable-name的方式打印數據的原始內容,對於比較複雜的數據結構,比如map類型,我們更關心的是它存儲的元素內容,而不是它的數據結構原始內容,還好gdb7.0提供Python接口可以通過實現Python腳本打印特殊的數據結構,已經有一些開源代碼提供對boost以及STL數據結構的解析
打印STL數據結構
首先查看系統下是否有/usr/share/gdb/python/libstdcxx目錄,如果有,說明gdb已經自帶對STL數據類型的解析,如果沒有可以自己安裝,詳細介紹見https://sourceware.org/gdb/wiki/STLSupport,這裡簡單說明一下
svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python
新建~/.gdbinit,鍵入如下內容
python
import sys
sys.path.insert(0, '/home/maude/gdb_printers/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end
其中/home/maude/gdb_printers/python是你實際下載svn代碼的路徑
打印boost數據結構
souceforge上有現成的boost-gdb-printers,但根據我的試驗發現在打印unordered_map等數據結構時會報錯,因此我做了一些修改並放在github上https://github.com/handy1989/boost-gdb-printers,經測試在boost的1.55和1.58版本下均可用
下載boost-gdb-printers,找到裡面的boost-gdb-printers.py,修改boost.vx_y為實際的版本,並獲取文件絕對路徑,假設為your_dir/boost-gdb-printers.py,在~/.gdbinit裡添加
source your_dir/boost-gdb-printers.py
這時即可打印boost數據結構,我們用以下代碼做一個簡單的測試
// filename: gdb_test.cpp
#include <stdio.h>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
using std::map;
using std::string;
struct TestData
{
int x;
string y;
};
void break_here()
{
}
int main()
{
boost::shared_ptr<TestData> shared_x(new TestData());
shared_x->x = 100;
shared_x->y = "hello world";
TestData data1;
data1.x = 100;
data1.y = "first";
TestData data2;
data2.x = 200;
data2.y = "second";
boost::unordered_map<int, TestData> unordered_map_x;
unordered_map_x[1] = data1;
unordered_map_x[2] = data2;
break_here();
return 0;
}
編譯如下
g++ -g gdb_test.cpp -I/your-boost-include-dir
your-boost-include-dir替換為實際的boost頭文件所在路徑,編寫gdb腳本gdb_test.gdb如下
b break_here
r
fin
p/r shared_x
p shared_x
p/r unordered_map_x
p unordered_map_x
q
y
執行gdb ./a.out -x gdb_test.gdb,查看變量的輸出如下
$1和$3分別是shared_ptr和unordered_map數據類型的原始打印格式,$2和$4是加載boost-gdb-printers之後的打印格式