C++@sublime GDB調試

來源:互聯網
上載者:User

標籤:

本文轉自:http://www.cppblog.com/lucency/archive/2012/08/09/59214.html

之前在網上搜尋了好久使用sublime調試C和C++的文章,但是徒勞無功;後來才醒悟sublime下的調試C/C++其實和命令列中偵錯工具是一樣的,所以即使是裝了sublime GDB後,一樣是要依據gdb手冊那種調試步驟的。

本文:

這篇文章基本上是摘自gdb手冊,除此之外就是加了實際的代碼範例,這樣可以更清楚的看到一些命令的執行效果。當然,這兒不會涉及到所有的gdb命令,而只是一些常用的。

我這兒使用的gdb的版本是6.8。不過因為只是一些常用命令,因此在老版本上應該也沒問題。作業系統是Windows XP。

一.概述
二.gdb的起停
三.gdb命令
命令文法
命令補全
擷取協助
四.樣本1
斷點
觀察點
捕捉點
刪除斷點
禁用斷點
斷點條件
指定位置
資料
五 樣本2
六 後記
一.概述

調試器的目的在於讓你查看程式運行時的內部狀態;或者在程式崩潰的時候,查看程式異常的原因。它們可以在一下幾個方面提供協助:

1)啟動程式,按照你的意願影響程式的行為

2)讓程式在特定條件發生時停止運行

3)當程式停止的時候,檢查程式正在做什麼

4)改變程式的某些東西,這樣就可以運行時修正bug,然後繼續測試其他的問題。

gdb支援多種語言的調試,不過最成熟的應該就是c/c++了。這兒也以c++程式為例來說明。如果想知道其他語言的支援情況,可以看gdb手冊。

二.gdb的起停

gdb最常用的啟動方式就是:

gdb program

或者在程式異常終止的時候這樣:

gdb program core

也或者,你可以attach到一個已經啟動並執行進程來調試它,就像這樣

gdb program PID

當然,也可以在命令列設定gdb的啟動參數,例如args:

gdb —args gcc -O2 -c foo.c

這樣就可以調試gcc,同時將參數"-O2 -c foo.c"傳給gcc。

可以用gdb -help查看gdb的所有選項。

要想中止gdb的運行,可以輸入quit或者q,也可以按ctrl-d按鍵組合。

三.gdb命令命令文法

gdb命令的形式為:command [arg1...argn]。每個命令單獨一行。行的長度沒有限制(當然,一般應該也不會有多長)。

許多gdb的命令都會有縮寫的形式,例如break可以縮寫為b。

如果直接按斷行符號,gdb會執行剛剛被執行命令。

如果命令中有#字元,那麼從#開始的字元都會被當成是注釋。這個一般用在命令檔案中。後面會有介紹。

命令補全

gdb具有補全功能。功能鍵是<TAB>。例如break,在輸入bre之後按<TAB> 鍵,gdb就會補全為break。如果只輸入b,然後按<TAB>,gdb會響一聲,這說明有多個以b開始的命令。這種情況下再按一 次<TAB>,gdb就會把所有以b開始的命令輸出出來。下面是此時的螢幕:

如果只是想看一下以某個(些)字元開始的命令,可以按<META>?,而不用按兩次<TAB>。在沒 有<META>鍵的電腦上,可以用<ESC>鍵代替。這個命令按起來有點麻煩,比按兩次<TAB>要麻煩多了,如果 不怕<TAB>鍵被按壞,我建議你還是按<TAB>吧。

命令補全可以用於gdb命令,gdb子命令,程式中的符號名(例如函數名等)。

在調試c++程式時,基本上肯定會遇到的問題就是重載函數。例如,在設定斷點的時候,假設有兩個名為overload的函數,這時為了區分到底是那 個函數,就需要加上函數參數類型(函數名加上參數列表作為邏輯上的一個詞)。為了使gdb能將參數列表兩邊的括弧作為這個詞的一部分,需要用單引號‘將函 數整個的括起來。看:

