UINT CallStack::isCrtStartupFunction( LPCWSTR functionName ) const { size_t len = wcslen(functionName); if (beginWith(functionName, len, L"_malloc_crt") || beginWith(functionName, len, L"_calloc_crt") || endWith(functionName, len, L"CRT_INIT") || endWith(functionName, len, L"initterm_e") || beginWith(functionName, len, L"_cinit") || beginWith(functionName, len, L"std::`dynamic initializer for '") // VS2015 || beginWith(functionName, len, L"common_initialize_environment_nolock<") || beginWith(functionName, len, L"common_configure_argv<") || beginWith(functionName, len, L"__acrt_initialize") || beginWith(functionName, len, L"__acrt_allocate_buffer_for_argv") || beginWith(functionName, len, L"_register_onexit_function") ) { return CALLSTACK_STATUS_STARTUPCRT; } if (endWith(functionName, len, L"DllMainCRTStartup") || endWith(functionName, len, L"mainCRTStartup") || beginWith(functionName, len, L"`dynamic initializer for '")) { // When we reach this point there is no reason going further down the stack return CALLSTACK_STATUS_NOTSTARTUPCRT; } return NULL; }
bool CallStack::isInternalModule( const PWSTR filename ) const { size_t len = wcslen(filename); return // VS2015 endWith(filename, len, L"\\atlmfc\\include\\atlsimpstr.h") || endWith(filename, len, L"\\atlmfc\\include\\cstringt.h") || endWith(filename, len, L"\\atlmfc\\src\\mfc\\afxmem.cpp") || endWith(filename, len, L"\\atlmfc\\src\\mfc\\strcore.cpp") || endWith(filename, len, L"\\vcstartup\\src\\heap\\new_scalar.cpp") || endWith(filename, len, L"\\vcstartup\\src\\heap\\new_array.cpp") || endWith(filename, len, L"\\vcstartup\\src\\heap\\new_debug.cpp") || endWith(filename, len, L"\\ucrt\\src\\appcrt\\heap\\align.cpp") || endWith(filename, len, L"\\ucrt\\src\\appcrt\\heap\\malloc.cpp") || endWith(filename, len, L"\\ucrt\\src\\appcrt\\heap\\debug_heap.cpp") || // VS2013 beginWith(filename, len, L"f:\\dd\\vctools\\crt\\crtw32\\") || //endWith(filename, len, L"\\crt\\crtw32\\misc\\dbgheap.c") || //endWith(filename, len, L"\\crt\\crtw32\\misc\\dbgnew.cpp") || //endWith(filename, len, L"\\crt\\crtw32\\misc\\dbgmalloc.c") || //endWith(filename, len, L"\\crt\\crtw32\\misc\\dbgrealloc.c") || //endWith(filename, len, L"\\crt\\crtw32\\heap\\new.cpp") || //endWith(filename, len, L"\\crt\\crtw32\\heap\\new2.cpp") || //endWith(filename, len, L"\\crt\\crtw32\\heap\\malloc.c") || //endWith(filename, len, L"\\crt\\crtw32\\heap\\realloc.c") || //endWith(filename, len, L"\\crt\\crtw32\\heap\\calloc.c") || //endWith(filename, len, L"\\crt\\crtw32\\heap\\calloc_impl.c") || //endWith(filename, len, L"\\crt\\crtw32\\string\\strdup.c") || //endWith(filename, len, L"\\crt\\crtw32\\string\\wcsdup.c") || // VS2010 endWith(filename, len, L"\\crt\\src\\afxmem.cpp") || endWith(filename, len, L"\\crt\\src\\dbgheap.c") || endWith(filename, len, L"\\crt\\src\\dbgnew.cpp") || endWith(filename, len, L"\\crt\\src\\dbgmalloc.c") || endWith(filename, len, L"\\crt\\src\\dbgcalloc.c") || endWith(filename, len, L"\\crt\\src\\dbgrealloc.c") || endWith(filename, len, L"\\crt\\src\\dbgdel.cp") || endWith(filename, len, L"\\crt\\src\\new.cpp") || endWith(filename, len, L"\\crt\\src\\newaop.cpp") || endWith(filename, len, L"\\crt\\src\\malloc.c") || endWith(filename, len, L"\\crt\\src\\realloc.c") || endWith(filename, len, L"\\crt\\src\\free.c") || endWith(filename, len, L"\\crt\\src\\strdup.c") || endWith(filename, len, L"\\crt\\src\\wcsdup.c") || endWith(filename, len, L"\\vc\\include\\xmemory0") || // default (false); }
// isCrtStartupAlloc - Determines whether the memory leak was generated from crt startup code. // This is not an actual memory leaks as it is freed by crt after the VLD object has been destroyed. // // Return Value: // // true if isCrtStartupModule for any callstack frame returns true. // bool CallStack::isCrtStartupAlloc() { if (m_status & CALLSTACK_STATUS_STARTUPCRT) { return true; } else if (m_status & CALLSTACK_STATUS_NOTSTARTUPCRT) { return false; } IMAGEHLP_LINE64 sourceInfo = { 0 }; sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64); BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_SIZE] = { 0 }; CriticalSectionLocker<DbgHelp> locker(g_DbgHelp); // Iterate through each frame in the call stack. for (UINT32 frame = 0; frame < m_size; frame++) { // Try to get the source file and line number associated with // this program counter address. SIZE_T programCounter = (*this)[frame]; BOOL foundline = FALSE; DWORD displacement = 0; DbgTrace(L"dbghelp32.dll %i: SymGetLineFromAddrW64\n", GetCurrentThreadId()); foundline = g_DbgHelp.SymGetLineFromAddrW64(g_currentProcess, programCounter, &displacement, &sourceInfo); if (foundline) { DWORD64 displacement64; LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker); if (beginWith(functionName, wcslen(functionName), L"`dynamic initializer for '")) { break; } if (isCrtStartupModule(sourceInfo.FileName)) { m_status |= CALLSTACK_STATUS_STARTUPCRT; return true; } } } m_status |= CALLSTACK_STATUS_NOTSTARTUPCRT; return false; }
// Resolve - Creates a nicely formatted rendition of the CallStack, including // symbolic information (function names and line numbers) if available. and // saves it for later retrieval. This is almost identical to Callstack::dump above. // // Note: The symbol handler must be initialized prior to calling this // function. // // - showInternalFrames (IN): If true, then all frames in the CallStack will be // dumped. Otherwise, frames internal to the heap will not be dumped. // // Return Value: // // None. // int CallStack::resolve(BOOL showInternalFrames) { if (m_resolved) { // already resolved, no need to do it again // resolving twice may report an incorrect module for the stack frames // if the memory was leaked in a dynamic library that was already unloaded. return 0; } if (m_status & CALLSTACK_STATUS_STARTUPCRT) { // there is no need to resolve a leak that will not be reported return 0; } if (m_status & CALLSTACK_STATUS_INCOMPLETE) { // This call stack appears to be incomplete. Using StackWalk64 may be // more reliable. Report(L" HINT: The following call stack may be incomplete. Setting \"StackWalkMethod\"\n" L" in the vld.ini file to \"safe\" instead of \"fast\" may result in a more\n" L" complete stack trace.\n"); } int unresolvedFunctionsCount = 0; IMAGEHLP_LINE64 sourceInfo = { 0 }; sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64); bool skipStartupLeaks = !!(g_vld.GetOptions() & VLD_OPT_SKIP_CRTSTARTUP_LEAKS); // Use static here to increase performance, and avoid heap allocs. // It's thread safe because of g_heapMapLock lock. static WCHAR stack_line[MAXREPORTLENGTH + 1] = L""; bool isPrevFrameInternal = false; bool isDynamicInitializer = false; DWORD NumChars = 0; CriticalSectionLocker<DbgHelp> locker(g_DbgHelp); const size_t max_line_length = MAXREPORTLENGTH + 1; m_resolvedCapacity = m_size * max_line_length; const size_t allocedBytes = m_resolvedCapacity * sizeof(WCHAR); m_resolved = new WCHAR[m_resolvedCapacity]; if (m_resolved) { ZeroMemory(m_resolved, allocedBytes); } // Iterate through each frame in the call stack. for (UINT32 frame = 0; frame < m_size; frame++) { // Try to get the source file and line number associated with // this program counter address. SIZE_T programCounter = (*this)[frame]; DWORD displacement = 0; // It turns out that calls to SymGetLineFromAddrW64 may free the very memory we are scrutinizing here // in this method. If this is the case, m_Resolved will be null after SymGetLineFromAddrW64 returns. // When that happens there is nothing we can do except crash. DbgTrace(L"dbghelp32.dll %i: SymGetLineFromAddrW64\n", GetCurrentThreadId()); BOOL foundline = g_DbgHelp.SymGetLineFromAddrW64(g_currentProcess, programCounter, &displacement, &sourceInfo, locker); if (skipStartupLeaks && foundline && !isDynamicInitializer && !(m_status & CALLSTACK_STATUS_NOTSTARTUPCRT) && isCrtStartupModule(sourceInfo.FileName)) { m_status |= CALLSTACK_STATUS_STARTUPCRT; delete[] m_resolved; m_resolved = NULL; m_resolvedCapacity = 0; m_resolvedLength = 0; return 0; } bool isFrameInternal = false; if (foundline && !showInternalFrames) { if (isInternalModule(sourceInfo.FileName)) { // Don't show frames in files internal to the heap. isFrameInternal = true; } } // show one allocation function for context if (NumChars > 0 && !isFrameInternal && isPrevFrameInternal) { m_resolvedLength += NumChars; if (m_resolved) { wcsncat_s(m_resolved, m_resolvedCapacity, stack_line, NumChars); } } isPrevFrameInternal = isFrameInternal; DWORD64 displacement64; BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_SIZE]; LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker); if (skipStartupLeaks && foundline && beginWith(functionName, wcslen(functionName), L"`dynamic initializer for '")) { isDynamicInitializer = true; } if (!foundline) displacement = (DWORD)displacement64; NumChars = resolveFunction( programCounter, foundline ? &sourceInfo : NULL, displacement, functionName, stack_line, _countof( stack_line )); if (NumChars > 0 && !isFrameInternal) { m_resolvedLength += NumChars; if (m_resolved) { wcsncat_s(m_resolved, m_resolvedCapacity, stack_line, NumChars); } } } // end for loop m_status |= CALLSTACK_STATUS_NOTSTARTUPCRT; return unresolvedFunctionsCount; }