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
  • 相关阅读:
    PAT (Advanced Level) Practice 1100 Mars Numbers (20分)
    PAT (Advanced Level) Practice 1107 Social Clusters (30分) (并查集)
    PAT (Advanced Level) Practice 1105 Spiral Matrix (25分)
    PAT (Advanced Level) Practice 1104 Sum of Number Segments (20分)
    PAT (Advanced Level) Practice 1111 Online Map (30分) (两次迪杰斯特拉混合)
    PAT (Advanced Level) Practice 1110 Complete Binary Tree (25分) (完全二叉树的判断+分享致命婴幼儿错误)
    PAT (Advanced Level) Practice 1109 Group Photo (25分)
    PAT (Advanced Level) Practice 1108 Finding Average (20分)
    P6225 [eJOI2019]异或橙子 树状数组 异或 位运算
    P4124 [CQOI2016]手机号码 数位DP
  • 原文地址:https://www.cnblogs.com/DeanWang/p/6280106.html
Copyright © 2011-2022 走看看