LCOV - code coverage report
Current view: top level - mozglue/misc - StackWalk.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 3 96 3.1 %
Date: 2017-07-14 16:53:18 Functions: 1 9 11.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* API for getting a stack trace of the C/C++ stack on the current thread */
       8             : 
       9             : #include "mozilla/ArrayUtils.h"
      10             : #include "mozilla/Assertions.h"
      11             : #include "mozilla/IntegerPrintfMacros.h"
      12             : #include "mozilla/StackWalk.h"
      13             : 
      14             : #include <string.h>
      15             : 
      16             : using namespace mozilla;
      17             : 
      18             : // The presence of this address is the stack must stop the stack walk. If
      19             : // there is no such address, the structure will be {nullptr, true}.
      20             : struct CriticalAddress
      21             : {
      22             :   void* mAddr;
      23             :   bool mInit;
      24             : };
      25             : static CriticalAddress gCriticalAddress;
      26             : 
      27             : // for _Unwind_Backtrace from libcxxrt or libunwind
      28             : // cxxabi.h from libcxxrt implicitly includes unwind.h first
      29             : #if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE)
      30             : #define _GNU_SOURCE
      31             : #endif
      32             : 
      33             : #if defined(HAVE_DLOPEN) || defined(XP_DARWIN)
      34             : #include <dlfcn.h>
      35             : #endif
      36             : 
      37             : #if (defined(XP_DARWIN) && \
      38             :      (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
      39             : #define MOZ_STACKWALK_SUPPORTS_MACOSX 1
      40             : #else
      41             : #define MOZ_STACKWALK_SUPPORTS_MACOSX 0
      42             : #endif
      43             : 
      44             : #if (defined(linux) && \
      45             :      ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
      46             :       defined(HAVE__UNWIND_BACKTRACE)))
      47             : #define MOZ_STACKWALK_SUPPORTS_LINUX 1
      48             : #else
      49             : #define MOZ_STACKWALK_SUPPORTS_LINUX 0
      50             : #endif
      51             : 
      52             : #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
      53             : #define HAVE___LIBC_STACK_END 1
      54             : #else
      55             : #define HAVE___LIBC_STACK_END 0
      56             : #endif
      57             : 
      58             : #if HAVE___LIBC_STACK_END
      59             : extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so
      60             : #endif
      61             : 
      62             : #ifdef ANDROID
      63             : #include <algorithm>
      64             : #include <unistd.h>
      65             : #include <pthread.h>
      66             : #endif
      67             : 
      68             : #if MOZ_STACKWALK_SUPPORTS_MACOSX
      69             : #include <pthread.h>
      70             : #include <sys/errno.h>
      71             : #ifdef MOZ_WIDGET_COCOA
      72             : #include <CoreServices/CoreServices.h>
      73             : #endif
      74             : 
      75             : typedef void
      76             : malloc_logger_t(uint32_t aType,
      77             :                 uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
      78             :                 uintptr_t aResult, uint32_t aNumHotFramesToSkip);
      79             : extern malloc_logger_t* malloc_logger;
      80             : 
      81             : static void
      82             : stack_callback(uint32_t aFrameNumber, void* aPc, void* aSp, void* aClosure)
      83             : {
      84             :   const char* name = static_cast<char*>(aClosure);
      85             :   Dl_info info;
      86             : 
      87             :   // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
      88             :   // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
      89             :   // correct one is the first that we find on our way up, so the
      90             :   // following check for gCriticalAddress.mAddr is critical.
      91             :   if (gCriticalAddress.mAddr || dladdr(aPc, &info) == 0  ||
      92             :       !info.dli_sname || strcmp(info.dli_sname, name) != 0) {
      93             :     return;
      94             :   }
      95             :   gCriticalAddress.mAddr = aPc;
      96             : }
      97             : 
      98             : static void
      99             : my_malloc_logger(uint32_t aType,
     100             :                  uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
     101             :                  uintptr_t aResult, uint32_t aNumHotFramesToSkip)
     102             : {
     103             :   static bool once = false;
     104             :   if (once) {
     105             :     return;
     106             :   }
     107             :   once = true;
     108             : 
     109             :   // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
     110             :   // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
     111             :   const char* name = "new_sem_from_pool";
     112             :   MozStackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0,
     113             :                const_cast<char*>(name), 0, nullptr);
     114             : }
     115             : 
     116             : // This is called from NS_LogInit() and from the stack walking functions, but
     117             : // only the first call has any effect.  We need to call this function from both
     118             : // places because it must run before any mutexes are created, and also before
     119             : // any objects whose refcounts we're logging are created.  Running this
     120             : // function during NS_LogInit() ensures that we meet the first criterion, and
     121             : // running this function during the stack walking functions ensures we meet the
     122             : // second criterion.
     123             : MFBT_API void
     124             : StackWalkInitCriticalAddress()
     125             : {
     126             :   if (gCriticalAddress.mInit) {
     127             :     return;
     128             :   }
     129             :   gCriticalAddress.mInit = true;
     130             :   // We must not do work when 'new_sem_from_pool' calls realloc, since
     131             :   // it holds a non-reentrant spin-lock and we will quickly deadlock.
     132             :   // new_sem_from_pool is not directly accessible using dlsym, so
     133             :   // we force a situation where new_sem_from_pool is on the stack and
     134             :   // use dladdr to check the addresses.
     135             : 
     136             :   // malloc_logger can be set by external tools like 'Instruments' or 'leaks'
     137             :   malloc_logger_t* old_malloc_logger = malloc_logger;
     138             :   malloc_logger = my_malloc_logger;
     139             : 
     140             :   pthread_cond_t cond;
     141             :   int r = pthread_cond_init(&cond, 0);
     142             :   MOZ_ASSERT(r == 0);
     143             :   pthread_mutex_t mutex;
     144             :   r = pthread_mutex_init(&mutex, 0);
     145             :   MOZ_ASSERT(r == 0);
     146             :   r = pthread_mutex_lock(&mutex);
     147             :   MOZ_ASSERT(r == 0);
     148             :   struct timespec abstime = { 0, 1 };
     149             :   r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime);
     150             : 
     151             :   // restore the previous malloc logger
     152             :   malloc_logger = old_malloc_logger;
     153             : 
     154             :   MOZ_ASSERT(r == ETIMEDOUT);
     155             :   r = pthread_mutex_unlock(&mutex);
     156             :   MOZ_ASSERT(r == 0);
     157             :   r = pthread_mutex_destroy(&mutex);
     158             :   MOZ_ASSERT(r == 0);
     159             :   r = pthread_cond_destroy(&cond);
     160             :   MOZ_ASSERT(r == 0);
     161             : }
     162             : 
     163             : static bool
     164             : IsCriticalAddress(void* aPC)
     165             : {
     166             :   return gCriticalAddress.mAddr == aPC;
     167             : }
     168             : #else
     169             : static bool
     170           0 : IsCriticalAddress(void* aPC)
     171             : {
     172           0 :   return false;
     173             : }
     174             : // We still initialize gCriticalAddress.mInit so that this code behaves
     175             : // the same on all platforms. Otherwise a failure to init would be visible
     176             : // only on OS X.
     177             : MFBT_API void
     178          11 : StackWalkInitCriticalAddress()
     179             : {
     180          11 :   gCriticalAddress.mInit = true;
     181          11 : }
     182             : #endif
     183             : 
     184             : #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
     185             : 
     186             : #include <windows.h>
     187             : #include <process.h>
     188             : #include <stdio.h>
     189             : #include <malloc.h>
     190             : #include "mozilla/ArrayUtils.h"
     191             : #include "mozilla/Atomics.h"
     192             : #include "mozilla/StackWalk_windows.h"
     193             : #include "mozilla/WindowsVersion.h"
     194             : 
     195             : #include <imagehlp.h>
     196             : // We need a way to know if we are building for WXP (or later), as if we are, we
     197             : // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
     198             : // A value of 9 indicates we want to use the new APIs.
     199             : #if API_VERSION_NUMBER < 9
     200             : #error Too old imagehlp.h
     201             : #endif
     202             : 
     203             : struct WalkStackData
     204             : {
     205             :   // Are we walking the stack of the calling thread? Note that we need to avoid
     206             :   // calling fprintf and friends if this is false, in order to avoid deadlocks.
     207             :   bool walkCallingThread;
     208             :   uint32_t skipFrames;
     209             :   HANDLE thread;
     210             :   HANDLE process;
     211             :   HANDLE eventStart;
     212             :   HANDLE eventEnd;
     213             :   void** pcs;
     214             :   uint32_t pc_size;
     215             :   uint32_t pc_count;
     216             :   uint32_t pc_max;
     217             :   void** sps;
     218             :   uint32_t sp_size;
     219             :   uint32_t sp_count;
     220             :   void* platformData;
     221             : };
     222             : 
     223             : DWORD gStackWalkThread;
     224             : CRITICAL_SECTION gDbgHelpCS;
     225             : 
     226             : #ifdef _M_AMD64
     227             : // Because various Win64 APIs acquire function-table locks, we need a way of
     228             : // preventing stack walking while those APIs are being called. Otherwise, the
     229             : // stack walker may suspend a thread holding such a lock, and deadlock when the
     230             : // stack unwind code attempts to wait for that lock.
     231             : //
     232             : // We're using an atomic counter rather than a critical section because we
     233             : // don't require mutual exclusion with the stack walker. If the stack walker
     234             : // determines that it's safe to start unwinding the suspended thread (i.e.
     235             : // there are no suppressions when the unwind begins), then it's safe to
     236             : // continue unwinding that thread even if other threads request suppressions
     237             : // in the meantime, because we can't deadlock with those other threads.
     238             : //
     239             : // XXX: This global variable is a larger-than-necessary hammer. A more scoped
     240             : // solution would be to maintain a counter per thread, but then it would be
     241             : // more difficult for WalkStackMain64 to read the suspended thread's counter.
     242             : static Atomic<size_t> sStackWalkSuppressions;
     243             : 
     244             : MFBT_API
     245             : AutoSuppressStackWalking::AutoSuppressStackWalking()
     246             : {
     247             :   ++sStackWalkSuppressions;
     248             : }
     249             : 
     250             : MFBT_API
     251             : AutoSuppressStackWalking::~AutoSuppressStackWalking()
     252             : {
     253             :   --sStackWalkSuppressions;
     254             : }
     255             : 
     256             : static uint8_t* sJitCodeRegionStart;
     257             : static size_t sJitCodeRegionSize;
     258             : uint8_t* sMsMpegJitCodeRegionStart;
     259             : size_t sMsMpegJitCodeRegionSize;
     260             : 
     261             : MFBT_API void
     262             : RegisterJitCodeRegion(uint8_t* aStart, size_t aSize)
     263             : {
     264             :   // Currently we can only handle one JIT code region at a time
     265             :   MOZ_RELEASE_ASSERT(!sJitCodeRegionStart);
     266             : 
     267             :   sJitCodeRegionStart = aStart;
     268             :   sJitCodeRegionSize = aSize;
     269             : }
     270             : 
     271             : MFBT_API void
     272             : UnregisterJitCodeRegion(uint8_t* aStart, size_t aSize)
     273             : {
     274             :   // Currently we can only handle one JIT code region at a time
     275             :   MOZ_RELEASE_ASSERT(sJitCodeRegionStart &&
     276             :                      sJitCodeRegionStart == aStart &&
     277             :                      sJitCodeRegionSize == aSize);
     278             : 
     279             :   sJitCodeRegionStart = nullptr;
     280             :   sJitCodeRegionSize = 0;
     281             : }
     282             : 
     283             : #endif // _M_AMD64
     284             : 
     285             : // Routine to print an error message to standard error.
     286             : static void
     287             : PrintError(const char* aPrefix)
     288             : {
     289             :   LPSTR lpMsgBuf;
     290             :   DWORD lastErr = GetLastError();
     291             :   FormatMessageA(
     292             :     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
     293             :     nullptr,
     294             :     lastErr,
     295             :     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
     296             :     (LPSTR)&lpMsgBuf,
     297             :     0,
     298             :     nullptr
     299             :   );
     300             :   fprintf(stderr, "### ERROR: %s: %s",
     301             :           aPrefix, lpMsgBuf ? lpMsgBuf : "(null)\n");
     302             :   fflush(stderr);
     303             :   LocalFree(lpMsgBuf);
     304             : }
     305             : 
     306             : static void
     307             : InitializeDbgHelpCriticalSection()
     308             : {
     309             :   static bool initialized = false;
     310             :   if (initialized) {
     311             :     return;
     312             :   }
     313             :   ::InitializeCriticalSection(&gDbgHelpCS);
     314             :   initialized = true;
     315             : }
     316             : 
     317             : static unsigned int WINAPI WalkStackThread(void* aData);
     318             : 
     319             : static bool
     320             : EnsureWalkThreadReady()
     321             : {
     322             :   static bool walkThreadReady = false;
     323             :   static HANDLE stackWalkThread = nullptr;
     324             :   static HANDLE readyEvent = nullptr;
     325             : 
     326             :   if (walkThreadReady) {
     327             :     return walkThreadReady;
     328             :   }
     329             : 
     330             :   if (!stackWalkThread) {
     331             :     readyEvent = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
     332             :                                FALSE /* initially non-signaled */,
     333             :                                nullptr);
     334             :     if (!readyEvent) {
     335             :       PrintError("CreateEvent");
     336             :       return false;
     337             :     }
     338             : 
     339             :     unsigned int threadID;
     340             :     stackWalkThread = (HANDLE)_beginthreadex(nullptr, 0, WalkStackThread,
     341             :                                              (void*)readyEvent, 0, &threadID);
     342             :     if (!stackWalkThread) {
     343             :       PrintError("CreateThread");
     344             :       ::CloseHandle(readyEvent);
     345             :       readyEvent = nullptr;
     346             :       return false;
     347             :     }
     348             :     gStackWalkThread = threadID;
     349             :     ::CloseHandle(stackWalkThread);
     350             :   }
     351             : 
     352             :   MOZ_ASSERT((stackWalkThread && readyEvent) ||
     353             :              (!stackWalkThread && !readyEvent));
     354             : 
     355             :   // The thread was created. Try to wait an arbitrary amount of time (1 second
     356             :   // should be enough) for its event loop to start before posting events to it.
     357             :   DWORD waitRet = ::WaitForSingleObject(readyEvent, 1000);
     358             :   if (waitRet == WAIT_TIMEOUT) {
     359             :     // We get a timeout if we're called during static initialization because
     360             :     // the thread will only start executing after we return so it couldn't
     361             :     // have signalled the event. If that is the case, give up for now and
     362             :     // try again next time we're called.
     363             :     return false;
     364             :   }
     365             :   ::CloseHandle(readyEvent);
     366             :   stackWalkThread = nullptr;
     367             :   readyEvent = nullptr;
     368             : 
     369             :   return walkThreadReady = true;
     370             : }
     371             : 
     372             : static void
     373             : WalkStackMain64(struct WalkStackData* aData)
     374             : {
     375             :   // Get a context for the specified thread.
     376             :   CONTEXT context;
     377             :   if (!aData->platformData) {
     378             :     memset(&context, 0, sizeof(CONTEXT));
     379             :     context.ContextFlags = CONTEXT_FULL;
     380             :     if (!GetThreadContext(aData->thread, &context)) {
     381             :       if (aData->walkCallingThread) {
     382             :         PrintError("GetThreadContext");
     383             :       }
     384             :       return;
     385             :     }
     386             :   } else {
     387             :     context = *static_cast<CONTEXT*>(aData->platformData);
     388             :   }
     389             : 
     390             : #if defined(_M_IX86) || defined(_M_IA64)
     391             :   // Setup initial stack frame to walk from.
     392             :   STACKFRAME64 frame64;
     393             :   memset(&frame64, 0, sizeof(frame64));
     394             : #ifdef _M_IX86
     395             :   frame64.AddrPC.Offset    = context.Eip;
     396             :   frame64.AddrStack.Offset = context.Esp;
     397             :   frame64.AddrFrame.Offset = context.Ebp;
     398             : #elif defined _M_IA64
     399             :   frame64.AddrPC.Offset    = context.StIIP;
     400             :   frame64.AddrStack.Offset = context.SP;
     401             :   frame64.AddrFrame.Offset = context.RsBSP;
     402             : #endif
     403             :   frame64.AddrPC.Mode      = AddrModeFlat;
     404             :   frame64.AddrStack.Mode   = AddrModeFlat;
     405             :   frame64.AddrFrame.Mode   = AddrModeFlat;
     406             :   frame64.AddrReturn.Mode  = AddrModeFlat;
     407             : #endif
     408             : 
     409             : #ifdef _WIN64
     410             :   // If there are any active suppressions, then at least one thread (we don't
     411             :   // know which) is holding a lock that can deadlock RtlVirtualUnwind. Since
     412             :   // that thread may be the one that we're trying to unwind, we can't proceed.
     413             :   //
     414             :   // But if there are no suppressions, then our target thread can't be holding
     415             :   // a lock, and it's safe to proceed. By virtue of being suspended, the target
     416             :   // thread can't acquire any new locks during the unwind process, so we only
     417             :   // need to do this check once. After that, sStackWalkSuppressions can be
     418             :   // changed by other threads while we're unwinding, and that's fine because
     419             :   // we can't deadlock with those threads.
     420             :   if (sStackWalkSuppressions) {
     421             :     return;
     422             :   }
     423             : #endif
     424             : 
     425             : #ifdef _M_AMD64
     426             :   bool firstFrame = true;
     427             : #endif
     428             : 
     429             :   // Skip our own stack walking frames.
     430             :   int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
     431             : 
     432             :   // Now walk the stack.
     433             :   while (true) {
     434             :     DWORD64 addr;
     435             :     DWORD64 spaddr;
     436             : 
     437             : #if defined(_M_IX86) || defined(_M_IA64)
     438             :     // 32-bit frame unwinding.
     439             :     // Debug routines are not threadsafe, so grab the lock.
     440             :     EnterCriticalSection(&gDbgHelpCS);
     441             :     BOOL ok = StackWalk64(
     442             : #if defined _M_IA64
     443             :       IMAGE_FILE_MACHINE_IA64,
     444             : #elif defined _M_IX86
     445             :       IMAGE_FILE_MACHINE_I386,
     446             : #endif
     447             :       aData->process,
     448             :       aData->thread,
     449             :       &frame64,
     450             :       &context,
     451             :       nullptr,
     452             :       SymFunctionTableAccess64, // function table access routine
     453             :       SymGetModuleBase64,       // module base routine
     454             :       0
     455             :     );
     456             :     LeaveCriticalSection(&gDbgHelpCS);
     457             : 
     458             :     if (ok) {
     459             :       addr = frame64.AddrPC.Offset;
     460             :       spaddr = frame64.AddrStack.Offset;
     461             :     } else {
     462             :       addr = 0;
     463             :       spaddr = 0;
     464             :       if (aData->walkCallingThread) {
     465             :         PrintError("WalkStack64");
     466             :       }
     467             :     }
     468             : 
     469             :     if (!ok) {
     470             :       break;
     471             :     }
     472             : 
     473             : #elif defined(_M_AMD64)
     474             :     // If we reach a frame in JIT code, we don't have enough information to
     475             :     // unwind, so we have to give up.
     476             :     if (sJitCodeRegionStart &&
     477             :         (uint8_t*)context.Rip >= sJitCodeRegionStart &&
     478             :         (uint8_t*)context.Rip < sJitCodeRegionStart + sJitCodeRegionSize) {
     479             :       break;
     480             :     }
     481             : 
     482             :     // We must also avoid msmpeg2vdec.dll's JIT region: they don't generate
     483             :     // unwind data, so their JIT unwind callback just throws up its hands and
     484             :     // terminates the process.
     485             :     if (sMsMpegJitCodeRegionStart &&
     486             :         (uint8_t*)context.Rip >= sMsMpegJitCodeRegionStart &&
     487             :         (uint8_t*)context.Rip < sMsMpegJitCodeRegionStart + sMsMpegJitCodeRegionSize) {
     488             :       break;
     489             :     }
     490             : 
     491             :     // 64-bit frame unwinding.
     492             :     // Try to look up unwind metadata for the current function.
     493             :     ULONG64 imageBase;
     494             :     PRUNTIME_FUNCTION runtimeFunction =
     495             :       RtlLookupFunctionEntry(context.Rip, &imageBase, NULL);
     496             : 
     497             :     if (runtimeFunction) {
     498             :       PVOID dummyHandlerData;
     499             :       ULONG64 dummyEstablisherFrame;
     500             :       RtlVirtualUnwind(UNW_FLAG_NHANDLER,
     501             :                        imageBase,
     502             :                        context.Rip,
     503             :                        runtimeFunction,
     504             :                        &context,
     505             :                        &dummyHandlerData,
     506             :                        &dummyEstablisherFrame,
     507             :                        nullptr);
     508             :     } else if (firstFrame) {
     509             :       // Leaf functions can be unwound by hand.
     510             :       context.Rip = *reinterpret_cast<DWORD64*>(context.Rsp);
     511             :       context.Rsp += sizeof(void*);
     512             :     } else {
     513             :       // Something went wrong.
     514             :       break;
     515             :     }
     516             : 
     517             :     addr = context.Rip;
     518             :     spaddr = context.Rsp;
     519             :     firstFrame = false;
     520             : #else
     521             : #error "unknown platform"
     522             : #endif
     523             : 
     524             :     if (addr == 0) {
     525             :       break;
     526             :     }
     527             : 
     528             :     if (skip-- > 0) {
     529             :       continue;
     530             :     }
     531             : 
     532             :     if (aData->pc_count < aData->pc_size) {
     533             :       aData->pcs[aData->pc_count] = (void*)addr;
     534             :     }
     535             :     ++aData->pc_count;
     536             : 
     537             :     if (aData->sp_count < aData->sp_size) {
     538             :       aData->sps[aData->sp_count] = (void*)spaddr;
     539             :     }
     540             :     ++aData->sp_count;
     541             : 
     542             :     if (aData->pc_max != 0 && aData->pc_count == aData->pc_max) {
     543             :       break;
     544             :     }
     545             : 
     546             : #if defined(_M_IX86) || defined(_M_IA64)
     547             :     if (frame64.AddrReturn.Offset == 0) {
     548             :       break;
     549             :     }
     550             : #endif
     551             :   }
     552             : }
     553             : 
     554             : static unsigned int WINAPI
     555             : WalkStackThread(void* aData)
     556             : {
     557             :   BOOL msgRet;
     558             :   MSG msg;
     559             : 
     560             :   // Call PeekMessage to force creation of a message queue so that
     561             :   // other threads can safely post events to us.
     562             :   ::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
     563             : 
     564             :   // and tell the thread that created us that we're ready.
     565             :   HANDLE readyEvent = (HANDLE)aData;
     566             :   ::SetEvent(readyEvent);
     567             : 
     568             :   while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) {
     569             :     if (msgRet == -1) {
     570             :       PrintError("GetMessage");
     571             :     } else {
     572             :       DWORD ret;
     573             : 
     574             :       struct WalkStackData* data = (WalkStackData*)msg.lParam;
     575             :       if (!data) {
     576             :         continue;
     577             :       }
     578             : 
     579             :       // Don't suspend the calling thread until it's waiting for
     580             :       // us; otherwise the number of frames on the stack could vary.
     581             :       ret = ::WaitForSingleObject(data->eventStart, INFINITE);
     582             :       if (ret != WAIT_OBJECT_0) {
     583             :         PrintError("WaitForSingleObject");
     584             :       }
     585             : 
     586             :       // Suspend the calling thread, dump his stack, and then resume him.
     587             :       // He's currently waiting for us to finish so now should be a good time.
     588             :       ret = ::SuspendThread(data->thread);
     589             :       if (ret == (DWORD)-1) {
     590             :         PrintError("ThreadSuspend");
     591             :       } else {
     592             :         WalkStackMain64(data);
     593             : 
     594             :         ret = ::ResumeThread(data->thread);
     595             :         if (ret == (DWORD)-1) {
     596             :           PrintError("ThreadResume");
     597             :         }
     598             :       }
     599             : 
     600             :       ::SetEvent(data->eventEnd);
     601             :     }
     602             :   }
     603             : 
     604             :   return 0;
     605             : }
     606             : 
     607             : /**
     608             :  * Walk the stack, translating PC's found into strings and recording the
     609             :  * chain in aBuffer. For this to work properly, the DLLs must be rebased
     610             :  * so that the address in the file agrees with the address in memory.
     611             :  * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
     612             :  * whose in memory address doesn't match its in-file address.
     613             :  */
     614             : 
     615             : MFBT_API bool
     616             : MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
     617             :              uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
     618             :              void* aPlatformData)
     619             : {
     620             :   StackWalkInitCriticalAddress();
     621             :   static HANDLE myProcess = nullptr;
     622             :   HANDLE myThread;
     623             :   DWORD walkerReturn;
     624             :   struct WalkStackData data;
     625             : 
     626             :   InitializeDbgHelpCriticalSection();
     627             : 
     628             :   // EnsureWalkThreadReady's _beginthreadex takes a heap lock and must be
     629             :   // avoided if we're walking another (i.e. suspended) thread.
     630             :   if (!aThread && !EnsureWalkThreadReady()) {
     631             :     return false;
     632             :   }
     633             : 
     634             :   HANDLE currentThread = ::GetCurrentThread();
     635             :   HANDLE targetThread =
     636             :     aThread ? reinterpret_cast<HANDLE>(aThread) : currentThread;
     637             :   data.walkCallingThread = (targetThread == currentThread);
     638             : 
     639             :   // Have to duplicate handle to get a real handle.
     640             :   if (!myProcess) {
     641             :     if (!::DuplicateHandle(::GetCurrentProcess(),
     642             :                            ::GetCurrentProcess(),
     643             :                            ::GetCurrentProcess(),
     644             :                            &myProcess,
     645             :                            PROCESS_ALL_ACCESS, FALSE, 0)) {
     646             :       if (data.walkCallingThread) {
     647             :         PrintError("DuplicateHandle (process)");
     648             :       }
     649             :       return false;
     650             :     }
     651             :   }
     652             :   if (!::DuplicateHandle(::GetCurrentProcess(),
     653             :                          targetThread,
     654             :                          ::GetCurrentProcess(),
     655             :                          &myThread,
     656             :                          THREAD_ALL_ACCESS, FALSE, 0)) {
     657             :     if (data.walkCallingThread) {
     658             :       PrintError("DuplicateHandle (thread)");
     659             :     }
     660             :     return false;
     661             :   }
     662             : 
     663             :   data.skipFrames = aSkipFrames;
     664             :   data.thread = myThread;
     665             :   data.process = myProcess;
     666             :   void* local_pcs[1024];
     667             :   data.pcs = local_pcs;
     668             :   data.pc_count = 0;
     669             :   data.pc_size = ArrayLength(local_pcs);
     670             :   data.pc_max = aMaxFrames;
     671             :   void* local_sps[1024];
     672             :   data.sps = local_sps;
     673             :   data.sp_count = 0;
     674             :   data.sp_size = ArrayLength(local_sps);
     675             :   data.platformData = aPlatformData;
     676             : 
     677             :   if (aThread) {
     678             :     // If we're walking the stack of another thread, we don't need to
     679             :     // use a separate walker thread.
     680             :     WalkStackMain64(&data);
     681             : 
     682             :     if (data.pc_count > data.pc_size) {
     683             :       data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
     684             :       data.pc_size = data.pc_count;
     685             :       data.pc_count = 0;
     686             :       data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
     687             :       data.sp_size = data.sp_count;
     688             :       data.sp_count = 0;
     689             :       WalkStackMain64(&data);
     690             :     }
     691             :   } else {
     692             :     data.eventStart = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
     693             :                                     FALSE /* initially non-signaled */, nullptr);
     694             :     data.eventEnd = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
     695             :                                   FALSE /* initially non-signaled */, nullptr);
     696             : 
     697             :     ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
     698             : 
     699             :     walkerReturn = ::SignalObjectAndWait(data.eventStart,
     700             :                                          data.eventEnd, INFINITE, FALSE);
     701             :     if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
     702             :       PrintError("SignalObjectAndWait (1)");
     703             :     }
     704             :     if (data.pc_count > data.pc_size) {
     705             :       data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
     706             :       data.pc_size = data.pc_count;
     707             :       data.pc_count = 0;
     708             :       data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
     709             :       data.sp_size = data.sp_count;
     710             :       data.sp_count = 0;
     711             :       ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
     712             :       walkerReturn = ::SignalObjectAndWait(data.eventStart,
     713             :                                            data.eventEnd, INFINITE, FALSE);
     714             :       if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
     715             :         PrintError("SignalObjectAndWait (2)");
     716             :       }
     717             :     }
     718             : 
     719             :     ::CloseHandle(data.eventStart);
     720             :     ::CloseHandle(data.eventEnd);
     721             :   }
     722             : 
     723             :   ::CloseHandle(myThread);
     724             : 
     725             :   for (uint32_t i = 0; i < data.pc_count; ++i) {
     726             :     (*aCallback)(i + 1, data.pcs[i], data.sps[i], aClosure);
     727             :   }
     728             : 
     729             :   return data.pc_count != 0;
     730             : }
     731             : 
     732             : 
     733             : static BOOL CALLBACK
     734             : callbackEspecial64(
     735             :   PCSTR aModuleName,
     736             :   DWORD64 aModuleBase,
     737             :   ULONG aModuleSize,
     738             :   PVOID aUserContext)
     739             : {
     740             :   BOOL retval = TRUE;
     741             :   DWORD64 addr = *(DWORD64*)aUserContext;
     742             : 
     743             :   /*
     744             :    * You'll want to control this if we are running on an
     745             :    *  architecture where the addresses go the other direction.
     746             :    * Not sure this is even a realistic consideration.
     747             :    */
     748             :   const BOOL addressIncreases = TRUE;
     749             : 
     750             :   /*
     751             :    * If it falls in side the known range, load the symbols.
     752             :    */
     753             :   if (addressIncreases
     754             :       ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize))
     755             :       : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))
     756             :      ) {
     757             :     retval = !!SymLoadModule64(GetCurrentProcess(), nullptr,
     758             :                                (PSTR)aModuleName, nullptr,
     759             :                                aModuleBase, aModuleSize);
     760             :     if (!retval) {
     761             :       PrintError("SymLoadModule64");
     762             :     }
     763             :   }
     764             : 
     765             :   return retval;
     766             : }
     767             : 
     768             : /*
     769             :  * SymGetModuleInfoEspecial
     770             :  *
     771             :  * Attempt to determine the module information.
     772             :  * Bug 112196 says this DLL may not have been loaded at the time
     773             :  *  SymInitialize was called, and thus the module information
     774             :  *  and symbol information is not available.
     775             :  * This code rectifies that problem.
     776             :  */
     777             : 
     778             : // New members were added to IMAGEHLP_MODULE64 (that show up in the
     779             : // Platform SDK that ships with VC8, but not the Platform SDK that ships
     780             : // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
     781             : // use them, and it's useful to be able to function correctly with the
     782             : // older library.  (Stock Windows XP SP2 seems to ship with dbghelp.dll
     783             : // version 5.1.)  Since Platform SDK version need not correspond to
     784             : // compiler version, and the version number in debughlp.h was NOT bumped
     785             : // when these changes were made, ifdef based on a constant that was
     786             : // added between these versions.
     787             : #ifdef SSRVOPT_SETCONTEXT
     788             : #define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64))
     789             : #else
     790             : #define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
     791             : #endif
     792             : 
     793             : BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr,
     794             :                                 PIMAGEHLP_MODULE64 aModuleInfo,
     795             :                                 PIMAGEHLP_LINE64 aLineInfo)
     796             : {
     797             :   BOOL retval = FALSE;
     798             : 
     799             :   /*
     800             :    * Init the vars if we have em.
     801             :    */
     802             :   aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE;
     803             :   if (aLineInfo) {
     804             :     aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
     805             :   }
     806             : 
     807             :   /*
     808             :    * Give it a go.
     809             :    * It may already be loaded.
     810             :    */
     811             :   retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
     812             :   if (retval == FALSE) {
     813             :     /*
     814             :      * Not loaded, here's the magic.
     815             :      * Go through all the modules.
     816             :      */
     817             :     // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
     818             :     // constness of the first parameter of
     819             :     // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
     820             :     // non-const to const over time).  See bug 391848 and bug
     821             :     // 415426.
     822             :     BOOL enumRes = EnumerateLoadedModules64(
     823             :       aProcess,
     824             :       (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64,
     825             :       (PVOID)&aAddr);
     826             :     if (enumRes != FALSE) {
     827             :       /*
     828             :        * One final go.
     829             :        * If it fails, then well, we have other problems.
     830             :        */
     831             :       retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
     832             :     }
     833             :   }
     834             : 
     835             :   /*
     836             :    * If we got module info, we may attempt line info as well.
     837             :    * We will not report failure if this does not work.
     838             :    */
     839             :   if (retval != FALSE && aLineInfo) {
     840             :     DWORD displacement = 0;
     841             :     BOOL lineRes = FALSE;
     842             :     lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo);
     843             :     if (!lineRes) {
     844             :       // Clear out aLineInfo to indicate that it's not valid
     845             :       memset(aLineInfo, 0, sizeof(*aLineInfo));
     846             :     }
     847             :   }
     848             : 
     849             :   return retval;
     850             : }
     851             : 
     852             : static bool
     853             : EnsureSymInitialized()
     854             : {
     855             :   static bool gInitialized = false;
     856             :   bool retStat;
     857             : 
     858             :   if (gInitialized) {
     859             :     return gInitialized;
     860             :   }
     861             : 
     862             :   InitializeDbgHelpCriticalSection();
     863             : 
     864             :   SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
     865             :   retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
     866             :   if (!retStat) {
     867             :     PrintError("SymInitialize");
     868             :   }
     869             : 
     870             :   gInitialized = retStat;
     871             :   /* XXX At some point we need to arrange to call SymCleanup */
     872             : 
     873             :   return retStat;
     874             : }
     875             : 
     876             : 
     877             : MFBT_API bool
     878             : MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
     879             : {
     880             :   aDetails->library[0] = '\0';
     881             :   aDetails->loffset = 0;
     882             :   aDetails->filename[0] = '\0';
     883             :   aDetails->lineno = 0;
     884             :   aDetails->function[0] = '\0';
     885             :   aDetails->foffset = 0;
     886             : 
     887             :   if (!EnsureSymInitialized()) {
     888             :     return false;
     889             :   }
     890             : 
     891             :   HANDLE myProcess = ::GetCurrentProcess();
     892             :   BOOL ok;
     893             : 
     894             :   // debug routines are not threadsafe, so grab the lock.
     895             :   EnterCriticalSection(&gDbgHelpCS);
     896             : 
     897             :   //
     898             :   // Attempt to load module info before we attempt to resolve the symbol.
     899             :   // This just makes sure we get good info if available.
     900             :   //
     901             : 
     902             :   DWORD64 addr = (DWORD64)aPC;
     903             :   IMAGEHLP_MODULE64 modInfo;
     904             :   IMAGEHLP_LINE64 lineInfo;
     905             :   BOOL modInfoRes;
     906             :   modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo);
     907             : 
     908             :   if (modInfoRes) {
     909             :     strncpy(aDetails->library, modInfo.LoadedImageName,
     910             :                 sizeof(aDetails->library));
     911             :     aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
     912             :     aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage;
     913             : 
     914             :     if (lineInfo.FileName) {
     915             :       strncpy(aDetails->filename, lineInfo.FileName,
     916             :                   sizeof(aDetails->filename));
     917             :       aDetails->filename[mozilla::ArrayLength(aDetails->filename) - 1] = '\0';
     918             :       aDetails->lineno = lineInfo.LineNumber;
     919             :     }
     920             :   }
     921             : 
     922             :   ULONG64 buffer[(sizeof(SYMBOL_INFO) +
     923             :     MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
     924             :   PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
     925             :   pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
     926             :   pSymbol->MaxNameLen = MAX_SYM_NAME;
     927             : 
     928             :   DWORD64 displacement;
     929             :   ok = SymFromAddr(myProcess, addr, &displacement, pSymbol);
     930             : 
     931             :   if (ok) {
     932             :     strncpy(aDetails->function, pSymbol->Name,
     933             :                 sizeof(aDetails->function));
     934             :     aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
     935             :     aDetails->foffset = static_cast<ptrdiff_t>(displacement);
     936             :   }
     937             : 
     938             :   LeaveCriticalSection(&gDbgHelpCS); // release our lock
     939             :   return true;
     940             : }
     941             : 
     942             : // i386 or PPC Linux stackwalking code
     943             : #elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || MOZ_STACKWALK_SUPPORTS_MACOSX)
     944             : 
     945             : #include <stdlib.h>
     946             : #include <string.h>
     947             : #include <stdio.h>
     948             : 
     949             : // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
     950             : // if __USE_GNU is defined.  I suppose its some kind of standards
     951             : // adherence thing.
     952             : //
     953             : #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
     954             : #define __USE_GNU
     955             : #endif
     956             : 
     957             : // This thing is exported by libstdc++
     958             : // Yes, this is a gcc only hack
     959             : #if defined(MOZ_DEMANGLE_SYMBOLS)
     960             : #include <cxxabi.h>
     961             : #endif // MOZ_DEMANGLE_SYMBOLS
     962             : 
     963           0 : void DemangleSymbol(const char* aSymbol,
     964             :                     char* aBuffer,
     965             :                     int aBufLen)
     966             : {
     967           0 :   aBuffer[0] = '\0';
     968             : 
     969             : #if defined(MOZ_DEMANGLE_SYMBOLS)
     970             :   /* See demangle.h in the gcc source for the voodoo */
     971           0 :   char* demangled = abi::__cxa_demangle(aSymbol, 0, 0, 0);
     972             : 
     973           0 :   if (demangled) {
     974           0 :     strncpy(aBuffer, demangled, aBufLen);
     975           0 :     aBuffer[aBufLen - 1] = '\0';
     976           0 :     free(demangled);
     977             :   }
     978             : #endif // MOZ_DEMANGLE_SYMBOLS
     979           0 : }
     980             : 
     981             : // {x86, ppc} x {Linux, Mac} stackwalking code.
     982             : #if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
     983             :      (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
     984             : 
     985             : MFBT_API bool
     986             : MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
     987             :              uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
     988             :              void* aPlatformData)
     989             : {
     990             :   MOZ_ASSERT(!aThread);
     991             :   MOZ_ASSERT(!aPlatformData);
     992             :   StackWalkInitCriticalAddress();
     993             : 
     994             :   // Get the frame pointer
     995             :   void** bp = (void**)__builtin_frame_address(0);
     996             : 
     997             :   void* stackEnd;
     998             : #if HAVE___LIBC_STACK_END
     999             :   stackEnd = __libc_stack_end;
    1000             : #elif defined(XP_DARWIN)
    1001             :   stackEnd = pthread_get_stackaddr_np(pthread_self());
    1002             : #elif defined(ANDROID)
    1003             :   pthread_attr_t sattr;
    1004             :   pthread_attr_init(&sattr);
    1005             :   pthread_getattr_np(pthread_self(), &sattr);
    1006             :   void* stackBase = stackEnd = nullptr;
    1007             :   size_t stackSize = 0;
    1008             :   if (gettid() != getpid()) {
    1009             :     // bionic's pthread_attr_getstack doesn't tell the truth for the main
    1010             :     // thread (see bug 846670). So don't use it for the main thread.
    1011             :     if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) {
    1012             :       stackEnd = static_cast<char*>(stackBase) + stackSize;
    1013             :     } else {
    1014             :       stackEnd = nullptr;
    1015             :     }
    1016             :   }
    1017             :   if (!stackEnd) {
    1018             :     // So consider the current frame pointer + an arbitrary size of 8MB
    1019             :     // (modulo overflow ; not really arbitrary as it's the default stack
    1020             :     // size for the main thread) if pthread_attr_getstack failed for
    1021             :     // some reason (or was skipped).
    1022             :     static const uintptr_t kMaxStackSize = 8 * 1024 * 1024;
    1023             :     uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize;
    1024             :     uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp));
    1025             :     stackEnd = reinterpret_cast<void*>(stackStart + kMaxStackSize);
    1026             :   }
    1027             : #else
    1028             : #  error Unsupported configuration
    1029             : #endif
    1030             :   return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames,
    1031             :                                aClosure, bp, stackEnd);
    1032             : }
    1033             : 
    1034             : #elif defined(HAVE__UNWIND_BACKTRACE)
    1035             : 
    1036             : // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
    1037             : #include <unwind.h>
    1038             : 
    1039             : struct unwind_info
    1040             : {
    1041             :   MozWalkStackCallback callback;
    1042             :   int skip;
    1043             :   int maxFrames;
    1044             :   int numFrames;
    1045             :   bool isCriticalAbort;
    1046             :   void* closure;
    1047             : };
    1048             : 
    1049             : static _Unwind_Reason_Code
    1050           0 : unwind_callback(struct _Unwind_Context* context, void* closure)
    1051             : {
    1052           0 :   unwind_info* info = static_cast<unwind_info*>(closure);
    1053           0 :   void* pc = reinterpret_cast<void*>(_Unwind_GetIP(context));
    1054             :   // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
    1055           0 :   if (IsCriticalAddress(pc)) {
    1056           0 :     info->isCriticalAbort = true;
    1057             :     // We just want to stop the walk, so any error code will do.  Using
    1058             :     // _URC_NORMAL_STOP would probably be the most accurate, but it is not
    1059             :     // defined on Android for ARM.
    1060           0 :     return _URC_FOREIGN_EXCEPTION_CAUGHT;
    1061             :   }
    1062           0 :   if (--info->skip < 0) {
    1063           0 :     info->numFrames++;
    1064           0 :     (*info->callback)(info->numFrames, pc, nullptr, info->closure);
    1065           0 :     if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
    1066             :       // Again, any error code that stops the walk will do.
    1067           0 :       return _URC_FOREIGN_EXCEPTION_CAUGHT;
    1068             :     }
    1069             :   }
    1070           0 :   return _URC_NO_REASON;
    1071             : }
    1072             : 
    1073             : MFBT_API bool
    1074           0 : MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
    1075             :              uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
    1076             :              void* aPlatformData)
    1077             : {
    1078           0 :   MOZ_ASSERT(!aThread);
    1079           0 :   MOZ_ASSERT(!aPlatformData);
    1080           0 :   StackWalkInitCriticalAddress();
    1081             :   unwind_info info;
    1082           0 :   info.callback = aCallback;
    1083           0 :   info.skip = aSkipFrames + 1;
    1084           0 :   info.maxFrames = aMaxFrames;
    1085           0 :   info.numFrames = 0;
    1086           0 :   info.isCriticalAbort = false;
    1087           0 :   info.closure = aClosure;
    1088             : 
    1089           0 :   (void)_Unwind_Backtrace(unwind_callback, &info);
    1090             : 
    1091             :   // We ignore the return value from _Unwind_Backtrace and instead determine
    1092             :   // the outcome from |info|.  There are two main reasons for this:
    1093             :   // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
    1094             :   //   _URC_FAILURE.  See
    1095             :   //   https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
    1096             :   // - If aMaxFrames != 0, we want to stop early, and the only way to do that
    1097             :   //   is to make unwind_callback return something other than _URC_NO_REASON,
    1098             :   //   which causes _Unwind_Backtrace to return a non-success code.
    1099           0 :   if (info.isCriticalAbort) {
    1100           0 :     return false;
    1101             :   }
    1102           0 :   return info.numFrames != 0;
    1103             : }
    1104             : 
    1105             : #endif
    1106             : 
    1107             : bool MFBT_API
    1108           0 : MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
    1109             : {
    1110           0 :   aDetails->library[0] = '\0';
    1111           0 :   aDetails->loffset = 0;
    1112           0 :   aDetails->filename[0] = '\0';
    1113           0 :   aDetails->lineno = 0;
    1114           0 :   aDetails->function[0] = '\0';
    1115           0 :   aDetails->foffset = 0;
    1116             : 
    1117             :   Dl_info info;
    1118           0 :   int ok = dladdr(aPC, &info);
    1119           0 :   if (!ok) {
    1120           0 :     return true;
    1121             :   }
    1122             : 
    1123           0 :   strncpy(aDetails->library, info.dli_fname, sizeof(aDetails->library));
    1124           0 :   aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
    1125           0 :   aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
    1126             : 
    1127           0 :   const char* symbol = info.dli_sname;
    1128           0 :   if (!symbol || symbol[0] == '\0') {
    1129           0 :     return true;
    1130             :   }
    1131             : 
    1132           0 :   DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function));
    1133             : 
    1134           0 :   if (aDetails->function[0] == '\0') {
    1135             :     // Just use the mangled symbol if demangling failed.
    1136           0 :     strncpy(aDetails->function, symbol, sizeof(aDetails->function));
    1137           0 :     aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
    1138             :   }
    1139             : 
    1140           0 :   aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
    1141           0 :   return true;
    1142             : }
    1143             : 
    1144             : #else // unsupported platform.
    1145             : 
    1146             : MFBT_API bool
    1147             : MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
    1148             :              uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
    1149             :              void* aPlatformData)
    1150             : {
    1151             :   MOZ_ASSERT(!aThread);
    1152             :   MOZ_ASSERT(!aPlatformData);
    1153             :   return false;
    1154             : }
    1155             : 
    1156             : MFBT_API bool
    1157             : MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
    1158             : {
    1159             :   aDetails->library[0] = '\0';
    1160             :   aDetails->loffset = 0;
    1161             :   aDetails->filename[0] = '\0';
    1162             :   aDetails->lineno = 0;
    1163             :   aDetails->function[0] = '\0';
    1164             :   aDetails->foffset = 0;
    1165             :   return false;
    1166             : }
    1167             : 
    1168             : #endif
    1169             : 
    1170             : #if defined(XP_WIN) || defined (XP_MACOSX) || defined (XP_LINUX)
    1171             : namespace mozilla {
    1172             : bool
    1173           0 : FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
    1174             :                       uint32_t aMaxFrames, void* aClosure, void** bp,
    1175             :                       void* aStackEnd)
    1176             : {
    1177             :   // Stack walking code courtesy Kipp's "leaky".
    1178             : 
    1179           0 :   int32_t skip = aSkipFrames;
    1180           0 :   uint32_t numFrames = 0;
    1181           0 :   while (bp) {
    1182           0 :     void** next = (void**)*bp;
    1183             :     // bp may not be a frame pointer on i386 if code was compiled with
    1184             :     // -fomit-frame-pointer, so do some sanity checks.
    1185             :     // (bp should be a frame pointer on ppc(64) but checking anyway may help
    1186             :     // a little if the stack has been corrupted.)
    1187             :     // We don't need to check against the begining of the stack because
    1188             :     // we can assume that bp > sp
    1189           0 :     if (next <= bp ||
    1190           0 :         next > aStackEnd ||
    1191           0 :         (uintptr_t(next) & 3)) {
    1192             :       break;
    1193             :     }
    1194             : #if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
    1195             :     // ppc mac or powerpc64 linux
    1196             :     void* pc = *(bp + 2);
    1197             :     bp += 3;
    1198             : #else // i386 or powerpc32 linux
    1199           0 :     void* pc = *(bp + 1);
    1200           0 :     bp += 2;
    1201             : #endif
    1202           0 :     if (IsCriticalAddress(pc)) {
    1203           0 :       return false;
    1204             :     }
    1205           0 :     if (--skip < 0) {
    1206             :       // Assume that the SP points to the BP of the function
    1207             :       // it called. We can't know the exact location of the SP
    1208             :       // but this should be sufficient for our use the SP
    1209             :       // to order elements on the stack.
    1210           0 :       numFrames++;
    1211           0 :       (*aCallback)(numFrames, pc, bp, aClosure);
    1212           0 :       if (aMaxFrames != 0 && numFrames == aMaxFrames) {
    1213           0 :         break;
    1214             :       }
    1215             :     }
    1216           0 :     bp = next;
    1217             :   }
    1218           0 :   return numFrames != 0;
    1219             : }
    1220             : } // namespace mozilla
    1221             : 
    1222             : #else
    1223             : 
    1224             : namespace mozilla {
    1225             : MFBT_API bool
    1226             : FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
    1227             :                       void* aClosure, void** aBp)
    1228             : {
    1229             :   return false;
    1230             : }
    1231             : }
    1232             : 
    1233             : #endif
    1234             : 
    1235             : MFBT_API void
    1236           0 : MozFormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
    1237             :                             uint32_t aFrameNumber, void* aPC,
    1238             :                             const MozCodeAddressDetails* aDetails)
    1239             : {
    1240           0 :   MozFormatCodeAddress(aBuffer, aBufferSize,
    1241             :                        aFrameNumber, aPC, aDetails->function,
    1242           0 :                        aDetails->library, aDetails->loffset,
    1243           0 :                        aDetails->filename, aDetails->lineno);
    1244           0 : }
    1245             : 
    1246             : MFBT_API void
    1247           0 : MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
    1248             :                      const void* aPC, const char* aFunction,
    1249             :                      const char* aLibrary, ptrdiff_t aLOffset,
    1250             :                      const char* aFileName, uint32_t aLineNo)
    1251             : {
    1252           0 :   const char* function = aFunction && aFunction[0] ? aFunction : "???";
    1253           0 :   if (aFileName && aFileName[0]) {
    1254             :     // We have a filename and (presumably) a line number. Use them.
    1255           0 :     snprintf(aBuffer, aBufferSize,
    1256             :              "#%02u: %s (%s:%u)",
    1257           0 :              aFrameNumber, function, aFileName, aLineNo);
    1258           0 :   } else if (aLibrary && aLibrary[0]) {
    1259             :     // We have no filename, but we do have a library name. Use it and the
    1260             :     // library offset, and print them in a way that scripts like
    1261             :     // fix_{linux,macosx}_stacks.py can easily post-process.
    1262           0 :     snprintf(aBuffer, aBufferSize,
    1263             :              "#%02u: %s[%s +0x%" PRIxPTR "]",
    1264           0 :              aFrameNumber, function, aLibrary, static_cast<uintptr_t>(aLOffset));
    1265             :   } else {
    1266             :     // We have nothing useful to go on. (The format string is split because
    1267             :     // '??)' is a trigraph and causes a warning, sigh.)
    1268           0 :     snprintf(aBuffer, aBufferSize,
    1269             :              "#%02u: ??? (???:???" ")",
    1270           0 :              aFrameNumber);
    1271             :   }
    1272           0 : }

Generated by: LCOV version 1.13