【Visual Leak Detector】在 QT 中使用 VLD(方式三)

2023-03-22 15:00:54

說明

使用 VLD 記憶體漏失檢測工具輔助開發時整理的學習筆記。


1. 使用前的準備

參考本人另一篇部落格 安裝 Visual Leak Detector 下載 vld-2.5.1-setup.exe 並按步驟安裝 VLD。這一種使用方式的特點是,在一臺電腦上安裝完成後,將 VLD 安裝目錄下的 lib 庫及 include 檔案拷貝到專案目錄中,在專案 pro 檔案中指明庫及標頭檔案的路徑,並將 vld.ini 檔案和 VLD 安裝目錄 bin 資料夾下的全部檔案拷貝到專案生成目錄下,最後在 mian.cpp 檔案中 #include "vld.h"。優點是,當把專案拷貝到別的電腦上編譯執行時,該電腦無需安裝 VLD,也不需要更改任何程式碼。

2. 在 QT 中使用 VLD

我的 VLD 安裝目錄為 D:\Program Files (x86)\Visual Leak Detector。安裝完成後,檔案列表如下:

需要用到的是 binincludelib 三個資料夾,以及 vld.ini 檔案。下文範例專案所在路徑為 E:\Cworkspace\Qt 5.9.0\QtDemo\testVLD,專案路徑下的檔案列表如下:

2.1 複製 lib 庫及標頭檔案

拷貝 include 資料夾中的 vld.hvld_def.h 到專案路徑下,拷貝整個 lib 資料夾到專案路徑下,這兩步拷貝完成後,專案路徑下的檔案列表如下:

2.2 在專案 .pro 檔案中指明路徑

在專案對應的 pro 檔案中新增 VLD 的標頭檔案和 lib 庫,pro 檔案中新增如下程式碼:

HEADERS += \
    vld.h \
    vld_def.h

win32{
    CONFIG(debug, debug | release) {
        contains(QT_ARCH, x86_64){
            LIBS += -L$$PWD/lib/Win64 -lvld
        }else{
            LIBS += -L$$PWD/lib/Win32 -lvld
        }
    }
}

2.3 設定 bin 資料夾下的依賴庫

拷貝 bin\Win32 資料夾中的四個檔案 dbghelp.dllMicrosoft.DTfW.DHL.manifestvld_x86.dllvld_x86.pdb 到 32 位 MSVC 在 Debug 模式下的生成目錄中,若不使用 DESTDIR 指令,但勾選 Shadow build ,預設的生成路徑為 E:\Cworkspace\Qt 5.9.0\QtDemo\build-testVLD-Desktop_Qt_5_9_2_MSVC2015_32bit-Debug\debug,拷貝結果如下:

64 位的做類似操作,拷貝 bin\Win64 資料夾中的四個檔案 dbghelp.dllMicrosoft.DTfW.DHL.manifestvld_x64.dllvld_x64.pdb 到 64 位 MSVC 在 Debug 模式下的生成目錄中,若不使用 DESTDIR 指令,但勾選 Shadow build ,預設的生成路徑為 E:\Cworkspace\Qt 5.9.0\QtDemo\build-testVLD-Desktop_Qt_5_9_2_MSVC2015_64bit-Debug\debug,拷貝結果如下:

更佳的做法是使用 DESTDIR 指令,實現 32 位、64 位在指定路徑下生成 exe,這樣可以將 exe 直接生成在對應的 Win32Win64 路徑下,而不需要將上述 4 個檔案分別拷貝到對應的 debug 目錄。為實現這種效果,首先將整個 bin 檔案拷貝到專案路徑下,拷貝完成後,專案路徑下的檔案列表如下:

在專案對應的 pro 檔案中使用 DESTDIR 指令設定生成路徑,新增如下程式碼:

contains(QT_ARCH, x86_64){
    DESTDIR = $$PWD/bin/Win64
}else{
    DESTDIR = $$PWD/bin/Win32
}

同時,為將 releasedebug 兩種版本區分出來,不至於在同一個資料夾中引起混亂,在 pro 檔案中額為新增如下程式碼:

TARGET_NAME = testVLD
CONFIG(debug, debug|release) {
    TARGET_NAME = $${TARGET_NAME}-d
}
TARGET = $${TARGET_NAME}

這樣設定之後,生成的 debug 版結果將比 release 版結果多一個 -d 字尾,便於區分。

2.4 複製 vld.ini 檔案

vld.ini 是 VLD 工具的組態檔,可以修改 vld.ini 內容以客製化記憶體漏失檢測報告。沒有該檔案其實也能正常執行,但為了後續可客製化,最好還是將 vld.ini 拷貝到生成目錄下。比如在上一步中的 E:\Cworkspace\Qt 5.9.0\QtDemo\build-testVLD-Desktop_Qt_5_9_2_MSVC2015_32bit-Debug\debugE:\Cworkspace\Qt 5.9.0\QtDemo\build-testVLD-Desktop_Qt_5_9_2_MSVC2015_64bit-Debug\debug,若在上一步中使用了 DESTDIR 指令,則生成目錄變為 E:\Cworkspace\Qt 5.9.0\QtDemo\testVLD\bin\Win32E:\Cworkspace\Qt 5.9.0\QtDemo\testVLD\bin\Win64

