zoukankan      html  css  js  c++  java
  • 《memory leak: stackwalk》

    ///////////////////////////////////////////////////////////////////////////////
    //
    // 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; 
    }
    View Code

    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] = '';
      aDetails->loffset = 0;
      aDetails->filename[0] = '';
      aDetails->lineno = 0;
      aDetails->function[0] = '';
      aDetails->foffset = 0;
    
      if (!EnsureSymInitialized()) {
        return false;
      }
    
      HANDLE myProcess = ::GetCurrentProcess();
      BOOL ok;
    
      // debug routines are not threadsafe, so grab the lock.
      EnterCriticalSection(&gDbgHelpCS);
    
      //
      // Attempt to load module info before we attempt to resolve the symbol.
      // This just makes sure we get good info if available.
      //
    
      DWORD64 addr = (DWORD64)aPC;
      IMAGEHLP_MODULE64 modInfo;
      IMAGEHLP_LINE64 lineInfo;
      BOOL modInfoRes;
      modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo);
    
      if (modInfoRes) {
        strncpy(aDetails->library, modInfo.LoadedImageName,
                    sizeof(aDetails->library));
        aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '';
        aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage;
    
        if (lineInfo.FileName) {
          strncpy(aDetails->filename, lineInfo.FileName,
                      sizeof(aDetails->filename));
          aDetails->filename[mozilla::ArrayLength(aDetails->filename) - 1] = '';
          aDetails->lineno = lineInfo.LineNumber;
        }
      }
    
      ULONG64 buffer[(sizeof(SYMBOL_INFO) +
        MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
      PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
      pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
      pSymbol->MaxNameLen = MAX_SYM_NAME;
    
      DWORD64 displacement;
      ok = SymFromAddr(myProcess, addr, &displacement, pSymbol);
    
      if (ok) {
        strncpy(aDetails->function, pSymbol->Name,
                    sizeof(aDetails->function));
        aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '';
        aDetails->foffset = static_cast<ptrdiff_t>(displacement);
      }
    
      LeaveCriticalSection(&gDbgHelpCS); // release our lock
      return true;
    }
    
    // i386 or PPC Linux stackwalking code
    #elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || MOZ_STACKWALK_SUPPORTS_MACOSX)
    
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
    // if __USE_GNU is defined.  I suppose its some kind of standards
    // adherence thing.
    //
    #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
    #define __USE_GNU
    #endif
    
    // This thing is exported by libstdc++
    // Yes, this is a gcc only hack
    #if defined(MOZ_DEMANGLE_SYMBOLS)
    #include <cxxabi.h>
    #endif // MOZ_DEMANGLE_SYMBOLS
    
    void DemangleSymbol(const char* aSymbol,
                        char* aBuffer,
                        int aBufLen)
    {
      aBuffer[0] = '';
    
    #if defined(MOZ_DEMANGLE_SYMBOLS)
      /* See demangle.h in the gcc source for the voodoo */
      char* demangled = abi::__cxa_demangle(aSymbol, 0, 0, 0);
    
      if (demangled) {
        strncpy(aBuffer, demangled, aBufLen);
        aBuffer[aBufLen - 1] = '';
        free(demangled);
      }
    #endif // MOZ_DEMANGLE_SYMBOLS
    }
    
    // {x86, ppc} x {Linux, Mac} stackwalking code.
    #if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && 
         (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
    
    MFBT_API bool
    MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
                 uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
                 void* aPlatformData)
    {
      MOZ_ASSERT(!aThread);
      MOZ_ASSERT(!aPlatformData);
      StackWalkInitCriticalAddress();
    
      // Get the frame pointer
      void** bp = (void**)__builtin_frame_address(0);
    
      void* stackEnd;
    #if HAVE___LIBC_STACK_END
      stackEnd = __libc_stack_end;
    #elif defined(XP_DARWIN)
      stackEnd = pthread_get_stackaddr_np(pthread_self());
    #elif defined(ANDROID)
      pthread_attr_t sattr;
      pthread_attr_init(&sattr);
      pthread_getattr_np(pthread_self(), &sattr);
      void* stackBase = stackEnd = nullptr;
      size_t stackSize = 0;
      if (gettid() != getpid()) {
        // bionic's pthread_attr_getstack doesn't tell the truth for the main
        // thread (see bug 846670). So don't use it for the main thread.
        if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) {
          stackEnd = static_cast<char*>(stackBase) + stackSize;
        } else {
          stackEnd = nullptr;
        }
      }
      if (!stackEnd) {
        // So consider the current frame pointer + an arbitrary size of 8MB
        // (modulo overflow ; not really arbitrary as it's the default stack
        // size for the main thread) if pthread_attr_getstack failed for
        // some reason (or was skipped).
        static const uintptr_t kMaxStackSize = 8 * 1024 * 1024;
        uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize;
        uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp));
        stackEnd = reinterpret_cast<void*>(stackStart + kMaxStackSize);
      }
    #else
    #  error Unsupported configuration
    #endif
      return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames,
                                   aClosure, bp, stackEnd);
    }
    
    #elif defined(HAVE__UNWIND_BACKTRACE)
    
    // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
    #include <unwind.h>
    
    struct unwind_info
    {
      MozWalkStackCallback callback;
      int skip;
      int maxFrames;
      int numFrames;
      bool isCriticalAbort;
      void* closure;
    };
    
    static _Unwind_Reason_Code
    unwind_callback(struct _Unwind_Context* context, void* closure)
    {
      unwind_info* info = static_cast<unwind_info*>(closure);
      void* pc = reinterpret_cast<void*>(_Unwind_GetIP(context));
      // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
      if (IsCriticalAddress(pc)) {
        info->isCriticalAbort = true;
        // We just want to stop the walk, so any error code will do.  Using
        // _URC_NORMAL_STOP would probably be the most accurate, but it is not
        // defined on Android for ARM.
        return _URC_FOREIGN_EXCEPTION_CAUGHT;
      }
      if (--info->skip < 0) {
        info->numFrames++;
        (*info->callback)(info->numFrames, pc, nullptr, info->closure);
        if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
          // Again, any error code that stops the walk will do.
          return _URC_FOREIGN_EXCEPTION_CAUGHT;
        }
      }
      return _URC_NO_REASON;
    }
    
    MFBT_API bool
    MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
                 uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
                 void* aPlatformData)
    {
      MOZ_ASSERT(!aThread);
      MOZ_ASSERT(!aPlatformData);
      StackWalkInitCriticalAddress();
      unwind_info info;
      info.callback = aCallback;
      info.skip = aSkipFrames + 1;
      info.maxFrames = aMaxFrames;
      info.numFrames = 0;
      info.isCriticalAbort = false;
      info.closure = aClosure;
    
      (void)_Unwind_Backtrace(unwind_callback, &info);
    
      // We ignore the return value from _Unwind_Backtrace and instead determine
      // the outcome from |info|.  There are two main reasons for this:
      // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
      //   _URC_FAILURE.  See
      //   https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
      // - If aMaxFrames != 0, we want to stop early, and the only way to do that
      //   is to make unwind_callback return something other than _URC_NO_REASON,
      //   which causes _Unwind_Backtrace to return a non-success code.
      if (info.isCriticalAbort) {
        return false;
      }
      return info.numFrames != 0;
    }
    
    #endif
    
    bool MFBT_API
    MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
    {
      aDetails->library[0] = '';
      aDetails->loffset = 0;
      aDetails->filename[0] = '';
      aDetails->lineno = 0;
      aDetails->function[0] = '';
      aDetails->foffset = 0;
    
      Dl_info info;
      int ok = dladdr(aPC, &info);
      if (!ok) {
        return true;
      }
    
      strncpy(aDetails->library, info.dli_fname, sizeof(aDetails->library));
      aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '';
      aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
    
      const char* symbol = info.dli_sname;
      if (!symbol || symbol[0] == '') {
        return true;
      }
    
      DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function));
    
      if (aDetails->function[0] == '') {
        // Just use the mangled symbol if demangling failed.
        strncpy(aDetails->function, symbol, sizeof(aDetails->function));
        aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '';
      }
    
      aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
      return true;
    }
    
    #else // unsupported platform.
    
    MFBT_API bool
    MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
                 uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
                 void* aPlatformData)
    {
      MOZ_ASSERT(!aThread);
      MOZ_ASSERT(!aPlatformData);
      return false;
    }
    
    MFBT_API bool
    MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
    {
      aDetails->library[0] = '';
      aDetails->loffset = 0;
      aDetails->filename[0] = '';
      aDetails->lineno = 0;
      aDetails->function[0] = '';
      aDetails->foffset = 0;
      return false;
    }
    
    #endif
    
    #if defined(XP_WIN) || defined (XP_MACOSX) || defined (XP_LINUX)
    namespace mozilla {
    bool
    FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
                          uint32_t aMaxFrames, void* aClosure, void** bp,
                          void* aStackEnd)
    {
      // Stack walking code courtesy Kipp's "leaky".
    
      int32_t skip = aSkipFrames;
      uint32_t numFrames = 0;
      while (bp) {
        void** next = (void**)*bp;
        // bp may not be a frame pointer on i386 if code was compiled with
        // -fomit-frame-pointer, so do some sanity checks.
        // (bp should be a frame pointer on ppc(64) but checking anyway may help
        // a little if the stack has been corrupted.)
        // We don't need to check against the begining of the stack because
        // we can assume that bp > sp
        if (next <= bp ||
            next > aStackEnd ||
            (uintptr_t(next) & 3)) {
          break;
        }
    #if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
        // ppc mac or powerpc64 linux
        void* pc = *(bp + 2);
        bp += 3;
    #else // i386 or powerpc32 linux
        void* pc = *(bp + 1);
        bp += 2;
    #endif
        if (IsCriticalAddress(pc)) {
          return false;
        }
        if (--skip < 0) {
          // Assume that the SP points to the BP of the function
          // it called. We can't know the exact location of the SP
          // but this should be sufficient for our use the SP
          // to order elements on the stack.
          numFrames++;
          (*aCallback)(numFrames, pc, bp, aClosure);
          if (aMaxFrames != 0 && numFrames == aMaxFrames) {
            break;
          }
        }
        bp = next;
      }
      return numFrames != 0;
    }
    } // namespace mozilla
    
    #else
    
    namespace mozilla {
    MFBT_API bool
    FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
                          void* aClosure, void** aBp)
    {
      return false;
    }
    }
    
    #endif
    
    MFBT_API void
    MozFormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
                                uint32_t aFrameNumber, void* aPC,
                                const MozCodeAddressDetails* aDetails)
    {
      MozFormatCodeAddress(aBuffer, aBufferSize,
                           aFrameNumber, aPC, aDetails->function,
                           aDetails->library, aDetails->loffset,
                           aDetails->filename, aDetails->lineno);
    }
    
    MFBT_API void
    MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
                         const void* aPC, const char* aFunction,
                         const char* aLibrary, ptrdiff_t aLOffset,
                         const char* aFileName, uint32_t aLineNo)
    {
      const char* function = aFunction && aFunction[0] ? aFunction : "???";
      if (aFileName && aFileName[0]) {
        // We have a filename and (presumably) a line number. Use them.
        snprintf(aBuffer, aBufferSize,
                 "#%02u: %s (%s:%u)",
                 aFrameNumber, function, aFileName, aLineNo);
      } else if (aLibrary && aLibrary[0]) {
        // We have no filename, but we do have a library name. Use it and the
        // library offset, and print them in a way that scripts like
        // fix_{linux,macosx}_stacks.py can easily post-process.
        snprintf(aBuffer, aBufferSize,
                 "#%02u: %s[%s +0x%" PRIxPTR "]",
                 aFrameNumber, function, aLibrary, static_cast<uintptr_t>(aLOffset));
      } else {
        // We have nothing useful to go on. (The format string is split because
        // '??)' is a trigraph and causes a warning, sigh.)
        snprintf(aBuffer, aBufferSize,
                 "#%02u: ??? (???:???" ")",
                 aFrameNumber);
      }
    }
    StackWalk.cpp
    /* -*- 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 */
    
    #ifndef mozilla_StackWalk_h
    #define mozilla_StackWalk_h
    
    /* WARNING: This file is intended to be included from C or C++ files. */
    
    #include "mozilla/Types.h"
    #include <stdint.h>
    
    /**
     * The callback for MozStackWalk.
     *
     * @param aFrameNumber  The frame number (starts at 1, not 0).
     * @param aPC           The program counter value.
     * @param aSP           The best approximation possible of what the stack
     *                      pointer will be pointing to when the execution returns
     *                      to executing that at aPC. If no approximation can
     *                      be made it will be nullptr.
     * @param aClosure      Extra data passed in via MozStackWalk().
     */
    typedef void
    (*MozWalkStackCallback)(uint32_t aFrameNumber, void* aPC, void* aSP,
                            void* aClosure);
    
    /**
     * Call aCallback for the C/C++ stack frames on the current thread, from
     * the caller of MozStackWalk to main (or above).
     *
     * @param aCallback    Callback function, called once per frame.
     * @param aSkipFrames  Number of initial frames to skip.  0 means that
     *                     the first callback will be for the caller of
     *                     MozStackWalk.
     * @param aMaxFrames   Maximum number of frames to trace.  0 means no limit.
     * @param aClosure     Caller-supplied data passed through to aCallback.
     * @param aThread      The thread for which the stack is to be retrieved.
     *                     Passing null causes us to walk the stack of the
     *                     current thread. On Windows, this is a thread HANDLE.
     *                     It is currently not supported on any other platform.
     * @param aPlatformData Platform specific data that can help in walking the
     *                      stack, this should be nullptr unless you really know
     *                      what you're doing! This needs to be a pointer to a
     *                      CONTEXT on Windows and should not be passed on other
     *                      platforms.
     *
     * May skip some stack frames due to compiler optimizations or code
     * generation.
     *
     * Note: this (and other helper methods) will only be available when
     * MOZ_STACKWALKING is defined, so any new consumers must #if based on that.
     */
    MFBT_API bool
    MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
                 uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
                 void* aPlatformData);
    
    typedef struct
    {
      /*
       * The name of the shared library or executable containing an
       * address and the address's offset within that library, or empty
       * string and zero if unknown.
       */
      char library[256];
      ptrdiff_t loffset;
      /*
       * The name of the file name and line number of the code
       * corresponding to the address, or empty string and zero if
       * unknown.
       */
      char filename[256];
      unsigned long lineno;
      /*
       * The name of the function containing an address and the address's
       * offset within that function, or empty string and zero if unknown.
       */
      char function[256];
      ptrdiff_t foffset;
    } MozCodeAddressDetails;
    
    /**
     * For a given pointer to code, fill in the pieces of information used
     * when printing a stack trace.
     *
     * @param aPC         The code address.
     * @param aDetails    A structure to be filled in with the result.
     */
    MFBT_API bool
    MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails);
    
    /**
     * Format the information about a code address in a format suitable for
     * stack traces on the current platform.  When available, this string
     * should contain the function name, source file, and line number.  When
     * these are not available, library and offset should be reported, if
     * possible.
     *
     * Note that this output is parsed by several scripts including the fix*.py and
     * make-tree.pl scripts in tools/rb/. It should only be change with care, and
     * in conjunction with those scripts.
     *
     * @param aBuffer      A string to be filled in with the description.
     *                     The string will always be null-terminated.
     * @param aBufferSize  The size, in bytes, of aBuffer, including
     *                     room for the terminating null.  If the information
     *                     to be printed would be larger than aBuffer, it
     *                     will be truncated so that aBuffer[aBufferSize-1]
     *                     is the terminating null.
     * @param aFrameNumber The frame number.
     * @param aPC          The code address.
     * @param aFunction    The function name. Possibly null or the empty string.
     * @param aLibrary     The library name. Possibly null or the empty string.
     * @param aLOffset     The library offset.
     * @param aFileName    The filename. Possibly null or the empty string.
     * @param aLineNo      The line number. Possibly zero.
     */
    MFBT_API void
    MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
                         const void* aPC, const char* aFunction,
                         const char* aLibrary, ptrdiff_t aLOffset,
                         const char* aFileName, uint32_t aLineNo);
    
    /**
     * Format the information about a code address in the same fashion as
     * MozFormatCodeAddress.
     *
     * @param aBuffer      A string to be filled in with the description.
     *                     The string will always be null-terminated.
     * @param aBufferSize  The size, in bytes, of aBuffer, including
     *                     room for the terminating null.  If the information
     *                     to be printed would be larger than aBuffer, it
     *                     will be truncated so that aBuffer[aBufferSize-1]
     *                     is the terminating null.
     * @param aFrameNumber The frame number.
     * @param aPC          The code address.
     * @param aDetails     The value filled in by MozDescribeCodeAddress(aPC).
     */
    MFBT_API void
    MozFormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
                                uint32_t aFrameNumber, void* aPC,
                                const MozCodeAddressDetails* aDetails);
    
    namespace mozilla {
    
    MFBT_API bool
    FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
                          uint32_t aMaxFrames, void* aClosure, void** aBp,
                          void* aStackEnd);
    
    } // namespace mozilla
    
    /**
     * Initialize the critical sections for this platform so that we can
     * abort stack walks when needed.
     */
    MFBT_API void
    StackWalkInitCriticalAddress(void);
    
    #endif
    StackWalk.h
  • 相关阅读:
    Java内部类
    sql几种连接的区别
    常见的十大算法
    使用yml文件配置数据库数据时的问题
    SpringBoot整合Mybatis
    不是书评 :《我是一只IT小小鸟》
    考试考完了·
    MSSQL站库分离情况的渗透思路
    VENOM cve-2015-3456 Qemu 虚拟机逃逸漏洞POC
    Python 实现指定目录下 删除指定大小的文件
  • 原文地址:https://www.cnblogs.com/DeanWang/p/6280106.html
Copyright © 2011-2022 走看看