Qt / MSVC 中使用内存泄露检测工具 VLD(Visual Leak Detector)

Qt / MSVC 中使用内存泄露检测工具 VLD(Visual Leak Detector)_第1张图片

 

一、简介

VLD = Visual Leak Detector,是一款用于 Visual C++ 的免费的内存泄露检测工具,官网 kinddragon.github.io, GitHub 。先说优点:

  • 为每个泄漏的块提供完整的堆栈跟踪,包括源文件和行号信息(如果可用)。
  • 检测大多数(如果不是全部)类型的进程内内存泄漏,包括基于 COM 的泄漏和纯 Win32 基于堆的泄漏。
  • 可以设置过滤指定的模块(DLL 甚至主 EXE),不参与内存泄露检查。
  • 提供泄漏块的完整数据转储(十六进制和 ASCII)。
  • 可自定义的内存泄漏报告:可以保存到文件或发送到调试器,并且可以包含可变的详细级别
  • 可以设置内存泄露报告的级别
  • 它是一个已经打包的 lib,使用时无须编译它的源代码,只需要引入头文件即可
  • 他的源代码使用 GNU 许可发布,并有详尽的文档及注释。对于想深入了解堆内存管理的读者,是一个不错的选择。

再谈缺点,或者说限制性:

1、只针对 Visual C++ ,所以 Qt 的项目如果是 MinGW 编译的需要调整为 MSVCXX 构建。

2、只能检测堆(Heap)上分配的内存泄漏,不能检测资源泄露(如 GDI 等系统资源)。

总的来说,对于在 Windows 环境下用 VC++ 编译的项目,是免费且十分容易使用的非常棒的内存泄露检查库。可惜目前停留在了 2.5.1 版 (2017-10-17)。

二、下载安装 

Releases · KindDragon/vld (github.com) ,基于众所周知的原因,下不到的请移步于此:(VisualLeakDetector)vld-2.5.1-setup.exe.7z

下载解压后安装一路点击就可以了。

三、在 Qt 中使用

使用 VLD 很简单,对于 Qt 项目只需要以下三步即可(假设我们都安装在 D:\VLD 目录下):

  • 复制头文件、lib文件

      将 D:\VLD\下的 include 子目录下的 .h 文件都复制到 Qt 项目对应的构建库目录下对应的 include 、 lib 子目录中。

构建目录可以通过菜单 工具/选项,Kits中找到项目对应的构建套件(KIT),并找到对应的 qmake.exe所在路径,其父路径就是我们要找的构建库目录,如我这里是 E:\Qt\Qt5.12.2\5.12.2\msvc2017。

Qt / MSVC 中使用内存泄露检测工具 VLD(Visual Leak Detector)_第2张图片

Qt / MSVC 中使用内存泄露检测工具 VLD(Visual Leak Detector)_第3张图片

  • 源码中引入 vld

        包含 vld.h 头文件即可。

  • 修改 .pro 项目文件(这一步,我测试不需要,也许是版本问题?) 
  • win32 {
               CONFIG(debug, debug|release) {
               #        DEFINES += _DEBUG
               VLD_PATH = C:/Program Files (x86)/Visual Leak Detector
               INCLUDEPATH += $VLD_PATH/include
               LIBS += -L$VLD_PATH/lib/Win32 -lvld
                }
           }

如果希望检查 DLL 的内存泄漏,则还要在每个 DLL 的至少一个源文件中包括 vld.h。

经过如上配置,跑起程序来,如果配置正确,则在【应用程序输出】窗口中,可以看到vld的输出,如下

18:14:33: Debugging starts

Visual Leak Detector read settings from: D:\VLD\vld.ini

Visual Leak Detector Version 2.5.1 installed.

退出程序,则在【应用程序输出】窗口中看到输出,如下

 No memory leaks detected.

Visual Leak Detector is now exiting.

18:14:40: Debugging has finished

很好,没有泄露,让我们刻意制造一个泄露:

    int * p = new int();
    *p = 0xAABBFF;

    qDebug() << "p address : " << p;

new 一个 int 型指针,赋值为 0xAABBFF,最后显示指针地址。测试下 VLD 是否能捕获到这个泄露,进一步,是否能显示泄露内存的数据。

跑起来程序后关闭窗口退出, VLD 显示如下,对关键数据用红色粗斜体标注,并配了#序号: 

