////////////////////////////////////////////////////////////////////////////////
-// $Id: vld.cpp,v 1.69 2006/11/18 05:07:04 dmouldin Exp $
//
// Visual Leak Detector - VisualLeakDetector Class Implementation
-// Copyright (c) 2005-2006 Dan Moulding
+// 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
#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 "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.
+#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.
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 symbold handling APIs.
-
-// Function pointer types for explicit dynamic linking with functions listed in
-// the import patch table.
-typedef void* (__cdecl *_calloc_dbg_t) (size_t, size_t, int, const char*, int);
-typedef void* (__cdecl *_malloc_dbg_t) (size_t, int, const char *, int);
-typedef void* (__cdecl *_realloc_dbg_t) (void *, size_t, int, const char *, int);
-typedef void* (__cdecl *calloc_t) (size_t, size_t);
-typedef HRESULT (__stdcall *CoGetMalloc_t) (DWORD, LPMALLOC *);
-typedef LPVOID (__stdcall *CoTaskMemAlloc_t) (ULONG);
-typedef LPVOID (__stdcall *CoTaskMemRealloc_t) (LPVOID, ULONG);
-typedef void* (__cdecl *crt_new_dbg_t) (unsigned int, int, const char *, int);
-typedef void* (__cdecl *malloc_t) (size_t);
-typedef void* (__cdecl *mfc_new_dbg_t) (unsigned int, const char *, int);
-typedef void* (__cdecl *new_t) (unsigned int);
-typedef void* (__cdecl *realloc_t) (void *, size_t);
+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.
-static CoGetMalloc_t pCoGetMalloc = NULL;
-static CoTaskMemAlloc_t pCoTaskMemAlloc = NULL;
-static CoTaskMemRealloc_t pCoTaskMemRealloc = NULL;
-static _calloc_dbg_t pcrt80d__calloc_dbg = NULL;
-static _malloc_dbg_t pcrt80d__malloc_dbg = NULL;
-static _realloc_dbg_t pcrt80d__realloc_dbg = NULL;
-static crt_new_dbg_t pcrt80d__scalar_new_dbg = NULL;
-static crt_new_dbg_t pcrt80d__vector_new_dbg = NULL;
-static calloc_t pcrt80d_calloc = NULL;
-static malloc_t pcrt80d_malloc = NULL;
-static realloc_t pcrt80d_realloc = NULL;
-static new_t pcrt80d_scalar_new = NULL;
-static new_t pcrt80d_vector_new = NULL;
-static _calloc_dbg_t pcrtd__calloc_dbg = NULL;
-static _malloc_dbg_t pcrtd__malloc_dbg = NULL;
-static _realloc_dbg_t pcrtd__realloc_dbg = NULL;
-static crt_new_dbg_t pcrtd__scalar_new_dbg = NULL;
-static calloc_t pcrtd_calloc = NULL;
-static malloc_t pcrtd_malloc = NULL;
-static realloc_t pcrtd_realloc = NULL;
-static new_t pcrtd_scalar_new = NULL;
-static mfc_new_dbg_t pmfc42d__scalar_new_dbg = NULL;
-static new_t pmfc42d_scalar_new = NULL;
-static mfc_new_dbg_t pmfc80d__scalar_new_dbg = NULL;
-static mfc_new_dbg_t pmfc80d__vector_new_dbg = NULL;
-static new_t pmfc80d_scalar_new = NULL;
-static new_t pmfc80d_vector_new = NULL;
-
-// The one and only VisualLeakDetector object instance.
-__declspec(dllexport) VisualLeakDetector vld;
// The import patch table: lists the heap-related API imports that VLD patches
// through to replacement functions provided by VLD. Having this table simply
"kernel32.dll", "HeapReAlloc", 0x0, _RtlReAllocateHeap,
// MFC new operators (exported by ordinal).
- "mfc42d.dll", (LPCSTR)714, 0x0, _mfc42d__scalar_new_dbg,
- "mfc42d.dll", (LPCSTR)711, 0x0, _mfc42d_scalar_new,
- // XXX MFC 7.x DLL new operators still need to be added to this
- // table, but I don't know their ordinals.
- "mfc80d.dll", (LPCSTR)895, 0x0, _mfc80d__scalar_new_dbg,
- "mfc80d.dll", (LPCSTR)269, 0x0, _mfc80d__vector_new_dbg,
- "mfc80d.dll", (LPCSTR)893, 0x0, _mfc80d_scalar_new,
- "mfc80d.dll", (LPCSTR)267, 0x0, _mfc80d_vector_new,
+ // 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.
- "msvcr80d.dll", "_calloc_dbg", 0x0, _crt80d__calloc_dbg,
- "msvcr80d.dll", "_malloc_dbg", 0x0, _crt80d__malloc_dbg,
- "msvcr80d.dll", "_realloc_dbg", 0x0, _crt80d__realloc_dbg,
- "msvcr80d.dll", "??2@YAPAXIHPBDH@Z", 0x0, _crt80d__scalar_new_dbg,
- "msvcr80d.dll", "??_U@YAPAXIHPBDH@Z", 0x0, _crt80d__vector_new_dbg,
- "msvcr80d.dll", "calloc", 0x0, _crt80d_calloc,
- "msvcr80d.dll", "malloc", 0x0, _crt80d_malloc,
- "msvcr80d.dll", "realloc", 0x0, _crt80d_realloc,
- "msvcr80d.dll", "??2@YAPAXI@Z", 0x0, _crt80d_scalar_new,
- "msvcr80d.dll", "??_U@YAPAXI@Z", 0x0, _crt80d_vector_new,
- "msvcrtd.dll", "_calloc_dbg", 0x0, _crtd__calloc_dbg,
- "msvcrtd.dll", "_malloc_dbg", 0x0, _crtd__malloc_dbg,
- "msvcrtd.dll", "??2@YAPAXIHPBDH@Z", 0x0, _crtd__scalar_new_dbg,
- "msvcrtd.dll", "_realloc_dbg", 0x0, _crtd__realloc_dbg,
- "msvcrtd.dll", "calloc", 0x0, _crtd_calloc,
- "msvcrtd.dll", "malloc", 0x0, _crtd_malloc,
- "msvcrtd.dll", "realloc", 0x0, _crtd_realloc,
- "msvcrtd.dll", "??2@YAPAXI@Z", 0x0, _crtd_scalar_new,
+ "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,
BlockMap::Iterator blockit;
BlockMap *blockmap;
size_t count;
- DWORD exitcode;
vldblockheader_t *header;
HANDLE heap;
HeapMap::Iterator heapit;
WCHAR leakfilew [MAX_PATH];
int leakline = 0;
ModuleSet::Iterator moduleit;
- SIZE_T sleepcount;
HANDLE thread;
BOOL threadsactive= FALSE;
TlsSet::Iterator tlsit;
+ DWORD dwCurProcessID;
if (m_options & VLD_OPT_VLDOFF) {
// VLD has been turned off.
// 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) {
continue;
}
- sleepcount = 0;
- thread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, (*tlsit)->threadid);
+ 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()?
}
- while (GetExitCodeThread(thread, &exitcode) == TRUE) {
- if (exitcode != STILL_ACTIVE) {
- // This thread exited.
- break;
- }
- else {
- // 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.
- threadsactive = TRUE;
- Sleep(100);
- sleepcount++;
- if ((sleepcount % 100) == 0) {
- // Just in case this takes a long time, let the human
- // know we are still here and alive.
- report(L"Visual Leak Detector: Waiting for threads to terminate...\n");
- }
- }
+ 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);
}
}
-// _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.
+// Private Leak Detection Functions
//
-// - imalloc (IN): Address of a pointer to receive the address of VLD's
-// implementation of the IMalloc interface.
+////////////////////////////////////////////////////////////////////////////////
+
+// 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:
//
-// Always returns S_OK.
+// None.
//
-HRESULT VisualLeakDetector::_CoGetMalloc (DWORD context, LPMALLOC *imalloc)
+VOID VisualLeakDetector::attachtoloadedmodules (ModuleSet *newmodules)
{
- HMODULE ole32;
+ 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;
- *imalloc = (LPMALLOC)&vld;
+ // 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;
- 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);
- }
+ 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);
+ }
- return S_OK;
-}
+ 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);
+ }
+ }
-// _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)
-{
- LPVOID block;
- SIZE_T fp;
- HMODULE ole32;
- tls_t *tls = vld.gettls();
+ // 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 (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 (_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;
+ }
- 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");
- }
+ 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);
+ }
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pCoTaskMemAlloc(size);
+ // Update the module's flags in the "new modules" set.
+ updateit = newit;
+ (*updateit).flags = moduleflags;
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
+ // Attach to the module.
+ patchmodule((HMODULE)modulebase, m_patchtable, tablesize);
+ }
}
-// _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.
+// 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 the value returned from CoTaskMemRealloc.
+// Returns a string containing the search path. The caller is responsible for
+// freeing the string.
//
-LPVOID VisualLeakDetector::_CoTaskMemRealloc (LPVOID mem, ULONG size)
+LPWSTR VisualLeakDetector::buildsymbolsearchpath ()
{
- LPVOID block;
- SIZE_T fp;
- HMODULE ole32;
- tls_t *tls = vld.gettls();
+ 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];
- 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;
- }
+ // 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);
- 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");
- }
+ // 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";.\\");
- // Do the allocation. The block will be mapped by _RtlReAllocateHeap.
- block = pCoTaskMemRealloc(mem, size);
+ // Append the Windows directory.
+ if (GetWindowsDirectory(windows, MAX_PATH) != 0) {
+ strapp(&path, L";");
+ strapp(&path, windows);
+ }
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
+ // Append the system directory.
+ if (GetSystemDirectory(system, MAX_PATH) != 0) {
+ strapp(&path, L";");
+ strapp(&path, system);
+ }
- return block;
+ // 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;
}
-// _crt80d__calloc_dbg - Calls to _calloc_dbg from msvcr80d.dll are patched
-// through to this function. 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.
-//
-// - 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.
+// configure - Configures VLD using values read from the vld.ini file.
//
// Return Value:
//
-// Returns the value returned by _calloc_dbg.
+// None.
//
-void* VisualLeakDetector::_crt80d__calloc_dbg (size_t num, size_t size, int type, const char *file, int line)
+VOID VisualLeakDetector::configure ()
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- tls_t *tls = vld.gettls();
-
- // _malloc_dbg is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+#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 (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 (_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);
}
-
- if (pcrt80d__calloc_dbg == NULL) {
- // This is the first call to this function. Link to the real
- // _malloc_dbg.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d__calloc_dbg = (_calloc_dbg_t)GetProcAddress(msvcr80d, "_calloc_dbg");
+ 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);
+ }
}
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrt80d__calloc_dbg(num, size, type, file, line);
+ // Read the boolean options.
+ GetPrivateProfileString(L"Options", L"VLD", L"on", buffer, BSIZE, inipath);
+ if (strtobool(buffer) == FALSE) {
+ m_options |= VLD_OPT_VLDOFF;
+ return;
+ }
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
+ GetPrivateProfileString(L"Options", L"AggregateDuplicates", L"", buffer, BSIZE, inipath);
+ if (strtobool(buffer) == TRUE) {
+ m_options |= VLD_OPT_AGGREGATE_DUPLICATES;
+ }
- return block;
-}
+ GetPrivateProfileString(L"Options", L"SelfTest", L"", buffer, BSIZE, inipath);
+ if (strtobool(buffer) == TRUE) {
+ m_options |= VLD_OPT_SELF_TEST;
+ }
-// _crt80d__malloc_dbg - Calls to _malloc_dbg from msvcr80d.dll are patched
-// through to this function. 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.
-//
-// - 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 _malloc_dbg.
-//
-void* VisualLeakDetector::_crt80d__malloc_dbg (size_t size, int type, const char *file, int line)
-{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- tls_t *tls = vld.gettls();
+ GetPrivateProfileString(L"Options", L"SlowDebuggerDump", L"", buffer, BSIZE, inipath);
+ if (strtobool(buffer) == TRUE) {
+ m_options |= VLD_OPT_SLOW_DEBUGGER_DUMP;
+ }
- // _malloc_dbg is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ GetPrivateProfileString(L"Options", L"StartDisabled", L"", buffer, BSIZE, inipath);
+ if (strtobool(buffer) == TRUE) {
+ m_options |= VLD_OPT_START_DISABLED;
+ }
- 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;
+ GetPrivateProfileString(L"Options", L"TraceInternalFrames", L"", buffer, BSIZE, inipath);
+ if (strtobool(buffer) == TRUE) {
+ m_options |= VLD_OPT_TRACE_INTERNAL_FRAMES;
}
- if (pcrt80d__malloc_dbg == NULL) {
- // This is the first call to this function. Link to the real
- // _malloc_dbg.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d__malloc_dbg = (_malloc_dbg_t)GetProcAddress(msvcr80d, "_malloc_dbg");
+ // 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;
}
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrt80d__malloc_dbg(size, type, file, line);
+ // Read the force-include module list.
+ GetPrivateProfileString(L"Options", L"ForceIncludeModules", L"", m_forcedmodulelist, MAXMODULELISTLENGTH, inipath);
+ _wcslwr_s(m_forcedmodulelist, MAXMODULELISTLENGTH);
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
+ // 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;
+ }
- return block;
+ // 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;
+ }
}
-// _crt80d__realloc_dbg - Calls to _realloc_dbg from msvcr80d.dll are patched
-// through to this function. 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.
-//
-// - 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 filel, at which this function is
-// being called.
+// enabled - Determines if memory leak detection is enabled for the current
+// thread.
//
// Return Value:
//
-// Returns the value returned by _realloc_dbg.
+// Returns true if Visual Leak Detector is enabled for the current thread.
+// Otherwise, returns false.
//
-void* VisualLeakDetector::_crt80d__realloc_dbg (void *mem, size_t size, int type, const char *file, int line)
+BOOL VisualLeakDetector::enabled ()
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- tls_t *tls = vld.gettls();
-
- // _realloc_dbg is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ 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 (!(m_status & VLD_STATUS_INSTALLED)) {
+ // Memory leak detection is not yet enabled because VLD is still
+ // initializing.
+ return FALSE;
}
- if (pcrt80d__realloc_dbg == NULL) {
- // This is the first call to this function. Link to the real
- // _realloc_dbg.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d__realloc_dbg = (_realloc_dbg_t)GetProcAddress(msvcr80d, "_realloc_dbg");
+ 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;
+ }
}
- // Do the allocation. The block will be mapped by _RtlReAllocateHeap.
- block = pcrt80d__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;
+ return ((tls->flags & VLD_TLS_ENABLED) != 0);
}
-// _crt80d__scalar_new_dbg - Calls to the CRT's debug scalar new operator from
-// msvcr80d.dll are patched through to this function. This function is just a
-// wrapper around the real CRT debug scalar new operator that sets appropriate
-// flags to be consulted when the memory is actually allocated by
-// RtlAllocateHeap.
+// eraseduplicates - Erases, from the block maps, blocks that appear to be
+// duplicate leaks of an already identified leak.
//
-// - 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.
+// - element (IN): BlockMap Iterator referencing the block of which to search
+// for duplicates.
//
// Return Value:
//
-// Returns the value returned by the CRT debug scalar new operator.
+// Returns the number of duplicate blocks erased from the block map.
//
-void* VisualLeakDetector::_crt80d__scalar_new_dbg (unsigned int size, int type, const char *file, int line)
+SIZE_T VisualLeakDetector::eraseduplicates (const BlockMap::Iterator &element)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- tls_t *tls = vld.gettls();
-
- // The debug new operator is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ BlockMap::Iterator blockit;
+ BlockMap *blockmap;
+ blockinfo_t *elementinfo;
+ SIZE_T erased = 0;
+ HeapMap::Iterator heapit;
+ blockinfo_t *info;
+ BlockMap::Iterator previt;
- 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;
- }
+ elementinfo = (*element).second;
- if (pcrt80d__scalar_new_dbg == NULL) {
- // This is the first call to this function. Link to the real CRT debug
- // new operator.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d__scalar_new_dbg = (crt_new_dbg_t)GetProcAddress(msvcr80d, "??2@YAPAXIHPBDH@Z");
+ // 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++;
+ }
+ }
}
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrt80d__scalar_new_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;
+ return erased;
}
-// _crt80d__vector_new_dbg - Calls to the CRT's debug vector new operator from
-// msvcr80d.dll are patched through to this function. This function is just a
-// wrapper around the real CRT debug vector new operator that sets appropriate
-// flags to be consulted when the memory is actually allocated by
-// RtlAllocateHeap.
-//
-// - 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.
+// gettls - Obtains the thread local storage structure for the calling thread.
//
// Return Value:
//
-// Returns the value returned by the CRT debug vector new operator.
+// Returns a pointer to the thread local storage structure. (This function
+// always succeeds).
//
-void* VisualLeakDetector::_crt80d__vector_new_dbg (unsigned int size, int type, const char *file, int line)
+tls_t* VisualLeakDetector::gettls ()
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- tls_t *tls = vld.gettls();
-
- // The debug new operator is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ 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->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 (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();
- if (pcrt80d__vector_new_dbg == NULL) {
- // This is the first call to this function. Link to the real CRT debug
- // new operator.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d__vector_new_dbg = (crt_new_dbg_t)GetProcAddress(msvcr80d, "??_U@YAPAXIHPBDH@Z");
+ // Add this thread's TLS to the TlsSet.
+ EnterCriticalSection(&m_tlslock);
+ m_tlsset->insert(tls);
+ LeaveCriticalSection(&m_tlslock);
}
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrt80d__vector_new_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;
+ return tls;
}
-// _crt80d_calloc - Calls to calloc from msvcr80d.dll are patched through to
-// this function. 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.
+// mapblock - Tracks memory allocations. Information about allocated blocks is
+// collected and then the block is mapped to this information.
//
-// - num (IN): The number of blocks, of size 'size', to be allocated.
+// - heap (IN): Handle to the heap from which the block has been allocated.
//
-// - size (IN): The size, in bytes, of the memory block to be 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:
//
-// Returns the valued returned from calloc.
+// None.
//
-void* VisualLeakDetector::_crt80d_calloc (size_t num, size_t size)
+VOID VisualLeakDetector::mapblock (HANDLE heap, LPCVOID mem, SIZE_T size, SIZE_T framepointer, BOOL crtalloc)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- tls_t *tls = vld.gettls();
-
- // malloc is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ blockinfo_t *blockinfo;
+ BlockMap::Iterator blockit;
+ BlockMap *blockmap;
+ HeapMap::Iterator heapit;
+ static SIZE_T serialnumber = 0;
- 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;
+ // Record the block's information.
+ blockinfo = new blockinfo_t;
+ if (m_options & VLD_OPT_SAFE_STACK_WALK) {
+ blockinfo->callstack = new SafeCallStack;
}
-
- if (pcrt80d_calloc == NULL) {
- // This is the first call to this function. Link to the real malloc.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d_calloc = (calloc_t)GetProcAddress(msvcr80d, "calloc");
+ 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;
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrt80d_calloc(num, size);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
+ // 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);
}
-// _crt80d_malloc - Calls to malloc from msvcr80d.dll are patched through to
-// this function. 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.
+// 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.
//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
+// - heap (IN): Handle to the newly created heap.
//
// Return Value:
//
-// Returns the valued returned from malloc.
+// None.
//
-void* VisualLeakDetector::_crt80d_malloc (size_t size)
+VOID VisualLeakDetector::mapheap (HANDLE heap)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- 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.
- FRAMEPOINTER(fp);
- tls->addrfp = fp;
- }
+ heapinfo_t *heapinfo;
+ HeapMap::Iterator heapit;
- if (pcrt80d_malloc == NULL) {
- // This is the first call to this function. Link to the real malloc.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d_malloc = (malloc_t)GetProcAddress(msvcr80d, "malloc");
+ // 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);
}
-
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrt80d_malloc(size);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
+ LeaveCriticalSection(&m_maplock);
}
-// _crt80d_realloc - Calls to realloc from msvcr80d.dll are patched through to
-// this function. 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.
+// remapblock - Tracks reallocations. Unmaps a block from its previously
+// collected information and remaps it to updated information.
//
-// - mem (IN): Pointer to the memory block to reallocate.
+// 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.
//
-// - size (IN): Size of the memory block to reallocate.
+// - heap (IN): Handle to the heap from which the memory is being reallocated.
//
-// Return Value:
+// - mem (IN): Pointer to the memory block being reallocated.
//
-// Returns the value returned from realloc.
+// - 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").
//
-void* VisualLeakDetector::_crt80d_realloc (void *mem, size_t size)
+// - 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)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- tls_t *tls = vld.gettls();
-
- // realloc is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ BlockMap::Iterator blockit;
+ BlockMap *blockmap;
+ HeapMap::Iterator heapit;
+ blockinfo_t *info;
- 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 (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;
}
- if (pcrt80d_realloc == NULL) {
- // This is the first call to this function. Link to the real realloc.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d_realloc = (realloc_t)GetProcAddress(msvcr80d, "realloc");
+ // 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;
}
- // Do the allocation. The block will be mapped by _RtlReAllocateHeap.
- block = pcrt80d_realloc(mem, size);
+ // 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;
+ }
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
+ // 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);
- return block;
+ // 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;
}
-// _crt80d_scalar_new - Calls to the CRT's scalar new operator from msvcr80d.dll
-// are patched through to this function. This function is just a wrapper
-// around the real CRT scalar new operator that sets appropriate flags to be
-// consulted when the memory is actually allocated by RtlAllocateHeap.
-//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
+// reportconfig - Generates a brief report summarizing Visual Leak Detector's
+// configuration, as loaded from the vld.ini file.
//
// Return Value:
//
-// Returns the value returned by the CRT scalar new operator.
+// None.
//
-void* VisualLeakDetector::_crt80d_scalar_new (unsigned int size)
+VOID VisualLeakDetector::reportconfig ()
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- 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.
- FRAMEPOINTER(fp);
- tls->addrfp = fp;
+ if (m_options & VLD_OPT_AGGREGATE_DUPLICATES) {
+ report(L" Aggregating duplicate leaks.\n");
}
-
- if (pcrt80d_scalar_new == NULL) {
- // This is the first call to this function. Link to the real CRT new
- // operator.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d_scalar_new = (new_t)GetProcAddress(msvcr80d, "??2@YAPAXI@Z");
+ 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");
}
-
- // Do tha allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrt80d_scalar_new(size);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
}
-// _crt80d_vector_new - Calls to the CRT's vector new operator from msvcr80d.dll
-// are patched through to this function. This function is just a wrapper
-// around the real CRT vector new operator that sets appropriate flags to be
-// consulted when the memory is actually allocated by RtlAllocateHeap.
+// reportleaks - Generates a memory leak report for the specified heap.
//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
+// - heap (IN): Handle to the heap for which to generate a memory leak
+// report.
//
// Return Value:
//
-// Returns the value returned by the CRT vector new operator.
+// None.
//
-void* VisualLeakDetector::_crt80d_vector_new (unsigned int size)
+VOID VisualLeakDetector::reportleaks (HANDLE heap)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcr80d;
- tls_t *tls = vld.gettls();
-
- // The new operator is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ 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;
- 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;
+ // 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;
}
- if (pcrt80d_vector_new == NULL) {
- // This is the first call to this function. Link to the real CRT new
- // operator.
- msvcr80d = GetModuleHandle(L"msvcr80d.dll");
- pcrt80d_vector_new = (new_t)GetProcAddress(msvcr80d, "??_U@YAPAXI@Z");
+ 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");
}
- // Do tha allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrt80d_vector_new(size);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
+ LeaveCriticalSection(&m_maplock);
}
-// _crtd__calloc_dbg - Calls to _calloc_dbg from msvcrtd.dll are patched through
-// to this function. 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.
+// unmapblock - Tracks memory blocks that are freed. Unmaps the specified block
+// from the block's information, relinquishing internally allocated resources.
//
-// - num (IN): The number of blocks, of size 'size', to be allocated.
+// - heap (IN): Handle to the heap to which this block is being freed.
//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
+// - mem (IN): Pointer to the memory block being freed.
//
-// - type (IN): The CRT "use type" of the block to be allocated.
+// Return Value:
//
-// - file (IN): The name of the file from which this function is being called.
+// None.
//
-// - line (IN): The line number, in the above file, at which this function is
-// being called.
+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:
//
-// Returns the value returned by _calloc_dbg.
+// None.
//
-void* VisualLeakDetector::_crtd__calloc_dbg (size_t num, size_t size, int type, const char *file, int line)
+VOID VisualLeakDetector::unmapheap (HANDLE heap)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcrtd;
- tls_t *tls = vld.gettls();
-
- // _malloc_dbg is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ BlockMap::Iterator blockit;
+ BlockMap *blockmap;
+ heapinfo_t *heapinfo;
+ HeapMap::Iterator heapit;
- 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;
+ // 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;
}
- if (pcrtd__calloc_dbg == NULL) {
- // This is the first call to this function. Link to the real
- // _malloc_dbg.
- msvcrtd = GetModuleHandle(L"msvcrtd.dll");
- pcrtd__calloc_dbg = (_calloc_dbg_t)GetProcAddress(msvcrtd, "_calloc_dbg");
+ // 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;
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrtd__calloc_dbg(num, size, type, file, line);
+ // Remove this heap's block map from the heap map.
+ m_heapmap->erase(heapit);
+ LeaveCriticalSection(&m_maplock);
+}
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
- return block;
-}
+////////////////////////////////////////////////////////////////////////////////
+//
+// Static Leak Detection Functions (Callbacks)
+//
+////////////////////////////////////////////////////////////////////////////////
-// _crtd__malloc_dbg - Calls to _malloc_dbg from msvcrtd.dll are patched through
-// to this function. 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.
+// 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).
//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
+// 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.
//
-// - type (IN): The CRT "use type" of the block to be allocated.
+// - modulepath (IN): The fully qualified path from where the module was
+// loaded.
//
-// - file (IN): The name of the file from which this function is being called.
+// - modulebase (IN): The base address at which the module has been loaded.
//
-// - line (IN): The line number, in the above file, at which this function is
-// being called.
+// - 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:
//
-// Returns the value returned by _malloc_dbg.
+// Always returns TRUE, which tells EnumerateLoadedModules64 to continue
+// enumerating.
//
-void* VisualLeakDetector::_crtd__malloc_dbg (size_t size, int type, const char *file, int line)
+BOOL VisualLeakDetector::addloadedmodule (PCWSTR modulepath, DWORD64 modulebase, ULONG modulesize, PVOID context)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcrtd;
- tls_t *tls = vld.gettls();
+ 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);
- // _malloc_dbg is a CRT function and allocates from the CRT heap.
- tls->flags |= VLD_TLS_CRTALLOC;
+ // Convert the module path to ASCII.
+ size = wcslen(modulepath) + 1;
+ modulepatha = new CHAR [size];
+ wcstombs_s(&count, modulepatha, size, modulepath, _TRUNCATE);
- 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;
- }
+ // 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 (pcrtd__malloc_dbg == NULL) {
- // This is the first call to this function. Link to the real
- // _malloc_dbg.
- msvcrtd = GetModuleHandle(L"msvcrtd.dll");
- pcrtd__malloc_dbg = (_malloc_dbg_t)GetProcAddress(msvcrtd, "_malloc_dbg");
+ 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;
+ }
+ }
}
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrtd__malloc_dbg(size, type, file, line);
+ // 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);
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
+ return TRUE;
+}
- return block;
+// 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;
}
-// _crtd__realloc_dbg - Calls to _realloc_dbg from msvcrtd.dll are patched
-// through to this function. 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.
+
+////////////////////////////////////////////////////////////////////////////////
//
-// - mem (IN): Pointer to the memory block to be reallocated.
+// Standard CRT and MFC IAT Replacement Functions
//
-// - size (IN): The size of the memory block to reallocate.
+// 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.
//
-// - type (IN): The CRT "use type" of the block to be reallocated.
+////////////////////////////////////////////////////////////////////////////////
+
+// _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.
//
-// - file (IN): The name of the file from which this function is being called.
+// - pcalloc (IN): Pointer to the particular calloc implementation to call.
//
-// - line (IN): The line number, in the above filel, at which this function is
-// being called.
+// - 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 by _realloc_dbg.
+// Returns the value returned from the specified calloc.
//
-void* VisualLeakDetector::_crtd__realloc_dbg (void *mem, size_t size, int type, const char *file, int line)
+void* VisualLeakDetector::_calloc (calloc_t pcalloc,
+ SIZE_T fp,
+ size_t num,
+ size_t size)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcrtd;
- tls_t *tls = vld.gettls();
+ void *block;
+ tls_t *tls = vld.gettls();
- // _realloc_dbg is a CRT function and allocates from the CRT heap.
+ // 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.
- FRAMEPOINTER(fp);
tls->addrfp = fp;
}
- if (pcrtd__realloc_dbg == NULL) {
- // This is the first call to this function. Link to the real
- // _realloc_dbg.
- msvcrtd = GetModuleHandle(L"msvcrtd.dll");
- pcrtd__realloc_dbg = (_realloc_dbg_t)GetProcAddress(msvcrtd, "_realloc_dbg");
- }
-
- // Do the allocation. The block will be mapped by _RtlReAllocateHeap.
- block = pcrtd__realloc_dbg(mem, size, type, file, line);
+ // 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;
return block;
}
-// _crtd__scalar_new_dbg - Calls to the CRT's debug scalar new operator from
-// msvcrtd.dll are patched through to this function. This function is just a
-// wrapper around the real CRT debug scalar new operator that sets appropriate
-// flags to be consulted when the memory is actually allocated by
+// _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.
//
-// - 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.
+// - pmalloc (IN): Pointer to the particular malloc implementation to call.
//
-// - file (IN): The name of the file from which this function is being called.
+// - fp (IN): Frame pointer from the call that initiated this allocation.
//
-// - line (IN): The line number, in the above file, at which this function is
-// being called.
+// - size (IN): The size, in bytes, of the memory block to be allocated.
//
// Return Value:
//
-// Returns the value returned by the CRT debug scalar new operator.
+// Returns the value returned from the specified malloc.
//
-void* VisualLeakDetector::_crtd__scalar_new_dbg (unsigned int size, int type, const char *file, int line)
+void *VisualLeakDetector::_malloc (malloc_t pmalloc, SIZE_T fp, size_t size)
{
void *block;
- SIZE_T fp;
- HMODULE msvcrtd;
tls_t *tls = vld.gettls();
- // The debug new operator is a CRT function and allocates from the CRT heap.
+ // 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.
- FRAMEPOINTER(fp);
tls->addrfp = fp;
}
- if (pcrtd__scalar_new_dbg == NULL) {
- // This is the first call to this function. Link to the real CRT debug
- // new operator.
- msvcrtd = GetModuleHandle(L"msvcrtd.dll");
- pcrtd__scalar_new_dbg = (crt_new_dbg_t)GetProcAddress(msvcrtd, "??2@YAPAXIHPBDH@Z");
- }
-
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrtd__scalar_new_dbg(size, type, file, line);
+ block = pmalloc(size);
// Reset thread local flags and variables for the next allocation.
tls->addrfp = 0x0;
return block;
}
-// _crtd_calloc - Calls to calloc from msvcrtd.dll are patched through to this
-// function. 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.
+// _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.
//
-// - num (IN): The number of blocks, of size 'size', to be allocated.
+// - 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 valued returned from calloc.
+// Returns the value returned by the specified CRT new operator.
//
-void* VisualLeakDetector::_crtd_calloc (size_t num, size_t size)
+void* VisualLeakDetector::_new (new_t pnew, SIZE_T fp, size_t size)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcrtd;
- tls_t *tls = vld.gettls();
+ void *block;
+ tls_t *tls = vld.gettls();
- // malloc is a CRT function and allocates from the CRT heap.
+ // 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.
- FRAMEPOINTER(fp);
tls->addrfp = fp;
}
- if (pcrtd_calloc == NULL) {
- // This is the first call to this function. Link to the real malloc.
- msvcrtd = GetModuleHandle(L"msvcrtd.dll");
- pcrtd_calloc = (calloc_t)GetProcAddress(msvcrtd, "calloc");
- }
-
// Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrtd_calloc(num, size);
+ block = pnew(size);
// Reset thread local flags and variables for the next allocation.
tls->addrfp = 0x0;
return block;
}
-// _crtd_malloc - Calls to malloc from msvcrtd.dll are patched through to this
-// function. This function is just a wrapper around the real malloc that sets
+// _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.
//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
+// - 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 valued returned from malloc.
+// Returns the value returned from the specified realloc.
//
-void* VisualLeakDetector::_crtd_malloc (size_t size)
+void* VisualLeakDetector::_realloc (realloc_t prealloc,
+ SIZE_T fp,
+ void *mem,
+ size_t size)
{
- void *block;
- SIZE_T fp;
- HMODULE msvcrtd;
- tls_t *tls = vld.gettls();
+ void *block;
+ tls_t *tls = vld.gettls();
- // malloc is a CRT function and allocates from the CRT heap.
+ // 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.
- FRAMEPOINTER(fp);
tls->addrfp = fp;
}
- if (pcrtd_malloc == NULL) {
- // This is the first call to this function. Link to the real malloc.
- msvcrtd = GetModuleHandle(L"msvcrtd.dll");
- pcrtd_malloc = (malloc_t)GetProcAddress(msvcrtd, "malloc");
+ // 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 = pcrtd_malloc(size);
+ block = p_calloc_dbg(num, size, type, file, line);
// Reset thread local flags and variables for the next allocation.
tls->addrfp = 0x0;
return block;
}
-// _crtd_realloc - Calls to realloc from msvcrtd.dll are patched through to this
-// function. 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.
+// __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.
//
-// - mem (IN): Pointer to the memory block to reallocate.
+// - p_malloc_dbg (IN): Pointer to the particular _malloc_dbg implementation to
+// call.
//
-// - size (IN): Size of the memory block to reallocate.
+// - 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 from realloc.
+// Returns the value returned by the specified _malloc_dbg.
//
-void* VisualLeakDetector::_crtd_realloc (void *mem, size_t size)
+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;
- SIZE_T fp;
- HMODULE msvcrtd;
- tls_t *tls = vld.gettls();
+ void *block;
+ tls_t *tls = vld.gettls();
- // realloc is a CRT function and allocates from the CRT heap.
+ // _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.
- FRAMEPOINTER(fp);
tls->addrfp = fp;
}
- if (pcrtd_realloc == NULL) {
- // This is the first call to this function. Link to the real realloc.
- msvcrtd = GetModuleHandle(L"msvcrtd.dll");
- pcrtd_realloc = (realloc_t)GetProcAddress(msvcrtd, "realloc");
+ // 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 _RtlReAllocateHeap.
- block = pcrtd_realloc(mem, size);
+ // 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;
return block;
}
-// _crtd_scalar_new - Calls to the CRT's scalar new operator from msvcrtd.dll
-// are patched through to this function. This function is just a wrapper
-// around the real CRT scalar new operator that sets appropriate flags to be
-// consulted when the memory is actually allocated by RtlAllocateHeap.
+// 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 CRT scalar new operator.
+// Returns the value returned by the specified CRT debug new operator.
//
-void* VisualLeakDetector::_crtd_scalar_new (unsigned int size)
+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;
- SIZE_T fp;
- HMODULE msvcrtd;
- tls_t *tls = vld.gettls();
+ 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_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.
- FRAMEPOINTER(fp);
tls->addrfp = fp;
}
- if (pcrtd_scalar_new == NULL) {
- // This is the first call to this function. Link to the real CRT new
- // operator.
- msvcrtd = GetModuleHandle(L"msvcrtd.dll");
- pcrtd_scalar_new = (new_t)GetProcAddress(msvcrtd, "??2@YAPAXI@Z");
+ // 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 tha allocation. The block will be mapped by _RtlAllocateHeap.
- block = pcrtd_scalar_new(size);
+ // 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;
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
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;
-
- // 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);
- EnterCriticalSection(&vld.m_loaderlock);
- EnumerateLoadedModulesW64(currentprocess, addloadedmodule, newmodules);
- LeaveCriticalSection(&vld.m_loaderlock);
-
- // 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;
- }
-
- return status;
-}
-
-// _mfc42d__scalar_new_dbg - Calls to the MFC debug scalar new operator from
-// mfc42d.dll are patched through to this function. This function is just a
-// wrapper around the real MFC debug scalar new operator that sets appropriate
-// flags to be consulted when the memory is actually allocated by
-// RtlAllocateHeap.
-//
-// - 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 MFC debug scalar new operator.
-//
-void* VisualLeakDetector::_mfc42d__scalar_new_dbg (unsigned int size, const char *file, int line)
-{
- void *block;
- SIZE_T fp;
- HMODULE mfc42d;
- tls_t *tls = vld.gettls();
-
- // The MFC new operators are CRT-based and allocate 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.
- FRAMEPOINTER(fp);
- tls->addrfp = fp;
- }
-
- if (pmfc42d__scalar_new_dbg == NULL) {
- // This is the first call to this function. Link to the real MFC debug
- // new operator.
- mfc42d = GetModuleHandle(L"mfc42d.dll");
- pmfc42d__scalar_new_dbg = (mfc_new_dbg_t)GetProcAddress(mfc42d, (LPCSTR)714);
- }
-
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pmfc42d__scalar_new_dbg(size, file, line);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
-}
-
-// _mfc42d_scalar_new - Calls to the MFC scalar new operator from mfc42d.dll are
-// patched through to this function. This function is just a wrapper around
-// the real MFC scalar new operator that sets appropriate flags to be
-// consulted when the memory is actually allocated by RtlAllocateHeap.
-//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
-//
-// Return Value:
-//
-// Returns the value returned by the MFC scalar new operator.
-//
-void* VisualLeakDetector::_mfc42d_scalar_new (unsigned int size)
-{
- void *block;
- SIZE_T fp;
- HMODULE mfc42d;
- tls_t *tls = vld.gettls();
-
- // The MFC new operators are CRT-based and allocate 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.
- FRAMEPOINTER(fp);
- tls->addrfp = fp;
- }
-
- if (pmfc42d_scalar_new == NULL) {
- // This is the first call to this function. Link to the real MFC new
- // operator.
- mfc42d = GetModuleHandle(L"mfc42d.dll");
- pmfc42d_scalar_new = (new_t)GetProcAddress(mfc42d, (LPCSTR)711);
- }
-
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pmfc42d_scalar_new(size);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
-}
-
-// _mfc80d__scalar_new_dbg - Calls to the MFC debug scalar new operator from
-// mfc80d.dll are patched through to this function. This function is just a
-// wrapper around the real MFC debug scalar new operator that sets appropriate
-// flags to be consulted when the memory is actually allocated by
-// RtlAllocateHeap.
-//
-// - 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 MFC debug scalar new operator.
-//
-void* VisualLeakDetector::_mfc80d__scalar_new_dbg (unsigned int size, const char *file, int line)
-{
- void *block;
- SIZE_T fp;
- HMODULE mfc80d;
- tls_t *tls = vld.gettls();
-
- // The MFC new operators are CRT-based and allocate 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.
- FRAMEPOINTER(fp);
- tls->addrfp = fp;
- }
-
- if (pmfc80d__scalar_new_dbg == NULL) {
- // This is the first call to this function. Link to the real MFC debug
- // new operator.
- mfc80d = GetModuleHandle(L"mfc80d.dll");
- pmfc80d__scalar_new_dbg = (mfc_new_dbg_t)GetProcAddress(mfc80d, (LPCSTR)895);
- }
-
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pmfc80d__scalar_new_dbg(size, file, line);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
-}
-
-// _mfc80d__vector_new_dbg - Calls to the MFC debug vector new operator from
-// mfc80d.dll are patched through to this function. This function is just a
-// wrapper around the real MFC debug vector new operator that sets appropriate
-// flags to be consulted when the memory is actually allocated by
-// RtlAllocateHeap.
-//
-// - 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 MFC debug vector new operator.
-//
-void* VisualLeakDetector::_mfc80d__vector_new_dbg (unsigned int size, const char *file, int line)
-{
- void *block;
- SIZE_T fp;
- HMODULE mfc80d;
- tls_t *tls = vld.gettls();
-
- // The MFC new operators are CRT-based and allocate 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.
- FRAMEPOINTER(fp);
- tls->addrfp = fp;
- }
-
- if (pmfc80d__vector_new_dbg == NULL) {
- // This is the first call to this function. Link to the real MFC debug
- // new operator.
- mfc80d = GetModuleHandle(L"mfc80d.dll");
- pmfc80d__vector_new_dbg = (mfc_new_dbg_t)GetProcAddress(mfc80d, (LPCSTR)269);
- }
-
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pmfc80d__vector_new_dbg(size, file, line);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
-}
-
-// _mfc80d_scalar_new - Calls to the MFC scalar new operator from mfc80d.dll are
-// patched through to this function. This function is just a wrapper around
-// the real MFC scalar new operator that sets appropriate flags to be
-// consulted when the memory is actually allocated by RtlAllocateHeap.
-//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
-//
-// Return Value:
-//
-// Returns the value returned by the MFC scalar new operator.
-//
-void* VisualLeakDetector::_mfc80d_scalar_new (unsigned int size)
-{
- void *block;
- SIZE_T fp;
- HMODULE mfc80d;
- tls_t *tls = vld.gettls();
-
- // The MFC new operators are CRT-based and allocate 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.
- FRAMEPOINTER(fp);
- tls->addrfp = fp;
- }
-
- if (pmfc80d_scalar_new == NULL) {
- // This is the first call to this function. Link to the real MFC 8.0 new
- // operator.
- mfc80d = GetModuleHandle(L"mfc80d.dll");
- pmfc80d_scalar_new = (new_t)GetProcAddress(mfc80d, (LPCSTR)893);
- }
-
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pmfc80d_scalar_new(size);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
-}
-
-// _mfc80d_vector_new - Calls to the MFC vector new operator from mfc80d.dll are
-// patched through to this function. This function is just a wrapper around
-// the real MFC vector new operator that sets appropriate flags to be
-// consulted when the memory is actually allocated by RtlAllocateHeap.
-//
-// - size (IN): The size, in bytes, of the memory block to be allocated.
-//
-// Return Value:
-//
-// Returns the value returned by the MFC vector new operator.
-//
-void* VisualLeakDetector::_mfc80d_vector_new (unsigned int size)
-{
- void *block;
- SIZE_T fp;
- HMODULE mfc80d;
- tls_t *tls = vld.gettls();
-
- // The MFC new operators are CRT-based and allocate 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.
- FRAMEPOINTER(fp);
- tls->addrfp = fp;
- }
-
- if (pmfc80d_vector_new == NULL) {
- // This is the first call to this function. Link to the real MFC 8.0 new
- // operator.
- mfc80d = GetModuleHandle(L"mfc80d.dll");
- pmfc80d_vector_new = (new_t)GetProcAddress(mfc80d, (LPCSTR)267);
- }
-
- // Do the allocation. The block will be mapped by _RtlAllocateHeap.
- block = pmfc80d_vector_new(size);
-
- // Reset thread local flags and variables for the next allocation.
- tls->addrfp = 0x0;
- tls->flags &= ~VLD_TLS_CRTALLOC;
-
- return block;
-}
-
-// _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;
-}
-
-// 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;
-}
-
-// 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;
-}
-
-// 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;
+ else {
+ if (strcmp(entry->importname, procname) == 0) {
+ return (FARPROC)entry->replacement;
+ }
+ }
}
- GetPrivateProfileString(L"Options", L"StartDisabled", L"", buffer, BSIZE, inipath);
- if (strtobool(buffer) == TRUE) {
- m_options |= VLD_OPT_START_DISABLED;
- }
+ // The requested function is not a patched function. Just return the real
+ // address of the requested function.
+ return GetProcAddress(module, procname);
+}
- GetPrivateProfileString(L"Options", L"TraceInternalFrames", L"", buffer, BSIZE, inipath);
- if (strtobool(buffer) == TRUE) {
- m_options |= VLD_OPT_TRACE_INTERNAL_FRAMES;
- }
+// _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;
- // 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;
- }
+ // Get the return address within the calling function.
+ FRAMEPOINTER(fp);
+ ra = *((SIZE_T*)fp + 1);
- // Read the force-include module list.
- GetPrivateProfileString(L"Options", L"ForceIncludeModules", L"", m_forcedmodulelist, MAXMODULELISTLENGTH, inipath);
- _wcslwr_s(m_forcedmodulelist, MAXMODULELISTLENGTH);
+ // Create the heap.
+ heap = HeapCreate(options, initsize, maxsize);
- // 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;
- }
+ // Map the created heap handle to a new block map.
+ vld.mapheap(heap);
- // 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;
+ // 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);
+ }
}
- // 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;
- }
+ return heap;
}
-// 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 inlcude a path, of
-// the module to detach from (ignored).
-//
-// - modulebase (IN): Base address of the module.
-//
-// - modulesize (IN): Total size of the module (ignored).
+// _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.
//
-// - context (IN): User-supplied context (ignored).
+// - heap (IN): Handle to the heap to be destroyed.
//
// Return Value:
//
-// Always returns TRUE.
+// Returns the valued returned by HeapDestroy.
//
-BOOL VisualLeakDetector::detachfrommodule (PCWSTR /*modulepath*/, DWORD64 modulebase, ULONG /*modulesize*/,
- PVOID /*context*/)
+BOOL VisualLeakDetector::_HeapDestroy (HANDLE heap)
{
- UINT tablesize = sizeof(m_patchtable) / sizeof(patchentry_t);
+ // 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);
- restoremodule((HMODULE)modulebase, m_patchtable, tablesize);
+ vld.unmapheap(heap);
- return TRUE;
+ return HeapDestroy(heap);
}
-// DidAlloc - Calls to IMalloc::DidAlloc will end up here. This function is just
-// a wrapper around the system implementation of IMalloc::DidAlloc.
+// _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.
//
-// - mem (IN): Pointer to a memory block to inquire about.
+// - 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 the system implementation of
-// IMalloc::DidAlloc.
+// Returns the value returned by LdrLoadDll.
//
-INT VisualLeakDetector::DidAlloc (LPVOID mem)
+NTSTATUS VisualLeakDetector::_LdrLoadDll (LPWSTR searchpath, PDWORD flags, unicodestring_t *modulename,
+ PHANDLE modulehandle)
{
- assert(m_imalloc != NULL);
- return m_imalloc->DidAlloc(mem);
+ 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;
}
-// enabled - Determines if memory leak detection is enabled for the current
-// thread.
+// _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 true if Visual Leak Detector is enabled for the current thread.
-// Otherwise, returns false.
+// Returns the return value from RtlAllocateHeap.
//
-BOOL VisualLeakDetector::enabled ()
+LPVOID VisualLeakDetector::_RtlAllocateHeap (HANDLE heap, DWORD flags, SIZE_T size)
{
- tls_t *tls = vld.gettls();
+ BOOL crtalloc;
+ BOOL excluded = FALSE;
+ SIZE_T fp;
+ LPVOID block;
+ moduleinfo_t moduleinfo;
+ ModuleSet::Iterator moduleit;
+ SIZE_T returnaddress;
+ 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;
- }
+ // 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;
- 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;
+ // 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;
}
- else {
- tls->flags |= VLD_TLS_ENABLED;
+ 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);
}
}
- return ((tls->flags & VLD_TLS_ENABLED) != 0);
+ // Reset thread local flags and variables for the next allocation.
+ tls->addrfp = 0x0;
+ tls->flags &= ~VLD_TLS_CRTALLOC;
+
+ return block;
}
-// eraseduplicates - Erases, from the block maps, blocks that appear to be
-// duplicate leaks of an already identified leak.
+// _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.
//
-// - element (IN): BlockMap Iterator referencing the block of which to search
-// for duplicates.
+// - 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 number of duplicate blocks erased from the block map.
+// Returns the value returned by RtlFreeHeap.
//
-SIZE_T VisualLeakDetector::eraseduplicates (const BlockMap::Iterator &element)
+BOOL VisualLeakDetector::_RtlFreeHeap (HANDLE heap, DWORD flags, LPVOID mem)
{
- BlockMap::Iterator blockit;
- BlockMap *blockmap;
- blockinfo_t *elementinfo;
- SIZE_T erased = 0;
- HeapMap::Iterator heapit;
- blockinfo_t *info;
- BlockMap::Iterator previt;
+ BOOL status;
- elementinfo = (*element).second;
+ // Unmap the block from the specified heap.
+ vld.unmapblock(heap, mem);
- // 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++;
- }
- }
- }
+ status = RtlFreeHeap(heap, flags, mem);
- return erased;
+ return status;
}
-// 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.
+// _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.
//
-// Return Value:
+// - heap (IN): Handle to the heap to reallocate memory from.
//
-// None.
+// - flags (IN): Heap control flags.
//
-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 currently allocated block which is to be
+// reallocated.
//
-// - mem (IN): Pointer to the memory block to inquire about.
+// - size (IN): Size, in bytes, of the block to reallocate.
//
// Return Value:
//
-// Returns the value returned by the system implementation of
-// IMalloc::GetSize.
+// Returns the value returned by RtlReAllocateHeap.
//
-ULONG VisualLeakDetector::GetSize (LPVOID mem)
+LPVOID VisualLeakDetector::_RtlReAllocateHeap (HANDLE heap, DWORD flags, LPVOID mem, SIZE_T size)
{
- assert(m_imalloc != NULL);
- return m_imalloc->GetSize(mem);
-}
+ BOOL crtalloc;
+ BOOL excluded = FALSE;
+ SIZE_T fp;
+ moduleinfo_t moduleinfo;
+ ModuleSet::Iterator moduleit;
+ LPVOID newmem;
+ SIZE_T returnaddress;
+ tls_t *tls = vld.gettls();
-// gettls - Obtains the thread local strorage 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);
+ // Reallocate the block.
+ newmem = RtlReAllocateHeap(heap, flags, mem, size);
- if (tls == NULL) {
- // This thread's thread local storage structure has not been allocated.
- tls = new tls_t;
- TlsSetValue(m_tlsindex, tls);
+ 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 = 0x0;
- tls->threadid = GetCurrentThreadId();
+ tls->flags &= ~VLD_TLS_CRTALLOC;
- // Add this thread's TLS to the TlsSet.
- EnterCriticalSection(&m_tlslock);
- m_tlsset->insert(tls);
- LeaveCriticalSection(&m_tlslock);
+ // 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);
+ }
}
- return tls;
-}
+ // Reset thread local flags and variables for the next allocation.
+ tls->addrfp = 0x0;
+ tls->flags &= ~VLD_TLS_CRTALLOC;
-// 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();
+ return newmem;
}
-// 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.
+// COM IAT Replacement Functions
//
-// - size (IN): Size, in bytes, of the memory block being allocated.
+////////////////////////////////////////////////////////////////////////////////
+
+// _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.
//
-// - 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.
+// - context (IN): Reserved; value must be 1.
//
-// - crtalloc (IN): Should be set to TRUE if this allocation is a CRT memory
-// block. Otherwise should be FALSE.
+// - imalloc (IN): Address of a pointer to receive the address of VLD's
+// implementation of the IMalloc interface.
//
// Return Value:
//
-// None.
+// Always returns S_OK.
//
-VOID VisualLeakDetector::mapblock (HANDLE heap, LPCVOID mem, SIZE_T size, SIZE_T framepointer, BOOL crtalloc)
+HRESULT VisualLeakDetector::_CoGetMalloc (DWORD context, LPMALLOC *imalloc)
{
- blockinfo_t *blockinfo;
- BlockMap::Iterator blockit;
- BlockMap *blockmap;
- HeapMap::Iterator heapit;
- static SIZE_T serialnumber = 0;
+ static CoGetMalloc_t pCoGetMalloc = NULL;
- // 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);
+ 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);
}
- 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;
+ 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;
}
- 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);
+
+ 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");
}
- LeaveCriticalSection(&m_maplock);
+
+ // 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;
}
-// 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.
+// _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.
//
-// - heap (IN): Handle to the newly created heap.
+// - mem (IN): Pointer to the memory block to reallocate.
+//
+// - size (IN): Size, in bytes, of the block to reallocate.
//
// Return Value:
//
-// None.
+// Returns the value returned from CoTaskMemRealloc.
//
-VOID VisualLeakDetector::mapheap (HANDLE heap)
+LPVOID VisualLeakDetector::_CoTaskMemRealloc (LPVOID mem, ULONG size)
{
- heapinfo_t *heapinfo;
- HeapMap::Iterator heapit;
+ static CoTaskMemRealloc_t pCoTaskMemRealloc = NULL;
- // 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);
+ 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;
}
- LeaveCriticalSection(&m_maplock);
+
+ 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;
}
-// 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.
+// Public COM IMalloc Implementation Functions
//
-// - object (IN): Address of a pointer to receive the requested interface
-// pointer.
+////////////////////////////////////////////////////////////////////////////////
+
+// 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::QueryInterface.
+// IMalloc::AddRef.
//
-HRESULT VisualLeakDetector::QueryInterface (REFIID iid, LPVOID *object)
+ULONG VisualLeakDetector::AddRef ()
{
assert(m_imalloc != NULL);
- return m_imalloc->QueryInterface(iid, object);
+ return m_imalloc->AddRef();
}
-// 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.
+// 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): Size, in bytes, of the memory block to reallocate.
+// - size (IN): The size of the memory block to allocate.
//
// Return Value:
//
-// Returns the value returned by the system implementation of
-// IMalloc::Realloc.
+// Returns the value returned by the system's IMalloc::Alloc implementation.
//
-LPVOID VisualLeakDetector::Realloc (LPVOID mem, ULONG size)
+LPVOID VisualLeakDetector::Alloc (ULONG size)
{
LPVOID block;
SIZE_T fp;
tls->addrfp = fp;
}
- // Do the allocation. The block will be mapped by _RtlReAllocateHeap.
+ // Do the allocation. The block will be mapped by _RtlAllocateHeap.
assert(m_imalloc != NULL);
- block = m_imalloc->Realloc(mem, size);
+ 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;
}
-// Release - Calls to IMalloc::Release will end up here. This function is just
-// a wrapper around the real IMalloc::Release implementation.
+// 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::Release.
+// IMalloc::DidAlloc.
//
-ULONG VisualLeakDetector::Release ()
+INT VisualLeakDetector::DidAlloc (LPVOID mem)
{
assert(m_imalloc != NULL);
- return m_imalloc->Release();
+ return m_imalloc->DidAlloc(mem);
}
-// 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.
+// Free - Calls to IMalloc::Free will end up here. This function is just a
+// wrapper around the real IMalloc::Free implementation.
//
-// - crtalloc (IN): Should be set to TRUE if this reallocation is for a CRT
-// memory block. Otherwise should be set to FALSE.
+// - mem (IN): Pointer to the memory block to be freed.
//
// Return Value:
//
// None.
//
-VOID VisualLeakDetector::remapblock (HANDLE heap, LPCVOID mem, LPCVOID newmem, SIZE_T size, SIZE_T framepointer,
- BOOL crtalloc)
+VOID VisualLeakDetector::Free (LPVOID mem)
{
- 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);
+ assert(m_imalloc != NULL);
+ m_imalloc->Free(mem);
+}
- // 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;
+// 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);
}
-// reportconfig - Generates a brief report summarizing Visual Leak Detector's
-// configuration, as loaded from the vld.ini file.
+// 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::reportconfig ()
+VOID VisualLeakDetector::HeapMinimize ()
{
- 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");
- }
+ assert(m_imalloc != NULL);
+ m_imalloc->HeapMinimize();
}
-// reportleaks - Generates a memory leak report for the specified heap.
+// QueryInterface - Calls to IMalloc::QueryInterface will end up here. This
+// function is just a wrapper around the real IMalloc::QueryInterface
+// implementation.
//
-// - heap (IN): Handle to the heap for which to generate a memory leak
-// report.
+// - iid (IN): COM interface ID to query about.
+//
+// - object (IN): Address of a pointer to receive the requested interface
+// pointer.
//
// Return Value:
//
-// None.
+// Returns the value returned by the system implementation of
+// IMalloc::QueryInterface.
//
-VOID VisualLeakDetector::reportleaks (HANDLE heap)
+HRESULT VisualLeakDetector::QueryInterface (REFIID iid, LPVOID *object)
{
- 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);
+ assert(m_imalloc != NULL);
+ return m_imalloc->QueryInterface(iid, object);
}
-// unmapblock - Tracks memory blocks that are freed. Unmaps the specified block
-// from the block's information, relinquishing internally allocated resources.
+// 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.
//
-// - heap (IN): Handle to the heap to which this block is being freed.
+// - mem (IN): Pointer to the memory block to reallocate.
//
-// - mem (IN): Pointer to the memory block being freed.
+// - size (IN): Size, in bytes, of the memory block to reallocate.
//
// Return Value:
//
-// None.
+// Returns the value returned by the system implementation of
+// IMalloc::Realloc.
//
-VOID VisualLeakDetector::unmapblock (HANDLE heap, LPCVOID mem)
+LPVOID VisualLeakDetector::Realloc (LPVOID mem, ULONG size)
{
- BlockMap::Iterator blockit;
- BlockMap *blockmap;
- HeapMap::Iterator heapit;
- blockinfo_t *info;
+ LPVOID block;
+ SIZE_T fp;
+ tls_t *tls = vld.gettls();
- // 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;
+ 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;
}
- // 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;
- }
+ // Do the allocation. The block will be mapped by _RtlReAllocateHeap.
+ assert(m_imalloc != NULL);
+ block = m_imalloc->Realloc(mem, size);
- // 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);
+ // Reset thread local flags and variables for the next allocation.
+ tls->addrfp = 0x0;
+ tls->flags &= ~VLD_TLS_CRTALLOC;
+
+ return block;
}
-// 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.
+// Release - Calls to IMalloc::Release will end up here. This function is just
+// a wrapper around the real IMalloc::Release implementation.
//
// Return Value:
//
-// None.
+// Returns the value returned by the system implementation of
+// IMalloc::Release.
//
-VOID VisualLeakDetector::unmapheap (HANDLE heap)
+ULONG VisualLeakDetector::Release ()
{
- 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);
+ assert(m_imalloc != NULL);
+ return m_imalloc->Release();
}