擷取協助

啟動gdb後,可以輸入命令help得到gdb的命令列表。注意的是這時輸出的每個條目都是一類命令。上個圖: 

得到命令類別後,可以用命令 help class得到此類別的所有命令。: 

上面的圖中只顯示了部分breakpoints命令。

其他跟協助相關的命令有:

a)help command

用於顯示命令command的協助資訊。

b)apropos args

這裡的args可以使Regex。顯示所有匹配args的命令的簡要說明。

c)complete args

顯示所有以args開始的命令。

另外還有兩個很有用的命令: info 和 show 。

a)info

用於顯示被偵錯工具的資訊。例如傳遞到當前函數的參數-info args;或者查看當前寄存器的值-info registers;也可以查看斷點-info breakpoints。可以用help info查看info的說明。

b)show

這個命令用於顯示gdb的資訊。也就是gdb的一些屬性值。可以用help show查看協助資訊。這個命令通常應該是配合set用的(用於設定gdb的屬性)。

好了,現在對gdb應該有個大概的認識了,下面我們就要拿幾個小例子來驗證一些常用命令的效果。Let‘s go!

四.樣本1

首先說明一點,要想高效的使用gdb的功能,需要在編譯器的時候要加上-g選項,這個選項會把調試資訊加到可執行檔中。

下面說一下樣本檔案,包括三個:gdb.h, gdb.cpp, test.cpp

gdb.h

#ifndef _GDB_H
#define _GDB_H 1

class gdb
{
public:
explicit gdb(int v);
void overload(int one);
void overload(int one, int two);
void catch_ex(int ex); //exception
void loop();
private:
int value;
int array[10];
};

#endif

gdb.cpp

#include "gdb.h"
#include <iostream>
using namespace std;

gdb::gdb(int v)
{
value = v;
for(int i=0; i<10; i++)
{
array[i] = i;
}
}

void gdb::overload(int one)
{
cout<<"function overload with one parameter: "<<one<<endl;
}

void gdb::overload(int one, int two)
{
cout<<"function overload with two paremeters: "<<one<<" "<<two<<endl;
}

void gdb::loop()
{
int loop_array[10];
for(int i=0; i<10; i++)
{
loop_array[i] = i;
}

int v=3;
v=3;
v=4;
}

void gdb::catch_ex(int ex)
{
int e = ex;
try
{
if(e <= 0)
{
throw e;
}
else
{
cout<<"function catch_ex: "<<ex<<endl;
}
}
catch(int x)
{
cout<<"exception: "<<x<<endl;
}
}

test.cpp

#include "gdb.h"

int main()
{
gdb g(5);
g.overload(1);
g.overload(1, 2);
g.loop();

g.catch_ex(3);
g.catch_ex(-1);

return 0;
}

好了,現在開始調試。首先啟動gdb,指定要調試的可執行檔。前面已經說過了,可以簡單地使用gdb program來啟動。或者可以首先啟動gdb,然後用file命令指定要調試的檔案。下面是僅啟動gdb後的畫面: 

現在用file命令指定要調試的檔案:

然後就可以用 run 或者 r 命令來運行程式。在運行之前,你可能需要為程式設定一些資訊,這些資訊有一下四種:

1)程式參數

可以用set args命令設定程式的參數。設定完後可以用show args查看設定的是否正確。如果set args後面不帶任何參數,則向程式傳遞的參數為空白。

2)環境

這兒的環境就是在系統/使用者設定檔中設定的環境變數,像HOME, PATH之類的.GDB提供了在調試的時候改變這些變數值的方式,這樣當需要的時候就不用退出gdb來重新設定.GDB提供的命令有:

a) path directory -- 將 directory 加到環境變數PATH前面. 注意 對PATH的改變只對調試的程式有效, GDB使用的PATH不會有改變.1

