通常軟件開發中,很難有工程師能夠一次性寫出正確無誤的程序代碼。而程序調試以及測試步驟將會在整個軟件開發過程中占據相當大的份量。本章開始主要介紹Linux系統下C++應用程序調試工具的使用情況。重點以Linux系統下gdb調試工具使用介紹,配合實際C++應用程序調試實例作詳細講述。讓初學者在學習Linux下C++應用程序開發之前,首先掌握基本的程序調試手段。
1.調試工具說明
對于經常在Windows平臺進行應用程序開發的開發者來講,往往并不需要太過在意相應的調試工具的命令使用。因為Windows這類圖形化平臺為開發者提供了可視化開發工具。開發者只需要根據提供的圖形化調試功能選項,即可輕松完成當前應用程序的調試工作。
但是對于Unix以及Linux這類操作系統來講,則沒有那么的幸運。凡是在該平臺編輯的應用程序在程序編譯的時候,需要采用操作系統提供的調試工具來實現命令行方式的調試應用。各類商用的Unix根據不同的廠家提供的調試工具并不一致,如Unix主機常用dbx調試工具、GNU提供的gdb等。但是對于Linux操作系統,通常默認支持GNU提供的gdb調試工具。本章主要介紹在Linux系統下gdb調試工具的具體使用。
Linux系統下的gdb調試工具是GNU組織下的一個受通用公共許可證保護的自由軟件。相比于其它平臺的調試工具,gdb提供了比較完備的應用程序調試功能。在gdb工具中,可以讓開發的應用程序運行到指定的位置,可以查看指定位置的相關變量、堆棧等信息。并且該工具支持大量的計算機語言調試。本章主要以C++語言程序作為調試對象。
Linux系統下的gdb調試工具為開發者調試應用程序提供了比較完備的調試功能。在詳細了解該工具的使用之前,首先通過一些該工具的簡單操作以及相應的簡單實例的調試,幫助大家對gdb調試工具有一個初步的認識。
2.gdb工具基本操作
對于軟件開發中的程序調試,一般在程序員開發的應用程序時大致有如下幾個步驟。
1)第一類,通過編譯器編譯程序產生的錯誤稱為語法類錯誤;
2)第二類則是指應用程序編譯通過并生成可執行程序,但執行時并沒有按照要求得到正確結果的錯誤;
3)還有因為程序內部隱含的特定條件下的處理錯誤而引起程序core的現象,該類錯誤一般會產生相應的core文件便于調試。
Linux下針對第一種應用程序語法類錯誤的解決方法,是開發者根據編譯器給出的錯誤提示進行代碼修改。這類代碼往往違反編譯器語法規定。但是面對隱含性錯誤以及應用程序core的情況,則需要專業的調試工具來實現單步跟蹤調試,此時gdb正是派上用場的時候。
1)啟動gdb
[developer@localhost developer]$ gdb //在shell下執行gdb啟動程序命令
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) //以下為gdb工具啟動信息
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU GeneralPublic License, and you are
welcome to change it and/or distribute copies of itunder certain conditions.
Type "show copying" to see theconditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as"i386-redhat-linux-gnu".
(gdb)
出現上述提示,表示gdb工具程序已經啟動,隨后提供的“(gdb)”即為當前可編輯操作命令處。當前Linux平臺下如果支持相應的GNU相關工具,那么在啟動該工具命令之后,顯示該工具基本信息表明該工具啟動成功。其中展示的基本信息中主要包括gdb工具的版本,相應的版權以及相關歡迎信息等。
2)退出gdb
gdb通過quit命令來退出該工具,當前調試會話下輸入該命令,并按下回車鍵。輸出結果如下所示。
[developer@localhost developer]$ gdb //gdb工具啟動
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) //以下為gdb工具啟動信息
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU GeneralPublic License, and you are
welcome to change it and/or distribute copies of itunder certain conditions.
Type "show copying" to see theconditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as"i386-redhat-linux-gnu".
(gdb) quit //當前調試會話中執行quit命令退出gdb工具
[developer@localhost developer]$
該信息表明退出gdb調試工具,返回當前shell進程中。對于該類基本命令操作的使用還不清楚可以直接在工具啟動后的當前編輯處使用help后加相應的命令,隨即會顯示該工具操作的使用說明。
3)應用程序如何加入調試信息
對于Linux系統下的C++應用程序,通常可以分為release與debug兩個應用版本。在軟件項目沒有正式完成發布之前,通常為debug版本。該版本中加入了對應的調試信息,但是該版本的應用程序編譯器對其并沒有作任何的優化操作。該版本主要用于開發者調試程序使用。
而release版本表示軟件開發調試完畢正式交付的發布版本。該版本中編譯器針對應用程序作了相應優化,使當前程序代碼以及運行的速度上得到最大的優化,提供高應用程序運行的效率。
Linux系統下release與debug兩個版本最主要的區別在于編譯程序時加入的編譯選項。對于需要調試程序來講,通常需要使用debug版本,然后可以使用g++編譯應用程序時加上-g命令,將會加入相應的應用程序調試信息,便于開發者調試。gdb工具的相關選項命令可以通過gdb –h命令來列舉,該命令顯示結果如下。
[developer@localhost developer]$ gdb –h //當前shell下執行gdb –h命令輸出幫助菜單
This is the GNU debugger. Usage: //以下為gdb工具操作命令幫助菜單
gdb[options] [executable-file [core-file or process-id]]
gdb[options] --args executable-file [inferior-arguments ...]
Options:
--args Arguments after executable-fileare passed to inferior
--[no]async Enable(disable) asynchronous version of CLI
-bBAUDRATE Set serial port baud rateused for remote debugging.
--batch Exit afterprocessing options.
通過gdb –h命令以及進入gdb工具后的help幫助命令。初學者可以簡單的查詢gdb相關選項以及調試命令的簡要使用說明。下面小節將會通過一個簡單完整的實例程序來演示Linux下gdb調試工具的基本應用操作情況。該實例并不包含任何錯誤,目的僅僅是通過gdb調試工具了解程序執行的完整過程。
4)一個完整的簡單C++程序調試實例
通過前面兩小節對gdb調試工具基本概念以及相應操作的介紹,下面會通過一個完整的C++程序調試的實例,幫助讀者直觀地了解Linux系統下C++應用程序調試的一般過程。
a.準備實例
本完整實例主要實現獲取當前時間字符串的功能,采用系統針對時間處理的API接口來獲取所需要格式的時間字符串,實例程序完整代碼編輯如下所示。
//實例chapter0301
//chapter0301.cpp
#include <iostream>
#include <string>
using namespace std;
/*基本功能,實現獲取當前時間,將其格式化為對應的整型數表示*/
string date_string()
{
time_tclock = time(NULL); //調用時間函數,將返回結果付給時間結構體變量
structtm* stm = localtime(&clock); //調用求取本地時間函數,返回結果存放于時間結構體中
charbuffer[15]; //時間字符串緩沖區
sprintf(buffer,"%4d%02d%02d%02d%02d%02d", //格式化時間為整型數存放于相應緩沖數組中
stm->tm_year+ 1900, //當前日期:年
stm->tm_mon+ 1, //當前日期:月
stm->tm_mday, //當前日期:日
stm->tm_hour, //當前時間點:時
stm->tm_min, //當前時間點:分
stm->tm_sec); //當前時間點:秒
returnbuffer; //處理完畢返回該數組名
}
/*主函數入口*/
int main()
{
stringdate; //定義時間表示字符串
cout<<"Getcurrent time:"<<endl; //提示獲取當前時間
date= date_string(); //調用獲取當前時間函數,返回值直接存放至時間字符串
cout<<date<<endl; //打印輸出時間字符串信息
return0;
}
Linux平臺下采用g++編譯器編譯源程序,為了便于gdb工具調試程序,在命令方式編譯時需加上-g選項,用于添加調試信息。Linux系統下需要編譯的源文件為chapter0301.cpp,相關makefile工程文件編譯命令編輯如下所示。
OBJECTS=chapter0301.o #makefile變量定義,目的是靈活可替換程序中間文件
CC=g++ #makefile變量定義,目的是靈活可替換編譯命令
chapter0301: $(OBJECTS) #具體的目標程序,以及隨后的編譯條件
$(CC)$(OBJECTS) -g -o chapter0301
clean: #makefile清除當前編譯結果操作
rm -fchapter0301 core $(OBJECTS) #具體的rm刪除命令操作
submit: #makefile提交當前可執行程序、庫以及頭文件操作
cp -f -rchapter0301 ../bin #具體的提交拷貝操作,提交可執行程序以及當前目錄頭文件
cp -f -r *.h../include
在當前shell下執行make命令,生成可執行程序文件,隨后通過makesubmit命令提交程序文件至本實例bin目錄,通過cd命令定位至實例bin目錄,執行該程序文件運行結果如下所示。
[developer @localhost src]$ make //make執行編譯代碼命令
g++ -c -o chapter0301.o chapter0301.cpp //執行g++編譯命令,生成工程文件
g++ chapter0301.o -g -ochapter0301 //執行g++編譯命令,生成可執行程序
[developer @localhost src]$ makesubmit //makesubmit提交可執行程序、頭文件
cp -f -r chapter0301 ../bin //執行cp拷貝命令,將可執行程序拷貝至bin目錄
cp -f -r *.h ../include //執行cp拷貝命令,將所有頭文件拷貝至include目錄
[developer @localhost src]$ cd../bin //定位至bin目錄
[developer@localhost bin]$./chapter0301 //執行程序
Get current time:
20081212145309
上述實例程序主要由主函數與一個獲取當前時間字符串函數組成。主要功能為通過封裝獲取當前時間的函數調用,在當前屏幕中打印輸出當前時間信息。要了解程序中當前時間獲取函數主要處理流程,需要初學者首先了解系統中C++程序處理時間的一般方法以及Linux系統時間的基本概念。
b.使用gdb調試
回歸主題,本實例主要內容是采用gdb調試工具初步認識完成應用程序基本運行流程。通過gdb一系列的命令操作,來了解一個完整的C++應用程序在運行時的具體處理細節。首先,程序在編譯之初已經通過增加-g選項來添加相關的調試信息。因此,需要采用gdb工具調試該應用程序,只需要在當前shell中輸入gdb后加相應調試的可執行程序即可,該操作結果如下。
[developer@localhost bin]$ gdb chapter0301 //通過gdb命令加載調試程序
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) //以下為gdb啟動信息
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU GeneralPublic License, and you are
welcome to change it and/or distribute copies of itunder certain conditions.
Type "show copying" to see theconditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as"i386-redhat-linux-gnu"...
(gdb) //gdb調試會話命令操作處
此時通過調試工具與可執行程序的關聯,其作用類似于在該調試工具中打開了處理的文件,進入了gdb工作界面。為了了解該完整C++實例程序具體處理流程以及相應的內部變量存放的數據,下面將會通過幾個常用的gdb調試命令達到目的。基本調試過程演示如下所示。
(gdb) break main //通過break命令在程序主函數入口設置斷點
Breakpoint 1 at 0x80489f
5: file chapter0301.cpp, line 23. //顯示斷點基本信息
(gdb) break date_string //通過break命令在當前時間獲取函數處設置斷點
Breakpoint 2 at 0x8048923: file chapter0301.cpp,line 7.
(gdb) info break //通過info命令查看當前程序擁有的斷點信息
Num Type Disp Enb Address What
1 breakpoint keep y 0x080489f
5 in
main at chapter0301.cpp:23
2 breakpoint keep y 0x08048923 in
date_string() at chapter0301.cpp:7
(gdb)
調試中斷點
為了弄清上述操作步驟,需要了解程序調試中斷點的基本概念。對于斷點,初學者在此處可以直接理解為程序處理指定的停止點。即通過斷點標識的設定,讓程序在調試工具中運行到指定的位置停住。上述操作中,共設置了兩個程序斷點,分別為main主程序入口點以及date_string獲取當前時間函數入口點。
此處可以簡單的了解到,斷點可以通過break命令加指定點來設定。該指定點可以為指定位置的代碼行數,也可以為指定地點函數名稱等。設置完程序處理斷點之后,通過info break命令組合可以查看當前調試會話中斷點的基本信息情況,具體打印出來的信息說明會在后面詳細講述,此處僅僅作為演示。
當程序的斷點設置好之后,可以在調試工具中運行該可執行程序,從而運行定位到斷點處。在gdb中運行可執行程序可以使用“run”,或者使用其單個字符‘r’表示,運行流程調試如下所示。
(gdb) run //當前調試會話中run命令執行可執行程序
Starting program: /mnt/hgfs/share/worktest/linux_c++/gdb/chapter0301
Breakpoint 1, main () at chapter0301.cpp:23 //顯示程序停在主函數入口第一個斷點處信息
23 string date; //并且列出當前斷口處執行的第一行代碼
(gdb) n //通過next單步調試命令,單步執行程序
24 cout<<"Get current time:"<<endl; //程序執行至打印獲取當前時間提示信息
(gdb) n //繼續通過next命令單步執行
Get current time:
25 date = date_string(); //在提示信息打印之后,程序單步執行至時間求取方法處
(gdb) n //再次next單步調試后,程序運行至第二個斷點處
Breakpoint 2, date_string() () at chapter0301.cpp:7 //顯示第二個斷點位置,表明進入了具體求取時間方法中
7 time_t clock = time(NULL); //執行調用日歷日期方法
(gdb) n //單步next執行
8 struct tm* stm = localtime(&clock); //執行調用日歷時間轉換為本地時間的方法
(gdb) n
11 sprintf(buffer, "%4d%02d%02d%02d%02d%02d", //單步執行通過sprintf格式化時間字符串
(gdb) n
18 return buffer; //單步執行返回求取的時間字符串
(gdb) n
19 }
(gdb) n //單步執行后,跳回至主程序執行
main () at chapter0301.cpp:26 //打印程序執行位置信息
26 cout<<date<<endl; //單步執行至打印輸出時間信息
(gdb) n
20081213103557 //單步執行至輸出時間結果字符串變量值
27 return 0; //最終返回0給操作系統作處理
(gdb) n
28 }
(gdb)
上述過程在gdb工具中運行了run命令,開始運行可執行程序到第一個指定的斷點處。從演示的結果看,程序運行到第一個指定的斷點處停住,并且打印了相關的提示信息,說明了程序具體的位置,并且顯示斷點在文件中具體的行數等信息,同時還打印了進入斷點處第一個即將執行的語句。
隨后將會通過next命令來單步的執行程序,從而了解整個可執行程序的流程走向。從上述演示來看,最后代碼會在調用當前時間函數處跳轉至指定的斷點,即進入函數內部執行,通過單步調試初學者可以了解到整個程序中執行的步驟,此處僅僅是通過gdb調試工具了解應用程序處理流程。下面將會就具體的gdb工具中的調試命令作詳細介紹。