mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-30 21:24:14 +00:00
16bf5dabb7
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@38809 a592a061-630c-0410-9148-cb99ea01b6c8
2749 lines
103 KiB
C++
2749 lines
103 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Visual Leak Detector - VisualLeakDetector Class Implementation
|
|
// Copyright (c) 2005-2009 Dan Moulding
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2.1 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
//
|
|
// See COPYING.txt for the full terms of the GNU Lesser General Public License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#pragma comment(lib, "dbghelp.lib")
|
|
|
|
#include <cassert>
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <sys/stat.h>
|
|
#include <windows.h>
|
|
#ifndef __out_xcount
|
|
#define __out_xcount(x) // Workaround for the specstrings.h bug in the Platform SDK.
|
|
#endif
|
|
#define DBGHELP_TRANSLATE_TCHAR
|
|
#include <dbghelp.h> // Provides symbol handling services.
|
|
#define VLDBUILD // Declares that we are building Visual Leak Detector.
|
|
#include "callstack.h" // Provides a class for handling call stacks.
|
|
#include "crtmfcpatch.h" // Provides CRT and MFC patch functions.
|
|
#include "map.h" // Provides a lightweight STL-like map template.
|
|
#include "ntapi.h" // Provides access to NT APIs.
|
|
#include "set.h" // Provides a lightweight STL-like set template.
|
|
#include "utility.h" // Provides various utility functions.
|
|
#include "vldheap.h" // Provides internal new and delete operators.
|
|
#include "vldint.h" // Provides access to the Visual Leak Detector internals.
|
|
|
|
#define BLOCKMAPRESERVE 64 // This should strike a balance between memory use and a desire to minimize heap hits.
|
|
#define HEAPMAPRESERVE 2 // Usually there won't be more than a few heaps in the process, so this should be small.
|
|
#define MAXSYMBOLNAMELENGTH 256 // Maximum symbol name length that we will allow. Longer names will be truncated.
|
|
#define MODULESETRESERVE 16 // There are likely to be several modules loaded in the process.
|
|
|
|
// Imported global variables.
|
|
extern vldblockheader_t *vldblocklist;
|
|
extern HANDLE vldheap;
|
|
extern CRITICAL_SECTION vldheaplock;
|
|
|
|
// Global variables.
|
|
HANDLE currentprocess; // Pseudo-handle for the current process.
|
|
HANDLE currentthread; // Pseudo-handle for the current thread.
|
|
CRITICAL_SECTION imagelock; // Serializes calls to the Debug Help Library PE image access APIs.
|
|
HANDLE processheap; // Handle to the process's heap (COM allocations come from here).
|
|
CRITICAL_SECTION stackwalklock; // Serializes calls to StackWalk64 from the Debug Help Library.
|
|
CRITICAL_SECTION symbollock; // Serializes calls to the Debug Help Library symbols handling APIs.
|
|
|
|
// The one and only VisualLeakDetector object instance.
|
|
__declspec(dllexport) VisualLeakDetector vld;
|
|
|
|
// Global function pointers for explicit dynamic linking with functions listed
|
|
// in the import patch table. Using explicit dynamic linking minimizes VLD's
|
|
// footprint by loading only modules that are actually used. These pointers will
|
|
// be linked to the real functions the first time they are used.
|
|
|
|
// The import patch table: lists the heap-related API imports that VLD patches
|
|
// through to replacement functions provided by VLD. Having this table simply
|
|
// makes it more convenient to add additional IAT patches.
|
|
patchentry_t VisualLeakDetector::m_patchtable [] = {
|
|
// Win32 heap APIs.
|
|
"kernel32.dll", "GetProcAddress", 0x0, _GetProcAddress, // Not heap related, but can be used to obtain pointers to heap functions.
|
|
"kernel32.dll", "HeapAlloc", 0x0, _RtlAllocateHeap,
|
|
"kernel32.dll", "HeapCreate", 0x0, _HeapCreate,
|
|
"kernel32.dll", "HeapDestroy", 0x0, _HeapDestroy,
|
|
"kernel32.dll", "HeapFree", 0x0, _RtlFreeHeap,
|
|
"kernel32.dll", "HeapReAlloc", 0x0, _RtlReAllocateHeap,
|
|
|
|
// MFC new operators (exported by ordinal).
|
|
// XXX why are the vector new operators missing for mfc42d.dll?
|
|
"mfc42d.dll", (LPCSTR)711, 0x0, VS60::mfcd_scalar_new,
|
|
"mfc42d.dll", (LPCSTR)712, 0x0, VS60::mfcd__scalar_new_dbg_4p,
|
|
"mfc42d.dll", (LPCSTR)714, 0x0, VS60::mfcd__scalar_new_dbg_3p,
|
|
"mfc42ud.dll", (LPCSTR)711, 0x0, VS60::mfcud_scalar_new,
|
|
"mfc42ud.dll", (LPCSTR)712, 0x0, VS60::mfcud__scalar_new_dbg_4p,
|
|
"mfc42ud.dll", (LPCSTR)714, 0x0, VS60::mfcud__scalar_new_dbg_3p,
|
|
"mfc70d.dll", (LPCSTR)257, 0x0, VS70::mfcd_vector_new,
|
|
"mfc70d.dll", (LPCSTR)258, 0x0, VS70::mfcd__vector_new_dbg_4p,
|
|
"mfc70d.dll", (LPCSTR)259, 0x0, VS70::mfcd__vector_new_dbg_3p,
|
|
"mfc70d.dll", (LPCSTR)832, 0x0, VS70::mfcd_scalar_new,
|
|
"mfc70d.dll", (LPCSTR)833, 0x0, VS70::mfcd__scalar_new_dbg_4p,
|
|
"mfc70d.dll", (LPCSTR)834, 0x0, VS70::mfcd__scalar_new_dbg_3p,
|
|
"mfc70ud.dll", (LPCSTR)258, 0x0, VS70::mfcud_vector_new,
|
|
"mfc70ud.dll", (LPCSTR)259, 0x0, VS70::mfcud__vector_new_dbg_4p,
|
|
"mfc70ud.dll", (LPCSTR)260, 0x0, VS70::mfcud__vector_new_dbg_3p,
|
|
"mfc70ud.dll", (LPCSTR)833, 0x0, VS70::mfcud_scalar_new,
|
|
"mfc70ud.dll", (LPCSTR)834, 0x0, VS70::mfcud__scalar_new_dbg_4p,
|
|
"mfc70ud.dll", (LPCSTR)835, 0x0, VS70::mfcud__scalar_new_dbg_3p,
|
|
"mfc71d.dll", (LPCSTR)267, 0x0, VS71::mfcd_vector_new,
|
|
"mfc71d.dll", (LPCSTR)268, 0x0, VS71::mfcd__vector_new_dbg_4p,
|
|
"mfc71d.dll", (LPCSTR)269, 0x0, VS71::mfcd__vector_new_dbg_3p,
|
|
"mfc71d.dll", (LPCSTR)893, 0x0, VS71::mfcd_scalar_new,
|
|
"mfc71d.dll", (LPCSTR)894, 0x0, VS71::mfcd__scalar_new_dbg_4p,
|
|
"mfc71d.dll", (LPCSTR)895, 0x0, VS71::mfcd__scalar_new_dbg_3p,
|
|
"mfc71ud.dll", (LPCSTR)267, 0x0, VS71::mfcud_vector_new,
|
|
"mfc71ud.dll", (LPCSTR)268, 0x0, VS71::mfcud__vector_new_dbg_4p,
|
|
"mfc71ud.dll", (LPCSTR)269, 0x0, VS71::mfcud__vector_new_dbg_3p,
|
|
"mfc71ud.dll", (LPCSTR)893, 0x0, VS71::mfcud_scalar_new,
|
|
"mfc71ud.dll", (LPCSTR)894, 0x0, VS71::mfcud__scalar_new_dbg_4p,
|
|
"mfc71ud.dll", (LPCSTR)895, 0x0, VS71::mfcud__scalar_new_dbg_3p,
|
|
"mfc80d.dll", (LPCSTR)267, 0x0, VS80::mfcd_vector_new,
|
|
"mfc80d.dll", (LPCSTR)268, 0x0, VS80::mfcd__vector_new_dbg_4p,
|
|
"mfc80d.dll", (LPCSTR)269, 0x0, VS80::mfcd__vector_new_dbg_3p,
|
|
"mfc80d.dll", (LPCSTR)893, 0x0, VS80::mfcd_scalar_new,
|
|
"mfc80d.dll", (LPCSTR)894, 0x0, VS80::mfcd__scalar_new_dbg_4p,
|
|
"mfc80d.dll", (LPCSTR)895, 0x0, VS80::mfcd__scalar_new_dbg_3p,
|
|
"mfc80ud.dll", (LPCSTR)267, 0x0, VS80::mfcud_vector_new,
|
|
"mfc80ud.dll", (LPCSTR)268, 0x0, VS80::mfcud__vector_new_dbg_4p,
|
|
"mfc80ud.dll", (LPCSTR)269, 0x0, VS80::mfcud__vector_new_dbg_3p,
|
|
"mfc80ud.dll", (LPCSTR)893, 0x0, VS80::mfcud_scalar_new,
|
|
"mfc80ud.dll", (LPCSTR)894, 0x0, VS80::mfcud__scalar_new_dbg_4p,
|
|
"mfc80ud.dll", (LPCSTR)895, 0x0, VS80::mfcud__scalar_new_dbg_3p,
|
|
"mfc90d.dll", (LPCSTR)267, 0x0, VS90::mfcd_vector_new,
|
|
"mfc90d.dll", (LPCSTR)268, 0x0, VS90::mfcd__vector_new_dbg_4p,
|
|
"mfc90d.dll", (LPCSTR)269, 0x0, VS90::mfcd__vector_new_dbg_3p,
|
|
"mfc90d.dll", (LPCSTR)931, 0x0, VS90::mfcd_scalar_new,
|
|
"mfc90d.dll", (LPCSTR)932, 0x0, VS90::mfcd__scalar_new_dbg_4p,
|
|
"mfc90d.dll", (LPCSTR)933, 0x0, VS90::mfcd__scalar_new_dbg_3p,
|
|
"mfc90ud.dll", (LPCSTR)267, 0x0, VS90::mfcud_vector_new,
|
|
"mfc90ud.dll", (LPCSTR)268, 0x0, VS90::mfcud__vector_new_dbg_4p,
|
|
"mfc90ud.dll", (LPCSTR)269, 0x0, VS90::mfcud__vector_new_dbg_3p,
|
|
"mfc90ud.dll", (LPCSTR)935, 0x0, VS90::mfcud_scalar_new,
|
|
"mfc90ud.dll", (LPCSTR)936, 0x0, VS90::mfcud__scalar_new_dbg_4p,
|
|
"mfc90ud.dll", (LPCSTR)937, 0x0, VS90::mfcud__scalar_new_dbg_3p,
|
|
"mfc100d.dll", (LPCSTR)267, 0x0, VS100::mfcd_vector_new,
|
|
"mfc100d.dll", (LPCSTR)268, 0x0, VS100::mfcd__vector_new_dbg_4p,
|
|
"mfc100d.dll", (LPCSTR)269, 0x0, VS100::mfcd__vector_new_dbg_3p,
|
|
"mfc100d.dll", (LPCSTR)1427, 0x0, VS100::mfcd_scalar_new,
|
|
"mfc100d.dll", (LPCSTR)1428, 0x0, VS100::mfcd__scalar_new_dbg_4p,
|
|
"mfc100d.dll", (LPCSTR)1429, 0x0, VS100::mfcd__scalar_new_dbg_3p,
|
|
"mfc100ud.dll", (LPCSTR)267, 0x0, VS100::mfcud_vector_new,
|
|
"mfc100ud.dll", (LPCSTR)268, 0x0, VS100::mfcud__vector_new_dbg_4p,
|
|
"mfc100ud.dll", (LPCSTR)269, 0x0, VS100::mfcud__vector_new_dbg_3p,
|
|
"mfc100ud.dll", (LPCSTR)1434, 0x0, VS100::mfcud_scalar_new,
|
|
"mfc100ud.dll", (LPCSTR)1435, 0x0, VS100::mfcud__scalar_new_dbg_4p,
|
|
"mfc100ud.dll", (LPCSTR)1436, 0x0, VS100::mfcud__scalar_new_dbg_3p,
|
|
|
|
// CRT new operators and heap APIs.
|
|
"msvcrtd.dll", "_calloc_dbg", 0x0, VS60::crtd__calloc_dbg,
|
|
"msvcrtd.dll", "_malloc_dbg", 0x0, VS60::crtd__malloc_dbg,
|
|
"msvcrtd.dll", "_realloc_dbg", 0x0, VS60::crtd__realloc_dbg,
|
|
"msvcrtd.dll", "??2@YAPAXIHPBDH@Z", 0x0, VS60::crtd__scalar_new_dbg,
|
|
// "msvcrtd.dll", "??_U@YAPAXIHPBDH@Z", 0x0, VS60::crtd__vector_new_dbg,
|
|
"msvcrtd.dll", "calloc", 0x0, VS60::crtd_calloc,
|
|
"msvcrtd.dll", "malloc", 0x0, VS60::crtd_malloc,
|
|
"msvcrtd.dll", "realloc", 0x0, VS60::crtd_realloc,
|
|
"msvcrtd.dll", "??2@YAPAXI@Z", 0x0, VS60::crtd_scalar_new,
|
|
// "msvcrtd.dll", "??_U@YAPAXI@Z", 0x0, VS60::crtd_vector_new,
|
|
"msvcr70d.dll", "_calloc_dbg", 0x0, VS70::crtd__calloc_dbg,
|
|
"msvcr70d.dll", "_malloc_dbg", 0x0, VS70::crtd__malloc_dbg,
|
|
"msvcr70d.dll", "_realloc_dbg", 0x0, VS70::crtd__realloc_dbg,
|
|
"msvcr70d.dll", "??2@YAPAXIHPBDH@Z", 0x0, VS70::crtd__scalar_new_dbg,
|
|
"msvcr70d.dll", "??_U@YAPAXIHPBDH@Z", 0x0, VS70::crtd__vector_new_dbg,
|
|
"msvcr70d.dll", "calloc", 0x0, VS70::crtd_calloc,
|
|
"msvcr70d.dll", "malloc", 0x0, VS70::crtd_malloc,
|
|
"msvcr70d.dll", "realloc", 0x0, VS70::crtd_realloc,
|
|
"msvcr70d.dll", "??2@YAPAXI@Z", 0x0, VS70::crtd_scalar_new,
|
|
"msvcr70d.dll", "??_U@YAPAXI@Z", 0x0, VS70::crtd_vector_new,
|
|
"msvcr71d.dll", "_calloc_dbg", 0x0, VS71::crtd__calloc_dbg,
|
|
"msvcr71d.dll", "_malloc_dbg", 0x0, VS71::crtd__malloc_dbg,
|
|
"msvcr71d.dll", "_realloc_dbg", 0x0, VS71::crtd__realloc_dbg,
|
|
"msvcr71d.dll", "??2@YAPAXIHPBDH@Z", 0x0, VS71::crtd__scalar_new_dbg,
|
|
"msvcr71d.dll", "??_U@YAPAXIHPBDH@Z", 0x0, VS71::crtd__vector_new_dbg,
|
|
"msvcr71d.dll", "calloc", 0x0, VS71::crtd_calloc,
|
|
"msvcr71d.dll", "malloc", 0x0, VS71::crtd_malloc,
|
|
"msvcr71d.dll", "realloc", 0x0, VS71::crtd_realloc,
|
|
"msvcr71d.dll", "??2@YAPAXI@Z", 0x0, VS71::crtd_scalar_new,
|
|
"msvcr71d.dll", "??_U@YAPAXI@Z", 0x0, VS71::crtd_vector_new,
|
|
"msvcr80d.dll", "_calloc_dbg", 0x0, VS80::crtd__calloc_dbg,
|
|
"msvcr80d.dll", "_malloc_dbg", 0x0, VS80::crtd__malloc_dbg,
|
|
"msvcr80d.dll", "_realloc_dbg", 0x0, VS80::crtd__realloc_dbg,
|
|
"msvcr80d.dll", "??2@YAPAXIHPBDH@Z", 0x0, VS80::crtd__scalar_new_dbg,
|
|
"msvcr80d.dll", "??_U@YAPAXIHPBDH@Z", 0x0, VS80::crtd__vector_new_dbg,
|
|
"msvcr80d.dll", "calloc", 0x0, VS80::crtd_calloc,
|
|
"msvcr80d.dll", "malloc", 0x0, VS80::crtd_malloc,
|
|
"msvcr80d.dll", "realloc", 0x0, VS80::crtd_realloc,
|
|
"msvcr80d.dll", "??2@YAPAXI@Z", 0x0, VS80::crtd_scalar_new,
|
|
"msvcr80d.dll", "??_U@YAPAXI@Z", 0x0, VS80::crtd_vector_new,
|
|
"msvcr90d.dll", "_calloc_dbg", 0x0, VS90::crtd__calloc_dbg,
|
|
"msvcr90d.dll", "_malloc_dbg", 0x0, VS90::crtd__malloc_dbg,
|
|
"msvcr90d.dll", "_realloc_dbg", 0x0, VS90::crtd__realloc_dbg,
|
|
"msvcr90d.dll", "??2@YAPAXIHPBDH@Z", 0x0, VS90::crtd__scalar_new_dbg,
|
|
"msvcr90d.dll", "??_U@YAPAXIHPBDH@Z", 0x0, VS90::crtd__vector_new_dbg,
|
|
"msvcr90d.dll", "calloc", 0x0, VS90::crtd_calloc,
|
|
"msvcr90d.dll", "malloc", 0x0, VS90::crtd_malloc,
|
|
"msvcr90d.dll", "realloc", 0x0, VS90::crtd_realloc,
|
|
"msvcr90d.dll", "??2@YAPAXI@Z", 0x0, VS90::crtd_scalar_new,
|
|
"msvcr90d.dll", "??_U@YAPAXI@Z", 0x0, VS90::crtd_vector_new,
|
|
"msvcr100d.dll", "_calloc_dbg", 0x0, VS100::crtd__calloc_dbg,
|
|
"msvcr100d.dll", "_malloc_dbg", 0x0, VS100::crtd__malloc_dbg,
|
|
"msvcr100d.dll", "_realloc_dbg", 0x0, VS100::crtd__realloc_dbg,
|
|
"msvcr100d.dll", scalar_new_dbg_name, 0x0, VS100::crtd__scalar_new_dbg,
|
|
"msvcr100d.dll", vector_new_dbg_name, 0x0, VS100::crtd__vector_new_dbg,
|
|
"msvcr100d.dll", "calloc", 0x0, VS100::crtd_calloc,
|
|
"msvcr100d.dll", "malloc", 0x0, VS100::crtd_malloc,
|
|
"msvcr100d.dll", "realloc", 0x0, VS100::crtd_realloc,
|
|
"msvcr100d.dll", scalar_new_name, 0x0, VS100::crtd_scalar_new,
|
|
"msvcr100d.dll", vector_new_name, 0x0, VS100::crtd_vector_new,
|
|
|
|
// NT APIs.
|
|
"ntdll.dll", "RtlAllocateHeap", 0x0, _RtlAllocateHeap,
|
|
"ntdll.dll", "RtlFreeHeap", 0x0, _RtlFreeHeap,
|
|
"ntdll.dll", "RtlReAllocateHeap", 0x0, _RtlReAllocateHeap,
|
|
|
|
// COM heap APIs.
|
|
"ole32.dll", "CoGetMalloc", 0x0, _CoGetMalloc,
|
|
"ole32.dll", "CoTaskMemAlloc", 0x0, _CoTaskMemAlloc,
|
|
"ole32.dll", "CoTaskMemRealloc", 0x0, _CoTaskMemRealloc
|
|
};
|
|
|
|
// Constructor - Initializes private data, loads configuration options, and
|
|
// attaches Visual Leak Detector to all other modules loaded into the current
|
|
// process.
|
|
//
|
|
VisualLeakDetector::VisualLeakDetector ()
|
|
{
|
|
WCHAR bom = BOM; // Unicode byte-order mark.
|
|
HMODULE kernel32;
|
|
ModuleSet *newmodules;
|
|
HMODULE ntdll;
|
|
LPWSTR symbolpath;
|
|
|
|
// Initialize configuration options and related private data.
|
|
_wcsnset_s(m_forcedmodulelist, MAXMODULELISTLENGTH, '\0', _TRUNCATE);
|
|
m_maxdatadump = 0xffffffff;
|
|
m_maxtraceframes = 0xffffffff;
|
|
m_options = 0x0;
|
|
m_reportfile = NULL;
|
|
wcsncpy_s(m_reportfilepath, MAX_PATH, VLD_DEFAULT_REPORT_FILE_NAME, _TRUNCATE);
|
|
m_status = 0x0;
|
|
|
|
// Load configuration options.
|
|
configure();
|
|
if (m_options & VLD_OPT_VLDOFF) {
|
|
report(L"Visual Leak Detector is turned off.\n");
|
|
return;
|
|
}
|
|
|
|
kernel32 = GetModuleHandle(L"kernel32.dll");
|
|
ntdll = GetModuleHandle(L"ntdll.dll");
|
|
|
|
// Initialize global variables.
|
|
currentprocess = GetCurrentProcess();
|
|
currentthread = GetCurrentThread();
|
|
InitializeCriticalSection(&imagelock);
|
|
LdrLoadDll = (LdrLoadDll_t)GetProcAddress(ntdll, "LdrLoadDll");
|
|
processheap = GetProcessHeap();
|
|
RtlAllocateHeap = (RtlAllocateHeap_t)GetProcAddress(ntdll, "RtlAllocateHeap");
|
|
RtlFreeHeap = (RtlFreeHeap_t)GetProcAddress(ntdll, "RtlFreeHeap");
|
|
RtlReAllocateHeap = (RtlReAllocateHeap_t)GetProcAddress(ntdll, "RtlReAllocateHeap");
|
|
InitializeCriticalSection(&stackwalklock);
|
|
InitializeCriticalSection(&symbollock);
|
|
vldheap = HeapCreate(0x0, 0, 0);
|
|
InitializeCriticalSection(&vldheaplock);
|
|
|
|
// Initialize remaining private data.
|
|
m_heapmap = new HeapMap;
|
|
m_heapmap->reserve(HEAPMAPRESERVE);
|
|
m_imalloc = NULL;
|
|
m_leaksfound = 0;
|
|
m_loadedmodules = NULL;
|
|
InitializeCriticalSection(&m_loaderlock);
|
|
InitializeCriticalSection(&m_maplock);
|
|
InitializeCriticalSection(&m_moduleslock);
|
|
m_selftestfile = __FILE__;
|
|
m_selftestline = 0;
|
|
m_tlsindex = TlsAlloc();
|
|
InitializeCriticalSection(&m_tlslock);
|
|
m_tlsset = new TlsSet;
|
|
|
|
if (m_options & VLD_OPT_SELF_TEST) {
|
|
// Self-test mode has been enabled. Intentionally leak a small amount of
|
|
// memory so that memory leak self-checking can be verified.
|
|
if (m_options & VLD_OPT_UNICODE_REPORT) {
|
|
wcsncpy_s(new WCHAR [wcslen(SELFTESTTEXTW) + 1], wcslen(SELFTESTTEXTW) + 1, SELFTESTTEXTW, _TRUNCATE);
|
|
m_selftestline = __LINE__ - 1;
|
|
}
|
|
else {
|
|
strncpy_s(new CHAR [strlen(SELFTESTTEXTA) + 1], strlen(SELFTESTTEXTA) + 1, SELFTESTTEXTA, _TRUNCATE);
|
|
m_selftestline = __LINE__ - 1;
|
|
}
|
|
}
|
|
if (m_options & VLD_OPT_START_DISABLED) {
|
|
// Memory leak detection will initially be disabled.
|
|
m_status |= VLD_STATUS_NEVER_ENABLED;
|
|
}
|
|
if (m_options & VLD_OPT_REPORT_TO_FILE) {
|
|
// Reporting to file enabled.
|
|
if (m_options & VLD_OPT_UNICODE_REPORT) {
|
|
// Unicode data encoding has been enabled. Write the byte-order
|
|
// mark before anything else gets written to the file. Open the
|
|
// file for binary writing.
|
|
if (_wfopen_s(&m_reportfile, m_reportfilepath, L"wb") == EINVAL) {
|
|
// Couldn't open the file.
|
|
m_reportfile = NULL;
|
|
}
|
|
else {
|
|
fwrite(&bom, sizeof(WCHAR), 1, m_reportfile);
|
|
setreportencoding(unicode);
|
|
}
|
|
}
|
|
else {
|
|
// Open the file in text mode for ASCII output.
|
|
if (_wfopen_s(&m_reportfile, m_reportfilepath, L"w") == EINVAL) {
|
|
// Couldn't open the file.
|
|
m_reportfile = NULL;
|
|
}
|
|
else {
|
|
setreportencoding(ascii);
|
|
}
|
|
}
|
|
if (m_reportfile == NULL) {
|
|
report(L"WARNING: Visual Leak Detector: Couldn't open report file for writing: %s\n"
|
|
L" The report will be sent to the debugger instead.\n", m_reportfilepath);
|
|
}
|
|
else {
|
|
// Set the "report" function to write to the file.
|
|
setreportfile(m_reportfile, m_options & VLD_OPT_REPORT_TO_DEBUGGER);
|
|
}
|
|
}
|
|
if (m_options & VLD_OPT_SLOW_DEBUGGER_DUMP) {
|
|
// Insert a slight delay between messages sent to the debugger for
|
|
// output. (For working around a bug in VC6 where data sent to the
|
|
// debugger gets lost if it's sent too fast).
|
|
insertreportdelay();
|
|
}
|
|
|
|
// This is highly unlikely to happen, but just in case, check to be sure
|
|
// we got a valid TLS index.
|
|
if (m_tlsindex == TLS_OUT_OF_INDEXES) {
|
|
report(L"ERROR: Visual Leak Detector could not be installed because thread local"
|
|
L" storage could not be allocated.");
|
|
return;
|
|
}
|
|
|
|
// Initialize the symbol handler. We use it for obtaining source file/line
|
|
// number information and function names for the memory leak report.
|
|
symbolpath = buildsymbolsearchpath();
|
|
SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
|
|
if (!SymInitializeW(currentprocess, symbolpath, FALSE)) {
|
|
report(L"WARNING: Visual Leak Detector: The symbol handler failed to initialize (error=%lu).\n"
|
|
L" File and function names will probably not be available in call stacks.\n", GetLastError());
|
|
}
|
|
delete [] symbolpath;
|
|
|
|
// Patch into kernel32.dll's calls to LdrLoadDll so that VLD can
|
|
// dynamically attach to new modules loaded during runtime.
|
|
patchimport(kernel32, ntdll, "ntdll.dll", "LdrLoadDll", _LdrLoadDll);
|
|
|
|
// Attach Visual Leak Detector to every module loaded in the process.
|
|
newmodules = new ModuleSet;
|
|
newmodules->reserve(MODULESETRESERVE);
|
|
EnumerateLoadedModulesW64(currentprocess, addloadedmodule, newmodules);
|
|
attachtoloadedmodules(newmodules);
|
|
m_loadedmodules = newmodules;
|
|
m_status |= VLD_STATUS_INSTALLED;
|
|
|
|
report(L"Visual Leak Detector Version " VLDVERSION L" installed.\n");
|
|
if (m_status & VLD_STATUS_FORCE_REPORT_TO_FILE) {
|
|
// The report is being forced to a file. Let the human know why.
|
|
report(L"NOTE: Visual Leak Detector: Unicode-encoded reporting has been enabled, but the\n"
|
|
L" debugger is the only selected report destination. The debugger cannot display\n"
|
|
L" Unicode characters, so the report will also be sent to a file. If no file has\n"
|
|
L" been specified, the default file name is \"" VLD_DEFAULT_REPORT_FILE_NAME L"\".\n");
|
|
|
|
}
|
|
reportconfig();
|
|
}
|
|
|
|
// Destructor - Detaches Visual Leak Detector from all modules loaded in the
|
|
// process, frees internally allocated resources, and generates the memory
|
|
// leak report.
|
|
//
|
|
VisualLeakDetector::~VisualLeakDetector ()
|
|
{
|
|
BlockMap::Iterator blockit;
|
|
BlockMap *blockmap;
|
|
size_t count;
|
|
vldblockheader_t *header;
|
|
HANDLE heap;
|
|
HeapMap::Iterator heapit;
|
|
SIZE_T internalleaks = 0;
|
|
const char *leakfile = NULL;
|
|
WCHAR leakfilew [MAX_PATH];
|
|
int leakline = 0;
|
|
ModuleSet::Iterator moduleit;
|
|
HANDLE thread;
|
|
BOOL threadsactive= FALSE;
|
|
TlsSet::Iterator tlsit;
|
|
DWORD dwCurProcessID;
|
|
|
|
if (m_options & VLD_OPT_VLDOFF) {
|
|
// VLD has been turned off.
|
|
return;
|
|
}
|
|
|
|
if (m_status & VLD_STATUS_INSTALLED) {
|
|
// Detach Visual Leak Detector from all previously attached modules.
|
|
EnumerateLoadedModulesW64(currentprocess, detachfrommodule, NULL);
|
|
|
|
dwCurProcessID = GetCurrentProcessId();
|
|
|
|
// See if any threads that have ever entered VLD's code are still active.
|
|
EnterCriticalSection(&m_tlslock);
|
|
for (tlsit = m_tlsset->begin(); tlsit != m_tlsset->end(); ++tlsit) {
|
|
if ((*tlsit)->threadid == GetCurrentThreadId()) {
|
|
// Don't wait for the current thread to exit.
|
|
continue;
|
|
}
|
|
|
|
thread = OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, FALSE, (*tlsit)->threadid);
|
|
if (thread == NULL) {
|
|
// Couldn't query this thread. We'll assume that it exited.
|
|
continue; // XXX should we check GetLastError()?
|
|
}
|
|
if (GetProcessIdOfThread(thread) != dwCurProcessID) {
|
|
//The thread ID has been recycled.
|
|
CloseHandle(thread);
|
|
continue;
|
|
}
|
|
while (WaitForSingleObject(thread, 10000) == WAIT_TIMEOUT) { // 10 seconds
|
|
// There is still at least one other thread running. The CRT
|
|
// will stomp it dead when it cleans up, which is not a
|
|
// graceful way for a thread to go down. Warn about this,
|
|
// and wait until the thread has exited so that we know it
|
|
// can't still be off running somewhere in VLD's code.
|
|
//
|
|
// Since we've been waiting a while, let the human know we are
|
|
// still here and alive.
|
|
threadsactive = TRUE;
|
|
report(L"Visual Leak Detector: Waiting for threads to terminate...\n");
|
|
}
|
|
CloseHandle(thread);
|
|
}
|
|
LeaveCriticalSection(&m_tlslock);
|
|
|
|
if (m_status & VLD_STATUS_NEVER_ENABLED) {
|
|
// Visual Leak Detector started with leak detection disabled and
|
|
// it was never enabled at runtime. A lot of good that does.
|
|
report(L"WARNING: Visual Leak Detector: Memory leak detection was never enabled.\n");
|
|
}
|
|
else {
|
|
// Generate a memory leak report for each heap in the process.
|
|
for (heapit = m_heapmap->begin(); heapit != m_heapmap->end(); ++heapit) {
|
|
heap = (*heapit).first;
|
|
reportleaks(heap);
|
|
}
|
|
|
|
// Show a summary.
|
|
if (m_leaksfound == 0) {
|
|
report(L"No memory leaks detected.\n");
|
|
}
|
|
else {
|
|
report(L"Visual Leak Detector detected %lu memory leak", m_leaksfound);
|
|
report((m_leaksfound > 1) ? L"s.\n" : L".\n");
|
|
}
|
|
}
|
|
|
|
// Free resources used by the symbol handler.
|
|
if (!SymCleanup(currentprocess)) {
|
|
report(L"WARNING: Visual Leak Detector: The symbol handler failed to deallocate resources (error=%lu).\n",
|
|
GetLastError());
|
|
}
|
|
|
|
// Free internally allocated resources used by the heapmap and blockmap.
|
|
for (heapit = m_heapmap->begin(); heapit != m_heapmap->end(); ++heapit) {
|
|
blockmap = &(*heapit).second->blockmap;
|
|
for (blockit = blockmap->begin(); blockit != blockmap->end(); ++blockit) {
|
|
delete (*blockit).second->callstack;
|
|
delete (*blockit).second;
|
|
}
|
|
delete blockmap;
|
|
}
|
|
delete m_heapmap;
|
|
|
|
// Free internally allocated resources used by the loaded module set.
|
|
for (moduleit = m_loadedmodules->begin(); moduleit != m_loadedmodules->end(); ++moduleit) {
|
|
delete (*moduleit).name;
|
|
delete (*moduleit).path;
|
|
}
|
|
delete m_loadedmodules;
|
|
|
|
// Free internally allocated resources used for thread local storage.
|
|
for (tlsit = m_tlsset->begin(); tlsit != m_tlsset->end(); ++tlsit) {
|
|
delete *tlsit;
|
|
}
|
|
delete m_tlsset;
|
|
|
|
// Do a memory leak self-check.
|
|
header = vldblocklist;
|
|
while (header) {
|
|
// Doh! VLD still has an internally allocated block!
|
|
// This won't ever actually happen, right guys?... guys?
|
|
internalleaks++;
|
|
leakfile = header->file;
|
|
leakline = header->line;
|
|
mbstowcs_s(&count, leakfilew, MAX_PATH, leakfile, _TRUNCATE);
|
|
report(L"ERROR: Visual Leak Detector: Detected a memory leak internal to Visual Leak Detector!!\n");
|
|
report(L"---------- Block %ld at " ADDRESSFORMAT L": %u bytes ----------\n", header->serialnumber,
|
|
VLDBLOCKDATA(header), header->size);
|
|
report(L" Call Stack:\n");
|
|
report(L" %s (%d): Full call stack not available.\n", leakfilew, leakline);
|
|
if (m_maxdatadump != 0) {
|
|
report(L" Data:\n");
|
|
if (m_options & VLD_OPT_UNICODE_REPORT) {
|
|
dumpmemoryw(VLDBLOCKDATA(header), (m_maxdatadump < header->size) ? m_maxdatadump : header->size);
|
|
}
|
|
else {
|
|
dumpmemorya(VLDBLOCKDATA(header), (m_maxdatadump < header->size) ? m_maxdatadump : header->size);
|
|
}
|
|
}
|
|
report(L"\n");
|
|
header = header->next;
|
|
}
|
|
if (m_options & VLD_OPT_SELF_TEST) {
|
|
if ((internalleaks == 1) && (strcmp(leakfile, m_selftestfile) == 0) && (leakline == m_selftestline)) {
|
|
report(L"Visual Leak Detector passed the memory leak self-test.\n");
|
|
}
|
|
else {
|
|
report(L"ERROR: Visual Leak Detector: Failed the memory leak self-test.\n");
|
|
}
|
|
}
|
|
|
|
if (threadsactive == TRUE) {
|
|
report(L"WARNING: Visual Leak Detector: Some threads appear to have not terminated normally.\n"
|
|
L" This could cause inaccurate leak detection results, including false positives.\n");
|
|
}
|
|
report(L"Visual Leak Detector is now exiting.\n");
|
|
}
|
|
else {
|
|
// VLD failed to load properly.
|
|
delete m_heapmap;
|
|
delete m_tlsset;
|
|
}
|
|
HeapDestroy(vldheap);
|
|
|
|
DeleteCriticalSection(&imagelock);
|
|
DeleteCriticalSection(&m_loaderlock);
|
|
DeleteCriticalSection(&m_maplock);
|
|
DeleteCriticalSection(&m_moduleslock);
|
|
DeleteCriticalSection(&stackwalklock);
|
|
DeleteCriticalSection(&symbollock);
|
|
DeleteCriticalSection(&vldheaplock);
|
|
|
|
if (m_tlsindex != TLS_OUT_OF_INDEXES) {
|
|
TlsFree(m_tlsindex);
|
|
}
|
|
|
|
if (m_reportfile != NULL) {
|
|
fclose(m_reportfile);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Private Leak Detection Functions
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// attachtoloadedmodules - Attaches VLD to all modules contained in the provided
|
|
// ModuleSet. Not all modules are in the ModuleSet will actually be included
|
|
// in leak detection. Only modules that import the global VisualLeakDetector
|
|
// class object, or those that are otherwise explicitly included in leak
|
|
// detection, will be checked for memory leaks.
|
|
//
|
|
// When VLD attaches to a module, it means that any of the imports listed in
|
|
// the import patch table which are imported by the module, will be redirected
|
|
// to VLD's designated replacements.
|
|
//
|
|
// - newmodules (IN): Pointer to a ModuleSet containing information about any
|
|
// loaded modules that need to be attached.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::attachtoloadedmodules (ModuleSet *newmodules)
|
|
{
|
|
size_t count;
|
|
DWORD64 modulebase;
|
|
UINT32 moduleflags;
|
|
IMAGEHLP_MODULE64 moduleimageinfo;
|
|
LPCSTR modulename;
|
|
#define MAXMODULENAME (_MAX_FNAME + _MAX_EXT)
|
|
WCHAR modulenamew [MAXMODULENAME];
|
|
LPCSTR modulepath;
|
|
DWORD modulesize;
|
|
ModuleSet::Iterator newit;
|
|
ModuleSet::Iterator oldit;
|
|
ModuleSet *oldmodules;
|
|
BOOL refresh;
|
|
UINT tablesize = sizeof(m_patchtable) / sizeof(patchentry_t);
|
|
ModuleSet::Muterator updateit;
|
|
|
|
// Iterate through the supplied set, until all modules have been attached.
|
|
for (newit = newmodules->begin(); newit != newmodules->end(); ++newit) {
|
|
modulebase = (DWORD64)(*newit).addrlow;
|
|
moduleflags = 0x0;
|
|
modulename = (*newit).name;
|
|
modulepath = (*newit).path;
|
|
modulesize = (DWORD)((*newit).addrhigh - (*newit).addrlow) + 1;
|
|
|
|
refresh = FALSE;
|
|
EnterCriticalSection(&m_moduleslock);
|
|
oldmodules = m_loadedmodules;
|
|
if (oldmodules != NULL) {
|
|
// This is not the first time we have been called to attach to the
|
|
// currently loaded modules.
|
|
oldit = oldmodules->find(*newit);
|
|
if (oldit != oldmodules->end()) {
|
|
// We've seen this "new" module loaded in the process before.
|
|
moduleflags = (*oldit).flags;
|
|
LeaveCriticalSection(&m_moduleslock);
|
|
if (moduleispatched((HMODULE)modulebase, m_patchtable, tablesize)) {
|
|
// This module is already attached. Just update the module's
|
|
// flags, nothing more.
|
|
updateit = newit;
|
|
(*updateit).flags = moduleflags;
|
|
continue;
|
|
}
|
|
else {
|
|
// This module may have been attached before and has been
|
|
// detached. We'll need to try reattaching to it in case it
|
|
// was unloaded and then subsequently reloaded.
|
|
refresh = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
LeaveCriticalSection(&m_moduleslock);
|
|
}
|
|
}
|
|
else {
|
|
LeaveCriticalSection(&m_moduleslock);
|
|
}
|
|
|
|
EnterCriticalSection(&symbollock);
|
|
if ((refresh == TRUE) && (moduleflags & VLD_MODULE_SYMBOLSLOADED)) {
|
|
// Discard the previously loaded symbols, so we can refresh them.
|
|
if (SymUnloadModule64(currentprocess, modulebase) == FALSE) {
|
|
report(L"WARNING: Visual Leak Detector: Failed to unload the symbols for %s. Function names and line"
|
|
L" numbers shown in the memory leak report for %s may be inaccurate.", modulename, modulename);
|
|
}
|
|
}
|
|
|
|
// Try to load the module's symbols. This ensures that we have loaded
|
|
// the symbols for every module that has ever been loaded into the
|
|
// process, guaranteeing the symbols' availability when generating the
|
|
// leak report.
|
|
moduleimageinfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
|
|
if ((SymGetModuleInfoW64(currentprocess, (DWORD64)modulebase, &moduleimageinfo) == TRUE) ||
|
|
((SymLoadModule64(currentprocess, NULL, modulepath, NULL, modulebase, modulesize) == modulebase) &&
|
|
(SymGetModuleInfoW64(currentprocess, modulebase, &moduleimageinfo) == TRUE))) {
|
|
moduleflags |= VLD_MODULE_SYMBOLSLOADED;
|
|
}
|
|
LeaveCriticalSection(&symbollock);
|
|
|
|
if (_stricmp("vld.dll", modulename) == 0) {
|
|
// What happens when a module goes through it's own portal? Bad things.
|
|
// Like infinite recursion. And ugly bald men wearing dresses. VLD
|
|
// should not, therefore, attach to itself.
|
|
continue;
|
|
}
|
|
|
|
mbstowcs_s(&count, modulenamew, MAXMODULENAME, modulename, _TRUNCATE);
|
|
if ((findimport((HMODULE)modulebase, m_vldbase, "vld.dll", "?vld@@3VVisualLeakDetector@@A") == FALSE) &&
|
|
(wcsstr(vld.m_forcedmodulelist, modulenamew) == NULL)) {
|
|
// This module does not import VLD. This means that none of the module's
|
|
// sources #included vld.h. Exclude this module from leak detection.
|
|
moduleflags |= VLD_MODULE_EXCLUDED;
|
|
}
|
|
else if (!(moduleflags & VLD_MODULE_SYMBOLSLOADED) || (moduleimageinfo.SymType == SymExport)) {
|
|
// This module is going to be included in leak detection, but complete
|
|
// symbols for this module couldn't be loaded. This means that any stack
|
|
// traces through this module may lack information, like line numbers
|
|
// and function names.
|
|
report(L"WARNING: Visual Leak Detector: A module, %s, included in memory leak detection\n"
|
|
L" does not have any debugging symbols available, or they could not be located.\n"
|
|
L" Function names and/or line numbers for this module may not be available.\n", modulename);
|
|
}
|
|
|
|
// Update the module's flags in the "new modules" set.
|
|
updateit = newit;
|
|
(*updateit).flags = moduleflags;
|
|
|
|
// Attach to the module.
|
|
patchmodule((HMODULE)modulebase, m_patchtable, tablesize);
|
|
}
|
|
}
|
|
|
|
// buildsymbolsearchpath - Builds the symbol search path for the symbol handler.
|
|
// This helps the symbol handler find the symbols for the application being
|
|
// debugged.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns a string containing the search path. The caller is responsible for
|
|
// freeing the string.
|
|
//
|
|
LPWSTR VisualLeakDetector::buildsymbolsearchpath ()
|
|
{
|
|
WCHAR directory [_MAX_DIR];
|
|
WCHAR drive [_MAX_DRIVE];
|
|
LPWSTR env;
|
|
DWORD envlen;
|
|
SIZE_T index;
|
|
SIZE_T length;
|
|
HMODULE module;
|
|
LPWSTR path = new WCHAR [MAX_PATH];
|
|
SIZE_T pos = 0;
|
|
WCHAR system [MAX_PATH];
|
|
WCHAR windows [MAX_PATH];
|
|
|
|
// Oddly, the symbol handler ignores the link to the PDB embedded in the
|
|
// executable image. So, we'll manually add the location of the executable
|
|
// to the search path since that is often where the PDB will be located.
|
|
path[0] = L'\0';
|
|
module = GetModuleHandle(NULL);
|
|
GetModuleFileName(module, path, MAX_PATH);
|
|
_wsplitpath_s(path, drive, _MAX_DRIVE, directory, _MAX_DIR, NULL, 0, NULL, 0);
|
|
wcsncpy_s(path, MAX_PATH, drive, _TRUNCATE);
|
|
strapp(&path, directory);
|
|
|
|
// When the symbol handler is given a custom symbol search path, it will no
|
|
// longer search the default directories (working directory, system root,
|
|
// etc). But we'd like it to still search those directories, so we'll add
|
|
// them to our custom search path.
|
|
//
|
|
// Append the working directory.
|
|
strapp(&path, L";.\\");
|
|
|
|
// Append the Windows directory.
|
|
if (GetWindowsDirectory(windows, MAX_PATH) != 0) {
|
|
strapp(&path, L";");
|
|
strapp(&path, windows);
|
|
}
|
|
|
|
// Append the system directory.
|
|
if (GetSystemDirectory(system, MAX_PATH) != 0) {
|
|
strapp(&path, L";");
|
|
strapp(&path, system);
|
|
}
|
|
|
|
// Append %_NT_SYMBOL_PATH%.
|
|
envlen = GetEnvironmentVariable(L"_NT_SYMBOL_PATH", NULL, 0);
|
|
if (envlen != 0) {
|
|
env = new WCHAR [envlen];
|
|
if (GetEnvironmentVariable(L"_NT_SYMBOL_PATH", env, envlen) != 0) {
|
|
strapp(&path, L";");
|
|
strapp(&path, env);
|
|
}
|
|
delete [] env;
|
|
}
|
|
|
|
// Append %_NT_ALT_SYMBOL_PATH%.
|
|
envlen = GetEnvironmentVariable(L"_NT_ALT_SYMBOL_PATH", NULL, 0);
|
|
if (envlen != 0) {
|
|
env = new WCHAR [envlen];
|
|
if (GetEnvironmentVariable(L"_NT_ALT_SYMBOL_PATH", env, envlen) != 0) {
|
|
strapp(&path, L";");
|
|
strapp(&path, env);
|
|
}
|
|
delete [] env;
|
|
}
|
|
|
|
// Remove any quotes from the path. The symbol handler doesn't like them.
|
|
pos = 0;
|
|
length = wcslen(path);
|
|
while (pos < length) {
|
|
if (path[pos] == L'\"') {
|
|
for (index = pos; index < length; index++) {
|
|
path[index] = path[index + 1];
|
|
}
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
// configure - Configures VLD using values read from the vld.ini file.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::configure ()
|
|
{
|
|
#define BSIZE 64
|
|
WCHAR buffer [BSIZE];
|
|
WCHAR filename [MAX_PATH];
|
|
WCHAR inipath [MAX_PATH];
|
|
BOOL keyopen = FALSE;
|
|
DWORD length;
|
|
HKEY productkey;
|
|
LONG regstatus;
|
|
struct _stat s;
|
|
DWORD valuetype;
|
|
|
|
if (_wstat(L".\\vld.ini", &s) == 0) {
|
|
// Found a copy of vld.ini in the working directory. Use it.
|
|
wcsncpy_s(inipath, MAX_PATH, L".\\vld.ini", _TRUNCATE);
|
|
}
|
|
else {
|
|
// Get the location of the vld.ini file from the registry.
|
|
regstatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, VLDREGKEYPRODUCT, 0, KEY_QUERY_VALUE, &productkey);
|
|
if (regstatus == ERROR_SUCCESS) {
|
|
keyopen = TRUE;
|
|
regstatus = RegQueryValueEx(productkey, L"IniFile", NULL, &valuetype, (LPBYTE)&inipath, &length);
|
|
}
|
|
if (keyopen) {
|
|
RegCloseKey(productkey);
|
|
}
|
|
if ((regstatus != ERROR_SUCCESS) || (_wstat(inipath, &s) != 0)) {
|
|
// The location of vld.ini could not be read from the registry. As a
|
|
// last resort, look in the Windows directory.
|
|
wcsncpy_s(inipath, MAX_PATH, L"vld.ini", _TRUNCATE);
|
|
}
|
|
}
|
|
|
|
// Read the boolean options.
|
|
GetPrivateProfileString(L"Options", L"VLD", L"on", buffer, BSIZE, inipath);
|
|
if (strtobool(buffer) == FALSE) {
|
|
m_options |= VLD_OPT_VLDOFF;
|
|
return;
|
|
}
|
|
|
|
GetPrivateProfileString(L"Options", L"AggregateDuplicates", L"", buffer, BSIZE, inipath);
|
|
if (strtobool(buffer) == TRUE) {
|
|
m_options |= VLD_OPT_AGGREGATE_DUPLICATES;
|
|
}
|
|
|
|
GetPrivateProfileString(L"Options", L"SelfTest", L"", buffer, BSIZE, inipath);
|
|
if (strtobool(buffer) == TRUE) {
|
|
m_options |= VLD_OPT_SELF_TEST;
|
|
}
|
|
|
|
GetPrivateProfileString(L"Options", L"SlowDebuggerDump", L"", buffer, BSIZE, inipath);
|
|
if (strtobool(buffer) == TRUE) {
|
|
m_options |= VLD_OPT_SLOW_DEBUGGER_DUMP;
|
|
}
|
|
|
|
GetPrivateProfileString(L"Options", L"StartDisabled", L"", buffer, BSIZE, inipath);
|
|
if (strtobool(buffer) == TRUE) {
|
|
m_options |= VLD_OPT_START_DISABLED;
|
|
}
|
|
|
|
GetPrivateProfileString(L"Options", L"TraceInternalFrames", L"", buffer, BSIZE, inipath);
|
|
if (strtobool(buffer) == TRUE) {
|
|
m_options |= VLD_OPT_TRACE_INTERNAL_FRAMES;
|
|
}
|
|
|
|
// Read the integer configuration options.
|
|
m_maxdatadump = GetPrivateProfileInt(L"Options", L"MaxDataDump", VLD_DEFAULT_MAX_DATA_DUMP, inipath);
|
|
m_maxtraceframes = GetPrivateProfileInt(L"Options", L"MaxTraceFrames", VLD_DEFAULT_MAX_TRACE_FRAMES, inipath);
|
|
if (m_maxtraceframes < 1) {
|
|
m_maxtraceframes = VLD_DEFAULT_MAX_TRACE_FRAMES;
|
|
}
|
|
|
|
// Read the force-include module list.
|
|
GetPrivateProfileString(L"Options", L"ForceIncludeModules", L"", m_forcedmodulelist, MAXMODULELISTLENGTH, inipath);
|
|
_wcslwr_s(m_forcedmodulelist, MAXMODULELISTLENGTH);
|
|
|
|
// Read the report destination (debugger, file, or both).
|
|
GetPrivateProfileString(L"Options", L"ReportFile", L"", filename, MAX_PATH, inipath);
|
|
if (wcslen(filename) == 0) {
|
|
wcsncpy_s(filename, MAX_PATH, VLD_DEFAULT_REPORT_FILE_NAME, _TRUNCATE);
|
|
}
|
|
_wfullpath(m_reportfilepath, filename, MAX_PATH);
|
|
GetPrivateProfileString(L"Options", L"ReportTo", L"", buffer, BSIZE, inipath);
|
|
if (_wcsicmp(buffer, L"both") == 0) {
|
|
m_options |= (VLD_OPT_REPORT_TO_DEBUGGER | VLD_OPT_REPORT_TO_FILE);
|
|
}
|
|
else if (_wcsicmp(buffer, L"file") == 0) {
|
|
m_options |= VLD_OPT_REPORT_TO_FILE;
|
|
}
|
|
else {
|
|
m_options |= VLD_OPT_REPORT_TO_DEBUGGER;
|
|
}
|
|
|
|
// Read the report file encoding (ascii or unicode).
|
|
GetPrivateProfileString(L"Options", L"ReportEncoding", L"", buffer, BSIZE, inipath);
|
|
if (_wcsicmp(buffer, L"unicode") == 0) {
|
|
m_options |= VLD_OPT_UNICODE_REPORT;
|
|
}
|
|
if ((m_options & VLD_OPT_UNICODE_REPORT) && !(m_options & VLD_OPT_REPORT_TO_FILE)) {
|
|
// If Unicode report encoding is enabled, then the report needs to be
|
|
// sent to a file because the debugger will not display Unicode
|
|
// characters, it will display question marks in their place instead.
|
|
m_options |= VLD_OPT_REPORT_TO_FILE;
|
|
m_status |= VLD_STATUS_FORCE_REPORT_TO_FILE;
|
|
}
|
|
|
|
// Read the stack walking method.
|
|
GetPrivateProfileString(L"Options", L"StackWalkMethod", L"", buffer, BSIZE, inipath);
|
|
if (_wcsicmp(buffer, L"safe") == 0) {
|
|
m_options |= VLD_OPT_SAFE_STACK_WALK;
|
|
}
|
|
}
|
|
|
|
// enabled - Determines if memory leak detection is enabled for the current
|
|
// thread.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns true if Visual Leak Detector is enabled for the current thread.
|
|
// Otherwise, returns false.
|
|
//
|
|
BOOL VisualLeakDetector::enabled ()
|
|
{
|
|
tls_t *tls = vld.gettls();
|
|
|
|
if (!(m_status & VLD_STATUS_INSTALLED)) {
|
|
// Memory leak detection is not yet enabled because VLD is still
|
|
// initializing.
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(tls->flags & VLD_TLS_DISABLED) && !(tls->flags & VLD_TLS_ENABLED)) {
|
|
// The enabled/disabled state for the current thread has not been
|
|
// initialized yet. Use the default state.
|
|
if (m_options & VLD_OPT_START_DISABLED) {
|
|
tls->flags |= VLD_TLS_DISABLED;
|
|
}
|
|
else {
|
|
tls->flags |= VLD_TLS_ENABLED;
|
|
}
|
|
}
|
|
|
|
return ((tls->flags & VLD_TLS_ENABLED) != 0);
|
|
}
|
|
|
|
// eraseduplicates - Erases, from the block maps, blocks that appear to be
|
|
// duplicate leaks of an already identified leak.
|
|
//
|
|
// - element (IN): BlockMap Iterator referencing the block of which to search
|
|
// for duplicates.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the number of duplicate blocks erased from the block map.
|
|
//
|
|
SIZE_T VisualLeakDetector::eraseduplicates (const BlockMap::Iterator &element)
|
|
{
|
|
BlockMap::Iterator blockit;
|
|
BlockMap *blockmap;
|
|
blockinfo_t *elementinfo;
|
|
SIZE_T erased = 0;
|
|
HeapMap::Iterator heapit;
|
|
blockinfo_t *info;
|
|
BlockMap::Iterator previt;
|
|
|
|
elementinfo = (*element).second;
|
|
|
|
// Iteratate through all block maps, looking for blocks with the same size
|
|
// and callstack as the specified element.
|
|
for (heapit = m_heapmap->begin(); heapit != m_heapmap->end(); ++heapit) {
|
|
blockmap = &(*heapit).second->blockmap;
|
|
for (blockit = blockmap->begin(); blockit != blockmap->end(); ++blockit) {
|
|
if (blockit == element) {
|
|
// Don't delete the element of which we are searching for
|
|
// duplicates.
|
|
continue;
|
|
}
|
|
info = (*blockit).second;
|
|
if ((info->size == elementinfo->size) && (*(info->callstack) == *(elementinfo->callstack))) {
|
|
// Found a duplicate. Erase it.
|
|
delete info->callstack;
|
|
delete info;
|
|
previt = blockit - 1;
|
|
blockmap->erase(blockit);
|
|
blockit = previt;
|
|
erased++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return erased;
|
|
}
|
|
|
|
// gettls - Obtains the thread local storage structure for the calling thread.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns a pointer to the thread local storage structure. (This function
|
|
// always succeeds).
|
|
//
|
|
tls_t* VisualLeakDetector::gettls ()
|
|
{
|
|
tls_t *tls;
|
|
|
|
// Get the pointer to this thread's thread local storage structure.
|
|
tls = (tls_t*)TlsGetValue(m_tlsindex);
|
|
assert(GetLastError() == ERROR_SUCCESS);
|
|
|
|
if (tls == NULL) {
|
|
// This thread's thread local storage structure has not been allocated.
|
|
tls = new tls_t;
|
|
TlsSetValue(m_tlsindex, tls);
|
|
tls->addrfp = 0x0;
|
|
tls->flags = 0x0;
|
|
tls->threadid = GetCurrentThreadId();
|
|
|
|
// Add this thread's TLS to the TlsSet.
|
|
EnterCriticalSection(&m_tlslock);
|
|
m_tlsset->insert(tls);
|
|
LeaveCriticalSection(&m_tlslock);
|
|
}
|
|
|
|
return tls;
|
|
}
|
|
|
|
// mapblock - Tracks memory allocations. Information about allocated blocks is
|
|
// collected and then the block is mapped to this information.
|
|
//
|
|
// - heap (IN): Handle to the heap from which the block has been allocated.
|
|
//
|
|
// - mem (IN): Pointer to the memory block being allocated.
|
|
//
|
|
// - size (IN): Size, in bytes, of the memory block being allocated.
|
|
//
|
|
// - framepointer (IN): Framepointer at the time this allocation first entered
|
|
// VLD's code. This is used from determining the starting point for the
|
|
// stack trace.
|
|
//
|
|
// - crtalloc (IN): Should be set to TRUE if this allocation is a CRT memory
|
|
// block. Otherwise should be FALSE.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::mapblock (HANDLE heap, LPCVOID mem, SIZE_T size, SIZE_T framepointer, BOOL crtalloc)
|
|
{
|
|
blockinfo_t *blockinfo;
|
|
BlockMap::Iterator blockit;
|
|
BlockMap *blockmap;
|
|
HeapMap::Iterator heapit;
|
|
static SIZE_T serialnumber = 0;
|
|
|
|
// Record the block's information.
|
|
blockinfo = new blockinfo_t;
|
|
if (m_options & VLD_OPT_SAFE_STACK_WALK) {
|
|
blockinfo->callstack = new SafeCallStack;
|
|
}
|
|
else {
|
|
blockinfo->callstack = new FastCallStack;
|
|
}
|
|
if (m_options & VLD_OPT_TRACE_INTERNAL_FRAMES) {
|
|
// Passing NULL for the frame pointer argument will force the stack
|
|
// trace to begin at the current frame.
|
|
blockinfo->callstack->getstacktrace(m_maxtraceframes, NULL);
|
|
}
|
|
else {
|
|
// Start the stack trace at the call that first entered VLD's code.
|
|
blockinfo->callstack->getstacktrace(m_maxtraceframes, (SIZE_T*)framepointer);
|
|
}
|
|
blockinfo->serialnumber = serialnumber++;
|
|
blockinfo->size = size;
|
|
|
|
// Insert the block's information into the block map.
|
|
EnterCriticalSection(&m_maplock);
|
|
heapit = m_heapmap->find(heap);
|
|
if (heapit == m_heapmap->end()) {
|
|
// We haven't mapped this heap to a block map yet. Do it now.
|
|
mapheap(heap);
|
|
heapit = m_heapmap->find(heap);
|
|
assert(heapit != m_heapmap->end());
|
|
}
|
|
if (crtalloc == TRUE) {
|
|
// The heap that this block was allocated from is a CRT heap.
|
|
(*heapit).second->flags |= VLD_HEAP_CRT;
|
|
}
|
|
blockmap = &(*heapit).second->blockmap;
|
|
blockit = blockmap->insert(mem, blockinfo);
|
|
if (blockit == blockmap->end()) {
|
|
// A block with this address has already been allocated. The
|
|
// previously allocated block must have been freed (probably by some
|
|
// mechanism unknown to VLD), or the heap wouldn't have allocated it
|
|
// again. Replace the previously allocated info with the new info.
|
|
blockit = blockmap->find(mem);
|
|
delete (*blockit).second->callstack;
|
|
delete (*blockit).second;
|
|
blockmap->erase(blockit);
|
|
blockmap->insert(mem, blockinfo);
|
|
}
|
|
LeaveCriticalSection(&m_maplock);
|
|
}
|
|
|
|
// mapheap - Tracks heap creation. Creates a block map for tracking individual
|
|
// allocations from the newly created heap and then maps the heap to this
|
|
// block map.
|
|
//
|
|
// - heap (IN): Handle to the newly created heap.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::mapheap (HANDLE heap)
|
|
{
|
|
heapinfo_t *heapinfo;
|
|
HeapMap::Iterator heapit;
|
|
|
|
// Create a new block map for this heap and insert it into the heap map.
|
|
heapinfo = new heapinfo_t;
|
|
heapinfo->blockmap.reserve(BLOCKMAPRESERVE);
|
|
heapinfo->flags = 0x0;
|
|
EnterCriticalSection(&m_maplock);
|
|
heapit = m_heapmap->insert(heap, heapinfo);
|
|
if (heapit == m_heapmap->end()) {
|
|
// Somehow this heap has been created twice without being destroyed,
|
|
// or at least it was destroyed without VLD's knowledge. Unmap the heap
|
|
// from the existing heapinfo, and remap it to the new one.
|
|
report(L"WARNING: Visual Leak Detector detected a duplicate heap (" ADDRESSFORMAT L").\n", heap);
|
|
heapit = m_heapmap->find(heap);
|
|
unmapheap((*heapit).first);
|
|
m_heapmap->insert(heap, heapinfo);
|
|
}
|
|
LeaveCriticalSection(&m_maplock);
|
|
}
|
|
|
|
// remapblock - Tracks reallocations. Unmaps a block from its previously
|
|
// collected information and remaps it to updated information.
|
|
//
|
|
// Note: If the block itself remains at the same address, then the block's
|
|
// information can simply be updated rather than having to actually erase and
|
|
// reinsert the block.
|
|
//
|
|
// - heap (IN): Handle to the heap from which the memory is being reallocated.
|
|
//
|
|
// - mem (IN): Pointer to the memory block being reallocated.
|
|
//
|
|
// - newmem (IN): Pointer to the memory block being returned to the caller
|
|
// that requested the reallocation. This pointer may or may not be the same
|
|
// as the original memory block (as pointed to by "mem").
|
|
//
|
|
// - size (IN): Size, in bytes, of the new memory block.
|
|
//
|
|
// - framepointer (IN): The frame pointer at which this reallocation entered
|
|
// VLD's code. Used for determining the starting point of the stack trace.
|
|
//
|
|
// - crtalloc (IN): Should be set to TRUE if this reallocation is for a CRT
|
|
// memory block. Otherwise should be set to FALSE.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::remapblock (HANDLE heap, LPCVOID mem, LPCVOID newmem, SIZE_T size, SIZE_T framepointer,
|
|
BOOL crtalloc)
|
|
{
|
|
BlockMap::Iterator blockit;
|
|
BlockMap *blockmap;
|
|
HeapMap::Iterator heapit;
|
|
blockinfo_t *info;
|
|
|
|
if (newmem != mem) {
|
|
// The block was not reallocated in-place. Instead the old block was
|
|
// freed and a new block allocated to satisfy the new size.
|
|
unmapblock(heap, mem);
|
|
mapblock(heap, newmem, size, framepointer, crtalloc);
|
|
return;
|
|
}
|
|
|
|
// The block was reallocated in-place. Find the existing blockinfo_t
|
|
// entry in the block map and update it with the new callstack and size.
|
|
EnterCriticalSection(&m_maplock);
|
|
heapit = m_heapmap->find(heap);
|
|
if (heapit == m_heapmap->end()) {
|
|
// We haven't mapped this heap to a block map yet. Obviously the
|
|
// block has also not been mapped to a blockinfo_t entry yet either,
|
|
// so treat this reallocation as a brand-new allocation (this will
|
|
// also map the heap to a new block map).
|
|
mapblock(heap, newmem, size, framepointer, crtalloc);
|
|
LeaveCriticalSection(&m_maplock);
|
|
return;
|
|
}
|
|
|
|
// Find the block's blockinfo_t structure so that we can update it.
|
|
blockmap = &(*heapit).second->blockmap;
|
|
blockit = blockmap->find(mem);
|
|
if (blockit == blockmap->end()) {
|
|
// The block hasn't been mapped to a blockinfo_t entry yet.
|
|
// Treat this reallocation as a new allocation.
|
|
mapblock(heap, newmem, size, framepointer, crtalloc);
|
|
LeaveCriticalSection(&m_maplock);
|
|
return;
|
|
}
|
|
|
|
// Found the blockinfo_t entry for this block. Update it with
|
|
// a new callstack and new size.
|
|
info = (*blockit).second;
|
|
info->callstack->clear();
|
|
if (crtalloc) {
|
|
// The heap that this block was allocated from is a CRT heap.
|
|
(*heapit).second->flags |= VLD_HEAP_CRT;
|
|
}
|
|
LeaveCriticalSection(&m_maplock);
|
|
|
|
// Update the block's callstack and size.
|
|
if (m_options & VLD_OPT_TRACE_INTERNAL_FRAMES) {
|
|
// Passing NULL for the frame pointer argument will force
|
|
// the stack trace to begin at the current frame.
|
|
info->callstack->getstacktrace(m_maxtraceframes, NULL);
|
|
}
|
|
else {
|
|
// Start the stack trace at the call that first entered
|
|
// VLD's code.
|
|
info->callstack->getstacktrace(m_maxtraceframes, (SIZE_T*)framepointer);
|
|
}
|
|
info->size = size;
|
|
}
|
|
|
|
// reportconfig - Generates a brief report summarizing Visual Leak Detector's
|
|
// configuration, as loaded from the vld.ini file.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::reportconfig ()
|
|
{
|
|
if (m_options & VLD_OPT_AGGREGATE_DUPLICATES) {
|
|
report(L" Aggregating duplicate leaks.\n");
|
|
}
|
|
if (wcslen(m_forcedmodulelist) != 0) {
|
|
report(L" Forcing inclusion of these modules in leak detection: %s\n", m_forcedmodulelist);
|
|
}
|
|
if (m_maxdatadump != VLD_DEFAULT_MAX_DATA_DUMP) {
|
|
if (m_maxdatadump == 0) {
|
|
report(L" Suppressing data dumps.\n");
|
|
}
|
|
else {
|
|
report(L" Limiting data dumps to %lu bytes.\n", m_maxdatadump);
|
|
}
|
|
}
|
|
if (m_maxtraceframes != VLD_DEFAULT_MAX_TRACE_FRAMES) {
|
|
report(L" Limiting stack traces to %u frames.\n", m_maxtraceframes);
|
|
}
|
|
if (m_options & VLD_OPT_UNICODE_REPORT) {
|
|
report(L" Generating a Unicode (UTF-16) encoded report.\n");
|
|
}
|
|
if (m_options & VLD_OPT_REPORT_TO_FILE) {
|
|
if (m_options & VLD_OPT_REPORT_TO_DEBUGGER) {
|
|
report(L" Outputting the report to the debugger and to %s\n", m_reportfilepath);
|
|
}
|
|
else {
|
|
report(L" Outputting the report to %s\n", m_reportfilepath);
|
|
}
|
|
}
|
|
if (m_options & VLD_OPT_SLOW_DEBUGGER_DUMP) {
|
|
report(L" Outputting the report to the debugger at a slower rate.\n");
|
|
}
|
|
if (m_options & VLD_OPT_SAFE_STACK_WALK) {
|
|
report(L" Using the \"safe\" (but slow) stack walking method.\n");
|
|
}
|
|
if (m_options & VLD_OPT_SELF_TEST) {
|
|
report(L" Perfoming a memory leak self-test.\n");
|
|
}
|
|
if (m_options & VLD_OPT_START_DISABLED) {
|
|
report(L" Starting with memory leak detection disabled.\n");
|
|
}
|
|
if (m_options & VLD_OPT_TRACE_INTERNAL_FRAMES) {
|
|
report(L" Including heap and VLD internal frames in stack traces.\n");
|
|
}
|
|
}
|
|
|
|
// reportleaks - Generates a memory leak report for the specified heap.
|
|
//
|
|
// - heap (IN): Handle to the heap for which to generate a memory leak
|
|
// report.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::reportleaks (HANDLE heap)
|
|
{
|
|
LPCVOID address;
|
|
LPCVOID block;
|
|
BlockMap::Iterator blockit;
|
|
BlockMap *blockmap;
|
|
crtdbgblockheader_t *crtheader;
|
|
SIZE_T duplicates;
|
|
heapinfo_t *heapinfo;
|
|
HeapMap::Iterator heapit;
|
|
blockinfo_t *info;
|
|
SIZE_T size;
|
|
|
|
// Find the heap's information (blockmap, etc).
|
|
EnterCriticalSection(&m_maplock);
|
|
heapit = m_heapmap->find(heap);
|
|
if (heapit == m_heapmap->end()) {
|
|
// Nothing is allocated from this heap. No leaks.
|
|
LeaveCriticalSection(&m_maplock);
|
|
return;
|
|
}
|
|
|
|
heapinfo = (*heapit).second;
|
|
blockmap = &heapinfo->blockmap;
|
|
for (blockit = blockmap->begin(); blockit != blockmap->end(); ++blockit) {
|
|
// Found a block which is still in the BlockMap. We've identified a
|
|
// potential memory leak.
|
|
block = (*blockit).first;
|
|
info = (*blockit).second;
|
|
address = block;
|
|
size = info->size;
|
|
if (heapinfo->flags & VLD_HEAP_CRT) {
|
|
// This block is allocated to a CRT heap, so the block has a CRT
|
|
// memory block header prepended to it.
|
|
crtheader = (crtdbgblockheader_t*)block;
|
|
if (CRT_USE_TYPE(crtheader->use) == CRT_USE_INTERNAL) {
|
|
// This block is marked as being used internally by the CRT.
|
|
// The CRT will free the block after VLD is destroyed.
|
|
continue;
|
|
}
|
|
// The CRT header is more or less transparent to the user, so
|
|
// the information about the contained block will probably be
|
|
// more useful to the user. Accordingly, that's the information
|
|
// we'll include in the report.
|
|
address = CRTDBGBLOCKDATA(block);
|
|
size = crtheader->size;
|
|
}
|
|
// It looks like a real memory leak.
|
|
if (m_leaksfound == 0) {
|
|
report(L"WARNING: Visual Leak Detector detected memory leaks!\n");
|
|
}
|
|
m_leaksfound++;
|
|
report(L"---------- Block %ld at " ADDRESSFORMAT L": %u bytes ----------\n", info->serialnumber, address, size);
|
|
if (m_options & VLD_OPT_AGGREGATE_DUPLICATES) {
|
|
// Aggregate all other leaks which are duplicates of this one
|
|
// under this same heading, to cut down on clutter.
|
|
duplicates = eraseduplicates(blockit);
|
|
if (duplicates) {
|
|
report(L"A total of %lu leaks match this size and call stack. Showing only the first one.\n",
|
|
duplicates + 1);
|
|
m_leaksfound += duplicates;
|
|
}
|
|
}
|
|
// Dump the call stack.
|
|
report(L" Call Stack:\n");
|
|
info->callstack->dump(m_options & VLD_OPT_TRACE_INTERNAL_FRAMES);
|
|
// Dump the data in the user data section of the memory block.
|
|
if (m_maxdatadump != 0) {
|
|
report(L" Data:\n");
|
|
if (m_options & VLD_OPT_UNICODE_REPORT) {
|
|
dumpmemoryw(address, (m_maxdatadump < size) ? m_maxdatadump : size);
|
|
}
|
|
else {
|
|
dumpmemorya(address, (m_maxdatadump < size) ? m_maxdatadump : size);
|
|
}
|
|
}
|
|
report(L"\n");
|
|
}
|
|
|
|
LeaveCriticalSection(&m_maplock);
|
|
}
|
|
|
|
// unmapblock - Tracks memory blocks that are freed. Unmaps the specified block
|
|
// from the block's information, relinquishing internally allocated resources.
|
|
//
|
|
// - heap (IN): Handle to the heap to which this block is being freed.
|
|
//
|
|
// - mem (IN): Pointer to the memory block being freed.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::unmapblock (HANDLE heap, LPCVOID mem)
|
|
{
|
|
BlockMap::Iterator blockit;
|
|
BlockMap *blockmap;
|
|
HeapMap::Iterator heapit;
|
|
blockinfo_t *info;
|
|
|
|
// Find this heap's block map.
|
|
EnterCriticalSection(&m_maplock);
|
|
heapit = m_heapmap->find(heap);
|
|
if (heapit == m_heapmap->end()) {
|
|
// We don't have a block map for this heap. We must not have monitored
|
|
// this allocation (probably happened before VLD was initialized).
|
|
LeaveCriticalSection(&m_maplock);
|
|
return;
|
|
}
|
|
|
|
// Find this block in the block map.
|
|
blockmap = &(*heapit).second->blockmap;
|
|
blockit = blockmap->find(mem);
|
|
if (blockit == blockmap->end()) {
|
|
// This block is not in the block map. We must not have monitored this
|
|
// allocation (probably happened before VLD was initialized).
|
|
LeaveCriticalSection(&m_maplock);
|
|
return;
|
|
}
|
|
|
|
// Free the blockinfo_t structure and erase it from the block map.
|
|
info = (*blockit).second;
|
|
delete info->callstack;
|
|
delete info;
|
|
blockmap->erase(blockit);
|
|
LeaveCriticalSection(&m_maplock);
|
|
}
|
|
|
|
// unmapheap - Tracks heap destruction. Unmaps the specified heap from its block
|
|
// map. The block map is cleared and deleted, relinquishing internally
|
|
// allocated resources.
|
|
//
|
|
// - heap (IN): Handle to the heap which is being destroyed.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::unmapheap (HANDLE heap)
|
|
{
|
|
BlockMap::Iterator blockit;
|
|
BlockMap *blockmap;
|
|
heapinfo_t *heapinfo;
|
|
HeapMap::Iterator heapit;
|
|
|
|
// Find this heap's block map.
|
|
EnterCriticalSection(&m_maplock);
|
|
heapit = m_heapmap->find(heap);
|
|
if (heapit == m_heapmap->end()) {
|
|
// This heap hasn't been mapped. We must not have monitored this heap's
|
|
// creation (probably happened before VLD was initialized).
|
|
LeaveCriticalSection(&m_maplock);
|
|
return;
|
|
}
|
|
|
|
// Free all of the blockinfo_t structures stored in the block map.
|
|
heapinfo = (*heapit).second;
|
|
blockmap = &heapinfo->blockmap;
|
|
for (blockit = blockmap->begin(); blockit != blockmap->end(); ++blockit) {
|
|
delete (*blockit).second->callstack;
|
|
delete (*blockit).second;
|
|
}
|
|
delete heapinfo;
|
|
|
|
// Remove this heap's block map from the heap map.
|
|
m_heapmap->erase(heapit);
|
|
LeaveCriticalSection(&m_maplock);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Static Leak Detection Functions (Callbacks)
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// addloadedmodule - Callback function for EnumerateLoadedModules64. This
|
|
// function records information about every module loaded in the process,
|
|
// each time adding the module's information to the provided ModuleSet (the
|
|
// "context" parameter).
|
|
//
|
|
// When EnumerateLoadedModules64 has finished calling this function for each
|
|
// loaded module, then the resulting ModuleSet can be used at any time to get
|
|
// information about any modules loaded into the process.
|
|
//
|
|
// - modulepath (IN): The fully qualified path from where the module was
|
|
// loaded.
|
|
//
|
|
// - modulebase (IN): The base address at which the module has been loaded.
|
|
//
|
|
// - modulesize (IN): The size, in bytes, of the loaded module.
|
|
//
|
|
// - context (IN): Pointer to the ModuleSet to which information about each
|
|
// module is to be added.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Always returns TRUE, which tells EnumerateLoadedModules64 to continue
|
|
// enumerating.
|
|
//
|
|
BOOL VisualLeakDetector::addloadedmodule (PCWSTR modulepath, DWORD64 modulebase, ULONG modulesize, PVOID context)
|
|
{
|
|
size_t count;
|
|
patchentry_t *entry;
|
|
CHAR extension [_MAX_EXT];
|
|
CHAR filename [_MAX_FNAME];
|
|
UINT index;
|
|
moduleinfo_t moduleinfo;
|
|
LPSTR modulenamea;
|
|
LPSTR modulepatha;
|
|
ModuleSet* newmodules = (ModuleSet*)context;
|
|
SIZE_T size;
|
|
UINT tablesize = sizeof(m_patchtable) / sizeof(patchentry_t);
|
|
|
|
// Convert the module path to ASCII.
|
|
size = wcslen(modulepath) + 1;
|
|
modulepatha = new CHAR [size];
|
|
wcstombs_s(&count, modulepatha, size, modulepath, _TRUNCATE);
|
|
|
|
// Extract just the filename and extension from the module path.
|
|
_splitpath_s(modulepatha, NULL, 0, NULL, 0, filename, _MAX_FNAME, extension, _MAX_EXT);
|
|
size = strlen(filename) + strlen(extension) + 1;
|
|
modulenamea = new CHAR [size];
|
|
strncpy_s(modulenamea, size, filename, _TRUNCATE);
|
|
strncat_s(modulenamea, size, extension, _TRUNCATE);
|
|
_strlwr_s(modulenamea, size);
|
|
|
|
if (_stricmp(modulenamea, "vld.dll") == 0) {
|
|
// Record Visual Leak Detector's own base address.
|
|
vld.m_vldbase = (HMODULE)modulebase;
|
|
}
|
|
else {
|
|
// See if this is a module listed in the patch table. If it is, update
|
|
// the corresponding patch table entries' module base address.
|
|
for (index = 0; index < tablesize; index++) {
|
|
entry = &m_patchtable[index];
|
|
if (_stricmp(entry->exportmodulename, modulenamea) == 0) {
|
|
entry->modulebase = (SIZE_T)modulebase;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Record the module's information and store it in the set.
|
|
moduleinfo.addrlow = (SIZE_T)modulebase;
|
|
moduleinfo.addrhigh = (SIZE_T)(modulebase + modulesize) - 1;
|
|
moduleinfo.flags = 0x0;
|
|
moduleinfo.name = modulenamea;
|
|
moduleinfo.path = modulepatha;
|
|
newmodules->insert(moduleinfo);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// detachfrommodule - Callback function for EnumerateLoadedModules64 that
|
|
// detaches Visual Leak Detector from the specified module. If the specified
|
|
// module has not previously been attached to, then calling this function will
|
|
// not actually result in any changes.
|
|
//
|
|
// - modulepath (IN): String containing the name, which may include a path, of
|
|
// the module to detach from (ignored).
|
|
//
|
|
// - modulebase (IN): Base address of the module.
|
|
//
|
|
// - modulesize (IN): Total size of the module (ignored).
|
|
//
|
|
// - context (IN): User-supplied context (ignored).
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Always returns TRUE.
|
|
//
|
|
BOOL VisualLeakDetector::detachfrommodule (PCWSTR /*modulepath*/, DWORD64 modulebase, ULONG /*modulesize*/,
|
|
PVOID /*context*/)
|
|
{
|
|
UINT tablesize = sizeof(m_patchtable) / sizeof(patchentry_t);
|
|
|
|
restoremodule((HMODULE)modulebase, m_patchtable, tablesize);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Standard CRT and MFC IAT Replacement Functions
|
|
//
|
|
// The addresses of these functions are not actually directly patched into the
|
|
// import address tables, but these functions do get indirectly called by the
|
|
// patch functions that are placed in the import address tables.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// _calloc - This function is just a wrapper around the real calloc that sets
|
|
// appropriate flags to be consulted when the memory is actually allocated by
|
|
// RtlAllocateHeap.
|
|
//
|
|
// - pcalloc (IN): Pointer to the particular calloc implementation to call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - num (IN): The number of blocks, of size 'size', to be allocated.
|
|
//
|
|
// - size (IN): The size, in bytes, of the memory block to be allocated.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned from the specified calloc.
|
|
//
|
|
void* VisualLeakDetector::_calloc (calloc_t pcalloc,
|
|
SIZE_T fp,
|
|
size_t num,
|
|
size_t size)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// malloc is a CRT function and allocates from the CRT heap.
|
|
tls->flags |= VLD_TLS_CRTALLOC;
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = pcalloc(num, size);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// _malloc - This function is just a wrapper around the real malloc that sets
|
|
// appropriate flags to be consulted when the memory is actually allocated by
|
|
// RtlAllocateHeap.
|
|
//
|
|
// - pmalloc (IN): Pointer to the particular malloc implementation to call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - size (IN): The size, in bytes, of the memory block to be allocated.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned from the specified malloc.
|
|
//
|
|
void *VisualLeakDetector::_malloc (malloc_t pmalloc, SIZE_T fp, size_t size)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// malloc is a CRT function and allocates from the CRT heap.
|
|
tls->flags |= VLD_TLS_CRTALLOC;
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = pmalloc(size);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// _new - This function is just a wrapper around the real CRT and MFC new
|
|
// operators that sets appropriate flags to be consulted when the memory is
|
|
// actually allocated by RtlAllocateHeap.
|
|
//
|
|
// - pnew (IN): Pointer to the particular new implementation to call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - size (IN): The size, in bytes, of the memory block to be allocated.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the specified CRT new operator.
|
|
//
|
|
void* VisualLeakDetector::_new (new_t pnew, SIZE_T fp, size_t size)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// The new operator is a CRT function and allocates from the CRT heap.
|
|
tls->flags |= VLD_TLS_CRTALLOC;
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = pnew(size);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// _realloc - This function is just a wrapper around the real realloc that sets
|
|
// appropriate flags to be consulted when the memory is actually allocated by
|
|
// RtlAllocateHeap.
|
|
//
|
|
// - prealloc (IN): Pointer to the particular realloc implementation to call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - mem (IN): Pointer to the memory block to reallocate.
|
|
//
|
|
// - size (IN): Size of the memory block to reallocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned from the specified realloc.
|
|
//
|
|
void* VisualLeakDetector::_realloc (realloc_t prealloc,
|
|
SIZE_T fp,
|
|
void *mem,
|
|
size_t size)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// realloc is a CRT function and allocates from the CRT heap.
|
|
tls->flags |= VLD_TLS_CRTALLOC;
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlReAllocateHeap.
|
|
block = prealloc(mem, size);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Debug CRT and MFC IAT Replacement Functions
|
|
//
|
|
// The addresses of these functions are not actually directly patched into the
|
|
// import address tables, but these functions do get indirectly called by the
|
|
// patch functions that are placed in the import address tables.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// __calloc_dbg - This function is just a wrapper around the real _calloc_dbg
|
|
// that sets appropriate flags to be consulted when the memory is actually
|
|
// allocated by RtlAllocateHeap.
|
|
//
|
|
// - p_calloc_dbg: Pointer to the particular _calloc_dbg implementation to
|
|
// call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - size (IN): The size, in bytes, of the memory block to be allocated.
|
|
//
|
|
// - type (IN): The CRT "use type" of the block to be allocated.
|
|
//
|
|
// - file (IN): The name of the file from which this function is being called.
|
|
//
|
|
// - line (IN): The line number, in the above file, at which this function is
|
|
// being called.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the specified _calloc_dbg.
|
|
//
|
|
void* VisualLeakDetector::__calloc_dbg (_calloc_dbg_t p_calloc_dbg,
|
|
SIZE_T fp,
|
|
size_t num,
|
|
size_t size,
|
|
int type,
|
|
char const *file,
|
|
int line)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// _malloc_dbg is a CRT function and allocates from the CRT heap.
|
|
tls->flags |= VLD_TLS_CRTALLOC;
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = p_calloc_dbg(num, size, type, file, line);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// __malloc_dbg - This function is just a wrapper around the real _malloc_dbg
|
|
// that sets appropriate flags to be consulted when the memory is actually
|
|
// allocated by RtlAllocateHeap.
|
|
//
|
|
// - p_malloc_dbg (IN): Pointer to the particular _malloc_dbg implementation to
|
|
// call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - size (IN): The size, in bytes, of the memory block to be allocated.
|
|
//
|
|
// - type (IN): The CRT "use type" of the block to be allocated.
|
|
//
|
|
// - file (IN): The name of the file from which this function is being called.
|
|
//
|
|
// - line (IN): The line number, in the above file, at which this function is
|
|
// being called.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the specified _malloc_dbg.
|
|
//
|
|
void* VisualLeakDetector::__malloc_dbg (_malloc_dbg_t p_malloc_dbg,
|
|
SIZE_T fp,
|
|
size_t size,
|
|
int type,
|
|
char const *file,
|
|
int line)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// _malloc_dbg is a CRT function and allocates from the CRT heap.
|
|
tls->flags |= VLD_TLS_CRTALLOC;
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = p_malloc_dbg(size, type, file, line);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// new_dbg_crt - This function is just a wrapper around the real CRT debug new
|
|
// operators that sets appropriate flags to be consulted when the memory is
|
|
// actually allocated by RtlAllocateHeap.
|
|
//
|
|
// - pnew_dbg_crt (IN): Pointer to the particular CRT new operator
|
|
// implementation to call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - size (IN): The size, in bytes, of the memory block to be allocated.
|
|
//
|
|
// - type (IN): The CRT "use type" of the block to be allocated.
|
|
//
|
|
// - file (IN): The name of the file from which this function is being called.
|
|
//
|
|
// - line (IN): The line number, in the above file, at which this function is
|
|
// being called.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the specified CRT debug new operator.
|
|
//
|
|
void* VisualLeakDetector::new_dbg_crt (new_dbg_crt_t pnew_dbg_crt,
|
|
SIZE_T fp,
|
|
size_t size,
|
|
int type,
|
|
char const *file,
|
|
int line)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// The debug new operator is a CRT function and allocates from the CRT heap.
|
|
tls->flags |= VLD_TLS_CRTALLOC;
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = pnew_dbg_crt(size, type, file, line);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// new_dbg_mfc - This function is just a wrapper around the real MFC debug new
|
|
// operators that sets appropriate flags to be consulted when the memory is
|
|
// actually allocated by RtlAllocateHeap.
|
|
//
|
|
// - pnew_dbg (IN): Pointer to the particular CRT new operator
|
|
// implementation to call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - size (IN): The size, in bytes, of the memory block to be allocated.
|
|
//
|
|
// - type (IN): The CRT "use type" of the block to be allocated.
|
|
//
|
|
// - file (IN): The name of the file from which this function is being called.
|
|
//
|
|
// - line (IN): The line number, in the above file, at which this function is
|
|
// being called.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the specified CRT debug new operator.
|
|
//
|
|
void* VisualLeakDetector::new_dbg_mfc (new_dbg_crt_t pnew_dbg,
|
|
SIZE_T fp,
|
|
size_t size,
|
|
int type,
|
|
char const *file,
|
|
int line)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = pnew_dbg(size, type, file, line);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// new_dbg_mfc - This function is just a wrapper around the real MFC debug new
|
|
// operators that sets appropriate flags to be consulted when the memory is
|
|
// actually allocated by RtlAllocateHeap.
|
|
//
|
|
// - pnew_dbg_mfc (IN): Pointer to the particular MFC new operator
|
|
// implementation to call.
|
|
//
|
|
// - fp (IN): Frame pointer of the call that initiated this allocation.
|
|
//
|
|
// - size (IN): The size, in bytes, of the memory block to be allocated.
|
|
//
|
|
// - file (IN): The name of the file from which this function is being called.
|
|
//
|
|
// - line (IN): The line number, in the above file, at which this function is
|
|
// being called.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the specified MFC debug new operator.
|
|
//
|
|
void* VisualLeakDetector::new_dbg_mfc (new_dbg_mfc_t pnew_dbg_mfc,
|
|
SIZE_T fp,
|
|
size_t size,
|
|
char const *file,
|
|
int line)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = pnew_dbg_mfc(size, file, line);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// __realloc_debug - This function is just a wrapper around the real
|
|
// _realloc_dbg that sets appropriate flags to be consulted when the memory is
|
|
// actually allocated by RtlAllocateHeap.
|
|
//
|
|
// - p_realloc_dbg (IN): Pointer to the particular __realloc_dbg implementation
|
|
// to call.
|
|
//
|
|
// - fp (IN): Frame pointer from the call that initiated this allocation.
|
|
//
|
|
// - mem (IN): Pointer to the memory block to be reallocated.
|
|
//
|
|
// - size (IN): The size of the memory block to reallocate.
|
|
//
|
|
// - type (IN): The CRT "use type" of the block to be reallocated.
|
|
//
|
|
// - file (IN): The name of the file from which this function is being called.
|
|
//
|
|
// - line (IN): The line number, in the above file, at which this function is
|
|
// being called.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the specified _realloc_dbg.
|
|
//
|
|
void* VisualLeakDetector::__realloc_dbg (_realloc_dbg_t p_realloc_dbg,
|
|
SIZE_T fp,
|
|
void *mem,
|
|
size_t size,
|
|
int type,
|
|
char const *file,
|
|
int line)
|
|
{
|
|
void *block;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// _realloc_dbg is a CRT function and allocates from the CRT heap.
|
|
tls->flags |= VLD_TLS_CRTALLOC;
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlReAllocateHeap.
|
|
block = p_realloc_dbg(mem, size, type, file, line);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Win32 IAT Replacement Functions
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// _GetProcAddress - Calls to GetProcAddress are patched through to this
|
|
// function. If the requested function is a function that has been patched
|
|
// through to one of VLD's handlers, then the address of VLD's handler
|
|
// function is returned instead of the real address. Otherwise, this
|
|
// function is just a wrapper around the real GetProcAddress.
|
|
//
|
|
// - module (IN): Handle (base address) of the module from which to retrieve
|
|
// the address of an exported function.
|
|
//
|
|
// - procname (IN): ANSI string containing the name of the exported function
|
|
// whose address is to be retrieved.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns a pointer to the requested function, or VLD's replacement for
|
|
// the function, if there is a replacement function.
|
|
//
|
|
FARPROC VisualLeakDetector::_GetProcAddress (HMODULE module, LPCSTR procname)
|
|
{
|
|
patchentry_t *entry;
|
|
UINT index;
|
|
UINT tablesize = sizeof(vld.m_patchtable) / sizeof(patchentry_t);
|
|
|
|
// See if there is an entry in the patch table that matches the requested
|
|
// function.
|
|
for (index = 0; index < tablesize; index++) {
|
|
entry = &vld.m_patchtable[index];
|
|
if ((entry->modulebase == 0x0) || ((HMODULE)entry->modulebase != module)) {
|
|
// This patch table entry is for a different module.
|
|
continue;
|
|
}
|
|
|
|
// This patch table entry is for the specified module. If the requested
|
|
// import's name matches the entry's import name (or ordinal), then
|
|
// return the address of the replacement instead of the address of the
|
|
// actual import.
|
|
if ((SIZE_T)entry->importname < (SIZE_T)vld.m_vldbase) {
|
|
// This entry's import name is not a valid pointer to data in
|
|
// vld.dll. It must be an ordinal value.
|
|
if ((UINT)entry->importname == (UINT)procname) {
|
|
return (FARPROC)entry->replacement;
|
|
}
|
|
}
|
|
else {
|
|
if (strcmp(entry->importname, procname) == 0) {
|
|
return (FARPROC)entry->replacement;
|
|
}
|
|
}
|
|
}
|
|
|
|
// The requested function is not a patched function. Just return the real
|
|
// address of the requested function.
|
|
return GetProcAddress(module, procname);
|
|
}
|
|
|
|
// _HeapCreate - Calls to HeapCreate are patched through to this function. This
|
|
// function is just a wrapper around the real HeapCreate that calls VLD's heap
|
|
// creation tracking function after the heap has been created.
|
|
//
|
|
// - options (IN): Heap options.
|
|
//
|
|
// - initsize (IN): Initial size of the heap.
|
|
//
|
|
// - maxsize (IN): Maximum size of the heap.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by HeapCreate.
|
|
//
|
|
HANDLE VisualLeakDetector::_HeapCreate (DWORD options, SIZE_T initsize, SIZE_T maxsize)
|
|
{
|
|
DWORD64 displacement;
|
|
SIZE_T fp;
|
|
SYMBOL_INFO *functioninfo;
|
|
HANDLE heap;
|
|
HeapMap::Iterator heapit;
|
|
SIZE_T ra;
|
|
BYTE symbolbuffer [sizeof(SYMBOL_INFO) + (MAXSYMBOLNAMELENGTH * sizeof(WCHAR)) - 1] = { 0 };
|
|
BOOL symfound;
|
|
|
|
// Get the return address within the calling function.
|
|
FRAMEPOINTER(fp);
|
|
ra = *((SIZE_T*)fp + 1);
|
|
|
|
// Create the heap.
|
|
heap = HeapCreate(options, initsize, maxsize);
|
|
|
|
// Map the created heap handle to a new block map.
|
|
vld.mapheap(heap);
|
|
|
|
// Try to get the name of the function containing the return address.
|
|
functioninfo = (SYMBOL_INFO*)&symbolbuffer;
|
|
functioninfo->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
functioninfo->MaxNameLen = MAXSYMBOLNAMELENGTH;
|
|
EnterCriticalSection(&symbollock);
|
|
symfound = SymFromAddrW(currentprocess, ra, &displacement, functioninfo);
|
|
LeaveCriticalSection(&symbollock);
|
|
if (symfound == TRUE) {
|
|
if (wcscmp(L"_heap_init", functioninfo->Name) == 0) {
|
|
// HeapCreate was called by _heap_init. This is a static CRT heap.
|
|
EnterCriticalSection(&vld.m_maplock);
|
|
heapit = vld.m_heapmap->find(heap);
|
|
assert(heapit != vld.m_heapmap->end());
|
|
(*heapit).second->flags |= VLD_HEAP_CRT;
|
|
LeaveCriticalSection(&vld.m_maplock);
|
|
}
|
|
}
|
|
|
|
return heap;
|
|
}
|
|
|
|
// _HeapDestroy - Calls to HeapDestroy are patched through to this function.
|
|
// This function is just a wrapper around the real HeapDestroy that calls
|
|
// VLD's heap destruction tracking function after the heap has been destroyed.
|
|
//
|
|
// - heap (IN): Handle to the heap to be destroyed.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the valued returned by HeapDestroy.
|
|
//
|
|
BOOL VisualLeakDetector::_HeapDestroy (HANDLE heap)
|
|
{
|
|
// After this heap is destroyed, the heap's address space will be unmapped
|
|
// from the process's address space. So, we'd better generate a leak report
|
|
// for this heap now, while we can still read from the memory blocks
|
|
// allocated to it.
|
|
vld.reportleaks(heap);
|
|
|
|
vld.unmapheap(heap);
|
|
|
|
return HeapDestroy(heap);
|
|
}
|
|
|
|
// _LdrLoadDll - Calls to LdrLoadDll are patched through to this function. This
|
|
// function invokes the real LdrLoadDll and then re-attaches VLD to all
|
|
// modules loaded in the process after loading of the new DLL is complete.
|
|
// All modules must be re-enumerated because the explicit load of the
|
|
// specified module may result in the implicit load of one or more additional
|
|
// modules which are dependencies of the specified module.
|
|
//
|
|
// - searchpath (IN): The path to use for searching for the specified module to
|
|
// be loaded.
|
|
//
|
|
// - flags (IN): Pointer to action flags.
|
|
//
|
|
// - modulename (IN): Pointer to a unicodestring_t structure specifying the
|
|
// name of the module to be loaded.
|
|
//
|
|
// - modulehandle (OUT): Address of a HANDLE to receive the newly loaded
|
|
// module's handle.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by LdrLoadDll.
|
|
//
|
|
NTSTATUS VisualLeakDetector::_LdrLoadDll (LPWSTR searchpath, PDWORD flags, unicodestring_t *modulename,
|
|
PHANDLE modulehandle)
|
|
{
|
|
ModuleSet::Iterator moduleit;
|
|
ModuleSet *newmodules;
|
|
ModuleSet *oldmodules;
|
|
NTSTATUS status;
|
|
|
|
EnterCriticalSection(&vld.m_loaderlock);
|
|
|
|
// Load the DLL.
|
|
status = LdrLoadDll(searchpath, flags, modulename, modulehandle);
|
|
|
|
if (STATUS_SUCCESS == status) {
|
|
// Create a new set of all loaded modules, including any newly loaded
|
|
// modules.
|
|
newmodules = new ModuleSet;
|
|
newmodules->reserve(MODULESETRESERVE);
|
|
EnumerateLoadedModulesW64(currentprocess, addloadedmodule, newmodules);
|
|
|
|
// Attach to all modules included in the set.
|
|
vld.attachtoloadedmodules(newmodules);
|
|
|
|
// Start using the new set of loaded modules.
|
|
EnterCriticalSection(&vld.m_moduleslock);
|
|
oldmodules = vld.m_loadedmodules;
|
|
vld.m_loadedmodules = newmodules;
|
|
LeaveCriticalSection(&vld.m_moduleslock);
|
|
|
|
// Free resources used by the old module list.
|
|
for (moduleit = oldmodules->begin(); moduleit != oldmodules->end(); ++moduleit) {
|
|
delete (*moduleit).name;
|
|
delete (*moduleit).path;
|
|
}
|
|
delete oldmodules;
|
|
}
|
|
|
|
LeaveCriticalSection(&vld.m_loaderlock);
|
|
|
|
return status;
|
|
}
|
|
|
|
// _RtlAllocateHeap - Calls to RtlAllocateHeap are patched through to this
|
|
// function. This function invokes the real RtlAllocateHeap and then calls
|
|
// VLD's allocation tracking function. Pretty much all memory allocations
|
|
// will eventually result in a call to RtlAllocateHeap, so this is where we
|
|
// finally map the allocated block.
|
|
//
|
|
// - heap (IN): Handle to the heap from which to allocate memory.
|
|
//
|
|
// - flags (IN): Heap allocation control flags.
|
|
//
|
|
// - size (IN): Size, in bytes, of the block to allocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the return value from RtlAllocateHeap.
|
|
//
|
|
LPVOID VisualLeakDetector::_RtlAllocateHeap (HANDLE heap, DWORD flags, SIZE_T size)
|
|
{
|
|
BOOL crtalloc;
|
|
BOOL excluded = FALSE;
|
|
SIZE_T fp;
|
|
LPVOID block;
|
|
moduleinfo_t moduleinfo;
|
|
ModuleSet::Iterator moduleit;
|
|
SIZE_T returnaddress;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// Allocate the block.
|
|
block = RtlAllocateHeap(heap, flags, size);
|
|
if ((block != NULL) && vld.enabled()) {
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
FRAMEPOINTER(fp);
|
|
}
|
|
else {
|
|
fp = tls->addrfp;
|
|
}
|
|
crtalloc = (tls->flags & VLD_TLS_CRTALLOC) ? TRUE : FALSE;
|
|
|
|
// Reset thread local flags and variables, in case any libraries called
|
|
// into while mapping the block allocate some memory.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &=~VLD_TLS_CRTALLOC;
|
|
|
|
// Find the information for the module that initiated this allocation.
|
|
returnaddress = *((SIZE_T*)fp + 1);
|
|
moduleinfo.addrhigh = returnaddress;
|
|
moduleinfo.addrlow = returnaddress;
|
|
EnterCriticalSection(&vld.m_moduleslock);
|
|
moduleit = vld.m_loadedmodules->find(moduleinfo);
|
|
if (moduleit != vld.m_loadedmodules->end()) {
|
|
excluded = (*moduleit).flags & VLD_MODULE_EXCLUDED ? TRUE : FALSE;
|
|
}
|
|
LeaveCriticalSection(&vld.m_moduleslock);
|
|
if (!excluded) {
|
|
// The module that initiated this allocation is included in leak
|
|
// detection. Map this block to the specified heap.
|
|
vld.mapblock(heap, block, size, fp, crtalloc);
|
|
}
|
|
}
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// _RtlFreeHeap - Calls to RtlFreeHeap are patched through to this function.
|
|
// This function calls VLD's free tracking function and then invokes the real
|
|
// RtlFreeHeap. Pretty much all memory frees will eventually result in a call
|
|
// to RtlFreeHeap, so this is where we finally unmap the freed block.
|
|
//
|
|
// - heap (IN): Handle to the heap to which the block being freed belongs.
|
|
//
|
|
// - flags (IN): Heap control flags.
|
|
//
|
|
// - mem (IN): Pointer to the memory block being freed.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by RtlFreeHeap.
|
|
//
|
|
BOOL VisualLeakDetector::_RtlFreeHeap (HANDLE heap, DWORD flags, LPVOID mem)
|
|
{
|
|
BOOL status;
|
|
|
|
// Unmap the block from the specified heap.
|
|
vld.unmapblock(heap, mem);
|
|
|
|
status = RtlFreeHeap(heap, flags, mem);
|
|
|
|
return status;
|
|
}
|
|
|
|
// _RtlReAllocateHeap - Calls to RtlReAllocateHeap are patched through to this
|
|
// function. This function invokes the real RtlReAllocateHeap and then calls
|
|
// VLD's reallocation tracking function. All arguments passed to this function
|
|
// are passed on to the real RtlReAllocateHeap without modification. Pretty
|
|
// much all memory re-allocations will eventually result in a call to
|
|
// RtlReAllocateHeap, so this is where we finally remap the reallocated block.
|
|
//
|
|
// - heap (IN): Handle to the heap to reallocate memory from.
|
|
//
|
|
// - flags (IN): Heap control flags.
|
|
//
|
|
// - mem (IN): Pointer to the currently allocated block which is to be
|
|
// reallocated.
|
|
//
|
|
// - size (IN): Size, in bytes, of the block to reallocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by RtlReAllocateHeap.
|
|
//
|
|
LPVOID VisualLeakDetector::_RtlReAllocateHeap (HANDLE heap, DWORD flags, LPVOID mem, SIZE_T size)
|
|
{
|
|
BOOL crtalloc;
|
|
BOOL excluded = FALSE;
|
|
SIZE_T fp;
|
|
moduleinfo_t moduleinfo;
|
|
ModuleSet::Iterator moduleit;
|
|
LPVOID newmem;
|
|
SIZE_T returnaddress;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
// Reallocate the block.
|
|
newmem = RtlReAllocateHeap(heap, flags, mem, size);
|
|
|
|
if (newmem != NULL) {
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
FRAMEPOINTER(fp);
|
|
}
|
|
else {
|
|
fp = tls->addrfp;
|
|
}
|
|
crtalloc = (tls->flags & VLD_TLS_CRTALLOC) ? TRUE : FALSE;
|
|
|
|
// Reset thread local flags and variables, in case any libraries called
|
|
// into while remapping the block allocate some memory.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
// Find the information for the module that initiated this reallocation.
|
|
returnaddress = *((SIZE_T*)fp + 1);
|
|
moduleinfo.addrhigh = returnaddress;
|
|
moduleinfo.addrlow = returnaddress;
|
|
EnterCriticalSection(&vld.m_moduleslock);
|
|
moduleit = vld.m_loadedmodules->find(moduleinfo);
|
|
if (moduleit != vld.m_loadedmodules->end()) {
|
|
excluded = (*moduleit).flags & VLD_MODULE_EXCLUDED ? TRUE : FALSE;
|
|
}
|
|
LeaveCriticalSection(&vld.m_moduleslock);
|
|
if (!excluded) {
|
|
// The module that initiated this allocation is included in leak
|
|
// detection. Remap the block.
|
|
vld.remapblock(heap, mem, newmem, size, fp, crtalloc);
|
|
}
|
|
}
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return newmem;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// COM IAT Replacement Functions
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// _CoGetMalloc - Calls to CoGetMalloc are patched through to this function.
|
|
// This function returns a pointer to Visual Leak Detector's implementation
|
|
// of the IMalloc interface, instead of returning a pointer to the system
|
|
// implementation. This allows VLD's implementation of the IMalloc interface
|
|
// (which is basically a thin wrapper around the system implementation) to be
|
|
// invoked in place of the system implementation.
|
|
//
|
|
// - context (IN): Reserved; value must be 1.
|
|
//
|
|
// - imalloc (IN): Address of a pointer to receive the address of VLD's
|
|
// implementation of the IMalloc interface.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Always returns S_OK.
|
|
//
|
|
HRESULT VisualLeakDetector::_CoGetMalloc (DWORD context, LPMALLOC *imalloc)
|
|
{
|
|
static CoGetMalloc_t pCoGetMalloc = NULL;
|
|
|
|
HMODULE ole32;
|
|
|
|
*imalloc = (LPMALLOC)&vld;
|
|
|
|
if (pCoGetMalloc == NULL) {
|
|
// This is the first call to this function. Link to the real
|
|
// CoGetMalloc and get a pointer to the system implementation of the
|
|
// IMalloc interface.
|
|
ole32 = GetModuleHandle(L"ole32.dll");
|
|
pCoGetMalloc = (CoGetMalloc_t)GetProcAddress(ole32, "CoGetMalloc");
|
|
pCoGetMalloc(context, &vld.m_imalloc);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// _CoTaskMemAlloc - Calls to CoTaskMemAlloc are patched through to this
|
|
// function. This function is just a wrapper around the real CoTaskMemAlloc
|
|
// that sets appropriate flags to be consulted when the memory is actually
|
|
// allocated by RtlAllocateHeap.
|
|
//
|
|
// - size (IN): Size of the memory block to allocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned from CoTaskMemAlloc.
|
|
//
|
|
LPVOID VisualLeakDetector::_CoTaskMemAlloc (ULONG size)
|
|
{
|
|
static CoTaskMemAlloc_t pCoTaskMemAlloc = NULL;
|
|
|
|
LPVOID block;
|
|
SIZE_T fp;
|
|
HMODULE ole32;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
FRAMEPOINTER(fp);
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
if (pCoTaskMemAlloc == NULL) {
|
|
// This is the first call to this function. Link to the real
|
|
// CoTaskMemAlloc.
|
|
ole32 = GetModuleHandle(L"ole32.dll");
|
|
pCoTaskMemAlloc = (CoTaskMemAlloc_t)GetProcAddress(ole32, "CoTaskMemAlloc");
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
block = pCoTaskMemAlloc(size);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// _CoTaskMemRealloc - Calls to CoTaskMemRealloc are patched through to this
|
|
// function. This function is just a wrapper around the real CoTaskMemRealloc
|
|
// that sets appropriate flags to be consulted when the memory is actually
|
|
// allocated by RtlAllocateHeap.
|
|
//
|
|
// - mem (IN): Pointer to the memory block to reallocate.
|
|
//
|
|
// - size (IN): Size, in bytes, of the block to reallocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned from CoTaskMemRealloc.
|
|
//
|
|
LPVOID VisualLeakDetector::_CoTaskMemRealloc (LPVOID mem, ULONG size)
|
|
{
|
|
static CoTaskMemRealloc_t pCoTaskMemRealloc = NULL;
|
|
|
|
LPVOID block;
|
|
SIZE_T fp;
|
|
HMODULE ole32;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
FRAMEPOINTER(fp);
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
if (pCoTaskMemRealloc == NULL) {
|
|
// This is the first call to this function. Link to the real
|
|
// CoTaskMemRealloc.
|
|
ole32 = GetModuleHandle(L"ole32.dll");
|
|
pCoTaskMemRealloc = (CoTaskMemRealloc_t)GetProcAddress(ole32, "CoTaskMemRealloc");
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlReAllocateHeap.
|
|
block = pCoTaskMemRealloc(mem, size);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Public COM IMalloc Implementation Functions
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// AddRef - Calls to IMalloc::AddRef end up here. This function is just a
|
|
// wrapper around the real IMalloc::AddRef implementation.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the system implementation of
|
|
// IMalloc::AddRef.
|
|
//
|
|
ULONG VisualLeakDetector::AddRef ()
|
|
{
|
|
assert(m_imalloc != NULL);
|
|
return m_imalloc->AddRef();
|
|
}
|
|
|
|
// Alloc - Calls to IMalloc::Alloc end up here. This function is just a wrapper
|
|
// around the real IMalloc::Alloc implementation that sets appropriate flags
|
|
// to be consulted when the memory is actually allocated by RtlAllocateHeap.
|
|
//
|
|
// - size (IN): The size of the memory block to allocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the system's IMalloc::Alloc implementation.
|
|
//
|
|
LPVOID VisualLeakDetector::Alloc (ULONG size)
|
|
{
|
|
LPVOID block;
|
|
SIZE_T fp;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
FRAMEPOINTER(fp);
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
|
|
assert(m_imalloc != NULL);
|
|
block = m_imalloc->Alloc(size);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// DidAlloc - Calls to IMalloc::DidAlloc will end up here. This function is just
|
|
// a wrapper around the system implementation of IMalloc::DidAlloc.
|
|
//
|
|
// - mem (IN): Pointer to a memory block to inquire about.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the system implementation of
|
|
// IMalloc::DidAlloc.
|
|
//
|
|
INT VisualLeakDetector::DidAlloc (LPVOID mem)
|
|
{
|
|
assert(m_imalloc != NULL);
|
|
return m_imalloc->DidAlloc(mem);
|
|
}
|
|
|
|
// Free - Calls to IMalloc::Free will end up here. This function is just a
|
|
// wrapper around the real IMalloc::Free implementation.
|
|
//
|
|
// - mem (IN): Pointer to the memory block to be freed.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::Free (LPVOID mem)
|
|
{
|
|
assert(m_imalloc != NULL);
|
|
m_imalloc->Free(mem);
|
|
}
|
|
|
|
// GetSize - Calls to IMalloc::GetSize will end up here. This function is just a
|
|
// wrapper around the real IMalloc::GetSize implementation.
|
|
//
|
|
// - mem (IN): Pointer to the memory block to inquire about.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the system implementation of
|
|
// IMalloc::GetSize.
|
|
//
|
|
ULONG VisualLeakDetector::GetSize (LPVOID mem)
|
|
{
|
|
assert(m_imalloc != NULL);
|
|
return m_imalloc->GetSize(mem);
|
|
}
|
|
|
|
// HeapMinimize - Calls to IMalloc::HeapMinimize will end up here. This function
|
|
// is just a wrapper around the real IMalloc::HeapMinimize implementation.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// None.
|
|
//
|
|
VOID VisualLeakDetector::HeapMinimize ()
|
|
{
|
|
assert(m_imalloc != NULL);
|
|
m_imalloc->HeapMinimize();
|
|
}
|
|
|
|
// QueryInterface - Calls to IMalloc::QueryInterface will end up here. This
|
|
// function is just a wrapper around the real IMalloc::QueryInterface
|
|
// implementation.
|
|
//
|
|
// - iid (IN): COM interface ID to query about.
|
|
//
|
|
// - object (IN): Address of a pointer to receive the requested interface
|
|
// pointer.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the system implementation of
|
|
// IMalloc::QueryInterface.
|
|
//
|
|
HRESULT VisualLeakDetector::QueryInterface (REFIID iid, LPVOID *object)
|
|
{
|
|
assert(m_imalloc != NULL);
|
|
return m_imalloc->QueryInterface(iid, object);
|
|
}
|
|
|
|
// Realloc - Calls to IMalloc::Realloc will end up here. This function is just a
|
|
// wrapper around the real IMalloc::Realloc implementation that sets
|
|
// appropriate flags to be consulted when the memory is actually allocated by
|
|
// RtlAllocateHeap.
|
|
//
|
|
// - mem (IN): Pointer to the memory block to reallocate.
|
|
//
|
|
// - size (IN): Size, in bytes, of the memory block to reallocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the system implementation of
|
|
// IMalloc::Realloc.
|
|
//
|
|
LPVOID VisualLeakDetector::Realloc (LPVOID mem, ULONG size)
|
|
{
|
|
LPVOID block;
|
|
SIZE_T fp;
|
|
tls_t *tls = vld.gettls();
|
|
|
|
if (tls->addrfp == 0x0) {
|
|
// This is the first call to enter VLD for the current allocation.
|
|
// Record the current frame pointer.
|
|
FRAMEPOINTER(fp);
|
|
tls->addrfp = fp;
|
|
}
|
|
|
|
// Do the allocation. The block will be mapped by _RtlReAllocateHeap.
|
|
assert(m_imalloc != NULL);
|
|
block = m_imalloc->Realloc(mem, size);
|
|
|
|
// Reset thread local flags and variables for the next allocation.
|
|
tls->addrfp = 0x0;
|
|
tls->flags &= ~VLD_TLS_CRTALLOC;
|
|
|
|
return block;
|
|
}
|
|
|
|
// Release - Calls to IMalloc::Release will end up here. This function is just
|
|
// a wrapper around the real IMalloc::Release implementation.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Returns the value returned by the system implementation of
|
|
// IMalloc::Release.
|
|
//
|
|
ULONG VisualLeakDetector::Release ()
|
|
{
|
|
assert(m_imalloc != NULL);
|
|
return m_imalloc->Release();
|
|
}
|