xpcom/base/nsStackWalk.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
     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/. */
     7 /* API for getting a stack trace of the C/C++ stack on the current thread */
     9 #include "mozilla/Assertions.h"
    10 #include "mozilla/IntegerPrintfMacros.h"
    11 #include "mozilla/StackWalk.h"
    12 #include "nsStackWalkPrivate.h"
    14 #include "nsStackWalk.h"
    16 using namespace mozilla;
    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   void* mAddr;
    22   bool mInit;
    23 };
    24 static CriticalAddress gCriticalAddress;
    26 // for _Unwind_Backtrace from libcxxrt or libunwind
    27 // cxxabi.h from libcxxrt implicitly includes unwind.h first
    28 #if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE)
    29 #define _GNU_SOURCE
    30 #endif
    32 #if defined(HAVE_DLOPEN) || defined(XP_MACOSX)
    33 #include <dlfcn.h>
    34 #endif
    36 #define NSSTACKWALK_SUPPORTS_MACOSX \
    37     (defined(XP_MACOSX) && \
    38      (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
    40 #define NSSTACKWALK_SUPPORTS_LINUX \
    41     (defined(linux) && \
    42      ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
    43       defined(HAVE__UNWIND_BACKTRACE)))
    45 #define NSSTACKWALK_SUPPORTS_SOLARIS \
    46     (defined(__sun) && \
    47      (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386)))
    49 #if NSSTACKWALK_SUPPORTS_MACOSX
    50 #include <pthread.h>
    51 #include <CoreServices/CoreServices.h>
    53 typedef void
    54 malloc_logger_t(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
    55                 uintptr_t result, uint32_t num_hot_frames_to_skip);
    56 extern malloc_logger_t *malloc_logger;
    58 static void
    59 stack_callback(void *pc, void *sp, void *closure)
    60 {
    61   const char *name = reinterpret_cast<char *>(closure);
    62   Dl_info info;
    64   // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
    65   // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
    66   // correct one is the first that we find on our way up, so the
    67   // following check for gCriticalAddress.mAddr is critical.
    68   if (gCriticalAddress.mAddr || dladdr(pc, &info) == 0  ||
    69       info.dli_sname == nullptr || strcmp(info.dli_sname, name) != 0)
    70     return;
    71   gCriticalAddress.mAddr = pc;
    72 }
    74 #ifdef DEBUG
    75 #define MAC_OS_X_VERSION_10_7_HEX 0x00001070
    77 static int32_t OSXVersion()
    78 {
    79   static int32_t gOSXVersion = 0x0;
    80   if (gOSXVersion == 0x0) {
    81     OSErr err = ::Gestalt(gestaltSystemVersion, (SInt32*)&gOSXVersion);
    82     MOZ_ASSERT(err == noErr);
    83   }
    84   return gOSXVersion;
    85 }
    87 static bool OnLionOrLater()
    88 {
    89   return (OSXVersion() >= MAC_OS_X_VERSION_10_7_HEX);
    90 }
    91 #endif
    93 static void
    94 my_malloc_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
    95                  uintptr_t result, uint32_t num_hot_frames_to_skip)
    96 {
    97   static bool once = false;
    98   if (once)
    99     return;
   100   once = true;
   102   // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
   103   // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
   104   const char *name = "new_sem_from_pool";
   105   NS_StackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0,
   106                const_cast<char*>(name), 0, nullptr);
   107 }
   109 // This is called from NS_LogInit() and from the stack walking functions, but
   110 // only the first call has any effect.  We need to call this function from both
   111 // places because it must run before any mutexes are created, and also before
   112 // any objects whose refcounts we're logging are created.  Running this
   113 // function during NS_LogInit() ensures that we meet the first criterion, and
   114 // running this function during the stack walking functions ensures we meet the
   115 // second criterion.
   116 void
   117 StackWalkInitCriticalAddress()
   118 {
   119   if(gCriticalAddress.mInit)
   120     return;
   121   gCriticalAddress.mInit = true;
   122   // We must not do work when 'new_sem_from_pool' calls realloc, since
   123   // it holds a non-reentrant spin-lock and we will quickly deadlock.
   124   // new_sem_from_pool is not directly accessible using dlsym, so
   125   // we force a situation where new_sem_from_pool is on the stack and
   126   // use dladdr to check the addresses.
   128   // malloc_logger can be set by external tools like 'Instruments' or 'leaks'
   129   malloc_logger_t *old_malloc_logger = malloc_logger;
   130   malloc_logger = my_malloc_logger;
   132   pthread_cond_t cond;
   133   int r = pthread_cond_init(&cond, 0);
   134   MOZ_ASSERT(r == 0);
   135   pthread_mutex_t mutex;
   136   r = pthread_mutex_init(&mutex,0);
   137   MOZ_ASSERT(r == 0);
   138   r = pthread_mutex_lock(&mutex);
   139   MOZ_ASSERT(r == 0);
   140   struct timespec abstime = {0, 1};
   141   r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime);
   143   // restore the previous malloc logger
   144   malloc_logger = old_malloc_logger;
   146   // On Lion, malloc is no longer called from pthread_cond_*wait*. This prevents
   147   // us from finding the address, but that is fine, since with no call to malloc
   148   // there is no critical address.
   149   MOZ_ASSERT(OnLionOrLater() || gCriticalAddress.mAddr != nullptr);
   150   MOZ_ASSERT(r == ETIMEDOUT);
   151   r = pthread_mutex_unlock(&mutex);
   152   MOZ_ASSERT(r == 0);
   153   r = pthread_mutex_destroy(&mutex);
   154   MOZ_ASSERT(r == 0);
   155   r = pthread_cond_destroy(&cond);
   156   MOZ_ASSERT(r == 0);
   157 }
   159 static bool IsCriticalAddress(void* aPC)
   160 {
   161   return gCriticalAddress.mAddr == aPC;
   162 }
   163 #else
   164 static bool IsCriticalAddress(void* aPC)
   165 {
   166   return false;
   167 }
   168 // We still initialize gCriticalAddress.mInit so that this code behaves
   169 // the same on all platforms. Otherwise a failure to init would be visible
   170 // only on OS X.
   171 void
   172 StackWalkInitCriticalAddress()
   173 {
   174   gCriticalAddress.mInit = true;
   175 }
   176 #endif
   178 #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
   180 #include "nscore.h"
   181 #include <windows.h>
   182 #include <process.h>
   183 #include <stdio.h>
   184 #include <malloc.h>
   185 #include "plstr.h"
   186 #include "mozilla/ArrayUtils.h"
   188 #include "nspr.h"
   189 #include <imagehlp.h>
   190 // We need a way to know if we are building for WXP (or later), as if we are, we
   191 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
   192 // A value of 9 indicates we want to use the new APIs.
   193 #if API_VERSION_NUMBER < 9
   194 #error Too old imagehlp.h
   195 #endif
   197 // Define these as static pointers so that we can load the DLL on the
   198 // fly (and not introduce a link-time dependency on it). Tip o' the
   199 // hat to Matt Pietrick for this idea. See:
   200 //
   201 //   http://msdn.microsoft.com/library/periodic/period97/F1/D3/S245C6.htm
   202 //
   203 extern "C" {
   205 extern HANDLE hStackWalkMutex; 
   207 bool EnsureSymInitialized();
   209 bool EnsureWalkThreadReady();
   211 struct WalkStackData {
   212   uint32_t skipFrames;
   213   HANDLE thread;
   214   bool walkCallingThread;
   215   HANDLE process;
   216   HANDLE eventStart;
   217   HANDLE eventEnd;
   218   void **pcs;
   219   uint32_t pc_size;
   220   uint32_t pc_count;
   221   uint32_t pc_max;
   222   void **sps;
   223   uint32_t sp_size;
   224   uint32_t sp_count;
   225   void *platformData;
   226 };
   228 void PrintError(char *prefix, WalkStackData* data);
   229 unsigned int WINAPI WalkStackThread(void* data);
   230 void WalkStackMain64(struct WalkStackData* data);
   233 DWORD gStackWalkThread;
   234 CRITICAL_SECTION gDbgHelpCS;
   236 }
   238 // Routine to print an error message to standard error.
   239 void PrintError(const char *prefix)
   240 {
   241     LPVOID lpMsgBuf;
   242     DWORD lastErr = GetLastError();
   243     FormatMessageA(
   244       FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
   245       nullptr,
   246       lastErr,
   247       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
   248       (LPSTR) &lpMsgBuf,
   249       0,
   250       nullptr
   251     );
   252     fprintf(stderr, "### ERROR: %s: %s",
   253                     prefix, lpMsgBuf ? lpMsgBuf : "(null)\n");
   254     fflush(stderr);
   255     LocalFree(lpMsgBuf);
   256 }
   258 bool
   259 EnsureWalkThreadReady()
   260 {
   261     static bool walkThreadReady = false;
   262     static HANDLE stackWalkThread = nullptr;
   263     static HANDLE readyEvent = nullptr;
   265     if (walkThreadReady)
   266         return walkThreadReady;
   268     if (stackWalkThread == nullptr) {
   269         readyEvent = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
   270                                    FALSE /* initially non-signaled */,
   271                                    nullptr);
   272         if (readyEvent == nullptr) {
   273             PrintError("CreateEvent");
   274             return false;
   275         }
   277         unsigned int threadID;
   278         stackWalkThread = (HANDLE)
   279             _beginthreadex(nullptr, 0, WalkStackThread, (void*)readyEvent,
   280                            0, &threadID);
   281         if (stackWalkThread == nullptr) {
   282             PrintError("CreateThread");
   283             ::CloseHandle(readyEvent);
   284             readyEvent = nullptr;
   285             return false;
   286         }
   287         gStackWalkThread = threadID;
   288         ::CloseHandle(stackWalkThread);
   289     }
   291     MOZ_ASSERT((stackWalkThread != nullptr && readyEvent != nullptr) ||
   292                (stackWalkThread == nullptr && readyEvent == nullptr));
   294     // The thread was created. Try to wait an arbitrary amount of time (1 second
   295     // should be enough) for its event loop to start before posting events to it.
   296     DWORD waitRet = ::WaitForSingleObject(readyEvent, 1000);
   297     if (waitRet == WAIT_TIMEOUT) {
   298         // We get a timeout if we're called during static initialization because
   299         // the thread will only start executing after we return so it couldn't
   300         // have signalled the event. If that is the case, give up for now and
   301         // try again next time we're called.
   302         return false;
   303     }
   304     ::CloseHandle(readyEvent);
   305     stackWalkThread = nullptr;
   306     readyEvent = nullptr;
   309     ::InitializeCriticalSection(&gDbgHelpCS);
   311     return walkThreadReady = true;
   312 }
   314 void
   315 WalkStackMain64(struct WalkStackData* data)
   316 {
   317     // Get the context information for the thread. That way we will
   318     // know where our sp, fp, pc, etc. are and can fill in the
   319     // STACKFRAME64 with the initial values.
   320     CONTEXT context;
   321     HANDLE myProcess = data->process;
   322     HANDLE myThread = data->thread;
   323     DWORD64 addr;
   324     DWORD64 spaddr;
   325     STACKFRAME64 frame64;
   326     // skip our own stack walking frames
   327     int skip = (data->walkCallingThread ? 3 : 0) + data->skipFrames;
   328     BOOL ok;
   330     // Get a context for the specified thread.
   331     if (!data->platformData) {
   332         memset(&context, 0, sizeof(CONTEXT));
   333         context.ContextFlags = CONTEXT_FULL;
   334         if (!GetThreadContext(myThread, &context)) {
   335             if (data->walkCallingThread) {
   336                 PrintError("GetThreadContext");
   337             }
   338             return;
   339         }
   340     } else {
   341         context = *static_cast<CONTEXT*>(data->platformData);
   342     }
   344     // Setup initial stack frame to walk from
   345     memset(&frame64, 0, sizeof(frame64));
   346 #ifdef _M_IX86
   347     frame64.AddrPC.Offset    = context.Eip;
   348     frame64.AddrStack.Offset = context.Esp;
   349     frame64.AddrFrame.Offset = context.Ebp;
   350 #elif defined _M_AMD64
   351     frame64.AddrPC.Offset    = context.Rip;
   352     frame64.AddrStack.Offset = context.Rsp;
   353     frame64.AddrFrame.Offset = context.Rbp;
   354 #elif defined _M_IA64
   355     frame64.AddrPC.Offset    = context.StIIP;
   356     frame64.AddrStack.Offset = context.SP;
   357     frame64.AddrFrame.Offset = context.RsBSP;
   358 #else
   359 #error "Should not have compiled this code"
   360 #endif
   361     frame64.AddrPC.Mode      = AddrModeFlat;
   362     frame64.AddrStack.Mode   = AddrModeFlat;
   363     frame64.AddrFrame.Mode   = AddrModeFlat;
   364     frame64.AddrReturn.Mode  = AddrModeFlat;
   366     // Now walk the stack
   367     while (1) {
   369         // debug routines are not threadsafe, so grab the lock.
   370         EnterCriticalSection(&gDbgHelpCS);
   371         ok = StackWalk64(
   372 #ifdef _M_AMD64
   373           IMAGE_FILE_MACHINE_AMD64,
   374 #elif defined _M_IA64
   375           IMAGE_FILE_MACHINE_IA64,
   376 #elif defined _M_IX86
   377           IMAGE_FILE_MACHINE_I386,
   378 #else
   379 #error "Should not have compiled this code"
   380 #endif
   381           myProcess,
   382           myThread,
   383           &frame64,
   384           &context,
   385           nullptr,
   386           SymFunctionTableAccess64, // function table access routine
   387           SymGetModuleBase64,       // module base routine
   388           0
   389         );
   390         LeaveCriticalSection(&gDbgHelpCS);
   392         if (ok) {
   393             addr = frame64.AddrPC.Offset;
   394             spaddr = frame64.AddrStack.Offset;
   395          } else {
   396             addr = 0;
   397             spaddr = 0;
   398             if (data->walkCallingThread) {
   399                 PrintError("WalkStack64");
   400             }
   401         }
   403         if (!ok || (addr == 0)) {
   404             break;
   405         }
   407         if (skip-- > 0) {
   408             continue;
   409         }
   411         if (data->pc_count < data->pc_size)
   412             data->pcs[data->pc_count] = (void*)addr;
   413         ++data->pc_count;
   415         if (data->sp_count < data->sp_size)
   416             data->sps[data->sp_count] = (void*)spaddr;
   417         ++data->sp_count;
   419         if (data->pc_max != 0 && data->pc_count == data->pc_max)
   420             break;
   422         if (frame64.AddrReturn.Offset == 0)
   423             break;
   424     }
   425     return;
   426 }
   429 unsigned int WINAPI
   430 WalkStackThread(void* aData)
   431 {
   432     BOOL msgRet;
   433     MSG msg;
   435     // Call PeekMessage to force creation of a message queue so that
   436     // other threads can safely post events to us.
   437     ::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
   439     // and tell the thread that created us that we're ready.
   440     HANDLE readyEvent = (HANDLE)aData;
   441     ::SetEvent(readyEvent);
   443     while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) {
   444         if (msgRet == -1) {
   445             PrintError("GetMessage");
   446         } else {
   447             DWORD ret;
   449             struct WalkStackData *data = (WalkStackData *)msg.lParam;
   450             if (!data)
   451               continue;
   453             // Don't suspend the calling thread until it's waiting for
   454             // us; otherwise the number of frames on the stack could vary.
   455             ret = ::WaitForSingleObject(data->eventStart, INFINITE);
   456             if (ret != WAIT_OBJECT_0)
   457                 PrintError("WaitForSingleObject");
   459             // Suspend the calling thread, dump his stack, and then resume him.
   460             // He's currently waiting for us to finish so now should be a good time.
   461             ret = ::SuspendThread( data->thread );
   462             if (ret == -1) {
   463                 PrintError("ThreadSuspend");
   464             }
   465             else {
   466                 WalkStackMain64(data);
   468                 ret = ::ResumeThread(data->thread);
   469                 if (ret == -1) {
   470                     PrintError("ThreadResume");
   471                 }
   472             }
   474             ::SetEvent(data->eventEnd);
   475         }
   476     }
   478     return 0;
   479 }
   481 /**
   482  * Walk the stack, translating PC's found into strings and recording the
   483  * chain in aBuffer. For this to work properly, the DLLs must be rebased
   484  * so that the address in the file agrees with the address in memory.
   485  * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
   486  * whose in memory address doesn't match its in-file address.
   487  */
   489 EXPORT_XPCOM_API(nsresult)
   490 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
   491              uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
   492              void *aPlatformData)
   493 {
   494     StackWalkInitCriticalAddress();
   495     static HANDLE myProcess = nullptr;
   496     HANDLE myThread;
   497     DWORD walkerReturn;
   498     struct WalkStackData data;
   500     if (!EnsureWalkThreadReady())
   501         return NS_ERROR_FAILURE;
   503     HANDLE targetThread = ::GetCurrentThread();
   504     data.walkCallingThread = true;
   505     if (aThread) {
   506         HANDLE threadToWalk = reinterpret_cast<HANDLE> (aThread);
   507         // walkCallingThread indicates whether we are walking the caller's stack
   508         data.walkCallingThread = (threadToWalk == targetThread);
   509         targetThread = threadToWalk;
   510     }
   512     // We need to avoid calling fprintf and friends if we're walking the stack of
   513     // another thread, in order to avoid deadlocks.
   514     const bool shouldBeThreadSafe = !!aThread;
   516     // Have to duplicate handle to get a real handle.
   517     if (!myProcess) {
   518         if (!::DuplicateHandle(::GetCurrentProcess(),
   519                                ::GetCurrentProcess(),
   520                                ::GetCurrentProcess(),
   521                                &myProcess,
   522                                PROCESS_ALL_ACCESS, FALSE, 0)) {
   523             if (!shouldBeThreadSafe) {
   524                 PrintError("DuplicateHandle (process)");
   525             }
   526             return NS_ERROR_FAILURE;
   527         }
   528     }
   529     if (!::DuplicateHandle(::GetCurrentProcess(),
   530                            targetThread,
   531                            ::GetCurrentProcess(),
   532                            &myThread,
   533                            THREAD_ALL_ACCESS, FALSE, 0)) {
   534         if (!shouldBeThreadSafe) {
   535             PrintError("DuplicateHandle (thread)");
   536         }
   537         return NS_ERROR_FAILURE;
   538     }
   540     data.skipFrames = aSkipFrames;
   541     data.thread = myThread;
   542     data.process = myProcess;
   543     void *local_pcs[1024];
   544     data.pcs = local_pcs;
   545     data.pc_count = 0;
   546     data.pc_size = ArrayLength(local_pcs);
   547     data.pc_max = aMaxFrames;
   548     void *local_sps[1024];
   549     data.sps = local_sps;
   550     data.sp_count = 0;
   551     data.sp_size = ArrayLength(local_sps);
   552     data.platformData = aPlatformData;
   554     if (aThread) {
   555         // If we're walking the stack of another thread, we don't need to
   556         // use a separate walker thread.
   557         WalkStackMain64(&data);
   559         if (data.pc_count > data.pc_size) {
   560             data.pcs = (void**) _alloca(data.pc_count * sizeof(void*));
   561             data.pc_size = data.pc_count;
   562             data.pc_count = 0;
   563             data.sps = (void**) _alloca(data.sp_count * sizeof(void*));
   564             data.sp_size = data.sp_count;
   565             data.sp_count = 0;
   566             WalkStackMain64(&data);
   567         }
   568     } else {
   569         data.eventStart = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
   570                               FALSE /* initially non-signaled */, nullptr);
   571         data.eventEnd = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
   572                             FALSE /* initially non-signaled */, nullptr);
   574         ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
   576         walkerReturn = ::SignalObjectAndWait(data.eventStart,
   577                            data.eventEnd, INFINITE, FALSE);
   578         if (walkerReturn != WAIT_OBJECT_0 && !shouldBeThreadSafe)
   579             PrintError("SignalObjectAndWait (1)");
   580         if (data.pc_count > data.pc_size) {
   581             data.pcs = (void**) _alloca(data.pc_count * sizeof(void*));
   582             data.pc_size = data.pc_count;
   583             data.pc_count = 0;
   584             data.sps = (void**) _alloca(data.sp_count * sizeof(void*));
   585             data.sp_size = data.sp_count;
   586             data.sp_count = 0;
   587             ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
   588             walkerReturn = ::SignalObjectAndWait(data.eventStart,
   589                                data.eventEnd, INFINITE, FALSE);
   590             if (walkerReturn != WAIT_OBJECT_0 && !shouldBeThreadSafe)
   591                 PrintError("SignalObjectAndWait (2)");
   592         }
   594         ::CloseHandle(data.eventStart);
   595         ::CloseHandle(data.eventEnd);
   596     }
   598     ::CloseHandle(myThread);
   600     for (uint32_t i = 0; i < data.pc_count; ++i)
   601         (*aCallback)(data.pcs[i], data.sps[i], aClosure);
   603     return data.pc_count == 0 ? NS_ERROR_FAILURE : NS_OK;
   604 }
   607 static BOOL CALLBACK callbackEspecial64(
   608   PCSTR aModuleName,
   609   DWORD64 aModuleBase,
   610   ULONG aModuleSize,
   611   PVOID aUserContext)
   612 {
   613     BOOL retval = TRUE;
   614     DWORD64 addr = *(DWORD64*)aUserContext;
   616     /*
   617      * You'll want to control this if we are running on an
   618      *  architecture where the addresses go the other direction.
   619      * Not sure this is even a realistic consideration.
   620      */
   621     const BOOL addressIncreases = TRUE;
   623     /*
   624      * If it falls in side the known range, load the symbols.
   625      */
   626     if (addressIncreases
   627        ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize))
   628        : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))
   629         ) {
   630         retval = !!SymLoadModule64(GetCurrentProcess(), nullptr,
   631                                    (PSTR)aModuleName, nullptr,
   632                                    aModuleBase, aModuleSize);
   633         if (!retval)
   634             PrintError("SymLoadModule64");
   635     }
   637     return retval;
   638 }
   640 /*
   641  * SymGetModuleInfoEspecial
   642  *
   643  * Attempt to determine the module information.
   644  * Bug 112196 says this DLL may not have been loaded at the time
   645  *  SymInitialize was called, and thus the module information
   646  *  and symbol information is not available.
   647  * This code rectifies that problem.
   648  */
   650 // New members were added to IMAGEHLP_MODULE64 (that show up in the
   651 // Platform SDK that ships with VC8, but not the Platform SDK that ships
   652 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
   653 // use them, and it's useful to be able to function correctly with the
   654 // older library.  (Stock Windows XP SP2 seems to ship with dbghelp.dll
   655 // version 5.1.)  Since Platform SDK version need not correspond to
   656 // compiler version, and the version number in debughlp.h was NOT bumped
   657 // when these changes were made, ifdef based on a constant that was
   658 // added between these versions.
   659 #ifdef SSRVOPT_SETCONTEXT
   660 #define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64))
   661 #else
   662 #define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
   663 #endif
   665 BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr, PIMAGEHLP_MODULE64 aModuleInfo, PIMAGEHLP_LINE64 aLineInfo)
   666 {
   667     BOOL retval = FALSE;
   669     /*
   670      * Init the vars if we have em.
   671      */
   672     aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE;
   673     if (nullptr != aLineInfo) {
   674         aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
   675     }
   677     /*
   678      * Give it a go.
   679      * It may already be loaded.
   680      */
   681     retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
   683     if (FALSE == retval) {
   684         BOOL enumRes = FALSE;
   686         /*
   687          * Not loaded, here's the magic.
   688          * Go through all the modules.
   689          */
   690         // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
   691         // constness of the first parameter of
   692         // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
   693         // non-const to const over time).  See bug 391848 and bug
   694         // 415426.
   695         enumRes = EnumerateLoadedModules64(aProcess, (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64, (PVOID)&aAddr);
   696         if (FALSE != enumRes)
   697         {
   698             /*
   699              * One final go.
   700              * If it fails, then well, we have other problems.
   701              */
   702             retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
   703         }
   704     }
   706     /*
   707      * If we got module info, we may attempt line info as well.
   708      * We will not report failure if this does not work.
   709      */
   710     if (FALSE != retval && nullptr != aLineInfo) {
   711         DWORD displacement = 0;
   712         BOOL lineRes = FALSE;
   713         lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo);
   714         if (!lineRes) {
   715             // Clear out aLineInfo to indicate that it's not valid
   716             memset(aLineInfo, 0, sizeof(*aLineInfo));
   717         }
   718     }
   720     return retval;
   721 }
   723 bool
   724 EnsureSymInitialized()
   725 {
   726     static bool gInitialized = false;
   727     bool retStat;
   729     if (gInitialized)
   730         return gInitialized;
   732     if (!EnsureWalkThreadReady())
   733         return false;
   735     SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
   736     retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
   737     if (!retStat)
   738         PrintError("SymInitialize");
   740     gInitialized = retStat;
   741     /* XXX At some point we need to arrange to call SymCleanup */
   743     return retStat;
   744 }
   747 EXPORT_XPCOM_API(nsresult)
   748 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
   749 {
   750     aDetails->library[0] = '\0';
   751     aDetails->loffset = 0;
   752     aDetails->filename[0] = '\0';
   753     aDetails->lineno = 0;
   754     aDetails->function[0] = '\0';
   755     aDetails->foffset = 0;
   757     if (!EnsureSymInitialized())
   758         return NS_ERROR_FAILURE;
   760     HANDLE myProcess = ::GetCurrentProcess();
   761     BOOL ok;
   763     // debug routines are not threadsafe, so grab the lock.
   764     EnterCriticalSection(&gDbgHelpCS);
   766     //
   767     // Attempt to load module info before we attempt to resolve the symbol.
   768     // This just makes sure we get good info if available.
   769     //
   771     DWORD64 addr = (DWORD64)aPC;
   772     IMAGEHLP_MODULE64 modInfo;
   773     IMAGEHLP_LINE64 lineInfo;
   774     BOOL modInfoRes;
   775     modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo);
   777     if (modInfoRes) {
   778         PL_strncpyz(aDetails->library, modInfo.ModuleName,
   779                     sizeof(aDetails->library));
   780         aDetails->loffset = (char*) aPC - (char*) modInfo.BaseOfImage;
   782         if (lineInfo.FileName) {
   783             PL_strncpyz(aDetails->filename, lineInfo.FileName,
   784                         sizeof(aDetails->filename));
   785             aDetails->lineno = lineInfo.LineNumber;
   786         }
   787     }
   789     ULONG64 buffer[(sizeof(SYMBOL_INFO) +
   790       MAX_SYM_NAME*sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
   791     PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
   792     pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
   793     pSymbol->MaxNameLen = MAX_SYM_NAME;
   795     DWORD64 displacement;
   796     ok = SymFromAddr(myProcess, addr, &displacement, pSymbol);
   798     if (ok) {
   799         PL_strncpyz(aDetails->function, pSymbol->Name,
   800                     sizeof(aDetails->function));
   801         aDetails->foffset = static_cast<ptrdiff_t>(displacement);
   802     }
   804     LeaveCriticalSection(&gDbgHelpCS); // release our lock
   805     return NS_OK;
   806 }
   808 EXPORT_XPCOM_API(nsresult)
   809 NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
   810                             char *aBuffer, uint32_t aBufferSize)
   811 {
   812     if (aDetails->function[0]) {
   813         _snprintf(aBuffer, aBufferSize, "%s+0x%08lX [%s +0x%016lX]",
   814                   aDetails->function, aDetails->foffset,
   815                   aDetails->library, aDetails->loffset);
   816     } else if (aDetails->library[0]) {
   817         _snprintf(aBuffer, aBufferSize, "UNKNOWN [%s +0x%016lX]",
   818                   aDetails->library, aDetails->loffset);
   819     } else {
   820         _snprintf(aBuffer, aBufferSize, "UNKNOWN 0x%016lX", aPC);
   821     }
   823     aBuffer[aBufferSize - 1] = '\0';
   825     uint32_t len = strlen(aBuffer);
   826     if (aDetails->filename[0]) {
   827         _snprintf(aBuffer + len, aBufferSize - len, " (%s, line %d)\n",
   828                   aDetails->filename, aDetails->lineno);
   829     } else {
   830         aBuffer[len] = '\n';
   831         if (++len != aBufferSize)
   832             aBuffer[len] = '\0';
   833     }
   834     aBuffer[aBufferSize - 2] = '\n';
   835     aBuffer[aBufferSize - 1] = '\0';
   836     return NS_OK;
   837 }
   839 // WIN32 x86 stack walking code
   840 // i386 or PPC Linux stackwalking code or Solaris
   841 #elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || NSSTACKWALK_SUPPORTS_LINUX || NSSTACKWALK_SUPPORTS_SOLARIS || NSSTACKWALK_SUPPORTS_MACOSX)
   843 #include <stdlib.h>
   844 #include <string.h>
   845 #include "nscore.h"
   846 #include <stdio.h>
   847 #include "plstr.h"
   849 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
   850 // if __USE_GNU is defined.  I suppose its some kind of standards
   851 // adherence thing.
   852 //
   853 #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
   854 #define __USE_GNU
   855 #endif
   857 // This thing is exported by libstdc++
   858 // Yes, this is a gcc only hack
   859 #if defined(MOZ_DEMANGLE_SYMBOLS)
   860 #include <cxxabi.h>
   861 #endif // MOZ_DEMANGLE_SYMBOLS
   863 void DemangleSymbol(const char * aSymbol, 
   864                     char * aBuffer,
   865                     int aBufLen)
   866 {
   867     aBuffer[0] = '\0';
   869 #if defined(MOZ_DEMANGLE_SYMBOLS)
   870     /* See demangle.h in the gcc source for the voodoo */
   871     char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
   873     if (demangled)
   874     {
   875         PL_strncpyz(aBuffer,demangled,aBufLen);
   876         free(demangled);
   877     }
   878 #endif // MOZ_DEMANGLE_SYMBOLS
   879 }
   882 #if NSSTACKWALK_SUPPORTS_SOLARIS
   884 /*
   885  * Stack walking code for Solaris courtesy of Bart Smaalder's "memtrak".
   886  */
   888 #include <synch.h>
   889 #include <ucontext.h>
   890 #include <sys/frame.h>
   891 #include <sys/regset.h>
   892 #include <sys/stack.h>
   894 static int    load_address ( void * pc, void * arg );
   895 static struct bucket * newbucket ( void * pc );
   896 static struct frame * cs_getmyframeptr ( void );
   897 static void   cs_walk_stack ( void * (*read_func)(char * address),
   898                               struct frame * fp,
   899                               int (*operate_func)(void *, void *, void *),
   900                               void * usrarg );
   901 static void   cs_operate ( void (*operate_func)(void *, void *, void *),
   902                            void * usrarg );
   904 #ifndef STACK_BIAS
   905 #define STACK_BIAS 0
   906 #endif /*STACK_BIAS*/
   908 #define LOGSIZE 4096
   910 /* type of demangling function */
   911 typedef int demf_t(const char *, char *, size_t);
   913 static demf_t *demf;
   915 static int initialized = 0;
   917 #if defined(sparc) || defined(__sparc)
   918 #define FRAME_PTR_REGISTER REG_SP
   919 #endif
   921 #if defined(i386) || defined(__i386)
   922 #define FRAME_PTR_REGISTER EBP
   923 #endif
   925 struct bucket {
   926     void * pc;
   927     int index;
   928     struct bucket * next;
   929 };
   931 struct my_user_args {
   932     NS_WalkStackCallback callback;
   933     uint32_t skipFrames;
   934     uint32_t maxFrames;
   935     uint32_t numFrames;
   936     void *closure;
   937 };
   940 static void myinit();
   942 #pragma init (myinit)
   944 static void
   945 myinit()
   946 {
   948     if (! initialized) {
   949 #ifndef __GNUC__
   950         void *handle;
   951         const char *libdem = "libdemangle.so.1";
   953         /* load libdemangle if we can and need to (only try this once) */
   954         if ((handle = dlopen(libdem, RTLD_LAZY)) != nullptr) {
   955             demf = (demf_t *)dlsym(handle,
   956                            "cplus_demangle"); /*lint !e611 */
   957                 /*
   958                  * lint override above is to prevent lint from
   959                  * complaining about "suspicious cast".
   960                  */
   961         }
   962 #endif /*__GNUC__*/
   963     }    
   964     initialized = 1;
   965 }
   968 static int
   969 load_address(void * pc, void * arg)
   970 {
   971     static struct bucket table[2048];
   972     static mutex_t lock;
   973     struct bucket * ptr;
   974     struct my_user_args * args = (struct my_user_args *) arg;
   976     unsigned int val = NS_PTR_TO_INT32(pc);
   978     ptr = table + ((val >> 2)&2047);
   980     mutex_lock(&lock);
   981     while (ptr->next) {
   982         if (ptr->next->pc == pc)
   983             break;
   984         ptr = ptr->next;
   985     }
   987     int stop = 0;
   988     if (ptr->next) {
   989         mutex_unlock(&lock);
   990     } else {
   991         (args->callback)(pc, args->closure);
   992         args->numFrames++;
   993         if (args->maxFrames != 0 && args->numFrames == args->maxFrames)
   994           stop = 1;   // causes us to stop getting frames
   996         ptr->next = newbucket(pc);
   997         mutex_unlock(&lock);
   998     }
   999     return stop;
  1003 static struct bucket *
  1004 newbucket(void * pc)
  1006     struct bucket * ptr = (struct bucket *) malloc(sizeof (*ptr));
  1007     static int index; /* protected by lock in caller */
  1009     ptr->index = index++;
  1010     ptr->next = nullptr;
  1011     ptr->pc = pc;    
  1012     return (ptr);    
  1016 static struct frame *
  1017 csgetframeptr()
  1019     ucontext_t u;
  1020     struct frame *fp;
  1022     (void) getcontext(&u);
  1024     fp = (struct frame *)
  1025         ((char *)u.uc_mcontext.gregs[FRAME_PTR_REGISTER] +
  1026         STACK_BIAS);
  1028     /* make sure to return parents frame pointer.... */
  1030     return ((struct frame *)((ulong_t)fp->fr_savfp + STACK_BIAS));
  1034 static void
  1035 cswalkstack(struct frame *fp, int (*operate_func)(void *, void *, void *),
  1036     void *usrarg)
  1039     while (fp != 0 && fp->fr_savpc != 0) {
  1041         if (operate_func((void *)fp->fr_savpc, nullptr, usrarg) != 0)
  1042             break;
  1043         /*
  1044          * watch out - libthread stacks look funny at the top
  1045          * so they may not have their STACK_BIAS set
  1046          */
  1048         fp = (struct frame *)((ulong_t)fp->fr_savfp +
  1049             (fp->fr_savfp?(ulong_t)STACK_BIAS:0));
  1054 static void
  1055 cs_operate(int (*operate_func)(void *, void *, void *), void * usrarg)
  1057     cswalkstack(csgetframeptr(), operate_func, usrarg);
  1060 EXPORT_XPCOM_API(nsresult)
  1061 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
  1062              uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
  1063              void *aPlatformData)
  1065     MOZ_ASSERT(!aThread);
  1066     MOZ_ASSERT(!aPlatformData);
  1067     struct my_user_args args;
  1069     StackWalkInitCriticalAddress();
  1071     if (!initialized)
  1072         myinit();
  1074     args.callback = aCallback;
  1075     args.skipFrames = aSkipFrames; /* XXX Not handled! */
  1076     args.maxFrames = aMaxFrames;
  1077     args.numFrames = 0;
  1078     args.closure = aClosure;
  1079     cs_operate(load_address, &args);
  1080     return args.numFrames == 0 ? NS_ERROR_FAILURE : NS_OK;
  1083 EXPORT_XPCOM_API(nsresult)
  1084 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
  1086     aDetails->library[0] = '\0';
  1087     aDetails->loffset = 0;
  1088     aDetails->filename[0] = '\0';
  1089     aDetails->lineno = 0;
  1090     aDetails->function[0] = '\0';
  1091     aDetails->foffset = 0;
  1093     char dembuff[4096];
  1094     Dl_info info;
  1096     if (dladdr(aPC, & info)) {
  1097         if (info.dli_fname) {
  1098             PL_strncpyz(aDetails->library, info.dli_fname,
  1099                         sizeof(aDetails->library));
  1100             aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
  1102         if (info.dli_sname) {
  1103             aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
  1104 #ifdef __GNUC__
  1105             DemangleSymbol(info.dli_sname, dembuff, sizeof(dembuff));
  1106 #else
  1107             if (!demf || demf(info.dli_sname, dembuff, sizeof (dembuff)))
  1108                 dembuff[0] = 0;
  1109 #endif /*__GNUC__*/
  1110             PL_strncpyz(aDetails->function,
  1111                         (dembuff[0] != '\0') ? dembuff : info.dli_sname,
  1112                         sizeof(aDetails->function));
  1116     return NS_OK;
  1119 EXPORT_XPCOM_API(nsresult)
  1120 NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
  1121                             char *aBuffer, uint32_t aBufferSize)
  1123     snprintf(aBuffer, aBufferSize, "%p %s:%s+0x%lx\n",
  1124              aPC,
  1125              aDetails->library[0] ? aDetails->library : "??",
  1126              aDetails->function[0] ? aDetails->function : "??",
  1127              aDetails->foffset);
  1128     return NS_OK;
  1131 #else // not __sun-specific
  1133 #if __GLIBC__ > 2 || __GLIBC_MINOR > 1
  1134 #define HAVE___LIBC_STACK_END 1
  1135 #else
  1136 #define HAVE___LIBC_STACK_END 0
  1137 #endif
  1139 #if HAVE___LIBC_STACK_END
  1140 extern void *__libc_stack_end; // from ld-linux.so
  1141 #endif
  1142 namespace mozilla {
  1143 nsresult
  1144 FramePointerStackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
  1145                       uint32_t aMaxFrames, void *aClosure, void **bp,
  1146                       void *aStackEnd)
  1148   // Stack walking code courtesy Kipp's "leaky".
  1150   int32_t skip = aSkipFrames;
  1151   uint32_t numFrames = 0;
  1152   while (1) {
  1153     void **next = (void**)*bp;
  1154     // bp may not be a frame pointer on i386 if code was compiled with
  1155     // -fomit-frame-pointer, so do some sanity checks.
  1156     // (bp should be a frame pointer on ppc(64) but checking anyway may help
  1157     // a little if the stack has been corrupted.)
  1158     // We don't need to check against the begining of the stack because
  1159     // we can assume that bp > sp
  1160     if (next <= bp ||
  1161         next > aStackEnd ||
  1162         (long(next) & 3)) {
  1163       break;
  1165 #if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
  1166     // ppc mac or powerpc64 linux
  1167     void *pc = *(bp+2);
  1168     bp += 3;
  1169 #else // i386 or powerpc32 linux
  1170     void *pc = *(bp+1);
  1171     bp += 2;
  1172 #endif
  1173     if (IsCriticalAddress(pc)) {
  1174       printf("Aborting stack trace, PC is critical\n");
  1175       return NS_ERROR_UNEXPECTED;
  1177     if (--skip < 0) {
  1178       // Assume that the SP points to the BP of the function
  1179       // it called. We can't know the exact location of the SP
  1180       // but this should be sufficient for our use the SP
  1181       // to order elements on the stack.
  1182       (*aCallback)(pc, bp, aClosure);
  1183       numFrames++;
  1184       if (aMaxFrames != 0 && numFrames == aMaxFrames)
  1185         break;
  1187     bp = next;
  1189   return numFrames == 0 ? NS_ERROR_FAILURE : NS_OK;
  1194 #define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__))
  1195 #if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code
  1197 EXPORT_XPCOM_API(nsresult)
  1198 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
  1199              uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
  1200              void *aPlatformData)
  1202   MOZ_ASSERT(!aThread);
  1203   MOZ_ASSERT(!aPlatformData);
  1204   StackWalkInitCriticalAddress();
  1206   // Get the frame pointer
  1207   void **bp;
  1208 #if defined(__i386) 
  1209   __asm__( "movl %%ebp, %0" : "=g"(bp));
  1210 #else
  1211   // It would be nice if this worked uniformly, but at least on i386 and
  1212   // x86_64, it stopped working with gcc 4.1, because it points to the
  1213   // end of the saved registers instead of the start.
  1214   bp = (void**) __builtin_frame_address(0);
  1215 #endif
  1217   void *stackEnd;
  1218 #if HAVE___LIBC_STACK_END
  1219   stackEnd = __libc_stack_end;
  1220 #else
  1221   stackEnd = reinterpret_cast<void*>(-1);
  1222 #endif
  1223   return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames,
  1224                                aClosure, bp, stackEnd);
  1228 #elif defined(HAVE__UNWIND_BACKTRACE)
  1230 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
  1231 #include <unwind.h>
  1233 struct unwind_info {
  1234     NS_WalkStackCallback callback;
  1235     int skip;
  1236     int maxFrames;
  1237     int numFrames;
  1238     bool isCriticalAbort;
  1239     void *closure;
  1240 };
  1242 static _Unwind_Reason_Code
  1243 unwind_callback (struct _Unwind_Context *context, void *closure)
  1245     unwind_info *info = static_cast<unwind_info *>(closure);
  1246     void *pc = reinterpret_cast<void *>(_Unwind_GetIP(context));
  1247     // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
  1248     if (IsCriticalAddress(pc)) {
  1249         printf("Aborting stack trace, PC is critical\n");
  1250         info->isCriticalAbort = true;
  1251         // We just want to stop the walk, so any error code will do.  Using
  1252         // _URC_NORMAL_STOP would probably be the most accurate, but it is not
  1253         // defined on Android for ARM.
  1254         return _URC_FOREIGN_EXCEPTION_CAUGHT;
  1256     if (--info->skip < 0) {
  1257         (*info->callback)(pc, nullptr, info->closure);
  1258         info->numFrames++;
  1259         if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
  1260             // Again, any error code that stops the walk will do.
  1261             return _URC_FOREIGN_EXCEPTION_CAUGHT;
  1264     return _URC_NO_REASON;
  1267 EXPORT_XPCOM_API(nsresult)
  1268 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
  1269              uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
  1270              void *aPlatformData)
  1272     MOZ_ASSERT(!aThread);
  1273     MOZ_ASSERT(!aPlatformData);
  1274     StackWalkInitCriticalAddress();
  1275     unwind_info info;
  1276     info.callback = aCallback;
  1277     info.skip = aSkipFrames + 1;
  1278     info.maxFrames = aMaxFrames;
  1279     info.numFrames = 0;
  1280     info.isCriticalAbort = false;
  1281     info.closure = aClosure;
  1283     (void)_Unwind_Backtrace(unwind_callback, &info);
  1285     // We ignore the return value from _Unwind_Backtrace and instead determine
  1286     // the outcome from |info|.  There are two main reasons for this:
  1287     // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
  1288     //   _URC_FAILURE.  See
  1289     //   https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
  1290     // - If aMaxFrames != 0, we want to stop early, and the only way to do that
  1291     //   is to make unwind_callback return something other than _URC_NO_REASON,
  1292     //   which causes _Unwind_Backtrace to return a non-success code.
  1293     if (info.isCriticalAbort)
  1294         return NS_ERROR_UNEXPECTED;
  1295     return info.numFrames == 0 ? NS_ERROR_FAILURE : NS_OK;
  1298 #endif
  1300 EXPORT_XPCOM_API(nsresult)
  1301 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
  1303   aDetails->library[0] = '\0';
  1304   aDetails->loffset = 0;
  1305   aDetails->filename[0] = '\0';
  1306   aDetails->lineno = 0;
  1307   aDetails->function[0] = '\0';
  1308   aDetails->foffset = 0;
  1310   Dl_info info;
  1311   int ok = dladdr(aPC, &info);
  1312   if (!ok) {
  1313     return NS_OK;
  1316   PL_strncpyz(aDetails->library, info.dli_fname, sizeof(aDetails->library));
  1317   aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
  1319   const char * symbol = info.dli_sname;
  1320   if (!symbol || symbol[0] == '\0') {
  1321     return NS_OK;
  1324   DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function));
  1326   if (aDetails->function[0] == '\0') {
  1327     // Just use the mangled symbol if demangling failed.
  1328     PL_strncpyz(aDetails->function, symbol, sizeof(aDetails->function));
  1331   aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
  1332   return NS_OK;
  1335 EXPORT_XPCOM_API(nsresult)
  1336 NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
  1337                             char *aBuffer, uint32_t aBufferSize)
  1339   if (!aDetails->library[0]) {
  1340     snprintf(aBuffer, aBufferSize, "UNKNOWN %p\n", aPC);
  1341   } else if (!aDetails->function[0]) {
  1342     snprintf(aBuffer, aBufferSize, "UNKNOWN [%s +0x%08" PRIXPTR "]\n",
  1343                                    aDetails->library, aDetails->loffset);
  1344   } else {
  1345     snprintf(aBuffer, aBufferSize, "%s+0x%08" PRIXPTR
  1346                                    " [%s +0x%08" PRIXPTR "]\n",
  1347                                    aDetails->function, aDetails->foffset,
  1348                                    aDetails->library, aDetails->loffset);
  1350   return NS_OK;
  1353 #endif
  1355 #else // unsupported platform.
  1357 EXPORT_XPCOM_API(nsresult)
  1358 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
  1359              uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
  1360              void *aPlatformData)
  1362     MOZ_ASSERT(!aThread);
  1363     MOZ_ASSERT(!aPlatformData);
  1364     return NS_ERROR_NOT_IMPLEMENTED;
  1367 namespace mozilla {
  1368 nsresult
  1369 FramePointerStackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
  1370                       void *aClosure, void **bp)
  1372     return NS_ERROR_NOT_IMPLEMENTED;
  1376 EXPORT_XPCOM_API(nsresult)
  1377 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
  1379     aDetails->library[0] = '\0';
  1380     aDetails->loffset = 0;
  1381     aDetails->filename[0] = '\0';
  1382     aDetails->lineno = 0;
  1383     aDetails->function[0] = '\0';
  1384     aDetails->foffset = 0;
  1385     return NS_ERROR_NOT_IMPLEMENTED;
  1388 EXPORT_XPCOM_API(nsresult)
  1389 NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
  1390                             char *aBuffer, uint32_t aBufferSize)
  1392     aBuffer[0] = '\0';
  1393     return NS_ERROR_NOT_IMPLEMENTED;
  1396 #endif

mercurial