#1 p address :  0x40b95a8
WARNING: Visual Leak Detector detected memory leaks!
#2 ---------- Block 8 at 0x040B95A8: 4 bytes ----------
#3   Leak Hash: 0xF4DB2A55, Count: 1, Total 4 bytes
  Call Stack (TID 11204):
    ucrtbased.dll!malloc()
    d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\heap\new_scalar.cpp (35): testvld.exe!operator new() + 0x9 bytes
    e:\test\qt\vld\testvld\mainwindow.cpp (19): testvld.exe!MainWindow::MainWindow() + 0x10 bytes
 #4  e:\test\qt\vld\testvld\main.cpp (7): testvld.exe!main() + 0xA bytes
    c:\users\qt\work\qt\qtbase\src\winmain\qtmain_win.cpp (97): testvld.exe!WinMain() + 0xD bytes
    d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (107): testvld.exe!invoke_main()
    d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (288): testvld.exe!__scrt_common_main_seh() + 0x5 bytes
    d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (331): testvld.exe!__scrt_common_main()
    d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_winmain.cpp (17): testvld.exe!WinMainCRTStartup()
    KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes
    ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0x11E bytes
    ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xEE bytes
 #5 Data:
    FF BB AA 00    
                       
   
                 ........ ........


Visual Leak Detector detected 1 memory leak (40 bytes).
Largest number used: 498 bytes.
Total allocations: 498 bytes.
Visual Leak Detector is now exiting.
10:21:38: Debugging has finished

#1 是我们程序中输出的地址0x40b95a8 和 VLD 检测到的(#2 Block 8 at 0x040B95A8: 4 bytes)地址一致,并且指出了数据长度为 4 字节。

#4 给出了产生泄露代码文件地址及(e:\test\qt\vld\testvld\main.cpp)、行号(7)及所在函数(testvld.exe!main())

#5 显示出泄露的内存数据片段,正是我们赋值的 0xAABBFF,只是大小端问题,显示为 FF BB AA 00 。

可见,VLD可以非常明确的捕获内存泄露并指出相关的具体信息。

上面是使用 Debug 版输出的情况,接下来看

Release版本如何使用?

在工程设置里加上 VLD_FORCE_ENABLE 预定义宏内存监控才会生效。

#define VLD_FORCE_ENABLE
#include "vld/vld.h"

相比 Debug 版,Release 版的信息少了很多

p address : 0xba1fa0

WARNING: Visual Leak Detector detected memory leaks!

---------- Block 8 at 0x00BA1FA0: 4 bytes ----------

Leak Hash: 0x25F7E302, Count: 1, Total 4 bytes

Call Stack (TID 544):

ucrtbase.dll!malloc()

testvld.exe!0x0098185C()

testvld.exe!0x00981107()

testvld.exe!0x0098105C()

testvld.exe!0x009826D5()

Data:

FF BB AA 00 ........ ........

Visual Leak Detector detected 1 memory leak (4 bytes).

Largest number used: 214 bytes.

Total allocations: 214 bytes.

Visual Leak Detector is now exiting.

10:57:20: Debugging has finished

对比两份输出,缺少的部分是函数调用堆栈,也就是说在 release 版中无法获取内存泄露代码的文件路径、行号、调用函数信息。

但因为提供了内存地址、内存数据片段及大小,对问题的排查帮助还是很大的。

四、在 MSVC++ 中使用

和 Qt 环境的主要区别就是路径的配置方式不同,以 VS2017为例,在项目右键属性中配置如下

Qt / MSVC 中使用内存泄露检测工具 VLD(Visual Leak Detector)_第4张图片

配置头文件查找目录

 

Qt / MSVC 中使用内存泄露检测工具 VLD(Visual Leak Detector)_第5张图片

 配置 lib 目录

配置好后,还需要在 VLD 安装目录下 bin 下对应的平台调试文件复制到程序所在目录,如

Qt / MSVC 中使用内存泄露检测工具 VLD(Visual Leak Detector)_第6张图片

 如上配置后,源码中也是同样的 #include "vld.h",之后编译运行即可。

五、配置内存泄露报告

VLD 的内存泄露报告是可以定制的,详细的配置描述请参考 Configuration Options · KindDragon/vld Wiki · GitHub

安装目录下的vld.ini 为默认全局配置,也可以为每个程序单独配置,在应用程序目录下放置vld.ini文件即可。常用的选项如下:
 

VLD:选择VLD的打开与关闭。在Debug模式下运行,关闭以后会有一行VLD关闭的提示信息。默认为 on。

AggregateDuplicates:设置为 yes 时,相同地方产生内存泄漏只输出一次,但是会统计发生的次数。默认是 no 。

ReportEncoding :report 文件的编码格式,可选有 ascii, unicode,默认是 ascii 。

ReportFile :report 文件的路径。默认是 “.\memory_leak_report.txt”

ReportTo :可选有 debugger, file, both,debugger 表示输出到 debug模式下的输出窗口;file 表示只输出到文件中; both顾名思义,全都都输出。默认是 debugger 。

* 如果需要将报告输出到指定文件中,则配置 ReportFile = .\报告文件名 且 ReportTo = both。
 

参考 

Home · KindDragon/vld Wiki · GitHub 

Qt Creator 安装 VLD_你好L的博客-CSDN博客_qt vld

QT 内存泄漏检测工具VLD 使用和安装_小何在线的博客-CSDN博客_qt vld

Configuration Options · KindDragon/vld Wiki · GitHub

你可能感兴趣的:(C++,Qt,qt)