使用 DrMemory 詳細教程


Dr Memory 簡介

Dr. Memory 是一個開源免費的內存檢測工具,它能夠及時發現內存相關的編程錯誤,比如未初始化訪問、內存非法訪問以及內存泄露等。它不僅能夠在 Linux 下面工作,也能在微軟的 Windows 操作系統上工作。不過,本文撰寫時,DrMemory 僅能支持 32 位程序,這是它的一個巨大缺陷,但相信隨着開發的進行,DrMemory 會推出支持 64 位程序的版本。

Dr Memory 與 Valgrind 類似,可以直接檢查已經編譯好的可執行文件。用戶不用改寫被檢查程序的源代碼,也無須重新鏈接第三方庫文件,使用起來非常方便。

Dr. Memory 建立在 DynamoRIO 這個動態二進制插樁平台上。動態監測程序的運行,並對內存訪問相關的執行代碼進行動態修改,記錄其行為,並采用先進的算法進行錯誤檢查。

根據 DrMemory 開發人員發表在 CGO 2011上的論文 Practical Memory Checking with Dr. Memory,DrMemory 對程序的正常執行影響較小,這在同類工具中是比較領先的。其 performance 和 Valgrind 的比較如圖 1 所示(圖片源自 DrMemory 主頁):

圖 1. 和 Valgrind 的性能比較

 

Valgrind 對程序的正常運行影響較大,一般來說如果進行全面內存檢測,會使程序的運行速度有 50 到 300 倍的減慢。而 DrMemory 在這個方面則有一定的優勢。

易用性和性能是 DrMemory 的主要優點,此外 DrMemory 可以用於調試 Windows 程序,因此它被廣泛認為是 Windows 上的 Valgrind 替代工具。在 Linux 平台中,DrMemory 也往往可以作為 Valgrind 之外的另一個選擇。

DrMemory 對內存泄露的監測采用了比較獨特的算法,大量減少了”false positive”,即虛假錯誤。如果您使用 Valgrind 等工具后仍無法找到程序中的內存錯誤,不妨試試 DrMemory 吧。

在 Linux 上,DrMemory 的目前版本尚不能調試 64 位程序,這是它的一個比較大的缺點。

DrMemory 的安裝

在 Linux 上,安裝 Dr Memory 非常簡單,簡單地將下載包解壓即可,如

tar –xzvf DrMemory-Linux-1.4.6-2.tar.gz

要想使用 DrMemory,要保證下面這些軟件已經正確安裝:

perl、objdump、addr2line。

在任何一個當前的 Linux 發行版中,這幾個軟件應該都已經安裝了,因此基本上您只需要下載 DrMemory 的 tar 包,然后解壓即可使用了。

Windows 上 DrMemory 提供了可執行安裝包,只需點擊下一步,即可安裝完畢。

Hello DrMemory,第一印象

DrMemory 的使用很簡單,可以說它是傻瓜式。首先我的DrMemory安裝路徑是C:\Program Files (x86)\Dr. Memory\bin64\drmemory.exe;示例程序可執行文件路徑:C:\Users\31937\Desktop\test\bin\Debug\test.exe,然后在執行drmemory.exe  C:\Users\31937\Desktop\test\bin\Debug\test.exe命令時,先需要將控制台路徑切換到你的DrMemory安裝路徑下,然后執行drmemory.exe  C:\Users\31937\Desktop\test\bin\Debug\test.exe命令

C:\Program Files (x86)\Dr. Memory\bin64>drmemory.exe  C:\Users\31937\Desktop\test\bin\Debug\test.exe

示例程序1:

1 #include <stdlib.h>
2 using namespace std;
3 
4 int main()
5 {
6     int *pPtr = (int *)malloc(sizeof(int));
7     return 0;
8 }

 

執行完命令后控制台顯示的結果為:

屏幕上會有如上所示的錯誤匯總,4 byte(s) of leak(s) 並且將定位在main.cpp的第6行。不錯吧。根據提示,更多的細節被寫入一個 result 文本文件。打開並查看該文件,就可以知道程序在哪里出現了內存錯誤了。真是太方便了。不過 result 文件是否容易閱讀呢?下面我們來詳細解釋如何閱讀 DrMemory 產生的 result 文件。

