gdb和dbx

類UNIX下C/C++開發,代碼調試比較麻煩,最原始的加跟蹤調試很土,也很費時,特別是一個龐大的項目,代碼行數非常大的時候調試起來非常費勁,當core dump時定位也不容易,這裏介紹幾個常用工具: gdb、dbx調試工具,valgrind內存檢查工具(Linux) 。

gdb(GNU DeBugger)是GNU的調試器,一般和gcc配搭使用。要使用GDB進行調試,編譯程序時要指定-g或-ggdb的編譯選項。如: gcc –g main.c 

gdb指令:f(file):指定可執行文件,l(list)列出源文件,r(run)運行可執行文件,可帶執行參數,b(break)設置斷點,c(continue)繼續被中斷程序執行,直到下一個斷點或者程序結束,p(print)輸出變量的值,如p aa;n(next)單步執行,s(step)程序執行到斷點時中斷執行,可以用s指令進行單步執行進入某一函數。q(quit)退出。

當在gdb運行時想運行shell命令,不必退出,執行shell切換到shell模式,執行shell命令。

GDB有能力在你調試程序的時候處理任何一種信號,你可以告訴GDB需要處理哪一種信號。你可以要求GDB收到你所指定的信號時,馬上停住正在運行的程序,以供你進行調試。你可以用GDB的handle命令來完成這一功能。handle <signal> <keywords...>

在GDB中定義一個信號處理。信號<signal>可以以SIG開頭或不以SIG開頭,可以用定義一個要處理信號的範圍(如:SIGIO-SIGKILL,表示處理從SIGIO信號到SIGKILL的信號,其中包括SIGIO,SIGIOT,SIGKILL三個信號),也可以使用關鍵字all來標明要處理所有的信號。一旦被調試的程序接收到信號,運行程序馬上會被GDB停住,以供調試。其<keywords>可以是以下幾種關鍵字的一個或多個。 nostop 不停止運行,只輸出顯示信號,stop:停住程序,print顯示一條信息,noprint當被調試的程序收到信號時,GDB不會告訴你收到信號的信息,noignore當被調試的程序收到信號時,GDB不處理信號。這表示,GDB會把這個信號交給被調試程序會處理。nopass,ignore當被調試的程序收到信號時,GDB不會讓被調試程序來處理這個信號。

查看core文件:運行gdb 執行文件 core文件 來加載core文件,使用where來查看coredump位置。如果系統未產生core文件,可使用ulimit -c 2048命令,後運行執行文件產生。

多進程調試:在我們的測試程序在父進程fork後,子進程調用sleep睡了60秒。這就是關鍵,這個sleep本來是不該存在於子進程代碼中的,而是爲了使用GDB調試後加入的,爲什麼要讓子進程剛剛運行就開始sleep呢?因爲我們要在子進程睡眠期間,利用shell命令獲取其process id,然後再利用gdb調試外部進程的方法attach到該process id上,調試該進程。

動態庫調試,運行執行程序加載動態庫來調試。

/***********************************************************************/

dbx(SUN studio)調試工具:在利用 dbx 對程序進行源代碼級調試前,必須使用 -g 選項

啓動:dbx program_name加載要調試的程序,調試JAVA程序dbx program_name.class|.jar,可以指定進程ID來連接正在運行的進程進行調試。

也可以在dbx運行時加載調試程序:(dbx)debug program_name

運行調試程序:run [運行參數]

查看core文件:可以dbx program_name core,或者dbx - core,再使用where命令顯示棧跟蹤,找到崩潰位置

設置斷點:stop in 函數;stop at file:line ; 也可以先通過file file-name設置當前文件,list function查看源文件,確定要設置斷點的行數。在C++中可以將關鍵字(inmember、inclass、infunction 或 inobject)在類,成員函數,對象中設置斷點。stop change variable可設置當變量改變時停止。條件停止斷點:stop cond condition。另外在動態庫中可以在動態共享庫的函數中以在程序啓動時在鏈接的庫中設置斷點的同樣方式來設置斷點。

