/////////////////////////////////////////////////////////////////////////////// // // StackWalk.cpp // // Author: Oleg Starodumov // // NOTE: THIS FILE IS "WORK IN PROGRESS", NOT YET READY // // /////////////////////////////////////////////////////////////////////////////// // // Description: <OS-TODO> // /////////////////////////////////////////////////////////////////////////////// // Include files // #include <windows.h> #include <tchar.h> #include <dbghelp.h> #include <stdio.h> #include <vector> /////////////////////////////////////////////////////////////////////////////// // Directives // #pragma comment( lib, "dbghelp.lib" ) /////////////////////////////////////////////////////////////////////////////// // CStackWalk class declaration // class CStackWalk { public: // Helper types struct CStackFrame { DWORD64 Ip; // Instruction pointer (EIP on x86) DWORD64 RetAddr; // Return address DWORD64 Bp; // Stack base pointer (EBP on x86) CStackFrame( DWORD64 _Ip, DWORD64 _RetAddr, DWORD64 _Bp ) : Ip( _Ip ), RetAddr( _RetAddr ), Bp( _Bp ) {} }; typedef std::vector<CStackFrame> FrameColl_t; public: // Constructors / destructor CStackWalk( HANDLE hProcess = GetCurrentProcess(), HANDLE hThread = GetCurrentThread() ); ~CStackWalk(); // Operations // Walk the stack bool Walk( CONTEXT* pContext = 0 ); // Accessors // Call stack FrameColl_t CallStack() const { return m_CallStack; } // Last error code DWORD LastError() const { return m_LastError; } // GetModuleBase function void SetModuleBaseFunc( PGET_MODULE_BASE_ROUTINE64 pFunc ) { m_pfnGetModBase = pFunc; } private: // Copy protection CStackWalk( const CStackWalk& ); CStackWalk& operator=( const CStackWalk& ); private: // Data members // Process handle HANDLE m_hProcess; // Thread handle HANDLE m_hThread; // Call stack FrameColl_t m_CallStack; // Last error code DWORD m_LastError; // User-supplied GetModuleBase function (optional) PGET_MODULE_BASE_ROUTINE64 m_pfnGetModBase; }; /////////////////////////////////////////////////////////////////////////////// // CStackWalk class implementation // // Bring in _ReturnAddress intrinsic #ifdef __cplusplus extern "C" #endif void* _ReturnAddress(void); #pragma intrinsic(_ReturnAddress) // GetCallerAddress() helper function static void* GetCallerAddress(); #pragma optimize ( "", off ) void* GetCallerAddress() { return _ReturnAddress(); } #pragma optimize ( "", on ) // Constructor CStackWalk::CStackWalk( HANDLE hProcess, HANDLE hThread ) : m_hProcess( hProcess ), m_hThread( hThread ), m_LastError( 0 ), m_pfnGetModBase( 0 ) { } // Destructor CStackWalk::~CStackWalk() { // no actions } // Stack walker function // Turn off optimizations to make sure that frame pointer is present #pragma optimize ( "", off ) bool CStackWalk::Walk( CONTEXT* pContext ) { // Obtain an address in the address range of this function // _after_ its stack frame has been constructed. // (We cannot just use the function's address, because it is before // the stack frame construction) DWORD64 MyAddress = (DWORD64)GetCallerAddress(); // Cleanup m_LastError = 0; m_CallStack.clear(); SetLastError( 0 ); // Collect the data needed by StackWalk64 // Machine type DWORD MachineType = 0; // Stack frame STACKFRAME64 StackFrame; memset( &StackFrame, 0, sizeof(StackFrame) ); // Architecture-specific initialization #ifdef _M_IX86 // Machine type MachineType = IMAGE_FILE_MACHINE_I386; // STACKFRAME64 structure if( pContext != 0 ) { StackFrame.AddrPC.Offset = pContext->Eip; StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrStack.Offset = pContext->Esp; StackFrame.AddrStack.Mode = AddrModeFlat; StackFrame.AddrFrame.Offset = pContext->Ebp; StackFrame.AddrFrame.Mode = AddrModeFlat; } else { // Initialize the stack frame structure so that StackWalk64 // attempts to walk the stack above the current function only, // excluding this function (Walk) itself. // // This is to avoid modifying the stack frame of the current function // between subsequent calls to StackWalk64, which IMO can affect // the possibility to walk the stack successfully. // unsigned long StackPtr; unsigned long BasePtr; __asm mov [StackPtr], esp __asm mov [BasePtr], ebp StackFrame.AddrPC.Offset = MyAddress; StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrStack.Offset = StackPtr; StackFrame.AddrStack.Mode = AddrModeFlat; StackFrame.AddrFrame.Offset = BasePtr; StackFrame.AddrFrame.Mode = AddrModeFlat; } #else #error This architecture is not supported. #endif //_M_IX86 // GetModuleBase function PGET_MODULE_BASE_ROUTINE64 pfnGetModBase = m_pfnGetModBase; if( pfnGetModBase == 0 ) pfnGetModBase = SymGetModuleBase64; // If we obtained the context ourselves, we have to skip // the first frame (the current function - Walk) bool bSkipFirst = ( pContext == 0 ); // Walk the stack while( 1 ) { // Reset last error code SetLastError( 0 ); // Call StackWalk64 if( !StackWalk64( MachineType, // Machine architecture type m_hProcess, // Process handle m_hThread, // Thread handle &StackFrame, // Stack frame 0, // Thread context (not needed for x86) 0, // Read memory function - not used SymFunctionTableAccess64, // Function table access function (FPO access on x86) pfnGetModBase, //SymGetModuleBase64, // Function that can determine module base for the given address 0 // Address translation function - not user ) ) { // StackWalk64 failed m_LastError = GetLastError(); break; } // Check the stack frame if( StackFrame.AddrFrame.Offset == 0 ) { // Invalid frame break; } bool bSaveFrame = true; if( StackFrame.AddrPC.Offset == 0 ) { // Do not save it bSaveFrame = false; } if( StackFrame.AddrPC.Offset == StackFrame.AddrReturn.Offset ) { // Do not save it bSaveFrame = false; } if( bSkipFirst ) { // Do not save it bSaveFrame = false; bSkipFirst = false; } // Save the stack frame if( bSaveFrame ) { CStackFrame NewFrame( StackFrame.AddrPC.Offset, StackFrame.AddrReturn.Offset, StackFrame.AddrFrame.Offset ); m_CallStack.push_back( NewFrame ); } // Proceed to the next frame } // Complete return ( m_CallStack.size() > 0 ); } #pragma optimize ( "", on ) /////////////////////////////////////////////////////////////////////////////// // main // int _tmain( int argc, const TCHAR* argv[] ) { BOOL bRet = FALSE; // Set options DWORD Options = SymGetOptions(); // SYMOPT_DEBUG option asks DbgHelp to print additional troubleshooting // messages to debug output - use the debugger's Debug Output window // to view the messages Options |= SYMOPT_DEBUG; ::SymSetOptions( Options ); // Initialize DbgHelp and load symbols for all modules of the current process bRet = ::SymInitialize ( GetCurrentProcess(), // Process handle of the current process NULL, // No user-defined search path -> use default TRUE // Load symbols for all modules in the current process ); if( !bRet ) { _tprintf( _T("Error: SymInitialize() failed. Error code: %u "), ::GetLastError() ); return 0; } // Obtain the call stack { CStackWalk StackWalk; if( StackWalk.Walk() ) { // Display the call stack CStackWalk::FrameColl_t Frames( StackWalk.CallStack() ); for( int i = 0; i < Frames.size(); i++ ) { _tprintf( _T("BP: %08I64x RetAddr: %08I64x IP: %08I64x "), Frames[i].Bp, Frames[i].RetAddr, Frames[i].Ip ); } } else { _tprintf( _T("Stack walk failed. Error: %u "), StackWalk.LastError() ); } } // Deinitialize DbgHelp bRet = ::SymCleanup( GetCurrentProcess() ); if( !bRet ) { _tprintf(_T("Error: SymCleanup() failed. Error code: %u "), ::GetLastError()); return 0; } // Complete return 0; }
http://www.debuginfo.com/examples/src/StackWalk.cpp
https://dxr.mozilla.org/mozilla-beta/source/mozglue/misc/StackWalk.cpp
https://dxr.mozilla.org/mozilla-central/source/mozglue/misc/StackWalk.h
/* -*- Mode: C++; tab- 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* API for getting a stack trace of the C/C++ stack on the current thread */ #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/StackWalk.h" #include <string.h> using namespace mozilla; // The presence of this address is the stack must stop the stack walk. If // there is no such address, the structure will be {nullptr, true}. struct CriticalAddress { void* mAddr; bool mInit; }; static CriticalAddress gCriticalAddress; // for _Unwind_Backtrace from libcxxrt or libunwind // cxxabi.h from libcxxrt implicitly includes unwind.h first #if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #if defined(HAVE_DLOPEN) || defined(XP_DARWIN) #include <dlfcn.h> #endif #if (defined(XP_DARWIN) && (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE))) #define MOZ_STACKWALK_SUPPORTS_MACOSX 1 #else #define MOZ_STACKWALK_SUPPORTS_MACOSX 0 #endif #if (defined(linux) && ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || defined(HAVE__UNWIND_BACKTRACE))) #define MOZ_STACKWALK_SUPPORTS_LINUX 1 #else #define MOZ_STACKWALK_SUPPORTS_LINUX 0 #endif #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) #define HAVE___LIBC_STACK_END 1 #else #define HAVE___LIBC_STACK_END 0 #endif #if HAVE___LIBC_STACK_END extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so #endif #ifdef ANDROID #include <algorithm> #include <unistd.h> #include <pthread.h> #endif #if MOZ_STACKWALK_SUPPORTS_MACOSX #include <pthread.h> #include <sys/errno.h> #ifdef MOZ_WIDGET_COCOA #include <CoreServices/CoreServices.h> #endif typedef void malloc_logger_t(uint32_t aType, uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3, uintptr_t aResult, uint32_t aNumHotFramesToSkip); extern malloc_logger_t* malloc_logger; static void stack_callback(uint32_t aFrameNumber, void* aPc, void* aSp, void* aClosure) { const char* name = static_cast<char*>(aClosure); Dl_info info; // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The // correct one is the first that we find on our way up, so the // following check for gCriticalAddress.mAddr is critical. if (gCriticalAddress.mAddr || dladdr(aPc, &info) == 0 || !info.dli_sname || strcmp(info.dli_sname, name) != 0) { return; } gCriticalAddress.mAddr = aPc; } static void my_malloc_logger(uint32_t aType, uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3, uintptr_t aResult, uint32_t aNumHotFramesToSkip) { static bool once = false; if (once) { return; } once = true; // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The // stack shows up as having two pthread_cond_wait$UNIX2003 frames. const char* name = "new_sem_from_pool"; MozStackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0, const_cast<char*>(name), 0, nullptr); } // This is called from NS_LogInit() and from the stack walking functions, but // only the first call has any effect. We need to call this function from both // places because it must run before any mutexes are created, and also before // any objects whose refcounts we're logging are created. Running this // function during NS_LogInit() ensures that we meet the first criterion, and // running this function during the stack walking functions ensures we meet the // second criterion. MFBT_API void StackWalkInitCriticalAddress() { if (gCriticalAddress.mInit) { return; } gCriticalAddress.mInit = true; // We must not do work when 'new_sem_from_pool' calls realloc, since // it holds a non-reentrant spin-lock and we will quickly deadlock. // new_sem_from_pool is not directly accessible using dlsym, so // we force a situation where new_sem_from_pool is on the stack and // use dladdr to check the addresses. // malloc_logger can be set by external tools like 'Instruments' or 'leaks' malloc_logger_t* old_malloc_logger = malloc_logger; malloc_logger = my_malloc_logger; pthread_cond_t cond; int r = pthread_cond_init(&cond, 0); MOZ_ASSERT(r == 0); pthread_mutex_t mutex; r = pthread_mutex_init(&mutex, 0); MOZ_ASSERT(r == 0); r = pthread_mutex_lock(&mutex); MOZ_ASSERT(r == 0); struct timespec abstime = { 0, 1 }; r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime); // restore the previous malloc logger malloc_logger = old_malloc_logger; MOZ_ASSERT(r == ETIMEDOUT); r = pthread_mutex_unlock(&mutex); MOZ_ASSERT(r == 0); r = pthread_mutex_destroy(&mutex); MOZ_ASSERT(r == 0); r = pthread_cond_destroy(&cond); MOZ_ASSERT(r == 0); } static bool IsCriticalAddress(void* aPC) { return gCriticalAddress.mAddr == aPC; } #else static bool IsCriticalAddress(void* aPC) { return false; } // We still initialize gCriticalAddress.mInit so that this code behaves // the same on all platforms. Otherwise a failure to init would be visible // only on OS X. MFBT_API void StackWalkInitCriticalAddress() { gCriticalAddress.mInit = true; } #endif #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code #include <windows.h> #include <process.h> #include <stdio.h> #include <malloc.h> #include "mozilla/ArrayUtils.h" #include <imagehlp.h> // We need a way to know if we are building for WXP (or later), as if we are, we // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill. // A value of 9 indicates we want to use the new APIs. #if API_VERSION_NUMBER < 9 #error Too old imagehlp.h #endif struct WalkStackData { // Are we walking the stack of the calling thread? Note that we need to avoid // calling fprintf and friends if this is false, in order to avoid deadlocks. bool walkCallingThread; uint32_t skipFrames; HANDLE thread; HANDLE process; HANDLE eventStart; HANDLE eventEnd; void** pcs; uint32_t pc_size; uint32_t pc_count; uint32_t pc_max; void** sps; uint32_t sp_size; uint32_t sp_count; void* platformData; }; DWORD gStackWalkThread; CRITICAL_SECTION gDbgHelpCS; // Routine to print an error message to standard error. static void PrintError(const char* aPrefix) { LPSTR lpMsgBuf; DWORD lastErr = GetLastError(); FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, lastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPSTR)&lpMsgBuf, 0, nullptr ); fprintf(stderr, "### ERROR: %s: %s", aPrefix, lpMsgBuf ? lpMsgBuf : "(null) "); fflush(stderr); LocalFree(lpMsgBuf); } static unsigned int WINAPI WalkStackThread(void* aData); static bool EnsureWalkThreadReady() { static bool walkThreadReady = false; static HANDLE stackWalkThread = nullptr; static HANDLE readyEvent = nullptr; if (walkThreadReady) { return walkThreadReady; } if (!stackWalkThread) { readyEvent = ::CreateEvent(nullptr, FALSE /* auto-reset*/, FALSE /* initially non-signaled */, nullptr); if (!readyEvent) { PrintError("CreateEvent"); return false; } unsigned int threadID; stackWalkThread = (HANDLE)_beginthreadex(nullptr, 0, WalkStackThread, (void*)readyEvent, 0, &threadID); if (!stackWalkThread) { PrintError("CreateThread"); ::CloseHandle(readyEvent); readyEvent = nullptr; return false; } gStackWalkThread = threadID; ::CloseHandle(stackWalkThread); } MOZ_ASSERT((stackWalkThread && readyEvent) || (!stackWalkThread && !readyEvent)); // The thread was created. Try to wait an arbitrary amount of time (1 second // should be enough) for its event loop to start before posting events to it. DWORD waitRet = ::WaitForSingleObject(readyEvent, 1000); if (waitRet == WAIT_TIMEOUT) { // We get a timeout if we're called during static initialization because // the thread will only start executing after we return so it couldn't // have signalled the event. If that is the case, give up for now and // try again next time we're called. return false; } ::CloseHandle(readyEvent); stackWalkThread = nullptr; readyEvent = nullptr; ::InitializeCriticalSection(&gDbgHelpCS); return walkThreadReady = true; } static void WalkStackMain64(struct WalkStackData* aData) { // Get a context for the specified thread. CONTEXT context; if (!aData->platformData) { memset(&context, 0, sizeof(CONTEXT)); context.ContextFlags = CONTEXT_FULL; if (!GetThreadContext(aData->thread, &context)) { if (aData->walkCallingThread) { PrintError("GetThreadContext"); } return; } } else { context = *static_cast<CONTEXT*>(aData->platformData); } #if defined(_M_IX86) || defined(_M_IA64) // Setup initial stack frame to walk from. STACKFRAME64 frame64; memset(&frame64, 0, sizeof(frame64)); #ifdef _M_IX86 frame64.AddrPC.Offset = context.Eip; frame64.AddrStack.Offset = context.Esp; frame64.AddrFrame.Offset = context.Ebp; #elif defined _M_IA64 frame64.AddrPC.Offset = context.StIIP; frame64.AddrStack.Offset = context.SP; frame64.AddrFrame.Offset = context.RsBSP; #endif frame64.AddrPC.Mode = AddrModeFlat; frame64.AddrStack.Mode = AddrModeFlat; frame64.AddrFrame.Mode = AddrModeFlat; frame64.AddrReturn.Mode = AddrModeFlat; #endif // Skip our own stack walking frames. int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames; // Now walk the stack. while (true) { DWORD64 addr; DWORD64 spaddr; #if defined(_M_IX86) || defined(_M_IA64) // 32-bit frame unwinding. // Debug routines are not threadsafe, so grab the lock. EnterCriticalSection(&gDbgHelpCS); BOOL ok = StackWalk64( #if defined _M_IA64 IMAGE_FILE_MACHINE_IA64, #elif defined _M_IX86 IMAGE_FILE_MACHINE_I386, #endif aData->process, aData->thread, &frame64, &context, nullptr, SymFunctionTableAccess64, // function table access routine SymGetModuleBase64, // module base routine 0 ); LeaveCriticalSection(&gDbgHelpCS); if (ok) { addr = frame64.AddrPC.Offset; spaddr = frame64.AddrStack.Offset; } else { addr = 0; spaddr = 0; if (aData->walkCallingThread) { PrintError("WalkStack64"); } } if (!ok) { break; } #elif defined(_M_AMD64) // 64-bit frame unwinding. // Try to look up unwind metadata for the current function. ULONG64 imageBase; PRUNTIME_FUNCTION runtimeFunction = RtlLookupFunctionEntry(context.Rip, &imageBase, NULL); if (!runtimeFunction) { // Alas, this is probably a JIT frame, for which we don't generate unwind // info and so we have to give up. break; } PVOID dummyHandlerData; ULONG64 dummyEstablisherFrame; RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, context.Rip, runtimeFunction, &context, &dummyHandlerData, &dummyEstablisherFrame, nullptr); addr = context.Rip; spaddr = context.Rsp; #else #error "unknown platform" #endif if (addr == 0) { break; } if (skip-- > 0) { continue; } if (aData->pc_count < aData->pc_size) { aData->pcs[aData->pc_count] = (void*)addr; } ++aData->pc_count; if (aData->sp_count < aData->sp_size) { aData->sps[aData->sp_count] = (void*)spaddr; } ++aData->sp_count; if (aData->pc_max != 0 && aData->pc_count == aData->pc_max) { break; } #if defined(_M_IX86) || defined(_M_IA64) if (frame64.AddrReturn.Offset == 0) { break; } #endif } } static unsigned int WINAPI WalkStackThread(void* aData) { BOOL msgRet; MSG msg; // Call PeekMessage to force creation of a message queue so that // other threads can safely post events to us. ::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE); // and tell the thread that created us that we're ready. HANDLE readyEvent = (HANDLE)aData; ::SetEvent(readyEvent); while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) { if (msgRet == -1) { PrintError("GetMessage"); } else { DWORD ret; struct WalkStackData* data = (WalkStackData*)msg.lParam; if (!data) { continue; } // Don't suspend the calling thread until it's waiting for // us; otherwise the number of frames on the stack could vary. ret = ::WaitForSingleObject(data->eventStart, INFINITE); if (ret != WAIT_OBJECT_0) { PrintError("WaitForSingleObject"); } // Suspend the calling thread, dump his stack, and then resume him. // He's currently waiting for us to finish so now should be a good time. ret = ::SuspendThread(data->thread); if (ret == -1) { PrintError("ThreadSuspend"); } else { WalkStackMain64(data); ret = ::ResumeThread(data->thread); if (ret == -1) { PrintError("ThreadResume"); } } ::SetEvent(data->eventEnd); } } return 0; } /** * Walk the stack, translating PC's found into strings and recording the * chain in aBuffer. For this to work properly, the DLLs must be rebased * so that the address in the file agrees with the address in memory. * Otherwise StackWalk will return FALSE when it hits a frame in a DLL * whose in memory address doesn't match its in-file address. */ MFBT_API bool MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames, uint32_t aMaxFrames, void* aClosure, uintptr_t aThread, void* aPlatformData) { StackWalkInitCriticalAddress(); static HANDLE myProcess = nullptr; HANDLE myThread; DWORD walkerReturn; struct WalkStackData data; if (!EnsureWalkThreadReady()) { return false; } HANDLE currentThread = ::GetCurrentThread(); HANDLE targetThread = aThread ? reinterpret_cast<HANDLE>(aThread) : currentThread; data.walkCallingThread = (targetThread == currentThread); // Have to duplicate handle to get a real handle. if (!myProcess) { if (!::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentProcess(), ::GetCurrentProcess(), &myProcess, PROCESS_ALL_ACCESS, FALSE, 0)) { if (data.walkCallingThread) { PrintError("DuplicateHandle (process)"); } return false; } } if (!::DuplicateHandle(::GetCurrentProcess(), targetThread, ::GetCurrentProcess(), &myThread, THREAD_ALL_ACCESS, FALSE, 0)) { if (data.walkCallingThread) { PrintError("DuplicateHandle (thread)"); } return false; } data.skipFrames = aSkipFrames; data.thread = myThread; data.process = myProcess; void* local_pcs[1024]; data.pcs = local_pcs; data.pc_count = 0; data.pc_size = ArrayLength(local_pcs); data.pc_max = aMaxFrames; void* local_sps[1024]; data.sps = local_sps; data.sp_count = 0; data.sp_size = ArrayLength(local_sps); data.platformData = aPlatformData; if (aThread) { // If we're walking the stack of another thread, we don't need to // use a separate walker thread. WalkStackMain64(&data); if (data.pc_count > data.pc_size) { data.pcs = (void**)_alloca(data.pc_count * sizeof(void*)); data.pc_size = data.pc_count; data.pc_count = 0; data.sps = (void**)_alloca(data.sp_count * sizeof(void*)); data.sp_size = data.sp_count; data.sp_count = 0; WalkStackMain64(&data); } } else { data.eventStart = ::CreateEvent(nullptr, FALSE /* auto-reset*/, FALSE /* initially non-signaled */, nullptr); data.eventEnd = ::CreateEvent(nullptr, FALSE /* auto-reset*/, FALSE /* initially non-signaled */, nullptr); ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data); walkerReturn = ::SignalObjectAndWait(data.eventStart, data.eventEnd, INFINITE, FALSE); if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) { PrintError("SignalObjectAndWait (1)"); } if (data.pc_count > data.pc_size) { data.pcs = (void**)_alloca(data.pc_count * sizeof(void*)); data.pc_size = data.pc_count; data.pc_count = 0; data.sps = (void**)_alloca(data.sp_count * sizeof(void*)); data.sp_size = data.sp_count; data.sp_count = 0; ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data); walkerReturn = ::SignalObjectAndWait(data.eventStart, data.eventEnd, INFINITE, FALSE); if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) { PrintError("SignalObjectAndWait (2)"); } } ::CloseHandle(data.eventStart); ::CloseHandle(data.eventEnd); } ::CloseHandle(myThread); for (uint32_t i = 0; i < data.pc_count; ++i) { (*aCallback)(i + 1, data.pcs[i], data.sps[i], aClosure); } return data.pc_count != 0; } static BOOL CALLBACK callbackEspecial64( PCSTR aModuleName, DWORD64 aModuleBase, ULONG aModuleSize, PVOID aUserContext) { BOOL retval = TRUE; DWORD64 addr = *(DWORD64*)aUserContext; /* * You'll want to control this if we are running on an * architecture where the addresses go the other direction. * Not sure this is even a realistic consideration. */ const BOOL addressIncreases = TRUE; /* * If it falls in side the known range, load the symbols. */ if (addressIncreases ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize)) : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize)) ) { retval = !!SymLoadModule64(GetCurrentProcess(), nullptr, (PSTR)aModuleName, nullptr, aModuleBase, aModuleSize); if (!retval) { PrintError("SymLoadModule64"); } } return retval; } /* * SymGetModuleInfoEspecial * * Attempt to determine the module information. * Bug 112196 says this DLL may not have been loaded at the time * SymInitialize was called, and thus the module information * and symbol information is not available. * This code rectifies that problem. */ // New members were added to IMAGEHLP_MODULE64 (that show up in the // Platform SDK that ships with VC8, but not the Platform SDK that ships // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to // use them, and it's useful to be able to function correctly with the // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll // version 5.1.) Since Platform SDK version need not correspond to // compiler version, and the version number in debughlp.h was NOT bumped // when these changes were made, ifdef based on a constant that was // added between these versions. #ifdef SSRVOPT_SETCONTEXT #define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64)) #else #define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64) #endif BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr, PIMAGEHLP_MODULE64 aModuleInfo, PIMAGEHLP_LINE64 aLineInfo) { BOOL retval = FALSE; /* * Init the vars if we have em. */ aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE; if (aLineInfo) { aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64); } /* * Give it a go. * It may already be loaded. */ retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo); if (retval == FALSE) { /* * Not loaded, here's the magic. * Go through all the modules. */ // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the // constness of the first parameter of // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from // non-const to const over time). See bug 391848 and bug // 415426. BOOL enumRes = EnumerateLoadedModules64( aProcess, (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64, (PVOID)&aAddr); if (enumRes != FALSE) { /* * One final go. * If it fails, then well, we have other problems. */ retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo); } } /* * If we got module info, we may attempt line info as well. * We will not report failure if this does not work. */ if (retval != FALSE && aLineInfo) { DWORD displacement = 0; BOOL lineRes = FALSE; lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo); if (!lineRes) { // Clear out aLineInfo to indicate that it's not valid memset(aLineInfo, 0, sizeof(*aLineInfo)); } } return retval; } static bool EnsureSymInitialized() { static bool gInitialized = false; bool retStat; if (gInitialized) { return gInitialized; } if (!EnsureWalkThreadReady()) { return false; } SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE); if (!retStat) { PrintError("SymInitialize"); } gInitialized = retStat; /* XXX At some point we need to arrange to call SymCleanup */ return retStat; } MFBT_API bool MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails) { aDetails->library[0] = '