b) show paths -- 顯示PATH的值。

c) show environment [varname] -- 顯示環境變數varname的值,如果不指定varname,則顯示所有環境變數的值。

d) set environment varname [= value] -- 設定環境變數varname的值為value。這個改變只是對調試的程式生效。如果不提供value,則將varname的值置為空白。

e) unset environment varname -- 從環境中移除傳遞給程式的變數varname。

3)工作目錄

在啟動gdb偵錯工具的時候,被調試的程式會從gdb繼承工作目錄。當然gdb也提供了命令來修改工作目錄:

a) cd directory -- 將 directory 設為新的工作目錄。

b) pwd -- 顯示當前工作目錄。

4)標準輸入輸出

還沒找到在windows裡面這個東西有啥用,現在也沒有linux可用,不好多說。有需要的自己看gdb手冊吧。我簡單抄一下手冊吧。

在gdb中,可以將run命令的輸入輸出重新導向到檔案或者其他終端。也可以通過tty命令設定被偵錯工具輸入輸出的裝置。命令格式是:

tty terminal 或者 set inferior-tty terminal.

tty 就是 set inferior-tty 的別名。

咚咚咚咚,下面正式開始!

上面我們已經啟動了程式, 也知道了如何運行程式。可是如果你直接執行run命令會發現,程式直接運行結束了。如果你想在某一行或者某個函數調用的地方,或者當某個變數/運算式的值改變的時候,也或者在某些事件發生的時候--例如拋出異常、載入動態庫,或者建立子進程--的時候停止程式運行,那應該怎麼辦呢?

有了gdb,一切就都好辦了:), 利用下面這三個強大的武器,你可以任意的停止程式。小心了,大家小心了,偶要祭出這三件寶物了,它們是:

斷點

斷點就是指定一個位置,使得程式運行到這個位置的時候會停下來(當然,還可以設定條件斷點,當運行到指定位置時,只有滿足了設定的條件,程式才會停下來),這樣便於觀察程式的內部狀態。斷點相關的命令主要有:

a)break location

在指定位置 location 處設定斷點,這裡的 location 可以是函數名,行號,指令地址等(關於如何指定 location ,可以看這裡)。

b)break

如果不指定任何參數,break會在選定的棧幀的下一條指令處設定斷點。

c)break ... if cond

設定條件斷點。每次到達斷點的時候都會對錶達式 cond 求值,只有當結果為非0的時候程式才會在這個斷點停下來。

d)tbreak args

設定一個只生效一次的斷點。args跟break命令裡的參數意義相同(也就是說,可以為location,為空白,或者條件)。

e)hbreak args

設定硬體斷點。

f)thbreak args

設定只生效一次的硬體斷點。

g)rbreak regex

在所有匹配Regex regex 的函數上設定斷點。

h)info breakpoints [n]

i)info break [n]

j)info watchpoints [n]

上面三個命令都是列出當前的斷點、觀察點和捕捉點,如果指定參數n,則僅列出第n個的資訊。

來實驗一把吧。首先用gdb啟動程式,假設我們想在test.cpp的 g.overload(1) 這一行添加一個斷點,那就執行命令:b test.cpp:6,執行完後:

這時可以用info break等命令查看斷點資訊:

然後我們運行程式,看看有什麼效果。

看到了吧,程式停在了斷點所在的行。這時可以用where或者frame查看當前的棧幀資訊,也可以用 print 查看一些變數或者運算式的資訊,或者info args查看參數資訊,等等。

其他的命令大家可以自己嘗試一下。

有時候我們並不確定要在哪裡加斷點,例如當我們想在某個變數被改變或者被讀、被寫的時候讓程式停下來,可能由於訪問變數的地方比較多,要想每個地方都加上斷點比較麻煩,而且很可能有遺漏,這時候我們就需要依賴另一個強大的命令了,也就是觀察點。

