Valgrind memcheck 用法

Valgrind是一組調(diào)試(debugging)和剖析(profiling)工具的集合幻妓。memcheck是其中應用最廣泛的一個,它檢查內(nèi)存有關的問題劫拢,包括諸如內(nèi)存訪問越界涌哲、內(nèi)存泄露等。

1. 使用步驟

1.1 編譯程序

  • -g:使用這個選項產(chǎn)生符號信息尚镰。這樣Valgrind產(chǎn)生的報告中會顯示的代碼位置是源代碼的位置阀圾,否則Valgrind只能猜函數(shù)名。
  • -fno-inline:不使用這個選項狗唉,Valgrind報告的調(diào)用棾鹾妫可能讓人迷惑。如果是編譯debug版本分俯,這個選項是默認的
  • -O1肾筐。使用這個選項在速度與準確性之間達到平衡。如果速度可容忍缸剪,最好使用-O0吗铐。
  • -Wall。使用這個選項時杏节,gcc能解決一些memcheck不能檢測到的問題唬渗。

1.2 用Valgrind運行程序

valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full \
--show-leak-kinds=all ./your_app arg1 arg2
  • --log-file 報告文件名。如果沒有指定奋渔,輸出到stderr镊逝。
  • --tool=memcheck 指定Valgrind使用的工具。Valgrind是一個工具集嫉鲸,包括Memcheck撑蒜、Cachegrind、Callgrind等多個工具玄渗。memcheck是缺省項座菠。
  • --leak-check 指定如何報告內(nèi)存泄漏(memcheck能檢查多種內(nèi)存使用錯誤,內(nèi)存泄漏是其中常見的一種)藤树,可選值有:
    • no 不報告
    • summary 顯示簡要信息浴滴,有多少個內(nèi)存泄漏。summary是缺省值也榄。
    • yes 和 full 顯示每個泄漏的內(nèi)存在哪里分配巡莹。
  • show-leak-kinds 指定顯示內(nèi)存泄漏的類型的組合。類型包括definite, indirect, possible,reachable甜紫。也可以指定all或none降宅。缺省值是definite,possible。

2. 錯誤類型

2.1 illegal read 非法讀 / illegal write 非法寫

如下代碼分配了長度為1024的緩存buf囚霸,然后在buf+1020的位置寫入一個8字節(jié)的uint64_t腰根,并打印它。這個uint64_t已經(jīng)超出buf的界限拓型。

 10     char* buf = (char*) malloc (1024);
 11     uint64_t* bigNum = (uint64_t*)(buf + 1020);
 12     *bigNum = 0x12345678AABBCCDD;
 13     printf ("bigNum is %llu\n", *bigNum);
 14     free(buf);

報告指出了非法寫的位置main.cpp:12和非法讀的位置main.cpp:13额嘿。

==17387== Invalid write of size 8
==17387==    at 0x400849: main (main.cpp:12)
==17387==  Address 0x51f343c is 1,020 bytes inside a block of size 1,024 alloc'd
==17387==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17387==    by 0x400828: main (main.cpp:10)
==17387== 
==17387== Invalid read of size 8
==17387==    at 0x400850: main (main.cpp:13)
==17387==  Address 0x51f343c is 1,020 bytes inside a block of size 1,024 alloc'd
==17387==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17387==    by 0x400828: main (main.cpp:10)

2.2 Use of uninitialised values 使用未初始化的值

如下代碼打印了一個未初始化的值unused。

10     int unused; 
11     printf ("unused=%d", unused);

報告指出了使用unused的位置main.cpp:11劣挫。

==17418== Use of uninitialised value of size 8
==17418==    at 0x4E7A39B: _itoa_word (_itoa.c:195)
==17418==    by 0x4E7C3E7: vfprintf (vfprintf.c:1596)
==17418==    by 0x4E85198: printf (printf.c:35)
==17418==    by 0x4007A2: main (main.cpp:11)

2.3 Use of uninitialised or unaddressable values in system calls 系統(tǒng)調(diào)用時使用未初始化的值或不能訪問的地址

如下代碼中arr沒有初始化册养,就調(diào)用write()輸出了。

10     char* arr = (char*)malloc(10);
11     write (1, arr, 10);

報告給出了系統(tǒng)調(diào)用write的位置main.cpp:11压固。