列出所有斷點:status

刪除斷點:delete 斷點號

單步執行:step、next,step 命令步入函數,而 next 命令步過函數。step up將一直執行,直至當前函數將控制返回調用它的函數爲止。

繼續執行:cont

查看調用棧:where 調用棧代表那些已被調用但尚未返回各自調用程序的所有當前活動例程。在棧中,函數及其參數按調用的順序進行存放。棧跟蹤顯示程序流執行的停止位置以及執行如何到達此點。它提供程序狀態的簡明描述。

檢查變量:print 變量名

退出:quit

連接已運行的進程:attach pid

分離已連接的進程:detach pid

查找符號:whereis symbol 打印指定符號所有具體值的列表

要打印輸出標識符的聲明whatis

對子進程使用運行時檢查,可通過attach 子進程ID連接到子進程進行調試。

要顯示當前進程的所有線程信息:執行thread,切換到指定線程:thread thr_id

信號處理:

cont -sig signal將信號轉發到進程;catch顯示當前正在捕獲的信號列表;ignore顯示程序忽略信號列表

shell方式切換:sh

------------------------------dbx調試跟蹤的常用子命令

dbx是UNIX下基於命令行界面的程序調試器。 dbx是通過交互執行dbx子命令來達到調試的目的的。在調試程序前,必須先將-g選項包含在編譯信息中,編譯生成帶調試信息的文件,即:cc -o filename -g file.c。 進入dbx通常只需輸入"dbx filename"即可,filename爲待調試的可執行程序名。dbx加載後就顯示提示符:(dbx),此時用戶就可以輸入dbx子命令進行調試了。

語法:
dbx [ -a ProcessID ] [ -c CommandFile ] [ -d NestingDepth ] [ -I Directory ] [ -E DebugEnvironment ] [ -p oldpath=newpath:...| pathfile ] [ -k ] [ -u ] [ -F ] [ -r ] [ -x ] [ ObjectFile [ CoreFile ] ]

-a ProcessID
將調試程序和正在運行的進程連接起來。要連接調試程序,您需要擁有對該進程使用 kill 命令的權限。使用 ps 命令來決定進程的 ID。如果您獲得許可,dbx 程序中斷該進程,決定對象文件的完整名稱,讀入符號信息和提示輸入命令。

-c CommandFile
讀入標準輸入之前,在文件中運行 dbx 子命令。$HOME 目錄中指定的文件將首先被處理;然後處理當前目錄中的文件。當前目錄中的命令文件將會覆蓋 $HOME 目錄中的命令文件。如果指定的文件既不存在於 $HOME 目錄也不存在於當前目錄,將會顯示警告消息。source 子命令可以在 dbx 程序運行後使用。

-d NestingDepth
設置程序塊的嵌套限制。缺省的嵌套深度限制爲 25。

-E DebugEnvironment
指定調試程序的環境變量。

-p oldpath=newpath:...| pathfile
以 oldpath=newpath 的格式在檢查覈心文件時指定替換的庫的路徑。oldpath 指定了將被替換的值(存儲在覈心文件中)而 newpath 指定了將要替換的新的值。這些可能是全部或者部分的,絕對路徑或者相對路徑。可以指定若干個替換,它們之間用冒號隔開。相反 -p 標誌可以指定文件名,映射以前讀入的描述格式。每行中只允許有一個映射從文件中讀出。

-F 可以用來關閉緩慢讀入模式並使 dbx 命令在啓動時就讀入全部符號。缺省情況下,緩慢讀入模式是打開的:它在 dbx 會話初始化時讀入需要的符號表信息。。在該模式下,dbx 將不會讀入那些符號信息尚未被讀入的變量和類型。因此,諸如 whereis i 等命令並不列出在所有函數中的變量 i 的全部實例。

-I Directory
(大寫 i)將 Directory 變量指定的目錄包含到搜索源文件目錄列表中。搜索源文件的缺省目錄爲:

