【Visual Leak Detector】库的 22 个 API 使用说明

这篇具有很好参考价值的文章主要介绍了【Visual Leak Detector】库的 22 个 API 使用说明。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

说明

使用 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.hvld_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_TESTVLD_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)的句柄,可以通过调用 Win32API 来获得(常用的有 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)的句柄,可以通过调用 Win32API 来获得。

// 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 相对应),将掩码值结合 VLDSetOptions9 个配置掩码宏可以人工推断出 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_PATHwindows 系统下文件路径的最大长度(详见 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_DUPLICATESVLD_OPT_SKIP_HEAPFREE_LEAKS,并设置 maxDataDump=256maxTraceFrames=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.2MSVC 2015 32bitDebug 模式,VLD 版本为 2.5.1),发现这个函数好像只能修改内部的 m_forcedModuleList 列表,并没有按照第二个参数 includeModules 立马更新指定模块的检测状态,即使接着使用 VLDRefreshModules() 函数进行刷新,也没有实现按新列表进行检测的效果。因此,要实现对某个 DLL 库的动态检测控制,最好还是使用 VLDEnableModuleVLDDisableModuleVLDRefreshModules 这三个接口。若没有动态检测控制的需求,则可以修改配置文件 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_HOOKvld_def.h 中有给出别名声明,自定义的报告函数必须是以下类型的函数。

typedef int (__cdecl * VLD_REPORT_HOOK)(int reportType, wchar_t *message, int *returnValue);

其中的 VLD_RPTHOOK_INSTALLVLD_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,可以不使用这个值;
  • 第二个参数是每次检测到泄漏时默认的输出信息,可以对此做一个字符串过滤,只输出想要的信息(可使用 OutputDebugStringWfputs 进行打印,其他输出函数可能无法正常使用,因为 VLD 源码未包含对应头文件);
  • 第三个参数是自定义报告函数传递给 VLD 的值,当 *returnValue = 1 时,会触发 __debugbreak() 函数(详见 __debugbreak 函数),将在代码中引起断点,并在其中提示用户运行调试程序,当 *returnValue != 1 时,无任何影响。

例如,我定义了以下自定义报告函数:

int MyReportHook(int, wchar_t* message, int* returnValue)
{
    // 不让VLD触发__debugbreak()函数
    *returnValue = 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),这种用法不知是库作者笔误,还是别有用途,有兴趣的可以深究下去。文章来源地址https://www.toymoban.com/news/detail-416076.html

4. 接口使用思路

  • 使用 VLDDisableVLDEnableVLDRestore 可以做到只检测指定线程的内存泄漏,或者排除指定线程的内存泄漏检测。
  • 使用 VLDGlobalDisableVLDGlobalEnable 可以做到只检测特定时间阶段的内存泄漏,比如只检测程序完成某项任务期间的内存泄漏,而不检测其他时间段的内存泄漏。
  • 使用 VLDEnableModuleVLDDisableModuleVLDSetModulesListVLDGetModulesListVLDRefreshModulesVLDResolveCallstacks 可以实现对指定模块进行内存检测的动态控制。
  • 使用 VLDReportLeaksVLDReportThreadLeaksVLDMarkAllLeaksAsReportedVLDMarkThreadLeaksAsReportedVLDSetReportOptionsVLDSetReportHookVLDGetReportFilename 可以实现对泄漏报告的动态定制。
  • 使用 VLDGetLeaksCountVLDGetThreadLeaksCount 可以实现对泄漏信息的实时获取。
  • 使用 VLDGetOptionsVLDSetOptions 可以实现运行过程中动态修改 VLD 的配置。

到了这里,关于【Visual Leak Detector】库的 22 个 API 使用说明的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Visual Leak Detector】在 VS 2015 中使用 VLD

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。 同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 使用前的准备 2. 在 VS 2015 中使用 VLD 2.1 无内存泄漏时的输出报告 2.2 有内存泄漏时的输出报告 3. 无法正常使用的可能原因 参考本人另一篇博客 安装 Visual

    2023年04月15日
    浏览(45)
  • 【Visual Leak Detector】在 VS 高版本中使用 VLD

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。 本篇介绍如何在 VS 高版本中使用 vld2.5.1 。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 使用前的准备 2. 在 VS 2015 及更早版本中使用 VLD 3. 在 VS 高版本中使用 VLD 3.1 参考资料:在 VS 2017 中使用 VLD 3.2 参考

    2024年02月02日
    浏览(41)
  • 【Visual Leak Detector】源码下载

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 源码的下载。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 下载途径 2. 不同下载途径的源文件差异 以 v2.5.1 版本为例,可以到 Github-KindDragon-vld 页面下载 master 的 zip 源码包,如下所示: 也可

    2023年04月20日
    浏览(57)
  • 【Visual Leak Detector】源码文件概览

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇对 VLD 源码包中的各文件用途做个概述。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 整体概览 2. 文件夹 .teamcity 3 文件夹 lib 3.1 文件夹 cppformat(生成 libformat) 3.2 文件夹 dbghelp 3.3 文件夹 gtest(生成

    2023年04月22日
    浏览(52)
  • 【Visual Leak Detector】源码编译 VLD 库

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 源码的编译。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. VLD 库的依赖文件 2. 源码编译生成 VLD 库 3. 配置环境变量 4. 使用 VLD 库 以 vld2.5.1 版本为例,下载源码 后,源码包中各文件的用途可

    2023年04月24日
    浏览(70)
  • 【Visual Leak Detector】源码调试 VLD 库

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 源码的调试。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. VLD 库源码调试步骤 1.1 设置为启动项目 1.2 设置调试程序 1.3 设置输出目录 1.4 拷贝 vld 依赖文件 1.5 加断点调试 2. 注意事项 以 vld

    2024年02月03日
    浏览(41)
  • 【Visual Leak Detector】核心源码剖析(VLD 1.0)

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇对 VLD 1.0 源码做内存泄漏检测的思路进行剖析。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 源码获取 2. 源码文件概览 3. 源码剖析 3.1 注册自定义 AllocHook 函数 3.2 使用 StackWalk64 获取调用堆栈信息

    2024年02月01日
    浏览(33)
  • 【Visual Leak Detector】VS 中 VLD 输出解析

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 使用方式 2. 输出报告 在 VS 中使用 VLD 的方法可以查看另外一篇博客:在 VS 2015 中使用 VLD。 在 VS 中使用 VLD 时的输出报告,与在 QT 中使用时是一致的,输出

    2023年04月15日
    浏览(50)
  • 【Visual Leak Detector】核心源码剖析(VLD 2.5.1)

    使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇对 VLD 2.5.1 源码做内存泄漏检测的思路进行剖析。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 源码获取 2. 源码文件概览 3. 源码剖析 3.1 通过 inline hook 修补 LdrpCallInitRoutine 3.2 通过 IAT hook 替换内存操

    2024年02月03日
    浏览(39)
  • Eigen库的基本使用说明(二)

     之前的文章中,简单的介绍了一些基本的操作,回归之前的内容可以参考一下链接: zEigen库的基本使用说明_每日亿学的博客-CSDN博客_eigen库  本章内容主要就是继续延伸Eigen库的使用内容也会实时进行更新,Eigen库在SLAM中使用广泛,需要对这个库有一定的熟悉。 首先最简单

    2023年04月22日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包