一、DrMemory 錯誤報告類型

DrMemory總共可以檢測出4種主要錯誤他們分別是內存非法訪問(Unaddressable Access)、未初始化讀(Uninitialized Access)、Heap 操作參數錯誤(Invalid Heap Argument) 、內存泄漏(Memory Leaks),下面對這幾種主要錯誤來進行詳細講解:

1)內存非法訪問(Unaddressable Access)

DrMemory 認為任何對未分配內存區域的讀寫都是非法的。

非法訪問就是對以上三種方法分配的內存區域之外進行的訪問。常見的問題包括 buffer overflow、數組越界、讀寫已經 free 的內存、堆棧溢出等等。讓我們測試下面這個問題程序。

例子程序2:

 1 #include <iostream>
 2 #include <stdlib.h>
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     char *x = (char *)malloc(sizeof(char));
 8     char c = *(x+8);   //buffer overlow
11     return 0;
12 }

Buffer overflow

例子程序的第8 行存在 buffer overflow。在內存中,buffer 的分布如下圖所示:

圖 2. Buffer 分布

 

訪問 x+8 將產生一個非法內存訪問。對此,Dr Memory 將給出如下的錯誤信息:

 首先用大寫的單詞 UNADDRESSABLE ACCESS 表明這是一個非法訪問錯誤。接着,“reading 0x01397620-0x01397621 1 byte(s)”表示這是一個非法讀,讀取的范圍為 0x01397620到 0x01397621,一共讀了 1 個 byte。接下來的三行是調用堆棧信息,可以方便地看到錯誤發生在哪個源文件的哪一行(程序 t 需要在用 gcc 編譯的時候給定-g 選項)。此外 DrMemory 還給出了一些輔助的錯誤信息。比如:

1.錯誤發生的位置:# 0 main               [C:/Users/31937/Desktop/test/main.cpp:8]

2.錯誤發生的時間:Note: @0:00:00.516 in thread 9716。這表明錯誤是程序開始的第 0.516 秒后發生的,有些情況下,人們可以根據這個時間進行輔助判斷。

3.錯誤細節:Note: refers to 7 byte(s) beyond last valid byte in prior malloc。這里給出了錯誤的詳細信息,如前所述,造成非法訪問的可能很多,在本例中是 buffer overflow,因此這里的詳細信息可以幫助我們了解非法訪問的具體原因。

Note: prev lower malloc:  0x01397618-0x01397619。這里給出了 overflow 之前的合法內存地址,有些情況下對於查錯 有一定的幫助。

Note: instruction: mov    0x08(%eax) -> %al。這里給出的是造成錯誤的具體指令。

2)未初始化讀(Uninitialized Access)

讀取未初始化的內存其結果是未知的,使用這樣的數據是很危險的。讓我們查看下面這個測試程序(並不危險的程序):

示例程序3:

 1 #include <iostream>
 2 #include <stdlib.h>
 3 using namespace std;
 4 
 5 class Test
 6 {
 7     public:int m_iNum;
 8 };
 9 int main()
10 {
11     Test pTest;
12     cout<<pTest.m_iNum;
13     return 0;
14 }

運行結果:

首先用大寫的單詞 UNINITIALIZED READ 表明這是一個未初始化讀錯誤。這是常見的類成員變量沒有進行初始化錯誤

3)Heap 操作參數錯誤(Invalid Heap Argument)

C 語言用 malloc()、free()等函數處理內存 heap 的使用。如果使用不當,會造成未知后果,比如傳入 free()的參數不正確,可能造成 crash,或者用 new 分配,卻用 free 來釋放內存。這類錯誤 DrMemory 稱之為 Invalid Heap Argument 錯誤。

示例程序4:

 1 #include <iostream>
 2 #include <stdlib.h>
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     int *pPtr = (int *)malloc(sizeof(int));
 8     free(pPtr);
 9     free(pPtr);
10     return 0;
11 }

運行結果

首先用大寫的單詞 INVALID HEAP ARGUMENT 表明這是一個Heap 操作參數錯誤。

4)內存泄漏(Memory Leaks) 

