程序調試階段:
測試:找出程序的錯誤或缺陷
固化:讓程序錯誤可重現
定位:確定相關代碼行
糾正:修改代碼 修正錯誤
驗證:確定修改解決了問題
1 gcc -Wall -pedantic -ansi //gcc 編譯 產生編譯的警告信息
1取樣法:在程序中添加printf等輸出程序執行過程中的信息,程序錯誤修復后需要刪除
1 #ifdef DEBUG 2 printf("….\n"); 3 #endif
定義調試級別,輸出不同類型的內容
1 #define BASIC_DEBUG 1 2 #define EXTRA_DEBUG 2 3 #define SUPER_DEBUG 4 4 #if (DEBUG & EXTRA_DEBUG) 5 printf... 6 #endif
C語言預處理器定義的一些宏可以幫助我們調試(符號前后各有兩個下划線)
無需編譯的調試技巧 定義全局變量debug 用戶在調用程序執行時使用 -d 調試選項,決定是否打開調試模式
將調試信息存儲於文件,可以方便自身或用戶自行調試代碼,查找問題
1 if (debug) { 2 sprintf(msg, ...) 3 write_debug(msg) 4 }
程序的受控執行
商業版本常見的調試器有adb、sdb、idebug、dbx等 能用那些調試器取決於UNIX系統
GNU使用調試器gdb,一些gdb的前端程序提供非常友好的界面,xxfdb,KDbg,ddd等
-g 選項是對程序進行調試性編譯的常用選項,需要在編譯每個文件時都加上這個選項,對鏈接器也要加上-g選項。(編譯器會把這個標志自動傳遞給鏈接器)
調試信息會使可執行程序的長度成倍增加(最多10倍),雖然可執行程序的大小增加,但使用內存的數量和原來是一樣的。
調試完后才能后,可以不經過編譯將可執行文件中的調試信息刪除
1 strip <file>
使用gdb進行調試
2019年12月3日
10:30
開始調試
1 //進入調試器 2 gdb ./xxx //xxx為可執行文件 此時進入gdb軟件 help可查看gdb提供的命令選項 3 //運行 4 run [option] // [option]將作為參數傳遞給程序 在程序執行錯誤,gdb將在出錯位置退出 若編譯時使用了 -g選項 5 //則在程序停止后輸出程序終止的位置
棧跟蹤
在到達錯誤位置時,輸入backtrance 簡寫 bt 或 where 輸出調用出錯函數的函數和出錯函數的位置
檢查變量
print 可以給出變量和其他表達式的內容,並將表達式的值賦給偽變量 $<number>
最后一次操作的結果總是以$開頭,而倒數第二次的結果為$$
列出程序源代碼
list
設置斷點
1 break 21 //在21行處打斷點 打斷點之后可以用print 輸出當前關注的變量值 2 cont //程序繼續執行到下一個斷點處 3 display //程序每次停在斷點位置時,自動打印關心的變量值 4 command //指定程序在到達斷點時執行的命令,以end結束 此時設置 >cont >end
程序每次運行到斷點處,自動打印關心的內容,並自動調用程序繼續執行指令 設定完成后程序將一直執行到最后 並在過程中輸出值
使用調試器打補丁 gcc 可以在程序進行調試時 直接更改變量的值來進行調試
程序下一次調試,使用info display 和 info break 來查看當前顯示與斷點的內容
1 set variable n = n+1 //設置在調試時,將變量n的值 +1
深入學習gdb 強大的功能
1.在支持硬件斷點的cpu上,gdb支持可以在符合某個條件時暫停程序運行
2.gdb可監控表達式的,即當某個表達式取一個特定的值時,gdb可以暫停程序的運行(這樣會對性能造成影響)
3.斷點、計數、條件可以結合在一起設置
4.gdb還可以將自己附在正在運行的程序上,對異常的程序可以在調試過程中直接進行修改,而不必停下編譯並重啟
可以在編譯時用 gcc -O -g來同時獲得程序優化和調試信息 但優化可能會改變程序執行順序
5.調試崩潰的程序時,Linx通常會產生一個 核心轉存儲(core dump).這個文件是程序的內存映像文件
一些工具
lint splint LClint等 清理程序中的垃圾,嚴格編譯程序,產生警告
函數調用工具
ctags 為程序中所有的函數創建索引,每個函數對應一個列表,列表列出函數調用位置
cxref 程序分析C語言源代碼並生成一個交叉引用的表格
cflow 程序打印出一個函數調用樹(function call tree),顯示函數之間的調用關系,可以理清程序調用架構,理解操作流程,了解函數的改動將會產生什么樣的影響
prof/gprof 產生執行存檔
想要查找程序的性能問題時,一種常用的技巧是執行存檔(execution profiling),需要特殊的編譯選項,執行存檔可以顯示執行它所花費的時間具體用在哪些步驟上。
給編譯器加上 -p 標志(針對prof程序) -pg 標志(針對gprof)
之后執行程序時,將生成mon.out(gmon.out)文件
斷言 assert
測試某個假設是否成立,如果不成立就停止程序的運行
#include <assert.h> void assert(int expression)
1 assert //宏對表達式進行求值,如果結果為0,就向標准錯誤輸出一些診斷信息,然后調用abort函數結束程序運行 2 3 #define NDEBUG //關閉斷言宏
在產品中保留assert並不可取,因為不希望客戶在看到一條assert之后程序強制退出,好的方法是編寫自己的錯誤中斷陷阱例程
內存調試
2019年12月3日
13:50
內存調試
1.內存泄露,malloc申請內存后賦給指針,指針的值被改變,此時沒有任何指針指向申請的內存,程序運行時間長了之后將會越來越慢,導致內存耗盡
2.在一個已分配的內存塊尾部的后面(或在它的前面)寫數據,就很可能會損壞malloc庫用於記錄內存分配的數據結構。之后,一個malloc或free調用都會導致段錯誤(Segmentation fault)。此時檢查錯誤發生的地點是很困難的。
ElectricFence可以使用Linux的虛擬內存機制來保護malloc 和 free使用的內存。
使用虛擬內存,在出現非法的內存訪問時,引發段沖突信號並停止程序的運行
valgrind 可以檢測出前邊所說的很多問題,特別是可以檢測出數組訪問錯誤和內存泄露
在程序運行結束時進行內存泄露的檢查 使用 valgrind --leak-check=yes 選項