文件編譯時該源文件所在的目錄。只有編譯器設置了對象中的源路徑時才能搜索目錄。
當前目錄。
當前程序所在的目錄。
-k 映射內存地址;這對於內核調試是非常有用的。

-r 立即運行對象文件。如果它成功結束,dbx 調試程序將會退出。否則,將會進入調試程序並報告中斷的原因。

注意:除非指定了 -r,dbx 命令將會提示用戶並等待命令輸入。
-u 讓 dbx 命令爲文件名加上 @ (at 符號) 前綴。該標誌減少符號名混淆的可能性。

-x 防止 dbx 命令跳過來自於 FORTRAN 源代碼的 _(下劃線)字符。該符號允許 dbx 在符號之間區別哪些除了下劃線以外都是相同的,例如 xxx 和xxx_。

下面是一些dbx的常用子命令:


1.基本操作命令
run arg1 arg2 ... :以arg1,arg2,...爲參數開始運行現有進程。
r:用上次使用的參數再次運行現有進程。
source filename:從文件名爲filename的文件中讀入dbx子命令並執行。
return:執行完目前的進程後返回。
sh command:不退出dbx,執行一條操作系統shell命令。
sh:暫時進入shell狀態。
quit:退出dbx,若程序未執行完則終止其執行。


2.置斷點與跟蹤點命令
stop var at n:置斷點命令,當第n行的變量var發生變化時將程序掛起。
stop var in proc:置斷點命令,當過程proc的變量var發生變化時將程序掛起。
stop at n:置斷點命令,當執行到第n行時將程序掛起。
stop in proc:置斷點命令,當執行到過程proc時將程序掛起。
trace var at n:置跟蹤點命令,當第n行的變量var改變時顯示跟蹤信息。
trace var in proc:置跟蹤點命令,當過程proc的變量var改變時顯示跟蹤信息。
trace n:置跟蹤點命令,當執行到第n行時顯示被跟蹤信息。
trace proc:置跟蹤點命令,當執行到過程proc時顯示被跟蹤信息。
trace expr at n:置跟蹤點命令,當執行到第n行時顯示var的值。
delete n|all:刪除第n行的/所有的斷點與跟蹤點。


3.調試命令
cont at n:運行直至第n行。
print var:打印變量var的值。
printf "string",expr,...:以C語言的格式打印。
where:打印當前調試狀態,包括當前進程的信息。
func:查看當前運行的進程名。
func proc:移至到調用proc進程的母進程處。
whatis name:顯示對變量名或過程名name的類型。
step [n]:單步執行一行或n行,遇到線程調用時進入線程調用。
next [n]:單步執行一行或n行,遇到線程調用時拒絕進入線程調用。
skip [n]:跳過一個或n個斷點,繼續往下執行
dump [proc] [>;file]:顯示當前或proc過程的所有變量名及其值
assign var=expr:給變量var賦以表達式expr的值


4.讀取被調試程序命令
list:列出從當前行開始的若干行源程序。
list n,m:列出從第n行到第m行的源程序。
/string:朝文件尾方向查找字符串string
?string:朝文件頭方向查找字符串string
file filename:將查看的文件切換到文件filename處。

(dbx)run——運行可執行二進制文件
(dbx)list——列出代碼
(dbx)next——跳轉到下一步執行
(dbx)print <variable name>——打印輸出值
(dbx)stop at <filename: line n>——設置斷點
(dbx)stop at <funcname :line n>
(dbx)cont——繼續執行
(dbx)cont at <line n>——繼續執行指定行
(dbx)call——調用func()
(dbx)delete <breakpoint number>——刪除斷點
(dbx)quit——退出執行

Example1:

$cc -g -o test test.c
$dbx test
(dbx)stop at main

Example2:

$ cc -g looper.c -o looper

$ ps -u UserIDPID     TTY      TIME    COMMAND
68      console   0:04    sh
467     lft3     10:48    looper$ dbx -a 467attached in main at line 5
5     for (i = 0; i < 10;);
(dbx)更多命令參考:http://publib16.boulder.ibm.com/doc_link/zh_CN/a_doc_lib/cmds/aixcmds2/dbx.htm

