寫 C++的同學(xué)想必有太多和內(nèi)存打交道的血淚經(jīng)驗(yàn)了,常常被 C++的內(nèi)存問題攪的焦頭爛額偶器。(寫 core 的經(jīng)驗(yàn)了)有很多同學(xué)一見到 core 就兩眼一抹黑斩萌,不知所措了。筆者 入"坑"C++之后屏轰,在調(diào)試 C++代碼的過程之中颊郎,學(xué)習(xí)了不少調(diào)試代碼內(nèi)存的工具。希望借這個(gè)機(jī)會(huì)來介紹一下筆者常用的工具霎苗,GDB姆吭,Valgrind等等,相信大家通過好好運(yùn)用這些工具唁盏,能更好的馴服內(nèi)存這匹"野馬"内狸。
1.利用 GDB 調(diào)試 CoreDump
CoreDump時(shí)一個(gè)二進(jìn)制的文件,進(jìn)程發(fā)生錯(cuò)誤崩潰時(shí)厘擂,內(nèi)核會(huì)產(chǎn)生一個(gè)瞬時(shí)的快照答倡,記錄該進(jìn)程的內(nèi)存、運(yùn)行堆棧狀態(tài)等信息保存在core文件之中驴党。做個(gè)簡(jiǎn)單的類比,core 文件相當(dāng)于飛機(jī)運(yùn)行時(shí)的"黑匣子"获茬,能夠幫助我們更好的調(diào)試 C++程序的問題港庄。OK倔既,接下來筆者將介紹一下如果利用GDB 來調(diào)試 CoreDump的文件。
- CoreDump 文件的大小
首先我們先確定一下操作系統(tǒng)是否會(huì)產(chǎn)生 CoreDump 文件鹏氧。通過ulimit -c
獲取 core 文件的限制大胁秤俊:
上面顯示筆者電腦的 core 文件的大小是0,我們需要調(diào)整一下把还。通過ulimit
調(diào)整為無限制实蓬。當(dāng)然這種調(diào)整是臨時(shí)的,reboot 之后就恢復(fù)為0了吊履。
ulimit -c ulimited
如果需要永久修改安皱,可以通過/etc/security/limits.conf 來修改 core 文件的大小。
CoreDump 文件的生成路徑
默認(rèn)情況下艇炎,core dump生成的文件名為core酌伊,而且就在程序當(dāng)前目錄下。通過修改/proc/sys/kernel/core_pattern可以控制core文件保存位置和文件格式缀踪。(建議將后綴改為進(jìn)程號(hào)) 筆者這里簡(jiǎn)單起見居砖,不進(jìn)行修改了。編寫core 代碼驴娃,這里筆者利用線程訪問了空指針
#include <unistd.h>
#include <thread>
void core() {
char* ch = nullptr;
*ch = 'a';
}
int main() {
auto t1 = std::thread(core);
sleep(5);
return 0;
}
-
編譯運(yùn)行該代碼奏候,產(chǎn)生段錯(cuò)誤,生成了 core 文件
利用 GDB 調(diào)試 core 文件
調(diào)試 core 文件需要利用原生編譯出的二進(jìn)制文件調(diào)試唇敞。這里有一點(diǎn)需要注意的蔗草,如果編譯 C++文件之時(shí)沒有加-g的編譯選項(xiàng),core 文件的調(diào)試內(nèi)容會(huì)不夠完整厚棵。筆者這里建議開啟對(duì)應(yīng)的編譯選項(xiàng)蕉世,這會(huì)導(dǎo)致對(duì)應(yīng)的二進(jìn)制文件變大,編譯時(shí)間變長(zhǎng)婆硬。(生產(chǎn)環(huán)境可以考慮關(guān)閉)使用gdb 二進(jìn)制文件 core 文件
打開 core 文件狠轻。
core 文件列出了兩個(gè)線程的信息。我們需要判斷對(duì)應(yīng)的問題代碼的定位彬犯,接下來我們一起來梳理一下:
用info thread
查看線程的運(yùn)行情況向楼,在這里我們就可以判斷代碼 core 在什么線程之中了,如果還是無法確定谐区,可以通過thread apply all bt
列出更加詳盡的堆棧信息湖蜕。
通過上述信息可以確認(rèn),thread 1的代碼存在問題宋列。我們通過
thread 1
切換到 thread 1昭抒,用bt
顯示堆棧信息繼續(xù)追查:之后我們來看看令人生疑的棧內(nèi)容,這里顯然棧0是我們懷疑的代碼,用frame 1
查看灭返。
好了盗迟,這里我們找到了引起問題罪魁禍?zhǔn)椎拇a,訪問了空指針熙含。
小結(jié)
程序運(yùn)行的 core 文件是我們調(diào)試代碼十分重要依據(jù)罚缕,通過 GDB 可以很好的給出我們修改代碼的線索和參考,熟悉掌握GDB 的調(diào)試技巧怎静,能夠大大解放我們調(diào)試問題代碼的生產(chǎn)力邮弹。
2.利用Valgrind判斷內(nèi)存泄露
亡羊補(bǔ)牢不如未雨綢繆,與其等到出現(xiàn)程序崩潰時(shí)使用 GDB 來調(diào)試解決蚓聘,不如事前確認(rèn)代碼之中可能引發(fā)的問題腌乡。所以筆者接下來要介紹一款來自大不列顛的C++代碼分析神器:Valgrind。(Valgrind的作者也通過開發(fā)Valgrind獲得了第二屆Google-O'Reilly開源代碼大獎(jiǎng)~~~)
Valgrind 十分強(qiáng)大或粮,適用于內(nèi)存分析导饲,泄漏檢測(cè)、鎖分析氯材,性能評(píng)估渣锦。筆者也只掌握了一些基本的入門使用。希望這里能夠拋磚引玉氢哮,更多復(fù)雜的用法煩請(qǐng)參考官方文檔袋毙。
Valgrind的安裝
Valgrind的安裝很簡(jiǎn)單,筆者的發(fā)行版帶了對(duì)應(yīng)的 deb 包冗尤。通過 apt-get 的包管理工具就可以直接安裝了听盖,其他的發(fā)行版也可以作為參考。
sudo apt-get install valgrind
Valgrind的使用
與 GDB 類似裂七,Valgrind 同樣推薦使用-g
作為編譯參數(shù)皆看。能夠更好的對(duì)代碼進(jìn)行分析。這里我們依舊使用之前的例子進(jìn)行測(cè)試:
valgrind ./untitiled
下面是 Valgrind 的分析結(jié)果:
這里有顯示Invalid write of size 1
背零,說明這里有一個(gè)不合法的寫入腰吟,并且寫入了1個(gè)字節(jié)的內(nèi)容。也就是指的是我們之前代碼之中寫入空指針的行為徙瓶。
接下來我們要展示 Valgrind更加強(qiáng)大的功能毛雇。它展示了程序的內(nèi)存使用情況,并且給出總結(jié):
這里列出了多種的內(nèi)存泄露情況:
definitely lost: 肯定的內(nèi)存泄漏侦镇,這表示在程序退出時(shí)灵疮,有內(nèi)存沒有回收,但是也沒有指針指向該內(nèi)存壳繁。這種情況最為嚴(yán)重震捣。
indirectly lost: 間接的內(nèi)存泄漏荔棉,如類之中定義的指針指向的內(nèi)存沒有回收。這種情況和上述相同蒿赢。
possibly lost: 可能出現(xiàn)內(nèi)存泄漏江耀。這種情況需要仔細(xì)排查,可能代碼沒有問題诉植,也可能有異常的內(nèi)存泄露。
still reachable: 程序沒主動(dòng)釋放內(nèi)存昵观,在退出時(shí)候該內(nèi)存仍能訪問到晾腔。這種情況一般問題不大,因?yàn)槌绦蛲顺鲋蟛僮飨到y(tǒng)會(huì)回收程序的內(nèi)存啊犬,所以這種情況一般問題不大灼擂。
這里沒有給出具體泄露的內(nèi)容,需要加入?yún)?shù)--leak-check=full
將完整的結(jié)果打印出來觉至,會(huì)指出對(duì)應(yīng)的引起內(nèi)存泄露的具體代碼剔应,可以繼續(xù)深入分析。
代碼調(diào)優(yōu)
這里進(jìn)行代碼調(diào)優(yōu)的時(shí)语御,需要利用qcachegrind來進(jìn)行分析峻贮。首先筆者先進(jìn)行安裝:
sudo apt-get install qcachegrind
之后我們調(diào)用Valgrind來生成運(yùn)行數(shù)據(jù):
valgrind --tool=callgrind -v main(需要分析的程序)
運(yùn)行之后在目錄下生成對(duì)應(yīng)的分析數(shù)據(jù),我們用qcachegrind 打開应闯,這里用的代碼是筆者之前實(shí)現(xiàn)的 SkipList纤控。
qcachegrind callgrind.out.29235
接下來我們來分析對(duì)應(yīng)的結(jié)果:
上圖顯示了各個(gè)函數(shù)的被調(diào)用的耗時(shí)百分比,我們可以選取對(duì)性能感興趣的函數(shù)來進(jìn)行深入分析碉纺。我們下面繼續(xù)分析其中一個(gè)函數(shù)被調(diào)用和它使用函數(shù)的性能情況
所以通過上述數(shù)據(jù)船万,我們可以給出性能分析的證據(jù)和線索,依據(jù)這些信息來更好的優(yōu)化我們代碼的性能骨田。
3.小結(jié)
本文介紹了亡羊補(bǔ)牢的工具 GDB耿导,也簡(jiǎn)介了未雨綢繆的Valgrind 。通過上述工具對(duì)C++程序更加深入分析态贤。工欲善其事舱呻,必先利其器,希望大家也能好好掌握這些提供生產(chǎn)力的工具抵卫,讓 C++不再惱人狮荔。