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