Linux C/C++ Memory Leak Detection Tool

Andrew.Hann發表於2015-03-20

目錄

1. 記憶體使用情況分析
2. 記憶體洩漏(memory leak)
3. Valgrind使用

 

1. 記憶體使用情況分析

0x1: 系統總記憶體的分析

可以從proc目錄下的meminfo檔案瞭解到當前系統記憶體的使用情況彙總,其中
可用的實體記憶體 = memfree + buffers + cached
當memfree不夠時,核心會通過回寫機制(pdflush執行緒)把cached和buffered記憶體回寫到後備儲存器,從而釋放相關記憶體供程式使用,或者通過手動方式顯式釋放cache記憶體:
echo 3 > /proc/sys/vm/drop_caches

$cat /proc/meminfo 
MemTotal:        8388608 kB
MemFree:         6880760 kB
Buffers:               0 kB
Cached:          1490828 kB
SwapCached:            0 kB
Active:          1224960 kB
Inactive:         282832 kB
Active(anon):      17028 kB
Inactive(anon):      348 kB
Active(file):    1207932 kB
Inactive(file):   282484 kB
Unevictable:           0 kB
Mlocked:            4884 kB
SwapTotal:       1999864 kB
SwapFree:        1999864 kB
Dirty:               596 kB
Writeback:             0 kB
AnonPages:        202420 kB
Mapped:            37400 kB
Shmem:              4736 kB
Slab:            1937380 kB
SReclaimable:    1739632 kB
SUnreclaim:       197748 kB
KernelStack:       15920 kB
PageTables:        30188 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:    51319712 kB
Committed_AS:   13594600 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      662532 kB
VmallocChunk:   34359070640 kB
HardwareCorrupted:     0 kB
AnonHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:        4096 kB
DirectMap2M:     2076672 kB
DirectMap1G:    98566144 kB

0x2: 程式使用記憶體統計

在32位作業系統中,每個程式擁有4G的虛擬記憶體空間,其中0~3GB是每個程式的私有使用者空間,這個空間對系統中其他程式是不可見的。3~4GB是linux核心空間,由系統所有的程式以及核心所共享的。通過訪問/proc/{pid}/下相關檔案,可以瞭解每個執行緒(程式)虛擬記憶體空間的使用情況,從而瞭解每個執行緒(程式)所消耗記憶體的多少

可以通過檢視/proc/{pid}/maps檔案來獲取相關的虛擬地址空間內容

sudo cat /proc/1417/maps
2b4494635000-2b449463b000 r-xp 00000000 03:01 819306                     /sbin/klogd
2b449463b000-2b449463c000 rw-p 2b449463b000 00:00 0 
2b449483b000-2b449483c000 rw-p 00006000 03:01 819306                     /sbin/klogd
2b449483c000-2b449483d000 rw-p 2b449483c000 00:00 0 
2b449483d000-2b4494859000 r-xp 00000000 03:01 409674                     /lib64/ld-2.5.so
2b4494859000-2b449485b000 rw-p 2b4494859000 00:00 0 
2b4494a59000-2b4494a5a000 r--p 0001c000 03:01 409674                     /lib64/ld-2.5.so
2b4494a5a000-2b4494a5b000 rw-p 0001d000 03:01 409674                     /lib64/ld-2.5.so
2b4494a5b000-2b4494ba9000 r-xp 00000000 03:01 409683                     /lib64/libc-2.5.so
2b4494ba9000-2b4494da9000 ---p 0014e000 03:01 409683                     /lib64/libc-2.5.so
2b4494da9000-2b4494dad000 r--p 0014e000 03:01 409683                     /lib64/libc-2.5.so
2b4494dad000-2b4494dae000 rw-p 00152000 03:01 409683                     /lib64/libc-2.5.so
2b4494dae000-2b4494db4000 rw-p 2b4494dae000 00:00 0 
2b44adeca000-2b44adeeb000 rw-p 2b44adeca000 00:00 0                      [heap]
7fff58134000-7fff58149000 rw-p 7ffffffe9000 00:00 0                      [stack]
7fff58162000-7fff58165000 r-xp 7fff58162000 00:00 0                      [vdso]
ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0                  [vsyscall]

當程式申請記憶體時,實際上是glibc中內建的記憶體管理器接收了該請求,隨著程式申請記憶體的增加,記憶體管理器會通過系統呼叫陷入核心,從而為程式分配更多的記憶體
針對堆段的管理,核心提供了兩個系統呼叫brk和mmap,brk用於更改堆頂地址,而mmap則為程式分配一塊虛擬地址空間
當程式向glibc申請記憶體時,如果申請記憶體的數量大於一個閥值的時候,glibc會採用mmap為程式分配一塊虛擬地址空間,而不是採用brk來擴充套件堆頂的指標。預設情況下,此閥值是128K,可以通過函式來修改此值
如果在實際的除錯過程中,懷疑某處發生了記憶體洩露,可以檢視該程式的maps表,看程式的堆段或者mmap段的虛擬地址空間是否持續增加,如果是,說明很可能發生了記憶體洩露,如果mmap段虛擬地址空間持續增加,還可以看到各個段的虛擬地址空間的大小,從而可以確定是申請了多大的記憶體,對除錯記憶體洩露類問題可以起到很好的定位作用

Relevant Link:

http://os.51cto.com/art/201304/388070.htm

 

2. 記憶體洩漏(memory leak)

記憶體洩漏(memory leak),指由於疏忽或錯誤造成程式未能釋放已經不再使用的記憶體的情況
在程式設計時進行動態記憶體分配是非常必要的,它可以在程式執行的過程中幫助分配所需的記憶體,而不是在程式啟動的時候就進行分配。然而有效地管理這些記憶體同樣也是非常重要的
在大型的、複雜的應用程式中,記憶體洩漏是常見的問題,當以前分配的一片記憶體不再需要使用或無法訪問時,但是卻並沒有釋放它,那麼對於該程式來說,會因此導致總可用記憶體的減少,這時就出現了記憶體洩漏

0x1: 記憶體洩漏的發生方式

1. 常發性記憶體洩漏
發生記憶體洩漏的程式碼會被多次執行到,每次被執行的時候都會導致一塊記憶體洩漏 

2. 偶發性記憶體洩漏
發生記憶體洩漏的程式碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測記憶體洩漏至關重要

3. 一次性記憶體洩漏
發生記憶體洩漏的程式碼只會被執行一次,或者由於演算法上的缺陷,導致總會有一塊且僅有一塊記憶體發生洩漏

4. 隱式記憶體洩漏
程式在執行過程中不停的分配記憶體,但是直到結束的時候才釋放記憶體。嚴格的說這裡並沒有發生記憶體洩漏,因為最終程式釋放了所有申請的記憶體。但是對於一個伺服器程式,需要執行幾天,幾周甚至幾個月,不及時釋放記憶體也可能導致最終耗盡系統的所有記憶體。所以,我們稱這類記憶體洩漏為隱式記憶體洩漏

0x2: 常用記憶體洩漏檢測工具

C/C++
1. Valgrind: Debugging and profiling Linux programs, aiming at programs written in C and C++ 
2. ccmalloc: Linux和Solaris下對C和C++程式的簡單的使用記憶體洩漏和malloc除錯庫 
3. LeakTracer: Linux、Solaris和HP-UX下跟蹤和分析C++程式中的記憶體洩漏 
4. Electric Fence: Linux分發版中由Bruce Perens編寫的malloc()除錯庫 
5. Leaky: Linux下檢測記憶體洩漏的程式 
6. Dmalloc: Debug Malloc Library 
7. MEMWATCH: 由Johan Lindh編寫,是一個開放原始碼C語言記憶體錯誤檢測工具,主要是通過gcc的precessor來進行 
8. KCachegrind: A visualization tool for the profiling data generated by Cachegrind and Calltree 

Java
1. Memory Analyzer: 是一款開源的JAVA記憶體分析軟體,查詢記憶體洩漏,能容易找到大塊記憶體並驗證誰在一直佔用它,它是基於Eclipse RCP(Rich Client Platform),可以下載RCP的獨立版本或者Eclipse的外掛 
2. JProbe: 分析Java的記憶體洩漏 
3. JProfiler: 一個全功能的Java剖析工具,專用於分析J2SE和J2EE應用程式。它把CPU、執行緒和記憶體的剖析組合在一個強大的應用中,GUI可以找到效能瓶頸、抓出記憶體洩漏、並解決執行緒的問題 
4. JRockit: 用來診斷Java記憶體洩漏並指出根本原因,專門針對Intel平臺並得到優化,能在Intel硬體上獲得最高的效能 
5. YourKit .NET & Java Profiling: 業界領先的Java和.NET程式效能分析工具 
6. AutomatedQA: AutomatedQA的獲獎產品performance profiling和memory debugging工具集的下一代替換產品,支援Microsoft, Borland, Intel, Compaq 和 GNU編譯器。可以為.NET和Windows程式生成全面細緻的報告,從而幫助您輕鬆隔離並排除程式碼中含有的效能問題和記憶體/資源洩露問題。支援.Net 1.0,1.1,2.0,3.0和Windows 32/64位應用程式 
7. Compuware DevPartner Java Edition: 包含Java記憶體檢測,程式碼覆蓋率測試,程式碼效能測試,執行緒死鎖,分散式應用等幾大功能模組 

.NET
1. .NET Memory Profiler: 找到記憶體洩漏並優化記憶體使用針對C#,VB.Net,或其它.Net程式 
2. Windows Leaks Detector: 探測任何Win32應用程式中的任何資源洩漏(記憶體,控制程式碼等),基於Win API呼叫鉤子 
3. DTrace: 一款開源動態跟蹤Dynamic Tracing,能在Unix類似平臺執行,使用者能夠動態檢測作業系統核心和使用者程式,以更精確地掌握系統的資源使用狀況,提高系統效能,減少支援成本,並進行有效的調節 
4. IBM Rational PurifyPlus: 幫助開發人員查明C/C++、託管.NET、Java和VB6程式碼中的效能和可靠性錯誤。PurifyPlus 將記憶體錯誤和洩漏檢測、應用程式效能描述、程式碼覆蓋分析等功能組合在一個單一、完整的工具包中 
5. Parasoft Insure++: 針對C/C++應用的執行時錯誤自動檢測工具,它能夠自動監測C/C++程式,發現其中存在著的記憶體破壞、記憶體洩漏、指標錯誤和I/O等錯誤。並通過使用一系列獨特的技術(SCI技術和變異測試等),徹底的檢查和測試我們的程式碼,精確定位錯誤的準確位置並給出詳細的診斷資訊。能作為Microsoft Visual C++的一個外掛執行 
6. Compuware DevPartner for Visual C++ BoundsChecker Suite: 為C++開發者設計的執行錯誤檢測和除錯工具軟體。作為Microsoft Visual Studio和C++ 6.0的一個外掛執行 
7. Electric Software GlowCode: 包括記憶體洩漏檢查,code profiler,函式呼叫跟蹤等功能。給C++和.Net開發者提供完整的錯誤診斷,和執行時效能分析工具包 

FireFox / IE
1. Leak Monitor: 一個Firefox擴充套件,能找出跟Firefox相關的洩漏型別 
2. IE Leak Detector (Drip/IE Sieve): Drip和IE Sieve leak detectors幫助網頁開發員提升動態網頁效能通過報告可避免的因為IE侷限的記憶體洩漏。
3. JavaScript Memory Leak Detector: 微軟全球產品開發歐洲團隊(Global Product Development- Europe team, GPDE) 釋出的一款除錯工具,用來探測JavaScript程式碼中的記憶體洩漏,執行為IE系列的一個外掛 

0x3: 記憶體檢查原理

Memcheck檢測記憶體問題的原理如下圖所示

Memcheck 能夠檢測出記憶體問題,關鍵在於其建立了兩個全域性表

1. Valid-Value表
對於程式的整個地址空間中的每一個位元組(byte),都有與之對應的 8 個 bits;對於 CPU 的每個暫存器,也有一個與之對應的 bit 向量。這些 bits 負責記錄該位元組或者暫存器值是否具有有效的、已初始化的值 

2. Valid-Address表
對於程式整個地址空間中的每一個位元組(byte),還有與之對應的 1 個 bit,負責記錄該地址是否能夠被讀寫 

檢測原理

1. 當要讀寫記憶體中某個位元組時,首先檢查這個位元組對應的 A bit(Valid-Adress Map)。如果該A bit顯示該位置是無效位置,memcheck 則報告讀寫錯誤 
2. 核心(core)類似於一個虛擬的 CPU 環境,這樣當記憶體中的某個位元組被載入到真實的 CPU 中時,該位元組對應的 V bit(Valid-Value Map) 也被載入到虛擬的 CPU 環境中。一旦暫存器中的值,被用來產生記憶體地址,或者該值能夠影響程式輸出,則 memcheck 會檢查對應的V bits,如果該值尚未初始化,則會報告使用未初始化記憶體錯誤 

Relevant Link:

http://blog.csdn.net/ithomer/article/details/6928318
http://www.ibm.com/developerworks/cn/linux/l-cn-valgrind/

 

3. Valgrind使用

Valgrind是一套Linux下,開放原始碼(GPL V2)的模擬除錯工具的集合。Valgrind由核心(core)以及基於核心的其他除錯工具組成。核心類似於一個框架(framework),它模擬了一個CPU環境,並提供服務給其他工具: 而其他工具則類似於外掛(plug-in),利用核心提供的服務完成各種特定的記憶體除錯任務

Valgrind包括如下一些工具

1. Memcheck
這是valgrind應用最廣泛的工具,一個重量級的記憶體檢查器,能夠發現開發中絕大多數記憶體錯誤使用情況,比如:使用未初始化的記憶體,使用已經釋放了的記憶體,記憶體訪問越界等。這也是本文將重點介紹的部分,Valgrind 中包含的 Memcheck 工具可以檢查以下的程式錯誤
    1) 使用未初始化的記憶體 (Use of uninitialised memory)
      2) 使用已經釋放了的記憶體 (Reading/writing memory after it has been free’d)
      3) 使用超過malloc分配的記憶體空間(Reading/writing off the end of malloc’d blocks)
      4) 對堆疊的非法訪問 (Reading/writing inappropriate areas on the stack)
      5) 申請的空間是否有釋放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
      6) malloc/free/new/delete申請和釋放記憶體的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
      7) src和dst的重疊(Overlapping src and dst pointers in memcpy() and related functions)
      8) 重複free

2. Callgrind
它主要用來檢查程式中函式呼叫過程中出現的問題

3. Cachegrind
它主要用來檢查程式中快取使用出現的問題

4. Helgrind
它主要用來檢查多執行緒程式中出現的競爭問題

5. Massif
它主要用來檢查程式中堆疊使用中出現的問題

6. Extension
可以利用core提供的功能,自己編寫特定的記憶體除錯工具 

0x1: 編譯安裝

wget http://valgrind.org/downloads/valgrind-3.4.1.tar.bz2
tar xvf valgrind-3.4.1.tar.bz2
cd valgrind-3.4.1/
./configure --prefix/home/zhenghan.zh/valgrind
make
make install

0x2: 檢測使用

為了使valgrind發現的錯誤更精確,如能夠定位到原始碼行,建議在編譯時加上-g引數,編譯優化選項請選擇O0,雖然這會降低程式的執行效率

#include <stdlib.h>

void fun()
{
    int *p = (int*)malloc(10 * sizeof(int));
    //記憶體越界寫入
    p[10] = 0;
}

int main(int argc, char* argv[])
{
    fun();
    return 0;
}

//gcc –g –O0 sample.c –o sample

執行valgrind

/home/zhenghan.zh/valgrind/bin/valgrind --tool=memcheck --leak-check=full /home/zhenghan.zh/memcheck/sample
/home/zhenghan.zh/valgrind/bin/valgrind /home/zhenghan.zh/memcheck/sample

Relevant Link:

http://zyan.cc/post/419/
http://www.ibm.com/developerworks/cn/linux/l-cn-valgrind/

 

Copyright (c) 2014 LittleHann All rights reserved

 

相關文章