觀察點

觀察點是一類特殊的斷點,如果針對某個變數或者運算式指定一個觀察點,那麼當它們的值被讀/寫的時候,gdb會停止程式的執行。你不需要像設定斷點時那樣明確指定這個觀察點在程式中的位置。觀察點相關的命令有:

a)watch expr [thread threadnum]

對 expr 設定一個觀察點。當 expr 的值被改變的時候,gdb會停止程式的運行。

如果指定了線程參數thread threadnum ,則 只有 線上程 threadnum 改變 expr 的值時,程式才會停止。

b)rwatch expr [thread threadnum]

對 expr 設定一個讀觀察點。當程式讀 expr 的值時,gdb會停止程式的運行。

c)awatch expr [thread threadnum]

對 expr 設定一個訪問觀察點。當程式讀或者寫 expr 時,gdb會停止程式的運行。

d)info watchpoints

顯示所有的斷點、觀察點、捕捉點。跟info break 相同。

下面再來看看觀察點的使用。

首先我們設定一個斷點在g.loop()這一行,然後運行到這裡。step進入loop()函數。這一串命令的執行如下:

這時我們已經進入loop()函數,現在我們設定幾個觀察點。設定命令序列和設定完後的效果如小:

可以看到有5個停止點,其中前兩個是我們設定的斷點,後面三個是觀察點。分別為watch, rwatch 和 awatch。另外還有一個地方就是,x86上預設是硬體觀察點。

好了,來運行一下試試。

到達第一個觀察點的時候程式停止運行,同時列印出了變數的舊值和新值,以及觀察點的位置。

下面我們接著運行,看看遇到後面的觀察點的時候又會怎樣。

程式在第三個觀察點停了下來(第二個觀察點為讀觀察點),同樣列印出了變數的新舊值和觀察點的位置。第二個觀察點由於是讀觀察點,而程式中沒有讀這個變數的地方,因此啟動並執行時候被跳過了。為了看一下讀觀察點的效果,我們再設定一個讀觀察點:

i是迴圈內的迭代器,它會被複製給loop_array[i]變數,也就是會被讀。可以看到,程式會停止運行,輸出i的值。注意:每次對i的讀操作都會使得程式停止。下面是兩次執行continue命令後的輸出:

觀察點的內容差不多就這些了。另外有個需要注意的地方就是: watch命令設定的觀察點只有在變數或者運算式的值被改變得時候才會使得程式停止運行,如果只是被寫,但是值沒有改變,則程式不會停止。

上面已經講了斷點、觀察點,而對於某些情況,這兩種停止點並不是最有效方式。例如想在c++程式中跑出異常的時候停 止程式,這時候用斷點就不夠有效了,因為程式中可能好多異常處理的地方,如果一個個設定斷點,那就太麻煩了(當然如果只處理某幾個異常,用斷點也無不可, 甚至用起來更靈活);而觀察點就更不可用了。

這種情況就需要用到捕捉點了。

捕捉點

捕捉點也是一類特殊的斷點,它可以使得程式在某種事件發生時停止運行,例如c++異常,或者載入動態庫、建立子進程等。設定捕捉點的命令是catch.

catch event

其中 event 可以是:

a)throw

c++拋出異常。

b)catch

c++捕捉異常。

c)exception

Ada異常。

d)exception unhandled

程式中未處理的異常。

e)assert

失敗的Ada斷言。

f)exec

對exec的調用(只在HP-UX和GNU/Linux中可用)。

g)fork

對fork的調用(只在HP-UX和GNU/Linux中可用)。

h)vfork

對vfork的調用(只在HP-UX和GNU/Linux中可用)。

i)load

動態載入共用庫(只在HP-UX中可用)。

j)load libname

動態載入共用庫 libname (只在HP-UX中可用)。

k)unload

卸載已載入的共用庫(只在HP-UX中可用)。

l)unload libname

