說明
使用 VLD 記憶體泄漏檢測工具輔助開發時整理的學習筆記,本篇主要介紹 VLD 庫提供的 22 個外部介面,同系列文章目錄可見 《記憶體泄漏檢測工具》目錄
目錄- 說明
- 1. 頭檔案簡介
- 2. 檔案 vld_def.h 簡介
- 3. 檔案 vld.h 簡介
- 3.1 介面 VLDDisable
- 3.2 介面 VLDEnable
- 3.3 介面 VLDRestore
- 3.4 介面 VLDGlobalDisable
- 3.5 介面 VLDGlobalEnable
- 3.6 介面 VLDReportLeaks
- 3.7 介面 VLDReportThreadLeaks
- 3.8 介面 VLDGetLeaksCount
- 3.9 介面 VLDGetThreadLeaksCount
- 3.10 介面 VLDMarkAllLeaksAsReported
- 3.11 介面 VLDMarkThreadLeaksAsReported
- 3.12 介面 VLDRefreshModules
- 3.13 介面 VLDEnableModule
- 3.14 介面 VLDDisableModule
- 3.15 介面 VLDGetOptions
- 3.16 介面 VLDGetReportFilename
- 3.17 介面 VLDSetOptions
- 3.18 介面 VLDSetModulesList
- 3.19 介面 VLDGetModulesList
- 3.20 介面 VLDSetReportOptions
- 3.21 介面 VLDSetReportHook
- 3.22 介面 VLDResolveCallstacks
- 4. 介面使用思路
1. 頭檔案簡介
VLD 2.5.1 安裝 完成后,安裝目錄的 include
檔案夾下有兩個頭檔案:vld.h
和 vld_def.h
,其中 vld.h
檔案會 #include "vld_def.h"
,因此在實際使用時,專案中要同時添加這兩個頭檔案(或將這兩個檔案放在編譯器的搜索路徑中),但只需包含 vld.h
檔案,
2. 檔案 vld_def.h 簡介
這個檔案里主要以宏的形式定義了 15 個 VLD 配置項的掩碼,這 15 個配置項與 vld.ini
組態檔中的 14 個配置項不是完全對應的,將這些配置項掩碼與 vld.h
檔案中的介面結合起來用,可以實作在運行程序中對 VLD 的配置進行動態修改,其中 9 個配置項可作為介面 VLDSetOptions
的輸入,另外 4 個配置項可作為介面 VLDSetReportOptions
的輸入,剩余的 2 個配置項分別是 VLD_OPT_SELF_TEST
與 VLD_OPT_VLDOFF
,其中 VLD_OPT_SELF_TEST
只能通過修改 vld.ini
檔案中的 SelfTest
進行設定(詳見 配置項 SelfTest),VLD_OPT_VLDOFF
只能通過修改 vld.ini
檔案中的 VLD
進行設定(詳見 配置項 VLD),當 VLD_OPT_VLDOFF
被設定后,所有介面也都會變得不可用,
#define VLD_OPT_AGGREGATE_DUPLICATES 0x0001 // If set, aggregate duplicate leaks in the leak report.
#define VLD_OPT_MODULE_LIST_INCLUDE 0x0002 // If set, modules in the module list are included, all others are excluded.
#define VLD_OPT_REPORT_TO_DEBUGGER 0x0004 // If set, the memory leak report is sent to the debugger.
#define VLD_OPT_REPORT_TO_FILE 0x0008 // If set, the memory leak report is sent to a file.
#define VLD_OPT_SAFE_STACK_WALK 0x0010 // If set, the stack is walked using the "safe" method (StackWalk64).
#define VLD_OPT_SELF_TEST 0x0020 // If set, perform a self-test to verify memory leak self-checking.
#define VLD_OPT_SLOW_DEBUGGER_DUMP 0x0040 // If set, inserts a slight delay between sending output to the debugger.
#define VLD_OPT_START_DISABLED 0x0080 // If set, memory leak detection will initially disabled.
#define VLD_OPT_TRACE_INTERNAL_FRAMES 0x0100 // If set, include useless frames (e.g. internal to VLD) in call stacks.
#define VLD_OPT_UNICODE_REPORT 0x0200 // If set, the leak report will be encoded UTF-16 instead of ASCII.
#define VLD_OPT_VLDOFF 0x0400 // If set, VLD will be completely deactivated. It will not attach to any modules.
#define VLD_OPT_REPORT_TO_STDOUT 0x0800 // If set, the memory leak report is sent to stdout.
#define VLD_OPT_SKIP_HEAPFREE_LEAKS 0x1000 // If set, VLD skip HeapFree memory leaks.
#define VLD_OPT_VALIDATE_HEAPFREE 0x2000 // If set, VLD verifies and reports heap consistency for HeapFree calls.
#define VLD_OPT_SKIP_CRTSTARTUP_LEAKS 0x4000 // If set, VLD skip crt srtartup memory leaks.
3. 檔案 vld.h 簡介
這個檔案里主要宣告了 VLD 的 22 個外部介面,檔案前面的編譯條件 defined _DEBUG || defined VLD_FORCE_ENABLE
表明 VLD 通常只能在 DEBUG
模式下運行,若要在 RELEASE
模式下運行,可以在包含頭檔案 vld.h
前預先定義 VLD_FORCE_ENABLE
宏,一個有趣的現象是,這個 _DEBUG
宏有時候在 RELEASE
模式下也會被定義,參考文章 神秘的 _DEBUG 宏從何處來?,介面中的一些型別別名定義如下:
typedef int VLD_BOOL;
typedef unsigned int VLD_UINT;
typedef size_t VLD_SIZET;
typedef void* VLD_HMODULE;
3.1 介面 VLDDisable
該函式用于禁用當前執行緒的記憶體泄漏檢測功能,
// VLDDisable - Disables Visual Leak Detector's memory leak detection at
// runtime. If memory leak detection is already disabled, then calling this
// function has no effect.
//
// Note: In multithreaded programs, this function operates on a per-thread
// basis. In other words, if you call this function from one thread, then
// memory leak detection is only disabled for that thread. If memory leak
// detection is enabled for other threads, then it will remain enabled for
// those other threads. It was designed to work this way to insulate you,
// the programmer, from having to ensure thread synchronization when calling
// VLDEnable() and VLDDisable(). Without this, calling these two functions
// unsynchronized could result in unpredictable and unintended behavior.
// But this also means that if you want to disable memory leak detection
// process-wide, then you need to call this function from every thread in
// the process.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDDisable ();
3.2 介面 VLDEnable
該函式用于啟用當前執行緒的記憶體泄漏檢測功能,
// VLDEnable - Enables Visual Leak Detector's memory leak detection at runtime.
// If memory leak detection is already enabled, which it is by default, then
// calling this function has no effect.
//
// Note: In multithreaded programs, this function operates on a per-thread
// basis. In other words, if you call this function from one thread, then
// memory leak detection is only enabled for that thread. If memory leak
// detection is disabled for other threads, then it will remain disabled for
// those other threads. It was designed to work this way to insulate you,
// the programmer, from having to ensure thread synchronization when calling
// VLDEnable() and VLDDisable(). Without this, calling these two functions
// unsynchronized could result in unpredictable and unintended behavior.
// But this also means that if you want to enable memory leak detection
// process-wide, then you need to call this function from every thread in
// the process.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDEnable ();
3.3 介面 VLDRestore
該函式用于還原當前執行緒記憶體泄漏檢測功能的開關狀態,
// VLDRestore - Restore Visual Leak Detector's previous state.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDRestore ();
為了便于理解,下面貼出這一介面對應的原始碼(詳見 vld.cpp 第 2548~2561
行):
void VisualLeakDetector::RestoreLeakDetectionState ()
{
tls_t *tls;
if (m_options & VLD_OPT_VLDOFF) {
// VLD has been turned off.
return;
}
// Restore state memory leak detection for the current thread.
tls = getTls();
tls->flags &= ~(VLD_TLS_DISABLED | VLD_TLS_ENABLED);
tls->flags |= tls->oldFlags & (VLD_TLS_DISABLED | VLD_TLS_ENABLED);
}
3.4 介面 VLDGlobalDisable
該函式用于全域禁用 VLD 功能,
// VLDGlobalDisable - Disables Visual Leak Detector's memory leak detection at
// runtime in all threads. If memory leak detection is already disabled,
// then calling this function has no effect.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDGlobalDisable ();
3.5 介面 VLDGlobalEnable
該函式用于全域啟用 VLD 功能,
// VLDGlobalEnable - Enables Visual Leak Detector's memory leak detection
// at runtime in all threads. If memory leak detection is already enabled,
// which it is by default, then calling this function has no effect.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDGlobalEnable ();
3.6 介面 VLDReportLeaks
該函式用于列印整個程式當前執行此函式前的記憶體泄漏報告,由于在程式關閉后 VLD 會自動輸出報告,因此若不需要在程式運行程序中輸出報告,就不需使用此函式,
// VLDReportLeaks - Report leaks up to the execution point.
//
// Return Value:
//
// None.
//
__declspec(dllimport) VLD_UINT VLDReportLeaks ();
3.7 介面 VLDReportThreadLeaks
該函式用于列印指定執行緒在執行此函式前的記憶體泄漏報告,
// VLDReportThreadLeaks - Report thread leaks up to the execution point.
//
// threadId: thread Id.
//
// Return Value:
//
// None.
//
__declspec(dllimport) VLD_UINT VLDReportThreadLeaks (VLD_UINT threadId);
3.8 介面 VLDGetLeaksCount
該函式用于獲取整個程式當前的記憶體泄漏數量,
// VLDGetLeaksCount - Return memory leaks count to the execution point.
//
// Return Value:
//
// None.
//
__declspec(dllimport) VLD_UINT VLDGetLeaksCount ();
3.9 介面 VLDGetThreadLeaksCount
該函式用于獲取指定執行緒當前的記憶體泄漏數量,
// VLDGetThreadLeaksCount - Return thread memory leaks count to the execution point.
//
// threadId: thread Id.
//
// Return Value:
//
// None.
//
__declspec(dllimport) VLD_UINT VLDGetThreadLeaksCount (VLD_UINT threadId);
3.10 介面 VLDMarkAllLeaksAsReported
該函式用于標記當前的泄漏為已經報告過,后續不再報告,
// VLDMarkAllLeaksAsReported - Mark all leaks as reported.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDMarkAllLeaksAsReported ();
3.11 介面 VLDMarkThreadLeaksAsReported
該函式用于標記指定執行緒當前的泄漏為已經報告過,后續不再報告,
// VLDMarkThreadLeaksAsReported - Mark thread leaks as reported.
//
// threadId: thread Id.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDMarkThreadLeaksAsReported (VLD_UINT threadId);
3.12 介面 VLDRefreshModules
該函式用于重繪加載的模塊串列,以便針對動態加載的模塊進行記憶體泄漏檢查,
// VLDRefreshModules - Look for recently loaded DLLs and patch them if necessary.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDRefreshModules();
3.13 介面 VLDEnableModule
該函式用于對指定模塊啟用記憶體泄漏檢測,輸入引數為指定模塊(dll
或者 exe
)的句柄,可以通過呼叫 Win32
的 API
來獲得(常用的有 GetModuleHandleW 函式、LoadLibraryA 函式),
// VLDEnableModule - Enable Memory leak checking on the specified module.
//
// module: module handle.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDEnableModule(VLD_HMODULE module);
例如,若想檢測一個已載入當前行程的元件 test.dll
是否存在記憶體泄漏,可以按以下方式使用該介面(需包含頭檔案 Windows.h
,若為動態加載的模塊,還需額外使用 VLDRefreshModules()
重繪內部的模塊串列):
// 呼叫 Win32 API 獲得模塊句柄
HMODULE h_dll = GetModuleHandleW(L"test.dll");
// 對該模塊啟用 VLD 功能
VLDEnableModule(h_dll);
// 呼叫模塊中的函式
...
3.14 介面 VLDDisableModule
該函式用于對指定模塊禁用記憶體泄漏檢測,與 VLDEnableModule
相對應,輸入引數為指定模塊(dll
或者 exe
)的句柄,可以通過呼叫 Win32
的API
來獲得,
// VLDDisableModule - Disable Memory leak checking on the specified module.
//
// module: module handle.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDDisableModule(VLD_HMODULE module);
3.15 介面 VLDGetOptions
該函式用于獲取當前的配置掩碼值(與 VLDSetOptions
相對應),將掩碼值結合 VLDSetOptions
的 9 個配置掩碼宏可以人工推斷出 VLD 當前的配置,
// VLDGetOptions - Return all current options.
//
// Return Value:
//
// Mask of current options.
//
__declspec(dllimport) VLD_UINT VLDGetOptions();
例如,若先前使用 VLDSetOptions
介面對 VLD 做了以下配置:
VLDSetOptions(VLD_OPT_AGGREGATE_DUPLICATES | VLD_OPT_SKIP_HEAPFREE_LEAKS, 256, 64);
則 VLDGetOptions()
的回傳值為 4097
,計算方式如下:
VLD_OPT_AGGREGATE_DUPLICATES | VLD_OPT_SKIP_HEAPFREE_LEAKS == 0x1001 == 4097
若呼叫此函式前,未使用 VLDSetOptions
介面更改配置,且未修改 vld.ini
組態檔,則該函式的回傳值為默認的 16386
,即 0x4002
,
3.16 介面 VLDGetReportFilename
該函式用于獲取生成的泄漏報告檔案路徑,注意:用于存盤檔案路徑的 wchar_t
陣列 filename
需預分配足夠大的記憶體,以防出現意外的情況,MAX_PATH
為 windows
系統下檔案路徑的最大長度(詳見 MAX_PATH,值為 260
),
// VLDGetReportFilename - Return current report filename.
//
// filename: current report filename (max characters - MAX_PATH).
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDGetReportFilename(wchar_t *filename);
3.17 介面 VLDSetOptions
該函式用于設定配置掩碼值(由相應的配置掩碼宏通過邏輯運算得到)、設定為每個泄漏塊資料轉儲的最大位元組數(參考 配置項 MaxDataDump)、設定對每個泄漏塊進行堆疊跟蹤的最大幀數(參考 配置項 MaxTraceFrames),
// VLDSetOptions - Update the report options via function call rather than INI file.
//
// option_mask: Only the following flags are checked
// VLD_OPT_AGGREGATE_DUPLICATES
// VLD_OPT_MODULE_LIST_INCLUDE
// VLD_OPT_SAFE_STACK_WALK
// VLD_OPT_SLOW_DEBUGGER_DUMP
// VLD_OPT_TRACE_INTERNAL_FRAMES
// VLD_OPT_START_DISABLED
// VLD_OPT_SKIP_HEAPFREE_LEAKS
// VLD_OPT_VALIDATE_HEAPFREE
//
// maxDataDump: maximum number of user-data bytes to dump for each leaked block.
//
// maxTraceFrames: maximum number of frames per stack trace for each leaked block.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDSetOptions(VLD_UINT option_mask, VLD_SIZET maxDataDump, VLD_UINT maxTraceFrames);
查看該介面的 原始碼 可知,第一個引數的合法輸入為以下 9 個配置掩碼宏之一(上面的介面說明并不全面)、或者它們的邏輯或(|
)組合:
// option_mask 的合法輸入:以下 9 個配置宏之一
#define VLD_OPT_AGGREGATE_DUPLICATES 0x0001 // If set, aggregate duplicate leaks in the leak report.
#define VLD_OPT_MODULE_LIST_INCLUDE 0x0002 // If set, modules in the module list are included, all others are excluded.
#define VLD_OPT_SAFE_STACK_WALK 0x0010 // If set, the stack is walked using the "safe" method (StackWalk64).
#define VLD_OPT_SLOW_DEBUGGER_DUMP 0x0040 // If set, inserts a slight delay between sending output to the debugger.
#define VLD_OPT_START_DISABLED 0x0080 // If set, memory leak detection will initially disabled.
#define VLD_OPT_TRACE_INTERNAL_FRAMES 0x0100 // If set, include useless frames (e.g. internal to VLD) in call stacks.
#define VLD_OPT_SKIP_HEAPFREE_LEAKS 0x1000 // If set, VLD skip HeapFree memory leaks.
#define VLD_OPT_VALIDATE_HEAPFREE 0x2000 // If set, VLD verifies and reports heap consistency for HeapFree calls.
#define VLD_OPT_SKIP_CRTSTARTUP_LEAKS 0x4000 // If set, VLD skip crt srtartup memory leaks.
例如,我想同時開啟 VLD_OPT_AGGREGATE_DUPLICATES
與 VLD_OPT_SKIP_HEAPFREE_LEAKS
,并設定 maxDataDump=256
,maxTraceFrames=64
,可以用以下幾種方式傳參,它們的效果是一樣的:
VLDSetOptions(VLD_OPT_AGGREGATE_DUPLICATES | VLD_OPT_SKIP_HEAPFREE_LEAKS, 256, 64);
VLDSetOptions(0x1001, 256, 64);
VLDSetOptions(4097, 256, 64);
需要注意的是,每次使用此函式時,內部都會先將這 9 個配置置零,然后使用新輸入的配置,因此,若輸入不合法,就會丟失對應 bit
處上一次的配置狀態,具體細節可查看原始碼,
3.18 介面 VLDSetModulesList
該函式用于設定要包含或者排除泄漏檢測的模塊串列,閱讀 原始碼 可知,第一個引數 modules
的最大有效長度為 512
,
// VLDSetModulesList - Set list of modules included/excluded in leak detection
// depending on parameter "includeModules".
//
// modules: list of modules to be forcefully included/excluded in leak detection.
//
// includeModules: include or exclude that modules.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDSetModulesList(const wchar_t *modules, VLD_BOOL includeModules);
原始碼(詳見 vld.cpp 第 867~882 行)中判斷某個模塊 modulename
是否在所給串列 m_forcedModuleList
中的核心代碼如下,由于使用的是 wcsstr
函式 進行字串比較,因此模塊名之間可用任意字符或字串進行分隔,且不區分大小寫,這與組態檔 vld.ini
中的 ForceIncludeModules
配置項一樣(詳見 配置項 ForceIncludeModules),另外,從原始碼中也可以看出,當第二個引數為 false
時,將執行后面的判斷陳述句(條件為 wcsstr(m_forcedModuleList, modulename) != NULL
),意味著不在串列中的模塊都會被開啟記憶體檢測,這可能會導致很多誤判,被大量的偽泄漏刷屏,因此需慎傳 false
,
// This module does not import VLD. This means that none of the module's
// sources #included vld.h.
if ((m_options & VLD_OPT_MODULE_LIST_INCLUDE) != 0)
{
if (wcsstr(m_forcedModuleList, modulename) == NULL) {
// Exclude this module from leak detection.
moduleFlags |= VLD_MODULE_EXCLUDED;
}
}
else
{
if (wcsstr(m_forcedModuleList, modulename) != NULL) {
// Exclude this module from leak detection.
moduleFlags |= VLD_MODULE_EXCLUDED;
}
}
在 QT 中實際使用時(測驗環境:QT 5.9.2,MSVC 2015 32bit,Debug 模式,VLD 版本為 2.5.1),發現這個函式好像只能修改內部的 m_forcedModuleList
串列,并沒有按照第二個引數 includeModules
立馬更新指定模塊的檢測狀態,即使接著使用 VLDRefreshModules()
函式進行重繪,也沒有實作按新串列進行檢測的效果,因此,要實作對某個 DLL
庫的動態檢測控制,最好還是使用 VLDEnableModule
、VLDDisableModule
、VLDRefreshModules
這三個介面,若沒有動態檢測控制的需求,則可以修改組態檔 vld.ini
中的 ForceIncludeModules
配置項(詳見 配置項 ForceIncludeModules),修改組態檔是沒有這個問題的,
3.19 介面 VLDGetModulesList
該函式用于獲取檢測中的模塊串列,獲取的模塊串列是由 VLDSetModulesList
設定的,也可能是由 vld.ini
檔案中的 ForceIncludeModules
設定的,用于存盤模塊串列的 wchar_t
陣列 modules
需預分配足夠大的記憶體(通常為 512
),以防出現意外的情況,第二個引數 size
為想要獲取的字串長度,一般設定為第一個引數 modules
的長度(通常為 512
),
// VLDGetModulesList - Return current list of included/excluded modules
// depending on flag VLD_OPT_TRACE_INTERNAL_FRAMES.
//
// modules: destination string for list of included/excluded modules (maximum length 512 characters).
//
// size: maximum string size.
//
// Return Value:
//
// VLD_BOOL: TRUE if include modules, otherwise FALSE.
//
__declspec(dllimport) VLD_BOOL VLDGetModulesList(wchar_t *modules, VLD_UINT size);
3.20 介面 VLDSetReportOptions
該函式用于設定泄漏報告輸出方式,
// VLDSetReportOptions - Update the report options via function call rather than INI file.
//
// Only the following flags are checked
// VLD_OPT_REPORT_TO_DEBUGGER
// VLD_OPT_REPORT_TO_FILE
// VLD_OPT_REPORT_TO_STDOUT
// VLD_OPT_UNICODE_REPORT
//
// filename is optional and can be NULL.
//
// Return Value:
//
// None.
//
__declspec(dllimport) void VLDSetReportOptions(VLD_UINT option_mask, const wchar_t *filename);
第一個引數的合法輸入為以下 4 個配置掩碼宏之一、或者它們的邏輯或(|
)組合,第二個引數可以為 NULL
但不能省略,
// option_mask 的合法輸入:以下 4 個配置宏之一
#define VLD_OPT_REPORT_TO_DEBUGGER 0x0004 // If set, the memory leak report is sent to the debugger.
#define VLD_OPT_REPORT_TO_FILE 0x0008 // If set, the memory leak report is sent to a file.
#define VLD_OPT_REPORT_TO_STDOUT 0x0800 // If set, the memory leak report is sent to stdout.
#define VLD_OPT_UNICODE_REPORT 0x0200 // If set, the leak report will be encoded UTF-16 instead of ASCII.
需要注意的是,如果設定了 VLD_OPT_UNICODE_REPORT
,即使沒設定 VLD_OPT_REPORT_TO_FILE
,泄漏報告也會輸出到檔案,這與 vld.ini
檔案中的 配置項 ReportEncoding 效果一樣,閱讀 原始碼 可知,若設定了 VLD_OPT_REPORT_TO_STDOUT
,程式會呼叫 fputs(messagea, stdout)
輸出泄漏報告至標準輸出窗,若設定了 VLD_OPT_REPORT_TO_DEBUGGER
,程式會呼叫 OutputDebugStringW(messagew)
輸出泄露報告至除錯窗(詳見 OutputDebugStringW 函式),
3.21 介面 VLDSetReportHook
該函式用于自定義記憶體泄漏報告回呼函式,與 CrtSetReportHook 函式 很相似,
// VLDSetReportHook - Installs or uninstalls a client-defined reporting function by hooking it
// into the C run-time debug reporting process (debug version only).
//
// mode: The action to take: VLD_RPTHOOK_INSTALL or VLD_RPTHOOK_REMOVE.
//
// pfnNewHook: Report hook to install or remove.
//
// Return Value:
//
// int: 0 if success.
//
__declspec(dllimport) int VLDSetReportHook(int mode, VLD_REPORT_HOOK pfnNewHook);
其中的 VLD_REPORT_HOOK
在 vld_def.h
中有給出別名宣告,自定義的報告函式必須是以下型別的函式,
typedef int (__cdecl * VLD_REPORT_HOOK)(int reportType, wchar_t *message, int *returnValue);
其中的 VLD_RPTHOOK_INSTALL
與 VLD_RPTHOOK_REMOVE
也在 vld_def.h
中有給出宏定義,安裝自定義報告函式時傳 VLD_RPTHOOK_INSTALL
,卸載時傳 VLD_RPTHOOK_INSTALL
,若設定成功,VLDSetReportHook
回傳 0
,否則回傳 -1
,
#define VLD_RPTHOOK_INSTALL 0
#define VLD_RPTHOOK_REMOVE 1
查看 API 原始碼(詳見 utility.cpp 第 674~774 行),可以得到以下幾點資訊:
- 自定義報告函式的回傳值為
true
(非零)時,其后不會呼叫默認的報告輸出函式,回傳值為false
(零)時,其后仍會呼叫默認的報告輸出函式, - VLD 傳遞給自定義報告函式的第一個引數
reportType
值恒為0
,可以不使用這個值; - 第二個引數是每次檢測到泄漏時默認的輸出資訊,可以對此做一個字串過濾,只輸出想要的資訊(可使用
OutputDebugStringW
或fputs
進行列印,其他輸出函式可能無法正常使用,因為 VLD 原始碼未包含對應頭檔案); - 第三個引數是自定義報告函式傳遞給 VLD 的值,當
*returnValue = https://www.cnblogs.com/young520/p/1
時,會觸發__debugbreak()
函式(詳見 __debugbreak 函式),將在代碼中引起斷點,并在其中提示用戶運行除錯程式,當*returnValue != 1
時,無任何影響,
例如,我定義了以下自定義報告函式:
int MyReportHook(int, wchar_t* message, int* returnValue)
{
// 不讓VLD觸發__debugbreak()函式
*returnValue = https://www.cnblogs.com/young520/p/0;
// 使用默認的 Block 資訊
if (wcsstr(message, L"Block") != NULL) {
return false;
}
// 使用自定義的退出資訊
if (wcsstr(message, L"Visual Leak Detector is now exiting") != NULL) {
wchar_t wcs[512]{};
wcscpy (wcs, L"This is a custom output: ");
wcscat (wcs, message);
OutputDebugStringW(wcs);
return true;
}
// 忽略其他資訊
return true;
}
然后在 main
主函式的開頭添加上:
VLDSetReportHook(VLD_RPTHOOK_INSTALL, MyReportHook);
DEBUG
模式下運行程式,程式結束后得到以下結果:
Visual Leak Detector read settings from: D:\Program Files (x86)\Visual Leak Detector\vld.ini
Visual Leak Detector Version 2.5.1 installed.
---------- Block 2 at 0x015D2418: 40 bytes ----------
---------- Block 1 at 0x015D27E0: 40 bytes ----------
This is a custom output: Visual Leak Detector is now exiting.
未使用自定義報告函式時(或者緊接著使用 VLD_RPTHOOK_REMOVE
卸載 MyReportHook
)的輸出為:
Visual Leak Detector read settings from: D:\Program Files (x86)\Visual Leak Detector\vld.ini
Visual Leak Detector Version 2.5.1 installed.
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 2 at 0x00964198: 40 bytes ----------
Leak Hash: 0x01EF1389, Count: 1, Total 40 bytes
Call Stack (TID 10072):
ucrtbased.dll!malloc()
f:\dd\vctools\crt\vcstartup\src\heap\new_array.cpp (15): testVLD.exe!operator new[]() + 0x9 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (60): testVLD.exe!main() + 0x7 bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (74): testVLD.exe!invoke_main() + 0x1B bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (264): testVLD.exe!__scrt_common_main_seh() + 0x5 bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (309): testVLD.exe!__scrt_common_main()
f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp (17): testVLD.exe!mainCRTStartup()
KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0x11E bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xEE bytes
Data:
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........
CD CD CD CD CD CD CD CD ........ ........
---------- Block 1 at 0x00964248: 40 bytes ----------
Leak Hash: 0x5D0E4327, Count: 1, Total 40 bytes
Call Stack (TID 10072):
ucrtbased.dll!malloc()
f:\dd\vctools\crt\vcstartup\src\heap\new_array.cpp (15): testVLD.exe!operator new[]() + 0x9 bytes
e:\cworkspace\qt 5.9\qtdemo\testvld\main.cpp (59): testVLD.exe!main() + 0x7 bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (74): testVLD.exe!invoke_main() + 0x1B bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (264): testVLD.exe!__scrt_common_main_seh() + 0x5 bytes
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (309): testVLD.exe!__scrt_common_main()
f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp (17): testVLD.exe!mainCRTStartup()
KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0x11E bytes
ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xEE bytes
Data:
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........
CD CD CD CD CD CD CD CD ........ ........
Visual Leak Detector detected 2 memory leaks (152 bytes).
Largest number used: 152 bytes.
Total allocations: 152 bytes.
Visual Leak Detector is now exiting.
3.22 介面 VLDResolveCallstacks
該函式用于對所有已跟蹤的呼叫堆疊進行符號決議,當呼叫堆疊中存在已卸載的模塊函式時,可以呼叫此函式重新整理呼叫堆疊中的符號資訊(函式名、行號、指令偏移量等資訊),
// VLDResolveCallstacks - Performs symbol resolution for all saved extent CallStack's that have
// been tracked by Visual Leak Detector. This function is necessary for applications that
// dynamically load and unload modules, and through which memory leaks might be included.
// If this is NOT called, stack traces may have stack frames with no symbol information. This
// happens because the symbol API's cannot look up symbols for a binary / module that has been unloaded
// from the process.
//
// Return Value:
//
// int: 0 if successfully resolved all callstacks.
//
__declspec(dllexport) int VLDResolveCallstacks();
按編碼規范來說,這個介面前面應該是 __declspec(dllimport)
,但它實際卻是 __declspec(dllexport)
,這種用法不知是庫作者筆誤,還是別有用途,有興趣的可以深究下去,
4. 介面使用思路
- 使用
VLDDisable
、VLDEnable
、VLDRestore
可以做到只檢測指定執行緒的記憶體泄漏,或者排除指定執行緒的記憶體泄漏檢測, - 使用
VLDGlobalDisable
、VLDGlobalEnable
可以做到只檢測特定時間階段的記憶體泄漏,比如只檢測程式完成某項任務期間的記憶體泄漏,而不檢測其他時間段的記憶體泄漏, - 使用
VLDEnableModule
、VLDDisableModule
、VLDSetModulesList
、VLDGetModulesList
、VLDRefreshModules
、VLDResolveCallstacks
可以實作對指定模塊進行記憶體檢測的動態控制, - 使用
VLDReportLeaks
、VLDReportThreadLeaks
、VLDMarkAllLeaksAsReported
、VLDMarkThreadLeaksAsReported
、VLDSetReportOptions
、VLDSetReportHook
、VLDGetReportFilename
可以實作對泄漏報告的動態定制, - 使用
VLDGetLeaksCount
、VLDGetThreadLeaksCount
可以實作對泄漏資訊的實時獲取, - 使用
VLDGetOptions
、VLDSetOptions
可以實作運行程序中動態修改 VLD 的配置,
本文作者:木三百川
本文鏈接:https://www.cnblogs.com/young520/p/17324641.html
著作權宣告:本文系博主原創文章,著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請附上出處鏈接,遵循 署名-非商業性使用-相同方式共享 4.0 國際版 (CC BY-NC-SA 4.0) 著作權協議,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/550283.html
標籤:C++