==17450== Syscall param write(buf) points to uninitialised byte(s)
==17450==    at 0x4F1AF10: __write_nocancel (syscall-template.S:82)
==17450==    by 0x400802: main (main.cpp:11)
==17450==  Address 0x51f3040 is 0 bytes inside a block of size 10 alloc'd
==17450==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17450==    by 0x4007E8: main (main.cpp:10)

2.4 illegal fress 非法釋放

如下代碼中球拦,釋放的地址arr+2不是分配時得到的地址arr。

10     char* arr = (char*)malloc(10);
11     free (arr+2);

報告中指出了釋放時的位置main.cpp:11帐我,也指出了分配時的位置main.cpp:10坎炼。

==3429== Invalid free() / delete / delete[] / realloc()
==3429==    at 0x4C2A82E: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3429==    by 0x4007FC: main (main.cpp:11)
==3429==  Address 0x51f3042 is 2 bytes inside a block of size 10 alloc'd
==3429==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3429==    by 0x4007E8: main (main.cpp:10)

2.5 When a heap block is freed with an inappropriate deallocation function 不匹配的釋放

如下代碼中分配得到一個int數(shù)組d[],釋放的時候卻使用delete拦键,而不是delete[]谣光。

10     int* d = new int[5];
11     delete d;

報告中指出了數(shù)組釋放的位置main.cpp:11,也指出了分配的位置main.cpp:10芬为。

==3498== Mismatched free() / delete / delete []
==3498==    at 0x4C2A4BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3498==    by 0x400828: main (main.cpp:11)
==3498==  Address 0x5a2ac80 is 0 bytes inside a block of size 20 alloc'd
==3498==    at 0x4C2AC27: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3498==    by 0x400818: main (main.cpp:10)

2.6 Overlapping source and destination blocks 移動/復制緩存數(shù)據(jù)時萄金,源與目標重疊

如下代碼中memcpy復制的源和目標重疊。

10     char buf[256] = "";
11     memcpy (buf+10, buf, 100);

報告指出了復制的位置main.cpp:11.

==3530== Source and destination overlap in memcpy(0x7feffffea, 0x7feffffe0, 100)
==3530==    at 0x4C2CFA0: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3530==    by 0x40087E: main (main.cpp:11)

2.7 Fishy argument values 函數(shù)調(diào)用的參數(shù)可能不合法

下面的代碼中媚朦,使用了負值-10調(diào)用malloc捡絮。

10     int sz = -10;
11     int* arr2 = (int*)malloc (sz);
12     free(arr2);

報告只是警告malloc()的參數(shù)不合法,但沒有指出位置莲镣。(就算不用Valgrind運行程序福稳,這個錯誤應該也能馬上發(fā)現(xiàn)吧?)

==3583== Warning: silly arg (-10) to malloc()

2.8 Memory leak detection 內(nèi)存泄露

以下代碼分配了100字節(jié)沒有釋放瑞侮。

10     int* arr = (int*)malloc (100);

報告依次列出了每一個泄露的內(nèi)存分配的位置(這里是main.cpp:10)的圆,最后還給出了所有關于泄露的統(tǒng)計總結(jié)(LEAK_SUMMARY部分)。

==3869== HEAP SUMMARY:
==3869==     in use at exit: 100 bytes in 1 blocks
==3869==   total heap usage: 1 allocs, 0 frees, 100 bytes allocated
==3869== 
==3869== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3869==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3869==    by 0x400798: main (main.cpp:10)
==3869== 
==3869== LEAK SUMMARY:
==3869==    definitely lost: 100 bytes in 1 blocks
==3869==    indirectly lost: 0 bytes in 0 blocks
==3869==    possibly lost: 0 bytes in 0 blocks
==3869==    still reachable: 0 bytes in 0 blocks
==3869==    suppressed: 0 bytes in 0 blocks

3. 關于內(nèi)存泄露

  • memcheck跟蹤所有malloc()/new()分配的堆塊半火,所以它能在程序退出時知道哪些塊還沒有釋放越妈。
  • 這里把程序能訪問到的指針集合叫做root-set。memcheck根據(jù)從root-set能不能到達某個塊钮糖,判斷這個塊是不是泄露了(reachable還是lost)梅掠。
    • 到達塊有兩種方式:一是start-pointer酌住,它指向塊的開始地址;二是interior-pointer阎抒,它指向塊的中間某個位置酪我。
    • 有start-pointer引用的緩存被認為是reachable。
    • 有interior-pointer引用的緩存則不一定了且叁。
      • 它可能是分配之后的指針故意向后移動的結(jié)果都哭。這時程序使用緩存開始的幾個字節(jié)保存特別信息,而真正的數(shù)據(jù)在后面某個位置逞带。比如指向C++的std::string內(nèi)部數(shù)組的指針欺矫、指向new[]分配的C++對象數(shù)組的指針、多重繼承的情況等展氓。這時緩存是reachable的穆趴。
      • 但它也可能只是偶然指向緩存。這時緩存就是lost的遇汞。
      • 所以interior-pointer引用的緩存是possible lost毡代。

根據(jù)從root-set到緩存是否需要跳轉(zhuǎn),memcheck 將分配的緩存的狀態(tài)分為幾種:

  • directly reachable: root-set中的指針指向緩存勺疼,不需跳轉(zhuǎn)
  • indirectly reachable: root-set中的指針不直接指向緩存教寂,需要跳轉(zhuǎn)
  • directly lost:沒有任何指針指向緩存
  • indirectly lost:有指針指向緩存,但從root-set中也無法到達這個指針

下圖列出了緩存的各種情況执庐。其中RRR是root-set中的節(jié)點酪耕,AAA和BBB是緩存。

     Pointer chain            AAA Leak Case   BBB Leak Case
     -------------            -------------   -------------
(1)  RRR ------------> BBB                    DR
(2)  RRR ---> AAA ---> BBB    DR              IR
(3)  RRR               BBB                    DL
(4)  RRR      AAA ---> BBB    DL              IL
(5)  RRR ------?-----> BBB                    (y)DR, (n)DL
(6)  RRR ---> AAA -?-> BBB    DR              (y)IR, (n)DL
(7)  RRR -?-> AAA ---> BBB    (y)DR, (n)DL    (y)IR, (n)IL
(8)  RRR -?-> AAA -?-> BBB    (y)DR, (n)DL    (y,y)IR, (n,y)IL, (_,n)DL
(9)  RRR      AAA -?-> BBB    DL              (y)IL, (n)DL

Pointer chain legend:
- RRR: a root set node or DR block
- AAA, BBB: heap blocks
- --->: a start-pointer
- -?->: an interior-pointer

Leak Case legend:
- DR: Directly reachable
- IR: Indirectly reachable
- DL: Directly lost
- IL: Indirectly lost
- (y)XY: it's XY if the interior-pointer is a real pointer
- (n)XY: it's XY if the interior-pointer is not a real pointer
- (_)XY: it's XY in either case

下面是memcheck中LEAK SUMMARY部分中的項目:

  • Still reachable: 上圖中(1)和(2)的情況轨淌,從root-set中通過start-pointer直接到達或跳轉(zhuǎn)到達緩存迂烁。這一項是確定不是lost的,用戶無需關系递鹉。
  • Definitely lost:(3)的情況盟步。沒有任何指針指向緩存。這一項確定是lost的躏结,用戶需解決却盘。
  • Indirectly lost:(4)和(9)中BBB的情況。有start-pointer或interior-pointer指向緩存媳拴,但從root-set不能到達這些指針黄橘。這一項可以推遲解決,因為Indirectly lost一定與definitely lost對應屈溉,definitely lost解決了塞关,indirectly lost也就變成reachable或者possible lost了。
  • Possibly lost: 有interior-pointer指向緩存子巾,從root-set能到達這些指針帆赢。因為memcheck不能判斷interior-pointer是否lost小压,所以需要用戶排除。
    所以實際上只要關心definitely lost和possible lost就可以了椰于。這也是memcheck的缺省值:
--show-leak-kinds=definite,possible
  • suppressed: 用戶在suppressed file中指定不要報告的項目怠益,不管這個緩存是前面的哪種情況。

4. memcheck的實現(xiàn)細節(jié)

4.1 V (Valid-value) bit

可以簡單地認為memcheck實現(xiàn)了一個與真實CPU對等的CPU廉羔,這個CPU對于真實CPU上處理和存儲的每一個bit溉痢,都有一個與這個bit關聯(lián)的額外的bit僻造。后一個bit叫做V bit憋他,表示前一個bit中是否包含合法值。

進程的整個地址空間對應一個bitmap髓削,如果CPU加載一個word大小的數(shù)據(jù)竹挡,它同時從bitmap中加載對應的32個V bit。如果U將這個word數(shù)據(jù)的全部或部分寫到一個不同的地址立膛,則同時將那個地址對應的32個V bit寫回bitmap揪罕。

即使是CPU內(nèi)部的bit也有一個相應的V bit。所有的CPU寄存器宝泵,不管是什么類型好啰,都有各自的V bit。memcheck在bitmap大量使用壓縮技術儿奶,以便緊湊地表達其中的V bit框往。

4.2 A (Valid-address) bit

內(nèi)存中的每個byte都有一個valid-address(A) bit與它對應。與V bit不同闯捎,CPU中的byte沒有A bit椰弊。A bit表示程序是否能合法讀寫相應的位置,A bit 不關心數(shù)據(jù)瓤鼻,V bit才關心秉版。

程序讀寫內(nèi)存時,memcheck檢查與該位置相應的A bit茬祷,如果 A bit無效清焕,則報告錯誤。

A bit設置的時機如下:

  • 程序啟動時祭犯,所有全局數(shù)據(jù)的范圍A bit設置有效
  • 調(diào)用malloc/new時耐朴,分配大小的區(qū)域,A bit設置有效盹憎,釋放后立即設置無效
  • 棧指針SP移動時筛峭,A bit相應設置。因為棧向下增長陪每,所以SP向下移動影晓,A bit設置有效镰吵,SP向上移動,A bit設置無效挂签。
  • 某些系統(tǒng)調(diào)用時疤祭,A bit相應設置。比如mmap()將文件映射到內(nèi)存空間饵婆,所以A bit設置有效勺馆。

相關鏈接

GDB 常用法
GDB 調(diào)試Coredump問題
嵌入式開發(fā)中GDB調(diào)試Coredump問題
嵌入式開發(fā)中GDB串口遠程調(diào)試
用backtrace()調(diào)試coredump問題
Valgrind memcheck 用法
Address Sanitizer 用法

參考資料

Memcheck: a memory error detector
http://valgrind.org/docs/manual/mc-manual.html#mc-manual.badfrees

valgrind 工具介紹和簡單的使用
https://www.cnblogs.com/AndyStudy/p/6409287.html

關于gcc 2.96
http://www.reibang.com/p/a317e8208f8e

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侨核,隨后出現(xiàn)的幾起案子草穆,更是在濱河造成了極大的恐慌,老刑警劉巖搓译,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悲柱,死亡現(xiàn)場離奇詭異,居然都是意外死亡些己,警方通過查閱死者的電腦和手機豌鸡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來段标,“玉大人涯冠,你說我怎么就攤上這事”婆樱” “怎么了蛇更?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長往堡。 經(jīng)常有香客問我械荷,道長,這世上最難降的妖魔是什么虑灰? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任吨瞎,我火速辦了婚禮,結(jié)果婚禮上穆咐,老公的妹妹穿的比我還像新娘颤诀。我一直安慰自己,他們只是感情好对湃,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布崖叫。 她就那樣靜靜地躺著,像睡著了一般拍柒。 火紅的嫁衣襯著肌膚如雪心傀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天拆讯,我揣著相機與錄音脂男,去河邊找鬼养叛。 笑死,一個胖子當著我的面吹牛宰翅,可吹牛的內(nèi)容都是我干的弃甥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼汁讼,長吁一口氣:“原來是場噩夢啊……” “哼淆攻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘿架,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤瓶珊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后眶明,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艰毒,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡筐高,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年搜囱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柑土。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜀肘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出稽屏,到底是詐尸還是另有隱情扮宠,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布狐榔,位于F島的核電站坛增,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏薄腻。R本人自食惡果不足惜收捣,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庵楷。 院中可真熱鬧罢艾,春花似錦、人聲如沸尽纽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弄贿。三九已至春锋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間差凹,已是汗流浹背期奔。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工豆拨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人能庆。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓施禾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親搁胆。 傳聞我的和親對象是個殘疾皇子弥搞,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容