Linux gdb調試

GDB是如今最廣爲人知的著名的自由和開放源碼軟件之一。它被大量GNU軟件項目以及衆多與GNU沒有關聯但卻希望能有一個高質量調試器的第三方軟件所使用。事實上,許多第三方工具合併gdb並將其作爲它們的調試功能的基礎,即便它們在gdb之上建立了各級圖形化抽象。你很可能已遇到過GDB——但可能根本沒有意識到這一點。

GDB建立在任何調試器都有兩個組成部分這一基本概念之上。首先,GDB的底層處理單獨進程或線程的啓動和關閉、跟蹤代碼執行以及在運行代碼中插入和刪除斷點。GDB支持大量不同的平臺和機制以在各種架構上實現這些(看似簡單的)操作。其具體的功能可能會受底層硬件功能的影響而偶爾有所變動。

$ gcc -o hello -g hello.c

$ gdb ./hello

(gdb) help

(gdb) break main (或者 b main)

(gdb) run

(gdb) next (或者 next i,不進入函數內部)

(gdb) step (或者 step i,進入函數內部)

(gdb) print argv[1]

(gdb) bt (或者backtrace, 查看程序運行到當前位置之前所有的堆棧幀情況)

/********************************************************************************/

valgrind是Linux(x86)環境的內存調試工具,可以在此工具中運行程序來檢查內存使用,可以自動檢測到內存泄漏及內存管理的BUG,使你的程序更加健壯。

1。valgrind安裝:下載地址:www.valgrind.org,安裝很簡單,執行configure,make,make install

2。valgrind工具集:

memcheck:檢測程序中的內存管理BUG,所有的寫/讀操作都會被、malloc/free都會被截獲

cachegrind:cache剖析器,它模擬 CPU中的一級緩存I1,D1和L2二級緩存,能夠精確地指出程序中 cache的丟失和命中。

Callgrind:同cachegrind一樣,但能跟蹤函數的調用返回關係,及有選擇的模擬cache。

Massif:堆棧分析器,它能檢測程序在堆棧中使用了多少內存,以及堆塊,堆管理塊和棧的大小。幫助我們減少程序對內存的使用。

Helgrind:檢測使用POSIX pthread多線程同步問題。

3。valgrind命令格式:

valgrind [valgrind-options] your-prog [your-prog-options]

valgrind-options選項:

--tool=<name> [default=memcheck]選擇工具

-h --help 幫助

--version 顯示版本

-q --quiet 只顯示錯誤

-v --verbose 更多顯示

-d 顯示valgrind自身調試信息

--trace-children=<yes|no> [default: no]跟蹤子進程通過exec系統調用,缺省是no

--child-silent-after-fork=<yes|no> [default: no] ??

--track-fds=<yes|no> [default: no]退出時顯示所有打開的文件描述符列表

--time-stamp=<yes|no> [default: no] 運行加時間戳

--log-fd=<number> [default: 2, stderr]輸出LOG到描述符文件

--log-file=<filename>指定日誌文件

--log-socket=<ip-address:port-number>日誌通過SOCKET輸出到指定IP端口

與錯誤相關的選項:

--xml=<yes|no> [default: no]將信息以xml格式輸出,只有memcheck可用

--num-callers=<number> [default: 12]缺省valgrind顯示12級函數調用幫助識別程序定位

--error-limit=<yes|no> [default: yes]如果錯誤總數超過1千萬,或不同的錯誤超過1000,則停止顯示新錯誤

--error-exitcode=<number> [default: 0]指定在運行時檢查到錯誤時返回的錯誤碼

--show-below-main=<yes|no> [default: no]缺省不顯示棧跟蹤報告在main下層的函數錯誤

--db-attach=<yes|no> [default: no]設置爲yes時valgrind會暫停提示是否轉入調試工具

--db-command=<command> [default: gdb -nw %f %p]指定調試工具缺省爲gdb

--leak-check=<no|summary|yes|full> [default: summary]內存檢測時,給出詳細的內存泄漏信息

--show-reachable=<yes|no> [default: no]內存檢測時顯示不可達指針

其它選項不很常用。有些也不很理解,這裏就省略了。

在檢測前,確認使用-g選項編譯你的程序,這樣以便能報告錯誤的行數

4。valgrind錯誤報告例子:

memcheck

非法的讀寫訪問,數組越界

Invalid read of size 4 at 0x40F6BBCC:

   (within /usr/lib/libpng.so.2.1.0.9) by 0x40F6B804:

   (within /usr/lib/libpng.so.2.1.0.9) by 0x40B07FF4: read_png_image(QImageIO *)

   (kernel/qpngio. by 0x40AC751B: QImageIO::read()

   (kernel/qimage.cpp:3621) Address 0xBFFFF0E0 is not stack’d, malloc’d or free’d

使用未初始化指針

Conditional jump or move depends on uninitialised value(s) at 0x402DFA94:

      _IO_vfprintf (_itoa.h:49) by 0x402E8476:

       _IO_printf (printf.c:36) by 0x8048472: main (tests/manuel1.c:8)

非法釋放

Invalid free() at 0x4004FFDF: free (vg_clientmalloc.c:577) by 0x80484C7:

       main (tests/doublefree.c:10) Address 0x3807F7B4 is 0 bytes

      inside a block of size 177 free’d at 0x4004FFDF: free (vg_clientmalloc.c:577) by 0x80484C7:

      main (tests/doublefree.c:10)

不適當的釋放

Mismatched free() / delete / delete [] at 0x40043249: free (vg_clientfuncs.c:171) by 0x4102BB4E:

   QGArray::~QGArray(void) (tools/qgarray.cpp:149) by 0x4C261C41:

    PptDoc::~PptDoc(void) (include/qmemarray.h:60) by 0x4C261F0E:

    PptXml::~PptXml(void) (pptxml.cc:44) Address 0x4BB292A8 is 0 bytes

    inside a block of size 64 alloc’d at 0x4004318C:

    operator new[](unsigned int) (vg_clientfuncs.c:152) by 0x4C21BC15: KLaola::

    readSBStream(int) const (klaola.cc:314) by 0x4C21C155: KLaola::

    stream(KLaola::OLENode const *) (klaola.cc:416) by 0x4C21788F:

     OLEFilter::convert(QCString const &) (olefilter.cc:272)

源與目的塊重疊

==27492== Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)

==27492== at 0x40026CDC: memcpy (mc_replace_strmem.c:71)

==27492== by 0x804865A: main (overlap.c:40)

缺省輸出報告文件名:工具名.out.pid

Helgrind線程BUG檢測工具:能夠報告一些線程使用的常見問題。比如:

釋放一個無效mutex、釋放未加鎖的mutex、釋放一個被其它線程持有的mutex、銷燬一個無效的或者加鎖的mutex、遞歸加鎖一個非遞歸鎖、釋放內存包含加鎖的mutex、必須處理使用pthread函數失敗返回錯誤碼、當線程退出時仍持有着鎖,調用pthread_cond_wait時使用未申請的mutex或一個被其它線程加鎖的mutex

錯誤報告如下:

Thread #1 unlocked a not-locked lock at 0x7FEFFFA90 at 0x4C2408D: pthread_mutex_unlock (hg_intercepts.c:492) by 0x40073A: nearly_main (tc09_bad_unlock.c:27) by 0x40079B: main (tc09_bad_unlock.c:50) Lock at 0x7FEFFFA90 was first observed at 0x4C25D01: pthread_mutex_init (hg_intercepts.c:326) by 0x40071F: nearly_main (tc09_bad_unlock.c:23) by 0x40079B: main (tc09_bad_unlock.c:50)

比較常用的是memcheck、Helgrind工具。經常使用valgrind能使你的程序更加完美。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章