1. 程式人生 > >Linux下gdb除錯用法命令

Linux下gdb除錯用法命令

一直在Fedora平臺下寫opencv的程式,需要對程式進行除錯,主要用的除錯工具是gdb. gdb提供瞭如下功能:

  • 1.在程式中設定斷點,Debug時遇到斷點處暫停
  • 2.可以監視某個變數,並利用print函式將該變數的值打印出來
  • 3.程式可step-by-step執行
  • 4.執行時修改變數的值
  • 5.跟蹤路徑
  • 6.執行緒切換等
    下面結合OpenCV針對C\C++程式使用gdb除錯進行介紹。

1.gdb安裝

首先檢查一下電腦中是否已經安裝gdb

gdb --version

如果已經存在gdb,則會得到如下結果:
這裡寫圖片描述
如果沒有給出gdb的版本號則需要手動下載

sudo
apt-get install gdb #針對Ubuntu系統 sudo dnf install gdb #針對Fedora系統

2.gcc\g++編譯基礎

在前面我們文章《opencv之在Linux下編譯opencv程式的兩種方式g++、cmake》介紹過gcc\g++編譯的基礎內容,當需要使用gdb除錯程式碼時,需要在gcc\g++編譯選項中新增-g選項,如下所示:

g++ -g `pkg-config opencv --cflags` opencv.cpp  -o opencv `pkg-config opencv --libs`  

這樣生成的opencv可執行檔案可以直接使用gdb進行除錯
可以直接在命令列輸入gdb+程式名進入除錯,在這裡使用

《opencv學習(三十一)之影象邊緣畫素填充估計copyMakeBorder()》中的程式進行演示,其程式碼如下所示:

/*程式說明
 *當按下按鍵‘c’代表使用BORDER_CONSTANT
 *RNG生成的隨機數作為畫素值進行填充
 *當按下按鍵‘r’代表使用BORDER_REPLICATE
 *影象擴充的邊框由原影象邊緣畫素的值進行填充
 */

#include <opencv2/imgproc.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <stdlib.h> #include <stdio.h> using namespace std; using namespace cv; //宣告全域性變數 Mat srcImage, dstImage; int g_top, g_bottom, g_left, g_right; int borderType; Scalar value; String windowName = "copyMakeBorder Demo"; RNG rng(12345); int main() { int c; srcImage = imread("dog.jpg"); //判斷影象是否載入成功 if(srcImage.empty()) { cout << "影象載入失敗!" << endl; return -1; } else cout << "影象記載成功" << endl << endl; imshow("原影象", srcImage); //程式使用說明 printf("\n \t copyMakeBorder Demo: \n"); printf("\t --------------------\n"); printf("**Press 'c' to set the border to a random constant value \n"); printf("**Press 'r' to set the border to be replicated \n"); printf("**Press 'ESC' to exit the program \n"); //建立視窗 namedWindow(windowName, WINDOW_AUTOSIZE); //初始化邊框引數 g_top = (int)(0.05*srcImage.rows); g_bottom = (int)(0.05*srcImage.rows); g_left = (int)(0.05*srcImage.cols); g_right = (int)(0.05*srcImage.cols); //顯示初始影象 dstImage = srcImage; imshow(windowName, dstImage); while(true) { c = waitKey(500); if((char)c == 27) //c為ESC程式退出 {break;} else if((char)c == 'c') {borderType = BORDER_CONSTANT;} else if((char)c == 'r') {borderType = BORDER_REPLICATE;} value = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); copyMakeBorder(srcImage, dstImage, g_top, g_bottom, g_left, g_right, borderType, value); imshow(windowName, dstImage); } return 0; }

使用g++對該程式進行編譯,如下

g++ -g `pkg-config opencv --cflags` copyMakeBorder.cpp -o copyMakeBorder `pkg-config opencv --libs` 

在程式資料夾中生成名字為copyMakeBorder的可執行檔案,下面進入gdb除錯狀態,直接輸入gdb+程式名即:

gdb copyMakeBorder

這裡寫圖片描述

3.gdb除錯基本命令

進入gdb後可直接在(gdb)後輸入相應命令進行除錯操作。

3.1 list命令

list命令可以所寫為l,可以列出所除錯程式的程式碼,其居具體用法包括:

  • list+lineNumber(中間有空格)
    可以打印出行第lineNumber行周圍的源程式,如:
(gdb)list 45

其列印結果如下:
這裡寫圖片描述

  • list 列印函式名稱為Function的函式上下文的源程式,如下
(gdb) list main

結果如下:
這裡寫圖片描述

  • list 輸出當前行後面的程式碼,如
(gdb) list

這裡寫圖片描述

  • list -顯示當前行前面的程式碼
(gdb) list -

這裡寫圖片描述

針對前面介紹的四種情況,在執行完一個命令後不輸入任何命令,gdb會預設執行上一個命令

3.2 run命令

在gdb中執行程式使用run命令,在程式執行前可以設定程式的工作遠景如:

  • 程式執行引數

    可以通過set args執行執行時的引數,針對的是main函式有如下書寫格式時的程式

int main(int argc, char** argv)
{
    ...
}
- 程式執行環境
path < dir >可設定程式的執行路徑;how paths可檢視程式的執行路徑;set environment varname [=value]用於設定環境變數;show environment [varname]用於檢視環境變數。
![這裡寫圖片描述](https://img-blog.csdn.net/20170215204054254?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva2VpdGhfYmI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
  • 工作目錄
    cd < dir >相當於在終端中輸入的cd命令;pwd命令用於顯示當前所在目錄
    這裡寫圖片描述
  • 輸入輸出
    info terminal用於顯示程式用到的終端模式;gdb中也可以使用重定向控制程式輸出如:run>outfile; tty命令可以指定輸入輸出裝置終端,如:tty /dev/ttyS1.

3.3 break命令

在使用gdb除錯時使用break命令來設定斷點,有如下幾種方法:

  • break < function >
    在進入指定的函式function時既停止執行,C++中可以使用class::function或function(type, type)格式來指定函式名稱
  • break < lineNumber>
    在指定的程式碼行打斷點
  • break +offset/break -offset
    在當前行的前面或後面的offset行打斷點,offset為自然數
  • break filename:lineNumber
    在名稱為filename的檔案中的第lineNumber行打斷點
  • break filename:function
    在名稱為filename的檔案中的function函式入口處打斷點
  • break *address
    在程式執行的記憶體地址處打斷點
  • break
    在下一條命令處停止執行
  • break … if < condition>
    在處理某些迴圈體中可使用此方法進行除錯,其中…可以是上述的break lineNumber、break +offset/break -offset中的引數,其中condition表示條件,在條件成立時程式即停止執行,如設定break if i=100表示當i為100時程式停止執行。
    檢視斷點時,也可以使用info命令如info breakpoints [n]、info break [n]其中n 表示斷點號來檢視斷點資訊。
    如下:
    這裡寫圖片描述

可以通過delete命令刪除所有的斷點,如下:
這裡寫圖片描述

3.4 逐步除錯

使用gdb工具除錯可以使用next命令單步執行程式程式碼,next的單步不會進入函式的內部,與next對應的step命令則在單步執行一個函式時進入函式內部,類似於VC++中的step into.其用法如下

  • next < count>
    單步跟蹤,如果有函式呼叫不會進入函式,如果後面不加count表示一條一條的執行,加count表示執行後面的count條指令,
(gdb) next 5

這裡寫圖片描述
- step < count>
單步跟蹤,如果有函式呼叫則進入該函式(進入該函式前提是此函式編譯有Debug資訊),與next類似,其不加count表示一條一條執行,加上count表示自當前行開始執行count條程式碼指令
- set step-mode
set step-mode on用於開啟step-mode模式,這樣在進行單步跟蹤時,程式不會因為沒有debug資訊而不停止執行,這很有利於檢視機器碼,可以通過set step-mode off關閉step-mode模式
- finish
執行程式直到當前函式完成並列印函式返回時的堆疊地址和返回值及引數值等資訊。
- until
執行程式直到退出迴圈體
- stepi(縮寫si)和nexti(縮寫ni)
stepi和nexti用於單步跟蹤一條及其指令,一條程式程式碼有可能由數條機器指令完成,stepi和nexi可以單步執行機器指令。

3.5 continue命令

當程式遇到斷點停止執行後可以使用continue命令恢復程式的執行到下一個斷點或直到程式結束,其具體用法如下:

  • continue [ignore-count]
    continue也可縮寫為c,fg命令與continue命令相同,故上述continue可以使用c或fg代替,如下:
    這裡寫圖片描述

3.6 print命令

print可以縮寫為p,可以通過print命令檢視引數或程式執行資料

(gdb) break 53
Breakpoint 1 at 0x80490a7: file copyMakeBorder.cpp, line 53.
(gdb) break 54
Breakpoint 2 at 0x80490f3: file copyMakeBorder.cpp, line 54.
(gdb) break 55
Breakpoint 3 at 0x8049129: file copyMakeBorder.cpp, line 55.
(gdb) break 56
Breakpoint 4 at 0x804915f: file copyMakeBorder.cpp, line 56.
(gdb) r
Starting program: /home/keith/program/opencv/opencv學習(三十一)之影象邊框/copyMakeBorder 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/libthread_db.so.1".
[New Thread 0xb543fb40 (LWP 11484)]
影象記載成功

[New Thread 0xb470db40 (LWP 11485)]
[New Thread 0xb3dffb40 (LWP 11486)]

     copyMakeBorder Demo: 
     --------------------
**Press 'c' to set the border to a random constant value 
**Press 'r' to set the border to be replicated 
**Press 'ESC' to exit the program 

Thread 1 "copyMakeBorder" hit Breakpoint 1, main () at copyMakeBorder.cpp:53
53      g_top = (int)(0.05*srcImage.rows);
(gdb) continue
Continuing.

Thread 1 "copyMakeBorder" hit Breakpoint 2, main () at copyMakeBorder.cpp:54
54      g_bottom = (int)(0.05*srcImage.rows);
(gdb) p g_top
$1 = 20
(gdb) p g_bottom
$2 = 0
(gdb) continue 
Continuing.

Thread 1 "copyMakeBorder" hit Breakpoint 3, main () at copyMakeBorder.cpp:55
55      g_left = (int)(0.05*srcImage.cols);
(gdb) p g_bottom 
$3 = 20
(gdb) 

部分截圖如下:
這裡寫圖片描述
值得注意的是print輸出可以指定輸出格式

  • x按16進位制格式顯示變數
  • d按十進位制顯示變數
  • u按十六進位制格式顯示無符號整形
  • o按八進位制格式顯示變數
  • t按二進位制格式顯示變數
  • c按字元格式顯示變數
  • f按浮點數格式顯示變數
    可以使用display命令設定一些自動顯示的變數,當程式暫停執行或單步跟蹤時,這些變數會自動顯示
    這裡寫圖片描述
    如果要修改變數的值也可以使用print命令如
print g_top = 24

使用print檢視程式執行時的資料時,每一個print都會被gdb記錄下來並且以 (美元符號)1、(美元符號)2…這樣的方式為每一個print命令編號,我們可以使用這個編號來訪問以前的表示式如:
這裡寫圖片描述

3.7 watch命令

watch命令一般來觀察某個表示式(變數也可視為一種表示式)的值是否發生了變化,如果由變化則程式立即停止執行,其具體用法如下:

  • watch < expr>
    為表示式(變數)expr設定一個觀察點一旦其數值由變化,程式立即停止執行
  • rwatch < expr>
    當表示式expr被讀時,程式立即停止執行
  • awatch < expr>
    當表示式expr的值被讀或被寫時程式立即停止執行
  • info watchpoints
    列出當前所設定的所有觀察點

3.8 return命令

如果在函式中設定了除錯斷點,在斷點後還有語句沒有執行完,這個時候我們可以使用return命令強制函式忽略還沒有執行的語句並返回。可以直接使用return命令用於取消當前函式的執行並立即返回函式值,也可以指定表示式如 return < expression>那麼該表示式的值會被作為函式的返回值。

3.9 info命令

info命令可以用來在除錯時檢視暫存器、斷點、觀察點和訊號等資訊。其用法如下:

  • info registers:檢視除了浮點暫存器以外的暫存器
  • info all-registers: 檢視所有的暫存器包括浮點暫存器
  • info registers < registersName>:檢視指定暫存器
  • info break: 檢視所有斷點資訊
  • info watchpoints: 檢視當前設定的所有觀察點
  • info signals info handle: 檢視有哪些訊號正在被gdb檢測
  • info line: 檢視原始碼在記憶體中的地址
  • info threads: 可以檢視多執行緒

3.10 run和quit命令

run(縮寫r)和quit(縮寫q)分別可以開始執行程式和退出gdb除錯

3.11 其他命令

  • whatis或ptype顯示變數的型別
  • bt顯示函式呼叫路徑