內存泄露是常見的內存錯誤,我們可能都曾經遇到過。不過 Dr.Memory 對內存泄露的定義比較獨特,在程序退出之前,Dr.Memory 把所有依然被分配的內存分為三類:

Still-reachable allocation

很多程序分配了內存之后,在其整個生命周期內都不釋放。雖然這是一種泄露,但實際上多數情況下這是無害的,甚至是特意這樣設計的。因此 Dr.Memory 並不認為這是一種內存泄露,而稱之為”Still-reachable allocation”。

Leak

有一些內存無法再被釋放,因為指向該內存的指針丟失了。比如下面這個代碼:

內存 Leak 例子代碼
1 char *ptr = (char *)malloc(sizeof(char)*10);
2 char *ptr1 = (char *)malloc(sizeof(char)*100);

3 ptr=ptr1; //leak

DrMemory 稱這類錯誤為內存泄露。因為這些內存已經沒有辦法被釋放了。

Possible Leak

如前所述指向內存的指針被修改會被認為是一個 Leak,但並非所有的指針修改都是一個 Leak。DrMemory 利用一些經驗規則(Heuristic)將以下幾種指針修改列為 Possible Leak。

第一種情況:C++程序利用 new[]分配了一個數組,該數組的每個元素都是 擁有自己的析構函數的復雜數據結構。這種情況下,New 操作符為每個元素加上一個 header 用來保存數組的個數,以便 delete[]操作符知道需要調用多少個析構函數。但 new[]返回 caller 的是 header 之后的地址,這樣就變成了一個 mid-allocation 指針。這可能被 Dr memory 認為是一個內存泄露。但可以使用-no_midchunk_new_ok 選項讓 DrMemory 將這類錯誤報告為”possible leak”而非”leak”。

參考下圖,理解這種情況。

圖 4.mid-chunk new

從堆分配器的角度來看,buffer 的起點在 A 處,但 new 返回 B,給 Object 變量賦值。從某種角度上看,指針 A 丟失了,是一個 leak,但實際上,當調用 delete []操作符時,C++運行時庫會自動將 Object 指針減 4,從而指向 A 點,再進行釋放。某些編譯器不使用這種做法,則沒有這個問題。

第二種情況,某些 C++編譯器在處理多繼承時,會出現 mid-chunk 指針。很抱歉,具體細節本人也不甚了解。Dr Memory 的原文如下:it includes instances of a pointer to a class with multiple inheritance that is cast to one of the parents: it can end up pointing to the subobject representation in the middle of the allocation. 您可以用-no_midchunk_inheritance_ok 選項將這類“錯誤”報告為”possible leak” 。

還有一種可能:std::string 類把一個 char[]數組放置在分配空間中,並返回一個指針直接指向它,造成了一個 mid-allocation 指針。您可以用-no_midchunk_string_ok 選項讓這類錯誤顯示為”possible leak”。

示例程序5:

1 #include <stdlib.h>
2 using namespace std; 3 4 int main() 5 { 6 int *pPtr = (int *)malloc(sizeof(int)); 7 return 0; 8 }

 

顯示的結果:

屏幕上會有如上所示的錯誤匯總,4 byte(s) of leak(s) 並且將定位在main.cpp的第6行。不錯吧。根據提示,更多的細節被寫入一個 result 文本文件。打開並查看該文件,就可以知道程序在哪里出現了內存錯誤了。真是太方便了。不過 result 文件是否容易閱讀呢?下面我們來詳細解釋如何閱讀 DrMemory 產生的 result 文件。

結束語

很高興也很遺憾我能為大家介紹一款新的內存調試工具。我們恐怕已經面臨太多的選擇,假如您用 Google 搜索,會找到很多類似的工具,他們中的多數都不易使用,也許您花了很多的精力去學習某款工具的使用,卻發現它根本就不適合您的環境。

可惜,不同的工具有不同的優點和缺點,直到今天,尚沒有一款工具能夠替代所有其它的同類。寫程序有時很無奈,尤其是面對內存錯誤的時候,多一個選擇也許會讓你擺脫困境。下一次,假如人們告訴您程序有內存泄露,那么不妨用 DrMemory 試一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM