Skip to content

Commit

Permalink
Try identifying CRT start-up leaks by function
Browse files Browse the repository at this point in the history
  • Loading branch information
ioannis-e committed Nov 22, 2015
1 parent ba0feae commit 8010efe
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 78 deletions.
86 changes: 32 additions & 54 deletions src/callstack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,28 +304,13 @@ bool CallStack::isCrtStartupAlloc()
SIZE_T programCounter = (*this)[frame];
DWORD64 displacement64;
LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker);
size_t len = wcslen(functionName);

if (beginWith(functionName, len, L"`dynamic initializer for '")) {
m_status |= CALLSTACK_STATUS_NOTSTARTUPCRT;
return false;
}

// We need to detect both "_CRT_INIT" for most x86 and x64 modules
// and "CRT_INIT" for some x64 modules
if (endWith(functionName, len, L"CRT_INIT")) {
m_status |= CALLSTACK_STATUS_STARTUPCRT;
return true;
}

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 && isCrtStartupModule(sourceInfo.FileName)) {
UINT status = isCrtStartupFunction(functionName);
if (status == CALLSTACK_STATUS_STARTUPCRT) {
m_status |= CALLSTACK_STATUS_STARTUPCRT;
return true;
} else if (status == CALLSTACK_STATUS_NOTSTARTUPCRT) {
break;
}
}

Expand Down Expand Up @@ -496,19 +481,16 @@ int CallStack::resolve(BOOL showInternalFrames)
LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker);

if (skipStartupLeaks && !(m_status & CALLSTACK_STATUS_NOTSTARTUPCRT)) {
size_t len = wcslen(functionName);
if (beginWith(functionName, len, L"`dynamic initializer for '")) {
m_status |= CALLSTACK_STATUS_NOTSTARTUPCRT;
}

if (!(m_status & CALLSTACK_STATUS_NOTSTARTUPCRT) &&
(endWith(functionName, len, L"CRT_INIT") || (foundline && isCrtStartupModule(sourceInfo.FileName)))) {
UINT status = isCrtStartupFunction(functionName);
if (status == CALLSTACK_STATUS_STARTUPCRT) {
m_status |= CALLSTACK_STATUS_STARTUPCRT;
delete[] m_resolved;
m_resolved = NULL;
m_resolvedCapacity = 0;
m_resolvedLength = 0;
return 0;
} else if (status == CALLSTACK_STATUS_NOTSTARTUPCRT) {
m_status |= CALLSTACK_STATUS_NOTSTARTUPCRT;
}
}

Expand Down Expand Up @@ -590,36 +572,32 @@ VOID CallStack::push_back (const UINT_PTR programcounter)
m_size++;
}

bool CallStack::isCrtStartupModule( const PWSTR filename ) const
UINT CallStack::isCrtStartupFunction( LPCWSTR functionName ) const
{
size_t len = wcslen(filename);
return
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")
// VS2015
endWith(filename, len, L"\\crts\\ucrt\\src\\appcrt\\stdio\\_file.cpp") ||
endWith(filename, len, L"\\crts\\ucrt\\src\\appcrt\\startup\\onexit.cpp") ||
endWith(filename, len, L"\\crts\\ucrt\\src\\appcrt\\startup\\initterm.cpp") ||
endWith(filename, len, L"\\crts\\ucrt\\src\\appcrt\\startup\\argv_parsing.cpp") ||
endWith(filename, len, L"\\crts\\ucrt\\src\\appcrt\\internal\\initialization.cpp") ||
endWith(filename, len, L"\\crts\\ucrt\\src\\appcrt\\internal\\shared_initialization.cpp") ||
endWith(filename, len, L"\\crts\\ucrt\\src\\desktopcrt\\env\\environment_initialization.cpp") ||
// VS2013
endWith(filename, len, L"\\crt\\crtw32\\lowio\\ioinit.c") ||
endWith(filename, len, L"\\crt\\crtw32\\misc\\onexit.c") ||
endWith(filename, len, L"\\crt\\crtw32\\stdio\\_file.c") ||
endWith(filename, len, L"\\crt\\crtw32\\startup\\stdargv.c") ||
endWith(filename, len, L"\\crt\\crtw32\\startup\\stdenvp.c") ||
endWith(filename, len, L"\\crt\\crtw32\\startup\\tidtable.c") ||
endWith(filename, len, L"\\crt\\crtw32\\mbstring\\mbctype.c") ||
// VS2012 and bellow
endWith(filename, len, L"\\crt\\src\\crt0dat.c") || //_cinit()
endWith(filename, len, L"\\crt\\src\\onexit.c") || //__onexitinit()
endWith(filename, len, L"\\crt\\src\\stdargv.c") || //_wsetargv()
endWith(filename, len, L"\\crt\\src\\stdenvp.c") || //_wsetenvp()
endWith(filename, len, L"\\crt\\src\\ioinit.c") || //_ioinit()
endWith(filename, len, L"\\crt\\src\\tidtable.c") || //_mtinit()
endWith(filename, len, L"\\crt\\src\\mbctype.c") || //__initmbctable()
// default
(false);
|| 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 furhter down the stack
return CALLSTACK_STATUS_NOTSTARTUPCRT;
}