卸載已載入的共用庫 libname (只在HP-UX中可用)。

還有一個設定只生效一次的捕捉點的命令是: tcatch event 。

下面再看一下捕捉點的使用。

首先在vtest.cpp的 g.catch_ex(-1); 設定一個斷點,然後運行,進入此函數。

現在我們設定一個捕捉點。繼續運行:

可以看到程式在拋出異常的地方停止了。

刪除斷點

當斷點不再需要了,那就應該刪除掉,否則每次執行到斷點的位置程式都要停下來,會把人逼瘋的。刪除斷點的命令有兩個:clear和delete。

a)clear [ location ]

如果不指定 location ,則刪除選擇的棧幀中下一條要執行的指令上的任何斷點。如果選擇的是最內部的棧幀(也就是當前正執行的函數的棧幀),則clear會將剛剛使程式停止的斷點被刪除。 關於 location 的說明可以看這裡。

b)delete [breakpoints] [ range ... ]

刪除指定範圍 range 那的所有的斷點、觀察點、捕捉點。如果不指定參數 range ,則會刪除所有的停止點。這裡的 range 指定的是斷點編號區間。可以用info break查看斷點資訊。

禁用斷點

如果不想刪除斷點,只是想暫時使它失效,則可以使用disable命令。disable命令的形式如下:

a)disable [breakpoints] [ range ...]

使指定區間 range 內的斷點失效。如果不指定 range ,則所有的斷點都失效。

使斷點生效的命令是enable,形式有:

a)enable [breakpoints] [ range ...]

使指定區間 range 內的斷點或者所有斷點生效。

b)enable [breakpoints] once range...

使指定區間內的斷點生效一次。

c)enable [breakpoints] delete range...

使指定區間內的斷點生效一次,然後刪除。

斷點條件

斷點條件使得只有在相應的條件滿足時,斷點才有效。這裡的條件運算式跟程式所用語言的邏輯運算式的文法相同,例如在c/c++語言裡,可以用 a==b 或者 x&&y這種運算式。

斷點條件可以在設定斷點的時候指定,也可以在斷點設定後通過condition命令來設定或者改變。 condition的形式為:

a)condition bnum expression

設定運算式 expression 為停止點 bnum 的條件。

b)condition bnum

刪除停止點 bnum 的條件。

還有一個命令,可以使得gdb忽略斷點的條件一定的次數,其形式為:

a)ingore bnum count

指定位置

許多gdb命令都接受一個用於指定程式位置的參數。位置的指定方式有下面幾種:

a) linenum

當前源檔案的行號。

b) -offset

當前行前面,跟當前行間隔為 offset 的行。當前行可以這樣確定:使用list命令,列印出來的最後一行就是當前行;或者對於斷點命令,選定的棧幀中,程式停止執行的位置就是當前行。

c) +offset

當前行後面,跟當前行間隔為 offset 的行。

d) filename:linenum

源檔案 filename 中的行 linenum

e) function

當前源檔案中的函數 function

f) filename:function

源檔案 filename 中的函數 function

g) * address

指定程式地址 address。常用的 address 形式有:

expression -- 當前語言中有效運算式。

funcaddr -- 函數的地址。在c/c++中就是函數名。

‘filename‘::funcaddr -- 源檔案 filename 中的函數地址 funcaddr

後面懶得寫了,暫時先放一放。發現就算是抄文檔,內容多了也是個很累人的活。唉,懶了,不行了...

待添加

資料

待添加

五 樣本2

待添加

六 後記

這篇文章只是撿了GDB中最常用的一些東西,而且還只是最常用的東西中的一小部分,有興趣或者需要的可以直接看GDB的文檔,可以在這裡找到。

1. 不知道是不是因為windows和linux系統的不同,用gdb啟動程式後,執行show paths後輸出:Executables and object file path: 。也就是說輸出的值是空的。

[email protected] GDB調試

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.