簡單:printf
在合理關(guān)鍵的路徑位置添加打印信息是簡單,有時也是有效的方法。如果程序的問題總是在固定的代碼位置復(fù)現(xiàn),那么通過在路徑上添加打印信息就是快速的定位問題出現(xiàn)在哪一代碼行的方法。除了直接使用printf函數(shù)外,可以借鑒日志文件的記錄格式來獲取更多的信息,下面是我自己常用來替代日志記錄的宏封裝,可以指定打印信息的級別以便于分析和篩選:
printf是輸出到標(biāo)準(zhǔn)輸出或者終端的,需要人為地實時進(jìn)行跟蹤或者保存,否則就會失去這些信息。對于中大型的項目來說,大量地使用printf來進(jìn)行跟蹤和調(diào)試是不現(xiàn)實的,也不可能用眼睛去掃描那不斷刷新的打印屏幕。程序運(yùn)行過程的日志文件記錄是實用規(guī)范的用來跟蹤程序運(yùn)行路徑的方法。
實現(xiàn)一套日志文件記錄接口沒有什么難度,簡單的只需要將上面宏中的printf改成fprintf就可以,因此在嵌入式開發(fā)中,往往不同的項目常常會實現(xiàn)自己的日志記錄接口。這只是一種輕量的重復(fù)勞動,倒問題不大,除了不利于程序的移植外。此外日志記錄還是有不少細(xì)節(jié)需要考慮的,比如:循環(huán)覆蓋寫、不同線程或進(jìn)程的同步寫等問題,有時實現(xiàn)很容易會疏漏。這里推薦一個用C語言實現(xiàn)開源日志庫:zlog,它提供了線程安全的日志記錄接口和獨立的日志接口行為配置文件,因此在不修改項目代碼的情況下,你可以通過修改外部的zlog日志格式和行為配置文件來改變?nèi)罩居涗浀母袷胶褪欠裼涗浀饺罩疚募小?/span>
獲取線程ID和名稱
在多線程的程序中,獲取線程的ID有現(xiàn)成的接口,但是獲取線程的名稱沒有。在Linux2.6及更低版本的linux中,線程是使用clone系統(tǒng)調(diào)用創(chuàng)建進(jìn)程來實現(xiàn)的,此時pthread的線程ID和線程本身的進(jìn)程ID是不一致的。這里提供一個參考的實現(xiàn)來獲取線程ID和名稱:
在linux上常用的調(diào)試工具就是gdb,它也是給力的調(diào)試?yán)鳌R虼?a href="http://www.jkendeljohnson.com/yqnews" target="_blank">嵌入式開發(fā)中遇到棘手的問題是,首先想到的就是gdb工具。使用gdb進(jìn)行嵌入式程序的調(diào)試有三種途徑:
交叉編譯gdb
如果開發(fā)板上的資源(內(nèi)存和flash)充足的話,可以考慮下載gdb的源代碼直接交叉編譯得到可在開發(fā)板上運(yùn)行的gdb工具,然后在編譯嵌入式程序時加-g參數(shù)以保留調(diào)試符號信息,就可以直接在開發(fā)板上進(jìn)行g(shù)db的調(diào)試了。
gdbserver遠(yuǎn)程調(diào)試
交叉編譯出的gdb一般會比較大,我這邊使用arm-none-linux-gnueabi工具鏈編出的stripped后的都有3.3M。考慮到資源情況就需要使用gdbserver遠(yuǎn)程調(diào)試的方法,詳細(xì)的步驟參考前文:使用gdbserver遠(yuǎn)程調(diào)試。這里使用時的說明幾個注意點:
gdb和gdbserver的版本必須是一致的,PC上運(yùn)行的應(yīng)該是編譯工具鏈提供的gdb。
PC上加載的程序和庫應(yīng)該是debug版本的,即not stripped的,以提供符號和代碼信息。
PC上使用的庫的版本應(yīng)該和開發(fā)板上使用的庫版本一致,否則會出現(xiàn)庫不匹配的warning。
PC上使用gdb時首先應(yīng)該設(shè)置好相應(yīng)的環(huán)境變量及信號處理方式,以有效控制程序行為。
PC上可以通過寫gdb的配置文件來實現(xiàn)環(huán)境和信號處理方式的設(shè)置,使用示例,下面是ak-gdbinit配置文件:
像我在項目中遇到的問題:程序運(yùn)行10幾個小時或者幾天后偶爾才出現(xiàn)段錯誤,這讓我使用gdbserver時很郁悶,開著gdb+gdbserver等了兩天都不出現(xiàn)段錯誤,而一不小心把gdb給關(guān)了就前功盡棄了。這種場景就是使用gdb+core文件調(diào)試的選擇了。
首先你得確定你的問題會不會生成core文件,SIGSEGV信號產(chǎn)生時,linux系統(tǒng)會生成core文件,所以我可以使用這種方法來調(diào)試。參考《APUE》P235可以知道還有以下信號的默認(rèn)動作是終止+core:
SIGABRT
SIGBUS
SIGEMT
SIGFPE
SIGILL
SIGIOT
SIGQUIT
SIGSEGV
SIGSYS
SIGTRAP
SIGXCPU
SIGXFSZ
然后就是設(shè)置保存core文件的環(huán)境和使用,參考之前的:Linux系統(tǒng)中core文件調(diào)試方法。
gdb加載core文件時如果出現(xiàn)下面的警告:
使用gdb調(diào)試時,對于多線程,以下的一些gdb命令是比較有用的: