Linux下幾款C++程序中的內存泄露檢查工具

本文更新記錄 作者gatieme, CSDNGitHub

2016-08-08 11:37, 添加了Linux內核內存檢測工具Kmemleak

2016-08-08 11:43, 添加了鏈接內存泄露檢測工具比較

Linux下編寫C或者C++程序,有很多工具,但是主要編譯器仍然是gcc和g++。最近用到STL中的List編程,爲了檢測寫的代碼是否會發現內存泄漏,瞭解了一下相關的知識。

所有使用動態內存分配(dynamic memory allocation)的程序都有機會遇上內存泄露(memory leakage)問題,在Linux裏有三種常用工具來檢測內存泄露的情況,包括:

參見 http://elinux.org/Memory_Debuggers 
偶然發現的內存泄露檢測工具比較

工具 描述
valgrind 一個強大開源的程序檢測工具
mtrace GNU擴展, 用來跟蹤malloc, mtrace爲內存分配函數(malloc, realloc, memalign, free)安裝hook函數
dmalloc 用於檢查C/C++內存泄露(leak)的工具,即檢查是否存在直到程序運行結束還沒有釋放的內存,以一個運行庫的方式發佈
memwatch 和dmalloc一樣,它能檢測未釋放的內存、同一段內存被釋放多次、位址存取錯誤及不當使用未分配之內存區域
mpatrol 一個跨平臺的 C++ 內存泄漏檢測器
dbgmem  
Electric Fence  

1 被測程序


爲了方便測試,,我們編寫了一個簡單的程序, 循環10次每次申請了一個100個字節的單元, 但是卻不釋放

2 valgrind


2.1 valgrind介紹


是不是說沒有一種內存檢查工具能夠在Linux使用呢,也不是,像開源的valgrind工具還是相當不錯的

  • Memcheck。這是valgrind應用最廣泛的工具,一個重量級的內存檢查器,能夠發現開發中絕大多數內存錯誤使用情況,比如:使用未初始化的內存,使用已經釋放了的內存,內存訪問越界等。這也是本文將重點介紹的部分。

  • Callgrind。它主要用來檢查程序中函數調用過程中出現的問題。

  • Cachegrind。它主要用來檢查程序中緩存使用出現的問題。

  • Helgrind。它主要用來檢查多線程程序中出現的競爭問題。

  • Massif。它主要用來檢查程序中堆棧使用中出現的問題。

  • Extension。可以利用core提供的功能,自己編寫特定的內存調試工具

主頁 下載
valgrind downloads

valgrind介紹

參照

Valgrind簡單用法

Unix下C程序內存泄漏檢測工具Valgrind安裝與使用

valgrind 的使用簡介

應用 Valgrind 發現 Linux 程序的內存問題

如何使用Valgrind memcheck工具進行C/C++的內存泄漏檢測

2.2 安裝


源碼安裝

下載地址)下載最新版的valgrind,按照裏面的README提示,安裝後就可以使用這個工具來檢測內存泄露和內存越界等

/configure

make

sudo make install
  • 1
  • 2
  • 3
  • 4
  • 5

當然也可以使用發行版的自帶的源中安裝

sudo apt install valgrind
  • 1

這是一個沒有界面的內存檢測工具,安裝後,輸入 valgrind ls -l 驗證一下該工具是否工作正常(這是README裏面的方法,實際上是驗證一下對ls -l命令的內存檢測),如果你看到一堆的信息說明你的工具可以使用了。

2.3 使用說明


Valgrind工具包包含多個工具,如Memcheck,Cachegrind,Helgrind, Callgrind,Massif。

Memcheck

最常用的工具,用來檢測程序中出現的內存問題,所有對內存的讀寫都會被檢測到,一切對malloc()/free()/new/delete的調用都會被捕獲。所以,Memcheck 工具主要檢查下面的程序錯誤

內容 描述
使用未初始化的內存 Use of uninitialised memory
使用已經釋放了的內存 Reading/writing memory after it has been free’d
使用超過 malloc分配的內存空間 Reading/writing off the end of malloc’d blocks
對堆棧的非法訪問 Reading/writing inappropriate areas on the stack
申請的空間是否有釋放 Memory leaks – where pointers to malloc’d blocks are lost forever
malloc/free/new/delete申請和釋放內存的匹配 Mismatched use of malloc/new/new [] vs free/delete/delete []
src和dst的重疊 Overlapping src and dst pointers in memcpy() and related functions

這些問題往往是C/C++程序員最頭疼的問題,Memcheck在這裏幫上了大忙。

Callgrind

和gprof類似的分析工具,但它對程序的運行觀察更是入微,能給我們提供更多的信息。和gprof不同,它不需要在編譯源代碼時附加特殊選項,但加上調試選項是推薦的。Callgrind收集程序運行時的一些數據,建立函數調用關係圖,還可以有選擇地進行cache模擬。在運行結束時,它會把分析數據寫入一個文件。callgrind_annotate可以把這個文件的內容轉化成可讀的形式。

Cachegrind

Cache分析器,它模擬CPU中的一級緩存I1,Dl和二級緩存,能夠精確地指出程序中cache的丟失和命中。如果需要,它還能夠爲我們提供cache丟失次數,內存引用次數,以及每行代碼,每個函數,每個模塊,整個程序產生的指令數。這對優化程序有很大的幫助。

Helgrind

它主要用來檢查多線程程序中出現的競爭問題。Helgrind尋找內存中被多個線程訪問,而又沒有一貫加鎖的區域,這些區域往往是線程之間失去同步的地方,而且會導致難以發掘的錯誤。Helgrind實現了名爲“Eraser”的競爭檢測算法,並做了進一步改進,減少了報告錯誤的次數。不過,Helgrind仍然處於實驗階段。

Massif

堆棧分析器,它能測量程序在堆棧中使用了多少內存,告訴我們堆塊,堆管理塊和棧的大小。Massif能幫助我們減少內存的使用,在帶有虛擬內存的現代系統中,它還能夠加速我們程序的運行,減少程序停留在交換區中的機率。

此外,lackey和nulgrind也會提供。Lackey是小型工具,很少用到;Nulgrind只是爲開發者展示如何創建一個工具

注意

Valgrind不檢查靜態分配數組的使用情況

Valgrind佔用了更多的內存–可達兩倍於你程序的正常使用量

如果你用Valgrind來檢測使用大量內存的程序就會遇到問題,它可能會用很長的時間來運行測試

2.4 使用說明


編譯程序

g++ -g -o leak leak.c 
  • 1

被檢測程序加入 –g -fno-inline 編譯選項保留調試信息, 否則後面的valgrind不能顯示到出錯行號。

valgrind被設計成非侵入式的,它直接工作於可執行文件上,因此在檢查前不需要重新編譯、連接和修改你的程序。要檢查一個程序很簡單,只需要執行下面的命令就可以了。

valgrind --tool=tool_name program_name
  • 1

比如我們要對ls -l命令做內存檢查,只需要執行下面的命令就可以了

valgrind --tool=memcheck ls -l
  • 1

小提示 
如果不知道有哪些參數, 可以先輸入valgrind –tool=, 然後狂按兩次tab, 會輸出linux系統的只能提示, 同樣,如果你輸入了valgrind –tool=mem再狂按兩次tab,linux系統會爲你自動補全

2.5 使用valgrind檢測Memcheck


下面我們就可以用valgrind對我們的程序檢測leak

valgrind --tool=memcheck --leak-check=full --show-reachable=yes --trace-children=yes    ./leak
  • 1

其中–leak-check=full 指的是完全檢查內存泄漏,

–show-reachable=yes是顯示內存泄漏的地點,

–trace-children=yes是跟入子進程。

當程序正常退出的時候valgrind自然會輸出內存泄漏的信息原理:

輸出內存泄漏的信息原

3 mtrace檢測內存泄露


3.1 mtrace簡介


在一般的linux發行版中,有一個自帶的工具可以很方便的替你完成這些事,這個工具就是mtrace.

mtrace其實是GNU擴展函數,用來跟蹤malloc。

mtrace爲內存分配函數(malloc, realloc, memalign, free)安裝hook函數。這些hook函數記錄內存的申請和釋放的trace信息。 
在程序中,這些trace信息可以被用來發現內存泄漏和釋放不是申請的內存。 
當調用mtrace,mtrace會檢查環境變量MALLOC_TRACE。該環境變量應該包含記錄trace信息的文件路徑。如果文件可以被成功打開,它的大小被截斷爲0。 
如果MALLOC_TRACE沒有設置,或者設置的文件不可用或者不可寫,那麼將不會安裝hook函數,mtrace不生效。

詳細說明可參考man page:man 3 mtrace

3.2 mtrace使用


mtrace能監測程序是否內存泄露

  • 在程序的起始處包含頭文件
#include <mcheck.h>
  • 1
  • 更改環境變量:export MALLOC_TRACE=”mtrace.out”可以加入如下代碼
setenv("MALLOC_TRACE", "mtrace.out", 1);
  • 1
  • 調用函數mtrace()
mtrace()
  • 1
  • 編譯程序帶上 -g 選項
gcc -g -c leak_mtrace.c -o leak_mtrace.o  -std=gnu9x -Wall
  • 1
  • 運行程序一次,儘量調用所有程序內的函數。這時調試信息就已經被寫入我們指定的mtrace.out文件中
./leak_mtrace 
  • 1
  • mtrace a.out mtrace.out查看內存監測情況
mtrace a.out  mtrace.out
  • 1

這裏寫圖片描述

4 dmalloc


dmalloc是一種用於檢查C/C++內存泄露(leak)的工具,即檢查是否存在直到程序運行結束還沒有釋放的內存,並且能夠精確指出在哪個源文件的第幾行。

5 Linux內核的Kmemleak

5.1 Kmemleak檢測工具介紹


Kmemleak工作於內核態是內核自帶的內核泄露檢測工具, 其源代碼位於mm/kmemleak.c

Kmemleak工作於內核態,Kmemleak 提供了一種可選的內核泄漏檢測,其方法類似於跟蹤內存收集器。當獨立的對象沒有被釋放時,其報告記錄在 /sys/kernel/debug/kmemleak中,Kmemcheck能夠幫助定位大多數內存錯誤的上下文。

5.2 Kmemleak使用過程概述

  • 首先`CONFIG_DEBUG_KMEMLEAK在Kernel hacking中被使能.

  • 查看內核打印信息詳細過程如下:

    1. 掛載debugfs文件系統 
      mount -t debugfs nodev /sys/kernel/debug/
    2. 開啓內核自動檢測線程 
      echo scan > /sys/kernel/debug/kmemleak
    3. 查看打印信息 
      cat /sys/kernel/debug/kmemleak
    4. 清除內核檢測報告,新的內存泄露報告將重新寫入/sys/kernel/debug/kmemleak 
      echo clear > /sys/kernel/debug/kmemleak

內存掃描參數可以進行修改通過向/sys/kernel/debug/kmemleak 文件寫入。 參數使用如下

off 禁用kmemleak(不可逆)
stack=on 啓用任務堆棧掃描(default)
stack=off 禁用任務堆棧掃描
scan=on 啓動自動記憶掃描線程(default)
scan=off 停止自動記憶掃描線程
scan=<secs> 設置n秒內自動記憶掃描
scan 開啓內核掃描
clear 清除內存泄露報告
dump=<addr> 轉存信息對象在<addr>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

通過“kmemleak = OFF”,也可以在啓動時禁用Kmemleak在內核命令行。在初始化kmemleak之前,內存的分配或釋放這些動作被存儲在一個前期日誌緩衝區。這個緩衝區的大小通過配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE設置。

5.3 Kmemleak動態檢測原理


通過的kmalloc、vmalloc、kmem_cache_alloc等內存分配會跟蹤其指針,連同其他的分配大小和堆棧跟蹤信息,存儲在PRIO搜索樹。相應的釋放函數調用跟蹤和指針就會從kmemleak數據結構中移除。 
分配的內存塊,被認爲是獨立的,如果沒有指針指向它起始地址或塊的內部的任何位置,可以發現掃描內存(包括已保存的寄存器)。這意味着,有可能沒有辦法爲內核通過所分配的地址傳遞塊到一個釋放函數,因此,該塊被認爲是一個內存泄漏。

掃描算法步驟: 
1. 標記的所有分配對象爲白色(稍後將剩餘的白色物體考慮獨立的)

  1. 掃描存儲器與所述數據片段和棧開始,檢查對地址的值存儲在PRIO搜索樹。如果一個白色的對象的指針被發現,該對象將被添加到灰名單

  2. 掃描的灰色對象匹配的地址(一些白色物體可以變成灰色,並添加結束時的灰名單),直到黑色集結束

  3. 剩下的白色物體被認爲是獨立兒,並報告寫入/sys/kernel/debug/kmemleak。 
    一些分配的內存塊的指針在內核的內部數據結構和它們不能被檢測爲孤兒。對避免這種情況,kmemleak也可以存儲的數量的值,指向一個內的塊的地址範圍內的地址,需要找到使塊不被認爲是泄漏.

5.4 kmem相關函數


從kernel源代碼中的目錄include /linux/kmemleak.h中可查看函數原型的頭

函數 功能
kmemleak_init 初始化kmemleak
kmemleak_alloc 一個內存塊分配的通知
kmemleak_alloc_percpu 通知的一個percpu的內存塊分配
kmemleak_free 通知的內存塊釋放
kmemleak_free_part 通知釋放部分內存塊
kmemleak_free_percpu 一個percpu內存塊釋放的通知
kmemleak_not_leak 當不是泄露時,標記對象
kmemleak_ignore 當泄漏時不掃描或報告對象
kmemleak_scan_area 添加掃描區域內的內存塊
kmemleak_no_scan 不掃描的內存塊
kmemleak_erase 刪除一個指針變量的舊值
kmemleak_alloc_recursive 爲kmemleak_alloc,只檢查遞歸
kmemleak_free_recursive 爲kmemleak_free,只檢查遞歸
版權聲明:本文爲博主原創文章 && 轉載請著名出處 @ http://blog.csdn.net/gatieme
轉自:http://blog.csdn.net/gatieme/article/details/51959654
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章