const std::wstring SymbolInfo::getProcForAddr(PROFILER_ADDR addr, std::wstring& procfilepath_out, int& proclinenum_out) { procfilepath_out = L""; proclinenum_out = 0; Module *mod = getModuleForAddr(addr); DbgHelp *dbgHelp = mod ? mod->dbghelp : &dbgHelpMs; unsigned char buffer[1024]; //blame MS for this abomination of a coding technique SYMBOL_INFOW* symbol_info = (SYMBOL_INFOW*)buffer; symbol_info->SizeOfStruct = sizeof(SYMBOL_INFOW); symbol_info->MaxNameLen = ((sizeof(buffer) - sizeof(SYMBOL_INFOW)) / sizeof(WCHAR)) - 1; DWORD64 displacement = 0; BOOL result = dbgHelp->SymFromAddrW(process_handle, (DWORD64)addr, &displacement, symbol_info); if(!result) { DWORD err = GetLastError(); wchar_t buf[256]; #if defined(_WIN64) if(is64BitProcess) swprintf(buf, 256, L"[%016llX]", addr); else swprintf(buf, 256, L"[%08X]", unsigned __int32(addr)); #else swprintf(buf, 256, L"[%08X]", addr); #endif return buf; }
SymbolInfo::~SymbolInfo() { //------------------------------------------------------------------------ //clean up //------------------------------------------------------------------------ if ( process_handle ) { DbgHelp *gcc = &dbgHelpGcc; #ifdef _WIN64 if (is64BitProcess) gcc = &dbgHelpGccWow64; #endif if (!gcc->SymCleanup(process_handle)) { //error } if (!dbgHelpMs.SymCleanup(process_handle)) { //error } process_handle = NULL; } }
std::string format_address_win32(const void* addr) const { if (!dbghelp.initialized()) return format_address_fallback(addr); DWORD64 symbol_info_buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; const auto symbol_info = reinterpret_cast<SYMBOL_INFO*>(symbol_info_buf); symbol_info->SizeOfStruct = sizeof(SYMBOL_INFO); symbol_info->MaxNameLen = MAX_SYM_NAME; IMAGEHLP_MODULE64 module_info; module_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); DWORD64 symbol_offset; const auto symbol_resolved = SymFromAddr( GetCurrentProcess(), reinterpret_cast<DWORD64>(addr), &symbol_offset, symbol_info); if (symbol_resolved) { const auto module_resolved = SymGetModuleInfo64( GetCurrentProcess(), symbol_info->ModBase, &module_info); if (module_resolved) { return format_symbol( module_info.ModuleName, symbol_info->Name, reinterpret_cast<const void*>(symbol_offset)); } else { return format_symbol(symbol_info->Name, addr); } } else { const auto module_resolved = SymGetModuleInfo64( GetCurrentProcess(), reinterpret_cast<DWORD64>(addr), &module_info); if (module_resolved) { const auto module_offset = reinterpret_cast<const char*>(addr) - module_info.BaseOfImage; return format_module(module_info.ModuleName, module_offset); } else { return format_address_fallback(addr); } } }
// getStackTrace - Traces the stack as far back as possible, or until 'maxdepth' // frames have been traced. Populates the CallStack with one entry for each // stack frame traced. // // Note: This function uses a documented Windows API to walk the stack. This // API is supposed to be the most reliable way to walk the stack. It claims // to be able to walk stack frames that do not follow the conventional stack // frame layout. However, this robustness comes at a cost: it is *extremely* // slow compared to walking frames by following frame (base) pointers. // // - maxdepth (IN): Maximum number of frames to trace back. // // - framepointer (IN): Frame (base) pointer at which to begin the stack trace. // If NULL, then the stack trace will begin at this function. // // Return Value: // // None. // VOID SafeCallStack::getStackTrace (UINT32 maxdepth, const context_t& context) { UINT32 count = 0; UINT_PTR function = context.func; if (function != NULL) { count++; push_back(function); } DWORD architecture = X86X64ARCHITECTURE; // Get the required values for initialization of the STACKFRAME64 structure // to be passed to StackWalk64(). Required fields are AddrPC and AddrFrame. CONTEXT currentContext; memset(¤tContext, 0, sizeof(currentContext)); currentContext.SPREG = context.SPREG; currentContext.BPREG = context.BPREG; currentContext.IPREG = context.IPREG; // Initialize the STACKFRAME64 structure. STACKFRAME64 frame; memset(&frame, 0x0, sizeof(frame)); frame.AddrPC.Offset = currentContext.IPREG; frame.AddrPC.Mode = AddrModeFlat; frame.AddrStack.Offset = currentContext.SPREG; frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Offset = currentContext.BPREG; frame.AddrFrame.Mode = AddrModeFlat; frame.Virtual = TRUE; CriticalSectionLocker<> cs(g_heapMapLock); CriticalSectionLocker<DbgHelp> locker(g_DbgHelp); // Walk the stack. while (count < maxdepth) { count++; DbgTrace(L"dbghelp32.dll %i: StackWalk64\n", GetCurrentThreadId()); if (!g_DbgHelp.StackWalk64(architecture, g_currentProcess, g_currentThread, &frame, ¤tContext, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL, locker)) { // Couldn't trace back through any more frames. break; } if (frame.AddrFrame.Offset == 0) { // End of stack. break; } // Push this frame's program counter onto the CallStack. push_back((UINT_PTR)frame.AddrPC.Offset); } }
// for kernel32.dll LPVOID VisualLeakDetector::_HeapReAlloc (HANDLE heap, DWORD flags, LPVOID mem, SIZE_T size) { PRINT_HOOKED_FUNCTION(); // Reallocate the block. LPVOID newmem = HeapReAlloc(heap, flags, mem, size); if ((newmem == NULL) || !g_vld.enabled()) return newmem; if (!g_DbgHelp.IsLockedByCurrentThread()) { // skip dbghelp.dll calls CAPTURE_CONTEXT(); CaptureContext cc(HeapReAlloc, size, context_); cc.Set(heap, mem, newmem, size); } return newmem; }
// HeapAlloc (kernel32.dll) call RtlAllocateHeap (ntdll.dll) LPVOID VisualLeakDetector::_HeapAlloc (HANDLE heap, DWORD flags, SIZE_T size) { PRINT_HOOKED_FUNCTION2(); // Allocate the block. LPVOID block = HeapAlloc(heap, flags, size); if ((block == NULL) || !g_vld.enabled()) return block; if (!g_DbgHelp.IsLockedByCurrentThread()) { // skip dbghelp.dll calls CAPTURE_CONTEXT(); CaptureContext cc(HeapAlloc, size, context_); cc.Set(heap, block, NULL, size); } return block; }
// HeapFree (kernel32.dll) call RtlFreeHeap (ntdll.dll) BOOL VisualLeakDetector::_HeapFree (HANDLE heap, DWORD flags, LPVOID mem) { PRINT_HOOKED_FUNCTION2(); BOOL status; if (!g_DbgHelp.IsLockedByCurrentThread()) // skip dbghelp.dll calls { // Record the current frame pointer. CAPTURE_CONTEXT(); context_.func = reinterpret_cast<UINT_PTR>(m_HeapFree); // Unmap the block from the specified heap. g_vld.unmapBlock(heap, mem, context_); } status = m_HeapFree(heap, flags, mem); return status; }
// 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; }
LPCWSTR CallStack::getFunctionName(SIZE_T programCounter, DWORD64& displacement64, SYMBOL_INFO* functionInfo, CriticalSectionLocker<DbgHelp>& locker) const { // Initialize structures passed to the symbol handler. functionInfo->SizeOfStruct = sizeof(SYMBOL_INFO); functionInfo->MaxNameLen = MAX_SYMBOL_NAME_LENGTH; // Try to get the name of the function containing this program // counter address. displacement64 = 0; LPCWSTR functionName; DbgTrace(L"dbghelp32.dll %i: SymFromAddrW\n", GetCurrentThreadId()); if (g_DbgHelp.SymFromAddrW(g_currentProcess, programCounter, &displacement64, functionInfo, locker)) { functionName = functionInfo->Name; } else { // GetFormattedMessage( GetLastError() ); fmt::WArrayWriter wf(functionInfo->Name, MAX_SYMBOL_NAME_LENGTH); wf.write(L"" ADDRESSCPPFORMAT, programCounter); functionName = wf.c_str(); displacement64 = 0; } return functionName; }
void SymbolInfo::loadSymbols(HANDLE process_handle_, bool download) { process_handle = process_handle_; wxBusyCursor busy; is64BitProcess = Is64BitProcess(process_handle); DWORD options = dbgHelpMs.SymGetOptions(); #ifdef _WIN64 if(!is64BitProcess) { options |= SYMOPT_INCLUDE_32BIT_MODULES; } #endif options |= SYMOPT_LOAD_LINES | SYMOPT_DEBUG; dbgHelpMs.SymSetOptions(options); dbgHelpGcc.SymSetOptions(options); #ifdef _WIN64 dbgHelpGccWow64.SymSetOptions(options); #endif std::wstring sympath; // Add the program's own directory to the search path. // Useful if someone's copied the EXE and PDB to a different machine or location. wchar_t szExePath[MAX_PATH] = L""; DWORD pathsize = MAX_PATH; BOOL gotImageName = FALSE; #ifdef _WIN64 // GetModuleFileNameEx doesn't always work across 64->32 bit boundaries. // Use QueryFullProcessImageName if we have it. { typedef BOOL WINAPI QueryFullProcessImageNameFn(HANDLE hProcess, DWORD dwFlags, LPTSTR lpExeName, PDWORD lpdwSize); QueryFullProcessImageNameFn *fn = (QueryFullProcessImageNameFn *)GetProcAddress(GetModuleHandle(L"kernel32"),"QueryFullProcessImageNameW"); if (fn) gotImageName = fn(process_handle, 0, szExePath, &pathsize); } #endif if (!gotImageName) gotImageName = GetModuleFileNameEx(process_handle, NULL, szExePath, pathsize); if (gotImageName) { // Convert the EXE path to its containing folder and append the // resulting folder to the symbol search path. wchar_t *p = wcsrchr(szExePath, '\\'); if (p != NULL) { *p = '\0'; sympath += std::wstring(L";") + szExePath; } } prefs.AdjustSymbolPath(sympath, download); for( int n=0;n<4;n++ ) { wenforce(dbgHelpMs.SymInitializeW(process_handle, L"", FALSE), "SymInitialize"); // Hook the debug output, so we actually can provide a clue as to // what's happening. dbgHelpMs.SymRegisterCallbackW64(process_handle, symCallback, NULL); // Add our PDB search paths. wenforce(dbgHelpMs.SymSetSearchPathW(process_handle, sympath.c_str()), "SymSetSearchPathW"); // Load symbol information for all modules. // Normally SymInitialize would do this, but we instead do it ourselves afterwards // so that we can hook the debug output for it. wenforce(dbgHelpMs.SymRefreshModuleList(process_handle), "SymRefreshModuleList"); wenforce(dbgHelpMs.SymEnumerateModulesW64(process_handle, EnumModules, this), "SymEnumerateModules64"); if (!modules.empty()) break; // Sometimes the module enumeration will fail (no error code, but no modules // will be returned). If we try again a little later it seems to work. // I suspect this may be if we try and enum modules too early on, before the process // has really had a chance to 'get going'. // Perhaps a better solution generally would be to manually load module symbols on demand, // as each sample comes in? That'd also solve the problem of modules getting loaded/unloaded // mid-profile. Yes, I'll probably do that some day. Sleep(100); dbgHelpMs.SymCleanup(process_handle); } DbgHelp *gcc = &dbgHelpGcc; #ifdef _WIN64 // We can't use the regular dbghelpw to profile 32-bit applications, // as it's got compiled-in things that assume 64-bit. So we instead have // a special Wow64 build, which is compiled as 64-bit code but using 32-bit // definitions. We load that instead. if (!is64BitProcess) gcc = &dbgHelpGccWow64; #endif // Now that we've loaded all the modules and debug info for the regular stuff, // we initialize the GCC dbghelp and let it have a go at the ones we couldn't do. wenforce(gcc->SymInitializeW(process_handle, NULL, FALSE), "SymInitialize"); gcc->SymSetDbgPrint(&symWineCallback); for (size_t n=0;n<modules.size();n++) { Module &mod = modules[n]; IMAGEHLP_MODULEW64 info; info.SizeOfStruct = sizeof(info); if (!dbgHelpMs.SymGetModuleInfoW64(process_handle, mod.base_addr, &info)) continue; // If we have a module with no symbol information from the MS dbghelp, // let the gcc one handle it instead. if (info.SymType == SymNone) { gcc->SymLoadModuleExW(process_handle, NULL, info.ImageName, info.ModuleName, info.BaseOfImage, info.ImageSize, NULL, 0); mod.dbghelp = gcc; } } if (g_symLog) g_symLog(L"\nFinished.\n"); sortModules(); }
bool Profiler::sampleTarget(SAMPLE_TYPE timeSpent, SymbolInfo *syminfo) { // DE: 20090325: Moved declaration of stack variables to reduce size of code inside Suspend/Resume thread CallStack stack; stack.depth = 0; STACKFRAME64 frame; PROFILER_ADDR ip, sp, bp; void *context; DWORD machine; #if defined(_WIN64) CONTEXT64 threadcontext64; CONTEXT32 threadcontext32; if (is64BitProcess) { context = &threadcontext64; threadcontext64.ContextFlags = CONTEXT64_FLAGS; machine = IMAGE_FILE_MACHINE_AMD64; // Can fail occasionally, for example if you have a debugger attached to the process. HRESULT result = SuspendThread(target_thread); if(result == 0xffffffff) return false; int prev_priority = GetThreadPriority(target_thread); SetThreadPriority(target_thread, THREAD_PRIORITY_TIME_CRITICAL); result = GetThreadContext(target_thread, &threadcontext64); SetThreadPriority(target_thread, prev_priority); if(!result){ // DE: 20090325: If GetThreadContext fails we must be sure to resume thread again ResumeThread(target_thread); return false; } ip = threadcontext64.Rip; sp = threadcontext64.Rsp; bp = threadcontext64.Rbp; } else { context = &threadcontext32; threadcontext32.ContextFlags = CONTEXT32_FLAGS; machine = IMAGE_FILE_MACHINE_I386; // Can fail occasionally, for example if you have a debugger attached to the process. HRESULT result = fn_Wow64SuspendThread(target_thread); if(result == 0xffffffff) return false; int prev_priority = GetThreadPriority(target_thread); SetThreadPriority(target_thread, THREAD_PRIORITY_TIME_CRITICAL); result = fn_Wow64GetThreadContext(target_thread, &threadcontext32); SetThreadPriority(target_thread, prev_priority); if(!result){ // DE: 20090325: If GetThreadContext fails we must be sure to resume thread again ResumeThread(target_thread); return false; } ip = threadcontext32.Eip; sp = threadcontext32.Esp; bp = threadcontext32.Ebp; } #else CONTEXT32 threadcontext32; context = &threadcontext32; threadcontext32.ContextFlags = CONTEXT32_FLAGS; machine = IMAGE_FILE_MACHINE_I386; // Can fail occasionally, for example if you have a debugger attached to the process. HRESULT result = SuspendThread(target_thread); if(result == 0xffffffff) return false; int prev_priority = GetThreadPriority(target_thread); SetThreadPriority(target_thread, THREAD_PRIORITY_TIME_CRITICAL); result = GetThreadContext(target_thread, &threadcontext32); SetThreadPriority(target_thread, prev_priority); if(!result){ // DE: 20090325: If GetThreadContext fails we must be sure to resume thread again ResumeThread(target_thread); return false; } applyHacks(target_process, threadcontext32); ip = threadcontext32.Eip; sp = threadcontext32.Esp; bp = threadcontext32.Ebp; #endif DbgHelp *prevDbgHelp = NULL; bool first = true; while(true) { // See which module this IP is in. Module *mod = syminfo->getModuleForAddr(ip); DbgHelp *dbgHelp = mod ? mod->dbghelp : &dbgHelpMs; // Use whichever dbghelp stack walker is best for this module type. // If we're switching between types, restart the stack walk from // the current place. if (dbgHelp != prevDbgHelp) { prevDbgHelp = dbgHelp; memset(&frame, 0, sizeof(frame)); frame.AddrStack.Offset = sp; frame.AddrPC.Offset = ip; frame.AddrFrame.Offset = bp; frame.AddrStack.Mode = frame.AddrPC.Mode = frame.AddrFrame.Mode = AddrModeFlat; frame.AddrReturn.Offset = ip; first = true; } // Add this IP to the stack trace. // We skip the first one, as the first call to StackWalk64 // simply fills in more registers for the current frame, // rather than walking down to the next one. if (!first) stack.addr[stack.depth++] = ip; first = false; BOOL result = dbgHelp->StackWalk64( machine, target_process, target_thread, &frame, context, NULL, dbgHelp->SymFunctionTableAccess64, dbgHelp->SymGetModuleBase64, NULL ); if (!result || stack.depth >= MAX_CALLSTACK_LEVELS) break; ip = (PROFILER_ADDR)frame.AddrPC.Offset; sp = (PROFILER_ADDR)frame.AddrStack.Offset; bp = (PROFILER_ADDR)frame.AddrFrame.Offset; // Stop once we hit the end of the stack. if (frame.AddrReturn.Offset == 0) { stack.addr[stack.depth++] = ip; break; } } if (!ResumeThread(target_thread)) throw ProfilerExcep(L"ResumeThread failed."); //NOTE: this has to go after ResumeThread. Otherwise mem allocation needed by std::map //may hit a lock held by the suspended thread. if (stack.depth > 0) { flatcounts[stack.addr[0]]+=timeSpent; callstacks[stack]+=timeSpent; } return true; }
// 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; }
// dump - Dumps a nicely formatted rendition of the CallStack, including // symbolic information (function names and line numbers) if available. // // 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. // void CallStack::dump(BOOL showInternalFrames, UINT start_frame) const { // The stack was dumped already if (m_resolved) { dumpResolved(); return; } 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"); } IMAGEHLP_LINE64 sourceInfo = { 0 }; sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64); // 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; CriticalSectionLocker<DbgHelp> locker(g_DbgHelp); // Iterate through each frame in the call stack. for (UINT32 frame = start_frame; 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, locker); 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 (!isFrameInternal && isPrevFrameInternal) Print(stack_line); isPrevFrameInternal = isFrameInternal; DWORD64 displacement64; BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_SIZE]; LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker); if (!foundline) displacement = (DWORD)displacement64; DWORD NumChars = resolveFunction(programCounter, foundline ? &sourceInfo : NULL, displacement, functionName, stack_line, _countof(stack_line)); UNREFERENCED_PARAMETER(NumChars); if (!isFrameInternal) Print(stack_line); } }