C/C++内存检测工具valgrind--memcheck
Valgrind簡(jiǎn)介
Valgrind是運(yùn)行在Linux上的一套基于仿真技術(shù)的程序調(diào)試和分析工具,作者是獲得過(guò)Google-O’Reilly開(kāi)源大獎(jiǎng)的Julian Seward,它包含一個(gè)內(nèi)核 —— 一個(gè)軟件合成的CPU,和一系列的小工具,每個(gè)工具都可以完成一項(xiàng)任務(wù)──調(diào)試,分析,或測(cè)試等,內(nèi)存檢測(cè),我們可以使用它的工具:Memcheck。
?
Valgrind安裝
方法 1. valgrind官網(wǎng):http://valgrind.org下載
方法 2. Ubuntu: sudo apt-get install valgrind
?
Memcheck的檢測(cè)范圍
它可以用來(lái)檢測(cè)c/c++程序中出現(xiàn)的內(nèi)存問(wèn)題,所有對(duì)內(nèi)存的讀寫(xiě)都會(huì)被檢測(cè)到,一切對(duì)malloc()/free()/new/delete的調(diào)用都會(huì)被捕獲。所以,它能檢測(cè)以下問(wèn)題:
1). 對(duì)未初始化內(nèi)存的使用;
2). 讀/寫(xiě)釋放后的內(nèi)存塊;
3). 讀/寫(xiě)超出malloc等分配的動(dòng)態(tài)內(nèi)存范圍;
4). 讀/寫(xiě)不適當(dāng)?shù)臈V袃?nèi)存塊;
5). 內(nèi)存泄漏,指向一塊內(nèi)存的指針永遠(yuǎn)丟失;
6). 不正確的malloc/free或new/delete匹配;
7). memcpy()相關(guān)函數(shù)中的dst和src指針重疊問(wèn)題。
?
Memcheck檢測(cè)使用步驟及注意事項(xiàng)
1、在編譯程序的時(shí)候打開(kāi)調(diào)試模式(gcc編譯器的-g選項(xiàng)),以便顯示行號(hào),編譯時(shí)去掉-O1 -O2等優(yōu)化選項(xiàng);檢查的是C++程序的時(shí)候,考慮加上選項(xiàng): -fno- inline ,這樣它函數(shù)調(diào)用鏈會(huì)很清晰。
2、執(zhí)行:valgrind --tool=memcheck --leak-check=full --log-file=./log.txt ./YourProgram
3、程序運(yùn)行結(jié)束,查看 log.txt 中的結(jié)果。
結(jié)果分析:
Valgrind(memcheck)包含這7類錯(cuò)誤,黑體為一般的錯(cuò)誤提示:
1、illegal read/illegal write errors 非法讀取/非法寫(xiě)入錯(cuò)誤
2、use of uninitialised values 使用未初始化的區(qū)域
3、use of uninitialised or unaddressable values in system calls 系統(tǒng)調(diào)用時(shí)使用了未初始化或不可尋址的地址
4、illegal frees 非法的釋放
5、when a heap block is freed with an inappropriate deallocation function 分配和釋放函數(shù)不匹配
6、overlapping source and destination blocks 源和目的內(nèi)存塊重疊
7、memory leak detection 內(nèi)存泄漏檢測(cè)
? 7.1 Still reachable 內(nèi)存指針還在還有機(jī)會(huì)使用或者釋放,指針指向的動(dòng)態(tài)內(nèi)存還沒(méi)有被釋放就退出了
? 7.2 Definitely lost 確定的內(nèi)存泄露,已經(jīng)不能夠訪問(wèn)這塊內(nèi)存
? 7.3 Indirectly lost 指向該內(nèi)存的指針都位于內(nèi)存泄露處
? 7.4 Possibly lost 可能的內(nèi)存泄露,仍然存在某個(gè)指針能夠訪問(wèn)某塊內(nèi)存,但該指針指向的已經(jīng)不是該內(nèi)存首位置
? 7.5 Suppressed 某些庫(kù)產(chǎn)生的錯(cuò)誤不予以提示,這些錯(cuò)誤會(huì)被統(tǒng)計(jì)到suppressed項(xiàng)目
?
測(cè)試案例:
最近剛剛用最小堆實(shí)現(xiàn)了一個(gè)定時(shí)容器,寫(xiě)完之后就使用Valgrind的memcheck工具檢測(cè)了一些內(nèi)存,log日志如下:
==38716== Memcheck, a memory error detector ==38716== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==38716== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==38716== Command: ./test_heap_timer_new ==38716== Parent PID: 17817 ==38716== ==38716== Invalid write of size 4 ==38716== at 0x10BFF8: HeapTimer<Event>::setPos(int) (heap_timer.hpp:59) ==38716== by 0x10BF30: HeapTimerContainer<Event>::percolateDown(int) (heap_timer.hpp:334) ==38716== by 0x10BAD3: HeapTimerContainer<Event>::popTimer() (heap_timer.hpp:300) ==38716== by 0x10B79C: HeapTimerContainer<Event>::tick() (heap_timer.hpp:196) ==38716== by 0x10B34C: main (test_heap_timer_new.cpp:307) ==38716== Address 0x4db2478 is 24 bytes inside a block of size 32 free'd ==38716== at 0x483D1CF: operator delete(void*, unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==38716== by 0x10BA8B: HeapTimerContainer<Event>::popTimer() (heap_timer.hpp:296) ==38716== by 0x10B79C: HeapTimerContainer<Event>::tick() (heap_timer.hpp:196) ==38716== by 0x10B34C: main (test_heap_timer_new.cpp:307) ==38716== Block was alloc'd at ==38716== at 0x483BE63: operator new(unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==38716== by 0x10B808: HeapTimerContainer<Event>::addTimer(long) (heap_timer.hpp:212) ==38716== by 0x10AC77: acceptConn(Event*, ITimerContainer<Event>*) (test_heap_timer_new.cpp:175) ==38716== by 0x10B2B5: main (test_heap_timer_new.cpp:293) ==38716== ==38716== ==38716== HEAP SUMMARY: ==38716== in use at exit: 0 bytes in 0 blocks ==38716== total heap usage: 10 allocs, 10 frees, 78,304 bytes allocated ==38716== ==38716== All heap blocks were freed -- no leaks are possible ==38716== ==38716== For lists of detected and suppressed errors, rerun with: -s ==38716== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)可以看到有一個(gè)4字節(jié)的無(wú)效內(nèi)存寫(xiě)入,是在HeapTimer::setPos(int) (heap_timer.hpp:59)里面,我找了半天,最終發(fā)現(xiàn)了原因。
原代碼鏈接:高性能定時(shí)器1——最小堆實(shí)現(xiàn)_Peerless__的博客-CSDN博客
代碼如下:
template <typename _UData> void HeapTimerContainer<_UData>::tick() {std::cout << "----------tick----------" << std::endl;HeapTimer<_UData> *tmp = _array[0];time_t cur = getMSec();// 循環(huán)處理到期的定時(shí)器while(!isEmpty()){if(!tmp){break;}// 如果定時(shí)器沒(méi)到期,則退出循環(huán)if(tmp->getExpire() > cur){break;}tmp->handleTimeOut();// 將堆頂元素刪除,同時(shí)生成新的堆頂定時(shí)器popTimer();tmp = _array[0];} }template <typename _UData> void HeapTimerContainer<_UData>::popTimer() {if(isEmpty()){return;}if(_array[0]){delete _array[0];// 將原來(lái)的堆頂元素替換為堆數(shù)組中最后一個(gè)元素_array[0] = _array[--_size];// 對(duì)新的堆頂元素執(zhí)行下濾操作percolateDown(0);} }// 最小堆的下濾操作,它確保數(shù)組中以第hole個(gè)節(jié)點(diǎn)作為根的子樹(shù)擁有最小堆性質(zhì) template <typename _UData> void HeapTimerContainer<_UData>::percolateDown(int hole) { /* if(_size == 0){return;}*/HeapTimer<_UData> *temp = _array[hole];int child = 0;for(; ((hole * 2 + 1) <= _size - 1); hole = child){child = hole * 2 + 1;if((child < (_size - 1)) && (_array[child + 1]->getExpire() < _array[child]->getExpire())){child++;}if(_array[child]->getExpire() < temp->getExpire()){_array[hole] = _array[child];_array[hole]->setPos(hole); // 調(diào)整定時(shí)器的位置時(shí),重新設(shè)置timer中pos保存的其在數(shù)組中的位置}else {break;}}_array[hole] = temp;_array[hole]->setPos(hole); }上面代碼的邏輯是,tick函數(shù)被調(diào)用就會(huì)從最小堆中取出最近要超時(shí)的定時(shí)器(也就是堆頂元素),檢查是否超時(shí),如果超時(shí)就執(zhí)行回調(diào)函數(shù),然后調(diào)用popTimer彈出堆頂元素。在popTimer中delete掉了剛超時(shí)的定時(shí)器,并將堆數(shù)組中最后一個(gè)元素放到堆頂,然后調(diào)用percolateDown來(lái)調(diào)整最小堆。Valgrind生成的日志中提示在HeapTimer::setPos(int) (heap_timer.hpp:59)出錯(cuò)了。后來(lái)發(fā)現(xiàn),如果堆中只有一個(gè)元素,這段代碼_array[0] = _array[–_size];也就變成了_array[0] = _array[0];然后就是在percolateDown函數(shù)中對(duì)已經(jīng)delete掉的對(duì)象調(diào)用了setPos函數(shù)。也就是使用了已經(jīng)delete掉的對(duì)象,但是程序運(yùn)行的時(shí)候竟然沒(méi)有崩潰,這是個(gè)挺大的隱患的,沒(méi)有崩潰更為恐怖,因?yàn)榭赡芟乱淮尉鸵罎ⅰ=鉀Q方案就是在percolateDown函數(shù)中加一個(gè)數(shù)組是否為空的判斷,如果為空直接返回就好了。
總結(jié)
以上是生活随笔為你收集整理的C/C++内存检测工具valgrind--memcheck的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux下内存检测工具:asan
- 下一篇: C/C++内存检测工具valgrind