2.5 在 main.cpp 檔案中新增標頭檔案

在專案的 main.cpp 檔案中,新增標頭檔案:

#include "vld.h"

選擇 MSVC 32bit 或者 MSVC 64bit 編譯器,選擇 Debug 模式,編譯執行,就可以正常使用了。

2.6 無記憶體漏失時的輸出報告

程式執行結束後,若沒有檢測到記憶體漏失,VLD 會輸出以下 4 行報告:

Visual Leak Detector read settings from: E:\Cworkspace\Qt 5.9.0\QtDemo\testVLD\bin\Win32\vld.ini
Visual Leak Detector Version 2.5.1 installed.
No memory leaks detected.
Visual Leak Detector is now exiting.

需要注意的是,此時讀取的組態檔 vld.ini 已經不是 VLD 安裝路徑下的那個了,從第一行可以看到具體路徑。使用 64 位 MSVC 時的輸出如下:

Visual Leak Detector read settings from: E:\Cworkspace\Qt 5.9.0\QtDemo\testVLD\bin\Win64\vld.ini
Visual Leak Detector Version 2.5.1 installed.
No memory leaks detected.
Visual Leak Detector is now exiting.

因為使用了 DESTDIR 指令, 32 位和 64 位會在所指定的路徑下生成,符合預期結果。

3. 無法正常使用的可能原因

按前述步驟進行設定後,就可以解除安裝已經安裝的 VLD 工具了,到 VLD 安裝路徑下,雙擊 unins000.exe,彈窗點選「是(Y)」 按鈕解除安裝 VLD,重新編譯執行程式,仍可正常使用。當把專案拷貝到別的電腦上編譯執行時,新電腦環境無需安裝 VLD,也不需要更改任何程式碼。若無法正常使用,考慮以下可能的原因。

  • 檢查編譯器版本,VLD 無法在 minGW 下使用,只能使用 MSVC 編譯器。
  • 檢查是否是 Debug 模式,VLD 無法直接在 Release 模式下使用。
  • 檢查檔案的位數是否正確,32 bit /64 bit 不能混用。
  • 清除上一次的編譯檔案,重新編譯執行一下。
  • 若路徑中存在空格,新增庫時一定要使用 $$quote() 將路徑括起來,否則路徑解析不正確。
  • 檢查生成目錄下是否包含有 VLD 的 4 個依賴檔案,32 位為 dbghelp.dllMicrosoft.DTfW.DHL.manifestvld_x86.dllvld_x86.pdb,64 位為 dbghelp.dllMicrosoft.DTfW.DHL.manifestvld_x64.dllvld_x64.pdb

4. 範例原始碼

4.1 工程 .pro 檔案

# testVLD.pro

QT -= gui

CONFIG += c++11 console
CONFIG -= app_bundle

SOURCES += main.cpp

HEADERS += \
    vld.h \
    vld_def.h

win32{
    CONFIG(debug, debug | release) {
        contains(QT_ARCH, x86_64){
            LIBS += -L$$PWD/lib/Win64 -lvld
        }else{
            LIBS += -L$$PWD/lib/Win32 -lvld
        }
    }
}

contains(QT_ARCH, x86_64){
    DESTDIR = $$PWD/bin/Win64
}else{
    DESTDIR = $$PWD/bin/Win32
}

TARGET_NAME = testVLD
CONFIG(debug, debug|release) {
    TARGET_NAME = $${TARGET_NAME}-d
}
TARGET = $${TARGET_NAME}

4.2 主函數 main.cpp 檔案

// mian.cpp

#include <QCoreApplication>
#include "vld.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    return a.exec();
}

4.3 範例工程目錄結構

工程目錄結構如下:

E:\Cworkspace\Qt 5.9.0\QtDemo\testVLD
│  main.cpp
│  testVLD.pro
│  testVLD.pro.user
│  vld.h
│  vld_def.h
│
├─bin
│  ├─Win32
│  │      dbghelp.dll
│  │      Microsoft.DTfW.DHL.manifest
│  │      testVLD-d.exe
│  │      testVLD-d.ilk
│  │      testVLD-d.pdb
│  │      testVLD.exe
│  │      testVLD.pdb
│  │      vld.ini
│  │      vld_x86.dll
│  │      vld_x86.pdb
│  │
│  └─Win64
│          dbghelp.dll
│          Microsoft.DTfW.DHL.manifest
│          testVLD-d.exe
│          testVLD-d.ilk
│          testVLD-d.pdb
│          testVLD.exe
│          testVLD.pdb
│          vld.ini
│          vld_x64.dll
│          vld_x64.pdb
│
└─lib
    ├─Win32
    │      vld.lib
    │
    └─Win64
            vld.lib