valgrind 详解
一、概述
Valgrind 是一套 Linux 下,開放源代碼(GPL V2)的仿真調(diào)試工具的集合。
Valgrind 由內(nèi)核(core)以及基于內(nèi)核的其他調(diào)試工具組成。內(nèi)核類似于一個框架(framework),它模擬了一個 CPU 環(huán)境,并提供服務(wù)給其他工具;而其他工具則類似于插件 (plug-in),利用內(nèi)核提供的服務(wù)完成各種特定的內(nèi)存調(diào)試任務(wù)。
Valgrind的體系結(jié)構(gòu)如下圖所示:
二、包含的工具
Valgrind 的最新版是3.11.0,它一般包含下列工具:
1、Memcheck
最常用的工具,用來檢測程序中出現(xiàn)的內(nèi)存問題,所有對內(nèi)存的讀寫都會被檢測到,一切對malloc() / free() / new / delete 的調(diào)用都會被捕獲。所以,它能檢測以下問題:
- 對未初始化內(nèi)存的使用;
- 讀/寫釋放后的內(nèi)存塊;
- 讀/寫超出malloc分配的內(nèi)存塊;
- 讀/寫不適當(dāng)?shù)臈V袃?nèi)存塊;
- 內(nèi)存泄漏,指向一塊內(nèi)存的指針永遠丟失;
- 不正確的malloc/free或new/delete匹配;
- memcpy()相關(guān)函數(shù)中的dst和src指針重疊。
2、Callgrind
和 gprof 類似的分析工具,但它對程序的運行觀察更是入微,能給我們提供更多的信息。和 gprof 不同,它不需要在編譯源代碼時附加特殊選項,但加上調(diào)試選項是推薦的。Callgrind 收集程序運行時的一些數(shù)據(jù),建立函數(shù)調(diào)用關(guān)系圖,還可以有選擇地進行 cache 模擬。在運行結(jié)束時,它會把分析數(shù)據(jù)寫入一個文件。callgrind_annotate 可以把這個文件的內(nèi)容轉(zhuǎn)化成可讀的形式。
3、Cachegrind
Cache 分析器,它模擬 CPU 中的一級緩存 I1,Dl 和二級緩存,能夠精確地指出程序中 cache 的丟失和命中。如果需要,它還能夠為我們提供 cache 丟失次數(shù),內(nèi)存引用次數(shù),以及每行代碼,每個函數(shù),每個模塊,整個程序產(chǎn)生的指令數(shù)。這對優(yōu)化程序有很大的幫助。
4、Helgrind
它主要用來檢查多線程程序中出現(xiàn)的競爭問題。
Helgrind 尋找內(nèi)存中被多個線程訪問,而又沒有一貫加鎖的區(qū)域,這些區(qū)域往往是線程之間失去同步的地方,而且會導(dǎo)致難以發(fā)掘的錯誤。Helgrind 實現(xiàn)了名為“Eraser”的競爭檢測算法,并做了進一步改進,減少了報告錯誤的次數(shù)。不過,Helgrind 仍然處于實驗階段。
5、Massif
堆棧分析器,它能測量程序在堆棧中使用了多少內(nèi)存,告訴我們堆塊,堆管理塊和棧的大小
Massif 能幫助我們減少內(nèi)存的使用,在帶有虛擬內(nèi)存的現(xiàn)代系統(tǒng)中,它還能夠加速我們程序的運行,減少程序停留在交換區(qū)中的幾率。
此外,lackey 和 nulgrind 也會提供。Lackey 是小型工具,很少用到;Nulgrind 只是為開發(fā)者展示如何創(chuàng)建一個工具。
三、原理
Memcheck 能夠檢測出內(nèi)存問題,關(guān)鍵在于其建立了兩個全局表。
(1)Valid-Value 表
對于進程的整個地址空間中的每一個字節(jié)(byte),都有與之對應(yīng)的 8 個 bits;對于 CPU 的每個寄存器,也有一個與之對應(yīng)的 bit 向量。這些 bits 負責(zé)記錄該字節(jié)或者寄存器值是否具有有效的、已初始化的值。
(2)Valid-Address 表
對于進程整個地址空間中的每一個字節(jié)(byte),還有與之對應(yīng)的 1 個 bit,負責(zé)記錄該地址是否能夠被讀寫。
檢測原理:
當(dāng)要讀寫內(nèi)存中某個字節(jié)時,首先檢查這個字節(jié)對應(yīng)的 A bit。如果該 A bit顯示該位置是無效位置,memcheck 則報告讀寫錯誤。
內(nèi)核(core)類似于一個虛擬的 CPU 環(huán)境,這樣當(dāng)內(nèi)存中的某個字節(jié)被加載到真實的 CPU 中時,該字節(jié)對應(yīng)的 V bit 也被加載到虛擬的 CPU 環(huán)境中。一旦寄存器中的值,被用來產(chǎn)生內(nèi)存地址,或者該值能夠影響程序輸出,則 memcheck 會檢查對應(yīng)的V bits,如果該值尚未初始化,則會報告使用未初始化內(nèi)存錯誤。
四、常用選項
(1)--help:顯示幫助信息;
(2)--version:顯示 valgrind 版本;
(3)--tool=<name>:運行 valgrind 中名為 toolname 的工具,默認 memcheck,還可以為cachegrid、drd、lackey、callgrind、helgrind、massif等;
(4)--quiet:安靜地運行,只打印錯誤信息;
(5)--verbose:更詳細的信息,增加錯誤數(shù)統(tǒng)計;
(6)--trace-childer=no | yes:跟蹤子線程;
(7)--track-fds=no | yes:跟蹤打開的文件描述;
(8)--time-stamp=no | yes:增加時間戳到 Log 信息;
(9)--log-fd=<number>:輸出Log信息到文件描述符;
(10)--log-file=<file>:輸出Log信息到指定的文件;
(11)--xml=yes:將錯誤信息以xml格式輸出,只有memcheck可用;
(12)--xml-file=<file>:XML輸出到指定文件;
(13)--error-limit=no | yes:如果錯誤太多,則停止顯示新錯誤;
(14)--error-exitcode=<number>:如果發(fā)現(xiàn)錯誤,則返回錯誤代碼;
(15)--leak-check=no | summary | full:對發(fā)現(xiàn)的內(nèi)存泄露給出的信息級別,只有memcheck可用。(建議添加)
(16)--show-reachable=no | yes,用于控制是否檢測控制范圍之外的泄漏,比如全局指針、static指針等。
(17)–num-callers=(num):這個值默認是12,最高是50。表示顯示多少層的堆棧,設(shè)置越高會使Valgrind運行越慢而且使用更多的內(nèi)存,但是在嵌套調(diào)用層次比較高的程序中非常實用。
(18)--trace-children=no | yes,是否跟入子進程。
五、栗子
#include <stdlib.h> #include <stdio.h> #include <memory.h>int main(void) {char *ptr = (char *)malloc(10);ptr[12] = 'a'; // 內(nèi)存越界memcpy(ptr +1, ptr, 5); // 踩內(nèi)存char a[10];a[12] = 'i'; // 數(shù)組越界free(ptr); // 重復(fù)釋放free(ptr);char *p1;*p1 = '1'; // 非法指針return 0;}gcc -o memleak main.cc -g
valgrind --tool=memcheck --leak-check=full ./memleak
結(jié)果:
10617== Memcheck, a memory error detector ==10617== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==10617== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==10617== Command: ./memleak ==10617== ==10617== Invalid write of size 1 // 踩了1個字節(jié)的內(nèi)存。 ==10617== at 0x1091DA: main (main.cc:8) ==10617== Address 0x4a5804c is 2 bytes after a block of size 10 alloc'd ==10617== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==10617== by 0x1091CD: main (main.cc:7) ==10617== ==10617== Invalid write of size 1 // 踩了1個字節(jié)的內(nèi)存。 ==10617== at 0x484043E: memcpy@GLIBC_2.2.5 (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==10617== by 0x1091F8: main (main.cc:9) ==10617== Address 0x4a5804a is 0 bytes after a block of size 10 alloc'd ==10617== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==10617== by 0x1091CD: main (main.cc:7) ==10617== ==10617== Invalid free() / delete / delete[] / realloc() // 重復(fù)釋放。 ==10617== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==10617== by 0x109214: main (main.cc:13) ==10617== Address 0x4a58040 is 0 bytes inside a block of size 10 free'd ==10617== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==10617== by 0x109208: main (main.cc:12) ==10617== Block was alloc'd at ==10617== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==10617== by 0x1091CD: main (main.cc:7) ==10617== ==10617== Use of uninitialised value of size 8 // 使用了未申請的內(nèi)存(8bit) ==10617== at 0x109219: main (main.cc:15) ==10617== ==10617== Invalid write of size 1 // 在非法地址上寫了1個字節(jié)。 ==10617== at 0x109219: main (main.cc:15) ==10617== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==10617== ==10617== // 非法指針,導(dǎo)致coredump ==10617== Process terminating with default action of signal 11 (SIGSEGV) ==10617== Access not within mapped region at address 0x0 ==10617== at 0x109219: main (main.cc:15) ==10617== If you believe this happened as a result of a stack ==10617== overflow in your program's main thread (unlikely but ==10617== possible), you can try to increase the size of the ==10617== main thread stack using the --main-stacksize= flag. ==10617== The main thread stack size used in this run was 8388608. ==10617== ==10617== HEAP SUMMARY: ==10617== in use at exit: 0 bytes in 0 blocks ==10617== total heap usage: 1 allocs, 2 frees, 10 bytes allocated ==10617== ==10617== All heap blocks were freed -- no leaks are possible ==10617== ==10617== Use --track-origins=yes to see where uninitialised values come from ==10617== For lists of detected and suppressed errors, rerun with: -s ==10617== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 0 from 0)六、堆棧分析器可視化
1、安裝
sudo apt install massif-visualizer
2、生成數(shù)據(jù)文件
valgrind --tool=massif ./memleak
3、可視化
massif-visualizer massif.out.8233
8233 為進程號
結(jié)果:
?4、簡單可視化
ms_print massif.out.8233
(SAW:Game Over!)
總結(jié)
以上是生活随笔為你收集整理的valgrind 详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编译 / __attribute__(c
- 下一篇: 什么是 DMZ 区?