//////////////////////////////////////////////////////////////////////////////// // // Visual Leak Detector - CallStack Class Implementations // Copyright (c) 2005-2014 VLD Team // // 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. // //////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #define VLDBUILD #include "callstack.h" // This class' header. #include "utility.h" // Provides various utility functions. #include "vldheap.h" // Provides internal new and delete operators. #include "vldint.h" // Provides access to VLD internals. #include "cppformat\format.h" // Imported global variables. extern HANDLE g_currentProcess; extern HANDLE g_currentThread; extern CriticalSection g_heapMapLock; extern VisualLeakDetector g_vld; extern DbgHelp g_DbgHelp; // Helper function to compare the begin of a string with a substring // template bool beginWith(const LPCWSTR filename, size_t len, wchar_t const (&substr)[N]) { size_t count = N - 1; return ((len >= count) && wcsncmp(filename, substr, count) == 0); } // Helper function to compare the end of a string with a substring // template bool endWith(const LPCWSTR filename, size_t len, wchar_t const (&substr)[N]) { size_t count = N - 1; return ((len >= count) && wcsncmp(filename + len - count, substr, count) == 0); } // Constructor - Initializes the CallStack with an initial size of zero and one // Chunk of capacity. // CallStack::CallStack () { m_capacity = CALLSTACK_CHUNK_SIZE; m_size = 0; m_status = 0x0; m_store.next = NULL; m_topChunk = &m_store; m_topIndex = 0; m_resolved = NULL; m_resolvedCapacity = 0; m_resolvedLength = 0; } // Destructor - Frees all memory allocated to the CallStack. // CallStack::~CallStack () { CallStack::chunk_t *chunk = m_store.next; CallStack::chunk_t *temp; while (chunk) { temp = chunk; chunk = temp->next; delete temp; } delete [] m_resolved; m_resolved = NULL; m_resolvedCapacity = 0; m_resolvedLength = 0; } CallStack* CallStack::Create() { CallStack* result = NULL; if (g_vld.GetOptions() & VLD_OPT_SAFE_STACK_WALK) { result = new SafeCallStack(); } else { result = new FastCallStack(); } return result; } // operator == - Equality operator. Compares the CallStack to another CallStack // for equality. Two CallStacks are equal if they are the same size and if // every frame in each is identical to the corresponding frame in the other. // // other (IN) - Reference to the CallStack to compare the current CallStack // against for equality. // // Return Value: // // Returns true if the two CallStacks are equal. Otherwise returns false. // BOOL CallStack::operator == (const CallStack &other) const { if (m_size != other.m_size) { // They can't be equal if the sizes are different. return FALSE; } // Walk the chunk list and within each chunk walk the frames array until we // either find a mismatch, or until we reach the end of the call stacks. const CallStack::chunk_t *prevChunk = NULL; const CallStack::chunk_t *chunk = &m_store; const CallStack::chunk_t *otherChunk = &other.m_store; while (prevChunk != m_topChunk) { UINT32 size = (chunk == m_topChunk) ? m_topIndex : CALLSTACK_CHUNK_SIZE; for (UINT32 index = 0; index < size; index++) { if (chunk->frames[index] != otherChunk->frames[index]) { // Found a mismatch. They are not equal. return FALSE; } } prevChunk = chunk; chunk = chunk->next; otherChunk = otherChunk->next; } // Reached the end of the call stacks. They are equal. return TRUE; } // operator [] - Random access operator. Retrieves the frame at the specified // index. // // Note: We give up a bit of efficiency here, in favor of efficiency of push // operations. This is because walking of a CallStack is done infrequently // (only if a leak is found), whereas pushing is done very frequently (for // each frame in the program's call stack when the program allocates some // memory). // // - index (IN): Specifies the index of the frame to retrieve. // // Return Value: // // Returns the program counter for the frame at the specified index. If the // specified index is out of range for the CallStack, the return value is // undefined. // UINT_PTR CallStack::operator [] (UINT32 index) const { UINT32 chunknumber = index / CALLSTACK_CHUNK_SIZE; const CallStack::chunk_t *chunk = &m_store; for (UINT32 count = 0; count < chunknumber; count++) { chunk = chunk->next; } return chunk->frames[index % CALLSTACK_CHUNK_SIZE]; } // clear - Resets the CallStack, returning it to a state where no frames have // been pushed onto it, readying it for reuse. // // Note: Calling this function does not release any memory allocated to the // CallStack. We give up a bit of memory-usage efficiency here in favor of // performance of push operations. // // Return Value: // // None. // VOID CallStack::clear () { m_size = 0; m_topChunk = &m_store; m_topIndex = 0; if (m_resolved) { delete [] m_resolved; m_resolved = NULL; } m_resolvedCapacity = 0; m_resolvedLength = 0; } LPCWSTR CallStack::getFunctionName(SIZE_T programCounter, DWORD64& displacement64, SYMBOL_INFO* functionInfo, CriticalSectionLocker& 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; } DWORD CallStack::resolveFunction(SIZE_T programCounter, IMAGEHLP_LINEW64* sourceInfo, DWORD displacement, LPCWSTR functionName, LPWSTR stack_line, DWORD stackLineSize) const { WCHAR callingModuleName[260]; HMODULE hCallingModule = GetCallingModule(programCounter); LPWSTR moduleName = L"(Module name unavailable)"; if (hCallingModule && GetModuleFileName(hCallingModule, callingModuleName, _countof(callingModuleName)) > 0) { moduleName = wcsrchr(callingModuleName, L'\\'); if (moduleName == NULL) moduleName = wcsrchr(callingModuleName, L'/'); if (moduleName != NULL) moduleName++; else moduleName = callingModuleName; } fmt::WArrayWriter w(stack_line, stackLineSize); // Display the current stack frame's information. if (sourceInfo) { if (displacement == 0) { w.write(L" {} ({}): {}!{}()\n", sourceInfo->FileName, sourceInfo->LineNumber, moduleName, functionName); } else { w.write(L" {} ({}): {}!{}() + 0x{:X} bytes\n", sourceInfo->FileName, sourceInfo->LineNumber, moduleName, functionName, displacement); } } else { if (displacement == 0) { w.write(L" {}!{}()\n", moduleName, functionName); } else { w.write(L" {}!{}() + 0x{:X} bytes\n", moduleName, functionName, displacement); } } DWORD NumChars = (DWORD)w.size(); stack_line[NumChars] = '\0'; return NumChars; } // 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 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]; DWORD64 displacement64; LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker); m_status |= isCrtStartupFunction(functionName); if (m_status & CALLSTACK_STATUS_STARTUPCRT) { return true; } else if (m_status & CALLSTACK_STATUS_NOTSTARTUPCRT) { return false; } } m_status |= CALLSTACK_STATUS_NOTSTARTUPCRT; return false; } // 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) { if (!m_resolved) { resolve(showInternalFrames); } // The stack was reoslved already if (m_resolved) { return Print(m_resolved); } } // 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. // // 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; DWORD NumChars = 0; CriticalSectionLocker 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]; if (GetCallingModule(programCounter) == g_vld.m_vldBase) continue; DWORD64 displacement64; BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_SIZE]; LPCWSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer, locker); if (skipStartupLeaks) { if (!(m_status & (CALLSTACK_STATUS_STARTUPCRT | CALLSTACK_STATUS_NOTSTARTUPCRT))) { m_status |= isCrtStartupFunction(functionName); } if (m_status & CALLSTACK_STATUS_STARTUPCRT) { delete[] m_resolved; m_resolved = NULL; m_resolvedCapacity = 0; m_resolvedLength = 0; return 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. DWORD displacement = 0; DbgTrace(L"dbghelp32.dll %i: SymGetLineFromAddrW64\n", GetCurrentThreadId()); BOOL 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 (NumChars > 0 && !isFrameInternal && isPrevFrameInternal) { m_resolvedLength += NumChars; if (m_resolved) { wcsncat_s(m_resolved, m_resolvedCapacity, stack_line, NumChars); } } isPrevFrameInternal = isFrameInternal; 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; } const WCHAR* CallStack::getResolvedCallstack( BOOL showinternalframes ) { resolve(showinternalframes); return m_resolved; } // push_back - Pushes a frame's program counter onto the CallStack. Pushes are // always appended to the back of the chunk list (aka the "top" chunk). // // Note: This function will allocate additional memory as necessary to make // room for new program counter addresses. // // - programcounter (IN): The program counter address of the frame to be pushed // onto the CallStack. // // Return Value: // // None. // VOID CallStack::push_back (const UINT_PTR programcounter) { if (m_size == m_capacity) { // At current capacity. Allocate additional storage. CallStack::chunk_t *chunk = new CallStack::chunk_t; chunk->next = NULL; m_topChunk->next = chunk; m_topChunk = chunk; m_topIndex = 0; m_capacity += CALLSTACK_CHUNK_SIZE; } else if (m_topIndex >= CALLSTACK_CHUNK_SIZE) { // There is more capacity, but not in this chunk. Go to the next chunk. // Note that this only happens if this CallStack has previously been // cleared (clearing resets the data, but doesn't give up any allocated // space). m_topChunk = m_topChunk->next; m_topIndex = 0; } m_topChunk->frames[m_topIndex++] = programcounter; m_size++; } 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 '") // VS2008 Release || (wcscmp(functionName, L"std::locale::facet::facet_Register") == 0) // VS2010 Release || (wcscmp(functionName, L"std::locale::facet::_Facet_Register") == 0) // VS2012 Release || beginWith(functionName, len, L"std::locale::_Init()") || beginWith(functionName, len, L"std::basic_streambuf<") // 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") // VS2015 Release || (wcscmp(functionName, L"setlocale") == 0) || (wcscmp(functionName, L"_wsetlocale") == 0) || (wcscmp(functionName, L"_Getctype") == 0) || (wcscmp(functionName, L"std::_Facet_Register") == 0) || endWith(functionName, len, L">::_Getcat") ) { 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); } // 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 very efficient method to walk the stack from // frame to frame, so it is quite fast. However, unconventional stack frames // (such as those created when frame pointer omission optimization is used) // will not be successfully walked by this function and will cause the // stack trace to terminate prematurely. // // - 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 FastCallStack::getStackTrace (UINT32 maxdepth, const context_t& context) { UINT32 count = 0; UINT_PTR function = context.func; if (function != NULL) { count++; push_back(function); } /*#if defined(_M_IX86) UINT_PTR* framePointer = (UINT_PTR*)context.BPREG; while (count < maxdepth) { if (*framePointer < (UINT_PTR)framePointer) { if (*framePointer == NULL) { // Looks like we reached the end of the stack. break; } else { // Invalid frame pointer. Frame pointer addresses should always // increase as we move up the stack. m_status |= CALLSTACK_STATUS_INCOMPLETE; break; } } if (*framePointer & (sizeof(UINT_PTR*) - 1)) { // Invalid frame pointer. Frame pointer addresses should always // be aligned to the size of a pointer. This probably means that // we've encountered a frame that was created by a module built with // frame pointer omission (FPO) optimization turned on. m_status |= CALLSTACK_STATUS_INCOMPLETE; break; } if (IsBadReadPtr((UINT*)*framePointer, sizeof(UINT_PTR*))) { // Bogus frame pointer. Again, this probably means that we've // encountered a frame built with FPO optimization. m_status |= CALLSTACK_STATUS_INCOMPLETE; break; } count++; push_back(*(framePointer + 1)); framePointer = (UINT_PTR*)*framePointer; } #elif defined(_M_X64)*/ UINT32 maxframes = min(62, maxdepth + 10); UINT_PTR* myFrames = new UINT_PTR[maxframes]; ZeroMemory(myFrames, sizeof(UINT_PTR) * maxframes); ULONG BackTraceHash; maxframes = RtlCaptureStackBackTrace(0, maxframes, reinterpret_cast(myFrames), &BackTraceHash); m_hashValue = BackTraceHash; UINT32 startIndex = 0; while (count < maxframes) { if (myFrames[count] == 0) break; if (myFrames[count] == context.fp) startIndex = count; count++; } count = startIndex; while (count < maxframes) { if (myFrames[count] == 0) break; push_back(myFrames[count]); count++; } delete [] myFrames; //#endif } // 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); } if (context.IPREG == NULL) { return; } count++; push_back(context.IPREG); 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 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); } } // getHashValue - Generate callstack hash value. // // Return Value: // // None. // DWORD SafeCallStack::getHashValue() const { DWORD hashcode = 0xD202EF8D; // Iterate through each frame in the call stack. for (UINT32 frame = 0; frame < m_size; frame++) { UINT_PTR programcounter = (*this)[frame]; hashcode = CalculateCRC32(programcounter, hashcode); } return hashcode; }