實習目標 | 1. 練習在 Visual Studio 2010 中運用 crtdbg 函式庫偵測記憶體未釋放的錯誤 (Visual Studio 2005/2008) 2. other tools: Valgrind example |
---|---|
步驟一 | 下列程式片段中有記憶體未釋放的錯誤:
struct File { char *name; char *data; } *filePtr; ... filePtr = (struct File *) malloc(sizeof(struct File)); filePtr->name = (char *) malloc(20*sizeof(char)); filePtr->data = (char *) malloc(500*sizeof(char)); ... free(filePtr);上面這個程式片段中配置給 name 和 data 的 520 個位元組並沒有釋放, 只釋放了配置給 struct File 結構的記憶體。 程式裡有這種錯誤會導致什麼現象? 微軟早期的視窗系統中系統執行幾天以後就會出現 "系統資源不足", 不穩定當機的現象, 有的時候你用你的手機也會發現不太穩定需要重開, 常常就是作業系統裡面的程式或是所執行的應用程式裡有這種記憶體 未釋放的錯誤, 程式如果只執行幾分鐘或是幾個小時還不會發生什麼問題, 可是一旦像你的手機上的程式長時間執行就穿幫了。 |
步驟二 | 下面幾個執行程式都有類似的錯誤, 常常在單獨測試時看不到錯誤, 當撰寫的程式很大時, 要找到究竟在什麼地方發生這種錯誤其實有相當的困難度: C/C++ 語言希望給程式撰寫者最大的操作彈性, 演算法裡面所使用的記憶體的配置與釋放完全由程式設計者來控制, 因此寫程式的人必須徹底了解到 "程式配置多少個位元組的記憶體就必須釋放多少個位元組的記憶體" 這個基本原則, 在現在個人電腦動輒有一兩 Giga bytes 的記憶體, 很容易給人一種錯覺, 覺得少釋放幾十個 bytes 應該沒有什麼了不起的, 甚至很難看到系統因此而當掉; 不過這是一種習慣, 一旦養成這種習慣, 你在寫程式時就會常常忘記釋放記憶體, 一旦你的程式必須在資源有限的硬體上來運作, 例如: 手機, PDA, 嵌入式系統, 硬體配件 (DVD, 相機, ...) (這些都是目前台灣賺錢的產品), 那不但會讓自己的程式死掉, 也會害整個機器無法運作下去, 一個工作團隊裡如果有一個成員有這種習慣, 整個開發團隊常常必須付出幾倍的偵錯時間, 如果不找出來這種錯誤, 最後的產品就是不穩定, 在競爭激烈的市場中一定很快被淘汰。 |
步驟三 | 請重新新增一個 testMemLeakage 專案, 新增一個使用 malloc 動態配置記憶體的 main.cpp 檔案如下: #include <stdio.h> #include <malloc.h> #include <conio.h> void main() { int *ptr; int i; ptr = (int *) malloc(100*sizeof(int)); for (i=0; i<100; i++) ptr[i] = i; free(ptr); printf("請按任意鍵繼續 . . .\n"); getch(); } 請 建置 並確定可以執行 [注意: 此處不使用 system("pause"); , 而使用 printf("...");
getch(); 是有特別原因的, Visual Studio 2010 中如果使用 system("pause")
的話, 稍後在步驟四中 crtdbg 如果刪掉 free() 那一列的話, 程式還是可以編譯, 可以執行, 但是就有記憶體未釋放的錯誤, 請以下述方法來自動偵測這種錯誤
|
步驟四 | 當你的程式很大, 有很多的記憶體配置敘述時, 光光知道程式有沒有釋放的記憶體還不太夠,
如果希望透過 memory_leak.h 來找到究竟是哪一個敘述配置的記憶體沒有釋放,
需要編輯 memory_leak.h 檔案, 將其中的
#define TEST_MEM_LEAKS_BREAK_NUM 0修改為前一步驟中看到的錯誤訊息裡面的 61 #define TEST_MEM_LEAKS_BREAK_NUM 61 再 "建置/重建方案" 重建整個專案裡所有程式, 然後請在 cmd 視窗中執行 testMemLeakage, 可以看到下列訊息視窗
或是 (win7~win10) 。。。
如果你的機器上只安裝了 VC2010, 應該會開啟 VC2010 來偵錯, 如果你的機器有安裝其它版本的 VC, 會出現選單讓你挑選: 請挑選 testMemLeakage - Microsoft Visual Studio: Visual Studio 2010 那個, 按下 "是" 以後 請注意: 如果你的機器和 201/203 PC 教室一樣安裝了 win10 1709 + Visual Studio 2017 + Visual Studio 2015 + Visual Studio 2010, 可能是這些版本的程式互相之間有一些衝突, 導致上面的步驟無法啟動 JIT 偵錯工具, 試看看在 "建置/重建方案" 重建整個專案裡所有程式以後, 直接按下 "偵錯/開始偵錯" 可以看到下面 Debugger 的畫面: 程式中斷在 dbgheap.c 的 _heap_alloc_dbg_impl() 這個函式裡面, 這不是你寫的, 但是從上面的呼叫堆疊中可以看到, 你的程式 main() 第 10 列呼叫 malloc() 然後呼叫 _nh_malloc_dbg() 然後呼叫 _nh_malloc_dbg_impl() 然後呼叫 _heap_alloc_dbg_impl(); 你可以用滑鼠雙擊 testMemLeakage.exe!main() 這一列, 程式編輯視窗就會跳到呼叫 malloc() 那一列 (main 的第 10 列), 這個地方配置的記憶體就是沒有釋放掉的記憶體 如果你的偵錯環境中沒有顯示 呼叫堆疊 視窗的話, 你可以在選單 偵錯/視窗選項中找到 (Alt+7), 把 呼叫堆疊 視窗顯示出來 |
步驟五 | 請撰寫一個使用 C++ 的 new 配置記憶體, 使用 delete[]
釋放記憶體的程式如下:
void main() { int *ptr; int i; ptr = new int[100]; for (i=0; i<100; i++) ptr[i] = i; // delete ptr; // 這是一個非常容易犯的錯誤 delete[] ptr; } 請將 delete[] ptr 這一列註解掉, 重複步驟三和步驟四的測試。 注意: 如果你使用 ptr = new int[100]; 來配置記憶體, 但是使用 delete ptr; 來刪除, 或是使用 ptr = new int; 來配置記憶體, 但是使用 delete[] ptr; 來刪除, memory_leak 毫無作用, 完全偵測不到, 可是卻一定有 memeory leakage, 這還蠻糟糕的, 需要使用別的工具來找, 我也還不知道有哪一個工具可以自動偵測, 所以請特別留意不要出現這種要命的記憶體錯誤 另外請注意如果你在建置或是重建專案的時候, 前一次執行的程式沒有關閉, 或是開了另一個視窗用檔案總管看 Debug\ 資料匣, 你的建置會失敗 (注意看建置的輸出訊息), 建置失敗當然就不會有你預期的結果 |
步驟六 | 請向助教說明你如何找到是哪一列的記憶體配置敘述錯誤, 將所完成的專案 (只需保留 .cpp, .h, .sln 以及 .vcxproj 檔案即可; 刪除掉 .sdf, .filters, .users, debug\ 資料匣, 以及 ipch\ 資料匣下的所有內容) 壓縮起來, 選擇 Lab2-3 上傳 (步驟三的 C 版本和步驟五的 C++ 版本可以分別上傳一次) |
在你自己的系統上, 如果你有灌其他的開發環境 (例如 visual studio 2008), 執行程式發生錯誤時系統 可能 不會執行 VC2010 的 debugger, 如果你在下圖選單中找不到, 你可以用下列設定可以改變系統預設的偵錯環境 (你需要有適當的權限) |
確定 "原生" 有勾選 |
Valgrind @ linux(valgrind-3.11.0 supports X86/Linux, AMD64/Linux, ARM/Linux, ARM64/Linux, PPC32/Linux, PPC64/Linux, PPC64LE/Linux, S390X/Linux, MIPS32/Linux, MIPS64/Linux, TILEGX/Linux, X86/Solaris, AMD64/Solaris, ARM/Android (2.3.x and later), ARM64/Android, X86/Android (4.0 and later), MIPS32/Android, X86/Darwin and AMD64/Darwin (Mac OS X 10.10, with initial support for 10.11)) |
|
一 | 下面這個簡單的 C 程式裡有三個錯誤, 讓我們用 Valgrind 來檢查這些
#include <stdio.h> #include <stdlib.h> int main() { int *ptr; ptr = (int *) malloc(100*sizeof(int)); ptr[100] = 10; // buffer overflow ptr[-1] = -1; // buffer underflow return 0; // memory leakage } |
二 | g++ testValgrind.cpp -o testValgrind
|
三 | g++ testValgrind.cpp -o testValgrind
|
四 | g++ testValgrind.cpp -o testValgrind valgrind --tool=memcheck --leak-check=yes
-v ./testValgrind
|
回
C++ 物件導向程式設計課程 首頁 製作日期: 03/06/2013 by 丁培毅 (Pei-yih Ting) E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615 海洋大學 電機資訊學院 資訊工程學系 Lagoon |