來自:HappenLee
連結:https://www.cnblogs.com/happenlee/p/9931423.html
寫 C++的同學想必有太多和內存打交道的血淚經驗了,常常被 C++的內存問題攪的焦頭爛額。(寫 core 的經驗了)有很多同學一見到 core 就兩眼一抹黑,不知所措了。筆者 入"坑"C++之後,在調試 C++代碼的過程之中,學習了不少調試代碼內存的工具。希望借這個機會來介紹一下筆者常用的工具,GDB,Valgrind等等,相信大家通過好好運用這些工具,能更好的馴服內存這匹"野馬"。
1、利用 GDB 調試 CoreDumpCoreDump時一個二進位的文件,進程發生錯誤崩潰時,內核會產生一個瞬時的快照,記錄該進程的內存、運行堆棧狀態等信息保存在core文件之中。做個簡單的類比,core 文件相當於飛機運行時的"黑匣子",能夠幫助我們更好的調試 C++程序的問題。OK,接下來筆者將介紹一下如果利用GDB 來調試 CoreDump的文件。
首先我們先確定一下作業系統是否會產生 CoreDump 文件。通過ulimit -c獲取 core 文件的限制大小:
上面顯示筆者電腦的 core 文件的大小是0,我們需要調整一下。通過ulimit調整為無限制。當然這種調整是臨時的,reboot 之後就恢復為0了。
ulimit -c ulimited
如果需要永久修改,可以通過/etc/security/limits.conf 來修改 core 文件的大小。
#include <unistd.h>
#include <thread>
void core() {
char* ch = nullptr;
*ch = 'a';
}
int main() {
auto t1 = std::thread(core);
sleep(5);
return 0;
}
core 文件列出了兩個線程的信息。我們需要判斷對應的問題代碼的定位,接下來我們一起來梳理一下:
用info thread查看線程的運行情況,在這裡我們就可以判斷代碼 core 在什麼線程之中了,如果還是無法確定,可以通過thread apply all bt列出更加詳盡的堆棧信息。
通過上述信息可以確認,thread 1的代碼存在問題。我們通過thread 1切換到 thread 1,用bt顯示堆棧信息繼續追查:
之後我們來看看令人生疑的棧內容,這裡顯然棧0是我們懷疑的代碼,用frame 1查看。
好了,這裡我們找到了引起問題罪魁禍首的代碼,訪問了空指針。
小結程序運行的 core 文件是我們調試代碼十分重要依據,通過 GDB 可以很好的給出我們修改代碼的線索和參考,熟悉掌握GDB 的調試技巧,能夠大大解放我們調試問題代碼的生產力。
2、利用Valgrind判斷內存洩露亡羊補牢不如未雨綢繆,與其等到出現程序崩潰時使用 GDB 來調試解決,不如事前確認代碼之中可能引發的問題。所以筆者接下來要介紹一款來自大不列顛的C++代碼分析神器:Valgrind。(Valgrind的作者也通過開發Valgrind獲得了第二屆Google-O'Reilly開原始碼大獎~~~)
Valgrind 十分強大,適用於內存分析,洩漏檢測、鎖分析,性能評估。筆者也只掌握了一些基本的入門使用。希望這裡能夠拋磚引玉,更多複雜的用法煩請參考官方文檔http://valgrind.org/docs/manual/QuickStart.html。
Valgrind的安裝很簡單,筆者的發行版帶了對應的 deb 包。通過 apt-get 的包管理工具就可以直接安裝了,其他的發行版也可以作為參考。
sudo apt-get install valgrind
與 GDB 類似,Valgrind 同樣推薦使用-g作為編譯參數。能夠更好的對代碼進行分析。這裡我們依舊使用之前的例子進行測試:
valgrind ./untitiled
下面是 Valgrind 的分析結果
這裡有顯示Invalid write of size 1,說明這裡有一個不合法的寫入,並且寫入了1個字節的內容。也就是指的是我們之前代碼之中寫入空指針的行為。
接下來我們要展示 Valgrind更加強大的功能。它展示了程序的內存使用情況,並且給出總結:
這裡列出了多種的內存洩露情況:
definitely lost:肯定的內存洩漏,這表示在程序退出時,有內存沒有回收,但是也沒有指針指向該內存。這種情況最為嚴重。
indirectly lost:間接的內存洩漏,如類之中定義的指針指向的內存沒有回收。這種情況和上述相同。
possibly lost: 可能出現內存洩漏。這種情況需要仔細排查,可能代碼沒有問題,也可能有異常的內存洩露。
still reachable:程序沒主動釋放內存,在退出時候該內存仍能訪問到。這種情況一般問題不大,因為程序退出之後作業系統會回收程序的內存,所以這種情況一般問題不大。
這裡沒有給出具體洩露的內容,需要加入參數--leak-check=full將完整的結果列印出來,會指出對應的引起內存洩露的具體代碼,可以繼續深入分析。
代碼調優這裡進行代碼調優的時,需要利用qcachegrind來進行分析。首先筆者先進行安裝:
sudo apt-get install qcachegrind
之後我們調用Valgrind來生成運行數據:
valgrind --tool=callgrind -v main(需要分析的程序)
運行之後在目錄下生成對應的分析數據,我們用qcachegrind 打開,這裡用的代碼是筆者之前實現的 SkipList。
qcachegrind callgrind.out.29235
接下來我們來分析對應的結果
上圖顯示了各個函數的被調用的耗時百分比,我們可以選取對性能感興趣的函數來進行深入分析。我們下面繼續分析其中一個函數被調用和它使用函數的性能情況
所以通過上述數據,我們可以給出性能分析的證據和線索,依據這些信息來更好的優化我們代碼的性能。
3、小結本文介紹了亡羊補牢的工具 GDB,也簡介了未雨綢繆的Valgrind 。通過上述工具對C++程序更加深入分析。工欲善其事,必先利其器,希望大家也能好好掌握這些提供生產力的工具,讓 C++不再惱人。
●編號408,輸入編號直達本文
●輸入m獲取文章目錄
分享C/C++技術文章