跳到主要內容

gdb 初步心得

對初學者來說, 最好有個針對常用情境的簡單指南, 之後有閒再看落落長的教學。這裡列一下最近常用的功能, 之後再慢慢更新。

前置動作

  • gcc/g++ 編譯時要加 -g
  • 要觀察用到的函式庫, 則要裝 x-dbg 版 (如 libjpeg62 -> libjpeg62-dbg), gdb 會優先載入 debug 版函式庫。並且需要用 directory 載入原始碼, 詳情見《追踪 glibc 裡的程式》的說明。
  • 若原始碼的位置和當初編 binary 時的位置不同 (常有的事), 可用 set substitute-path from to 做路徑字串取代。
  • 可用 objdump --source FILE 確認是否真的有編到 -g。有的話可以在輸出裡看到程式碼。

雖說通篇我都寫 gdb, 但是 cgdb 好用許多, 推薦使用。聽 command 說 vimgdb 更好用, 不過要 patch vim 後才能用, 就 ... 先備忘吧。

執行方式

從頭執行

  • bash> gdb --args PROGRAM PROGRAM-ARG1 ...
  • gdb> start # 進入 main 後停下來

檢查掛點原因 (參考《產生 core dump 的方法》確保有 core dump)

  • bash> gdb PROGRAM CORE
  • gdb> bt 20 # 看掛掉的 call stack 最底層 20 個 function call

通常載入 PROGRAM 讀 debug symbol 較花時間, 我習慣用 gdb PROGRAM 進 gdb 後, 再用 core CORE 指令看不同的 core dump。

若希望從頭重來, 有設好中斷點就用 r, 沒有則繼續用 start, 不用離開 gdb, 可簡省載入 PROG 的時間。

設中斷點

  • b LOC # 設中斷點, 或用 cgdb 直接在程式視窗按空白鍵
  • i b # 列出全部中斷點
  • d NUM # 移除編號 NUM 的中斷點
  • save breakpoints FILE # 存下目前設的中斷點到檔案 FILE
  • so FILE # 載入之前設的中斷點
  • dis 1-10: 暫停使用 breakpoints 1-10。
  • ena 1-10: 恢復使用 breakpoints 1-10。

關於 LOC: 見《Specify Location - Debugging with GDB》。我比較常用 filename:linenum、linenum 或 +N。因為不易對到 C++ 的函式名稱, 所以我都用行號。

我還沒有適當情境試 conditional break, 但應該很有用, 備忘。

移動

  • n # 跳下行
  • s # 若有函式, 跳進去; 反之則同 n
  • fin # 執行到函式結尾, 返回上一層
  • until LOC # 執行到 LOC 再停, 我以前都傻傻的先設中斷再按 c ...
  • c # 執行到下個中斷點
  • 跳過下一行程式 (ref.), 記得要設中斷點才行
    • b +1
    • j +1
  • ret # 不執行函式剩下的程式, 直接返回上層 frame
  • ps. 按 Enter 可重覆上個指令, 在移動指令時和切 stack frame (後述) 時特別好用

在 call stack 之間移動

  • up # 往上移一個
  • do # 往下移一個
  • f N # 跳到 stack frame N

觀察值

  • p EXPRESSION # 印出 EXPRESSION 的值, 可是變數、函式等
  • 印出 smart pointer 的值 (ref.): 得先取出裡面的 pointer 再取出它的 member function / field
  • whatis VAR # 看型別
  • ptype VAR # 看型別的宣告內容, 了解有那些欄位可讀
  • p P@N # 印出位置 P 開始 N 個變數的值
  • p *P@N # 印出對位置 P 開始 N 個變數取值後的值
  • p *argv@argc # 在 main() 函式裡執行這指令, 會印出命令列參數內容

thread

其它

  • set var X = ... # 執行期間改變變數 X, 以在執行期驗證小修改是否有效, 簡省編譯時間
  • handle SIGSTOP nostop noprint # 收到 SIGSTOP 時不要停且不要輸出訊息, 在 Android 環境有時 gdb 會莫明地一直收到 SIGSTOP

參考資料

進階指令

  • commands BREAKPOINT_ID: 定義在進入 breakpoint 後, 執行一系列指令, 比方 "p some_var; c"。
  • define NEW_CMD: 類似函式, 定義由一堆指令組成的新指令。

留言

這個網誌中的熱門文章

(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫

以使用 LevelDB 為例。 抓好並編好相關檔案,編譯方式見第三方函式庫附的說明:$ ls include/ # header files leveldb/ $ ls out-shared/libleveldb.so* # shared library out-shared/libleveldb.so@ out-shared/libleveldb.so.1@ out-shared/libleveldb.so.1.20* 下面的例子用 clang++ 編譯,這裡用到的參數和 g++ 一樣。 問題一:找不到 header$ clang++ sample.cpp sample.cpp:5:10: fatal error: 'leveldb/db.h' file not found #include "leveldb/db.h" ^ 1 error generated. 解法:用 -I 指定 header 位置 問題二:找不到 shared library$ clang++ sample.cpp -I include/ /tmp/sample-2e7dd8.o: In function `main': sample.cpp:(.text+0x1e): undefined reference to `leveldb::Options::Options()' sample.cpp:(.text+0x6f): undefined reference to `leveldb::DB::Open(leveldb::Options const&, std::string const&, leveldb::DB**)' sample.cpp:(.text+0x10c): undefined reference to `leveldb::Status::ToString() const' sample.cpp:(.text+0x7d0): undefined reference to `leveldb::Status::ToString() const' clang: error: linker command failed with exit code 1 (u…

熟悉系統工具好處多多

記一下以前很困擾, 現在秒殺的小事。 更新這篇的時候, 忘了函式庫用的 man page 裝在那個 package。以前就會想辦法 google, 運氣好一下會找到, 運氣不好會多找一會兒。 這回我想到新作法:$ strace -e open man 3 printf > /dev/null # 發現是讀 /usr/share/man/man3/printf.3.gz $ dpkg --search /usr/share/man/man3/printf.3.gz # 找到套件名稱 manpages-dev $ aptitude show manpages-dev # 確認描述符合, 收工

virtualbox 使用 USB 裝置

2012-12-16 更新 現在 (4.x 版) 似乎無需做任何設定, 只要有裝 Oracle VM VirtualBox Extension Pack, 在 VirtualBox 視窗右下角按 USB 的圖示, 再點目標裝置, 即可加入或移除該裝置 同一時間只有 host 或 guest 可擁有該裝置, 所以從 guest OS 移除, 相當於接回 host OS 目前 VirtualBox 只支援 USB 2.0 的插槽, 若偵測不到時, 注意一下是否為這個問題 有時拔拔插插, VirtualBox 會進入奇怪的狀態, 接上去 guest OS 無法連接且跳出 device is busy 的錯誤訊息。試看看拔除該裝置, 重開 guest OS (續上則) 若重開 guest OS 無效, 並且 host OS 已移除該裝置, VirtualBox 的 USB 清單卻仍顯示 "captured", 試看看拔除該裝置, 重開 host OS原文網路上搜一下, 比較多是 Ubuntu 當 host 的解法, 我的情況是 Win7 當 host, Ubuntu 當 guest。 這兩篇說明很詳細《Learn How to Set Up USB and Networking Options in VirtualBox》《幻影千瞳的部落格: VirtualBox 使用筆記(二):使用 USB 裝置》 現在的版本圖形介面很好用了, 不用像第二篇說的那樣用指令操作。這裡記下我的操作步驟: 關掉 guest OS 在 VirtualBox 選單, 選擇 guest OS -> Settings -> USB -> Enable USB 2.0 會出現訊息框, 說明要安裝 Oracle VM VirtualBox Extension Pack。下載後安裝它 host OS 插入 USB 隨身碟 在 VirtualBox 選單, 選擇 guest OS -> Settings -> USB, 點右邊有綠色 "+" 的 USB 頭的圖示, 選擇該 USB 隨身碟, 加入它的 filter 從 host OS 移除 USB 隨身碟 開啟 guest OS 插入 USB 隨身碟, 於是 guest OS 會自動偵測…