return NULL;
}

bool CallStack::isInternalModule( const PWSTR filename ) const
Expand Down
2 changes: 1 addition & 1 deletion src/callstack.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class CallStack
// human readable form. Currently this is only called by the dump method.
void dumpResolved() const;
bool isInternalModule( const PWSTR filename ) const;
bool isCrtStartupModule( const PWSTR filename ) const;
UINT isCrtStartupFunction( LPCWSTR functionName ) const;
LPCWSTR getFunctionName(SIZE_T programCounter, DWORD64& displacement64,
SYMBOL_INFO* functionInfo, CriticalSectionLocker<DbgHelp>& locker) const;
DWORD resolveFunction(SIZE_T programCounter, IMAGEHLP_LINEW64* sourceInfo, DWORD displacement,
Expand Down
54 changes: 33 additions & 21 deletions src/vld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,9 @@ VisualLeakDetector::VisualLeakDetector ()
delete oldmodules;
m_status |= VLD_STATUS_INSTALLED;

HMODULE dbghelp = GetModuleHandleW(L"dbghelp.dll");
if (dbghelp)
ChangeModuleState(dbghelp, false);
m_dbghlpBase = GetModuleHandleW(L"dbghelp.dll");
if (m_dbghlpBase)
ChangeModuleState(m_dbghlpBase, false);

Report(L"Visual Leak Detector Version " VLDVERSION L" installed.\n");
if (m_status & VLD_STATUS_FORCE_REPORT_TO_FILE) {
Expand Down Expand Up @@ -803,7 +803,7 @@ VOID VisualLeakDetector::attachToLoadedModules (ModuleSet *newmodules)
moduleimageinfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
BOOL SymbolsLoaded = g_DbgHelp.SymGetModuleInfoW64(g_currentProcess, modulebase, &moduleimageinfo, locker);

if (!SymbolsLoaded)
if (!SymbolsLoaded || moduleimageinfo.BaseOfImage != modulebase)
{
DbgTrace(L"dbghelp32.dll %i: SymLoadModuleEx\n", GetCurrentThreadId());
DWORD64 module = g_DbgHelp.SymLoadModuleExW(g_currentProcess, NULL, modulepath, NULL, modulebase, modulesize, NULL, 0, locker);
Expand Down Expand Up @@ -1707,14 +1707,10 @@ SIZE_T VisualLeakDetector::getLeaksCount (heapinfo_t* heapinfo, DWORD threadId)
// This block is marked as being used internally by the CRT.
// The CRT will free the block after VLD is destroyed.
continue;
} else if (m_options & VLD_OPT_SKIP_CRTSTARTUP_LEAKS) {
// Check for crt startup allocations
if (info->callStack && info->callStack->isCrtStartupAlloc()) {
info->reported = true;
continue;
}
}
} else if (m_options & VLD_OPT_SKIP_CRTSTARTUP_LEAKS) {
}

if (m_options & VLD_OPT_SKIP_CRTSTARTUP_LEAKS) {
// Check for crt startup allocations
if (info->callStack && info->callStack->isCrtStartupAlloc()) {
info->reported = true;
Expand Down Expand Up @@ -1808,12 +1804,6 @@ SIZE_T VisualLeakDetector::reportLeaks (heapinfo_t* heapinfo, bool &firstLeak, S
// This block is marked as being used internally by the CRT.
// The CRT will free the block after VLD is destroyed.
continue;
} else if (m_options & VLD_OPT_SKIP_CRTSTARTUP_LEAKS) {
// Check for crt startup allocations
if (info->callStack && info->callStack->isCrtStartupAlloc()) {
info->reported = true;
continue;
}
}

// The CRT header is more or less transparent to the user, so
Expand All @@ -1822,7 +1812,9 @@ SIZE_T VisualLeakDetector::reportLeaks (heapinfo_t* heapinfo, bool &firstLeak, S
// we'll include in the report.
address = CRTDBGBLOCKDATA(block);
size = crtheader->size;
} else if (m_options & VLD_OPT_SKIP_CRTSTARTUP_LEAKS) {
}

if (m_options & VLD_OPT_SKIP_CRTSTARTUP_LEAKS) {
// Check for crt startup allocations
if (info->callStack && info->callStack->isCrtStartupAlloc()) {
info->reported = true;
Expand Down Expand Up @@ -2824,7 +2816,6 @@ CaptureContext::CaptureContext(context_t &context, BOOL debug, void* func, UINT_
}

m_bFirst = (GET_RETURN_ADDRESS(m_tls->context) == NULL);
m_bExclude = g_vld.isModuleExcluded(fp);
if (m_bFirst) {
// This is the first call to enter VLD for the current allocation.
// Record the current frame pointer.
Expand All @@ -2837,7 +2828,7 @@ CaptureContext::CaptureContext(context_t &context, BOOL debug, void* func, UINT_

CaptureContext::~CaptureContext() {
if (m_bFirst) {
if ((!m_bExclude) && (m_tls->blockWithoutGuard)) {
if ((m_tls->blockWithoutGuard) && (!IsExcludedModule())) {
blockinfo_t* pblockInfo = NULL;
if (m_tls->newBlockWithoutGuard == NULL) {
g_vld.mapBlock(m_tls->heap,
Expand Down Expand Up @@ -2899,7 +2890,28 @@ void CaptureContext::Set(HANDLE heap, LPVOID mem, LPVOID newmem, SIZE_T size) {
}

void CaptureContext::Reset() {
m_tls->context = { NULL };
m_tls->context.func = NULL;
m_tls->context.fp = NULL;
#if defined(_M_IX86)
m_tls->context.Ebp = m_tls->context.Esp = m_tls->context.Eip = NULL;
#elif defined(_M_X64)
m_tls->context.Rbp = m_tls->context.Rsp = m_tls->context.Rip = NULL;
#endif
m_tls->flags &= ~VLD_TLS_DEBUGCRTALLOC;
Set(NULL, NULL, NULL, NULL);
}

BOOL CaptureContext::IsExcludedModule() {
HMODULE hModule = GetCallingModule(m_fp);
if (hModule == g_vld.m_dbghlpBase)
return TRUE;

UINT tablesize = _countof(g_vld.m_patchTable);
for (UINT index = 0; index < tablesize; index++) {
if (((HMODULE)g_vld.m_patchTable[index].moduleBase == hModule)) {
return !g_vld.m_patchTable[index].reportLeaks;
}
}

return g_vld.isModuleExcluded((UINT_PTR)hModule);
}
4 changes: 2 additions & 2 deletions src/vld_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ void* VisualLeakDetector::__new_dbg_mfc (new_dbg_crt_t pnew_dbg,
#ifdef PRINTHOOKCALLS
DbgReport(_T(__FUNCTION__) _T( "\n"));
#endif
CaptureContext cc(context, TRUE, NULL);
CaptureContext cc(context, FALSE, NULL);

// Do the allocation. The block will be mapped by _RtlAllocateHeap.
return pnew_dbg(size, type, file, line);
Expand Down Expand Up @@ -900,7 +900,7 @@ void* VisualLeakDetector::__new_dbg_mfc (new_dbg_mfc_t pnew_dbg_mfc,
#ifdef PRINTHOOKCALLS
DbgReport(_T(__FUNCTION__) _T( "\n"));
#endif
CaptureContext cc(context, TRUE, NULL);
CaptureContext cc(context, FALSE, NULL);

// Do the allocation. The block will be mapped by _RtlAllocateHeap.
return pnew_dbg_mfc(size, file, line);
Expand Down
2 changes: 2 additions & 0 deletions src/vldint.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class CaptureContext {
CaptureContext& operator=(const CaptureContext&);
private:
__forceinline void Capture(context_t &context);
BOOL IsExcludedModule();
void Reset();
private:
tls_t *m_tls;
Expand Down Expand Up @@ -430,6 +431,7 @@ class VisualLeakDetector : public IMalloc
CriticalSection m_tlsLock; // Protects accesses to the Set of TLS structures.
TlsMap *m_tlsMap; // Set of all thread-local storage structures for the process.
HMODULE m_vldBase; // Visual Leak Detector's own module handle (base address).
HMODULE m_dbghlpBase;

VOID __stdcall ChangeModuleState(HMODULE module, bool on);
static GetProcAddress_t m_GetProcAddress;
Expand Down

0 comments on commit 8010efe

Please sign in to comment.