1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/base/nsStackWalk.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1396 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* API for getting a stack trace of the C/C++ stack on the current thread */ 1.11 + 1.12 +#include "mozilla/Assertions.h" 1.13 +#include "mozilla/IntegerPrintfMacros.h" 1.14 +#include "mozilla/StackWalk.h" 1.15 +#include "nsStackWalkPrivate.h" 1.16 + 1.17 +#include "nsStackWalk.h" 1.18 + 1.19 +using namespace mozilla; 1.20 + 1.21 +// The presence of this address is the stack must stop the stack walk. If 1.22 +// there is no such address, the structure will be {nullptr, true}. 1.23 +struct CriticalAddress { 1.24 + void* mAddr; 1.25 + bool mInit; 1.26 +}; 1.27 +static CriticalAddress gCriticalAddress; 1.28 + 1.29 +// for _Unwind_Backtrace from libcxxrt or libunwind 1.30 +// cxxabi.h from libcxxrt implicitly includes unwind.h first 1.31 +#if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE) 1.32 +#define _GNU_SOURCE 1.33 +#endif 1.34 + 1.35 +#if defined(HAVE_DLOPEN) || defined(XP_MACOSX) 1.36 +#include <dlfcn.h> 1.37 +#endif 1.38 + 1.39 +#define NSSTACKWALK_SUPPORTS_MACOSX \ 1.40 + (defined(XP_MACOSX) && \ 1.41 + (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE))) 1.42 + 1.43 +#define NSSTACKWALK_SUPPORTS_LINUX \ 1.44 + (defined(linux) && \ 1.45 + ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \ 1.46 + defined(HAVE__UNWIND_BACKTRACE))) 1.47 + 1.48 +#define NSSTACKWALK_SUPPORTS_SOLARIS \ 1.49 + (defined(__sun) && \ 1.50 + (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386))) 1.51 + 1.52 +#if NSSTACKWALK_SUPPORTS_MACOSX 1.53 +#include <pthread.h> 1.54 +#include <CoreServices/CoreServices.h> 1.55 + 1.56 +typedef void 1.57 +malloc_logger_t(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, 1.58 + uintptr_t result, uint32_t num_hot_frames_to_skip); 1.59 +extern malloc_logger_t *malloc_logger; 1.60 + 1.61 +static void 1.62 +stack_callback(void *pc, void *sp, void *closure) 1.63 +{ 1.64 + const char *name = reinterpret_cast<char *>(closure); 1.65 + Dl_info info; 1.66 + 1.67 + // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The 1.68 + // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The 1.69 + // correct one is the first that we find on our way up, so the 1.70 + // following check for gCriticalAddress.mAddr is critical. 1.71 + if (gCriticalAddress.mAddr || dladdr(pc, &info) == 0 || 1.72 + info.dli_sname == nullptr || strcmp(info.dli_sname, name) != 0) 1.73 + return; 1.74 + gCriticalAddress.mAddr = pc; 1.75 +} 1.76 + 1.77 +#ifdef DEBUG 1.78 +#define MAC_OS_X_VERSION_10_7_HEX 0x00001070 1.79 + 1.80 +static int32_t OSXVersion() 1.81 +{ 1.82 + static int32_t gOSXVersion = 0x0; 1.83 + if (gOSXVersion == 0x0) { 1.84 + OSErr err = ::Gestalt(gestaltSystemVersion, (SInt32*)&gOSXVersion); 1.85 + MOZ_ASSERT(err == noErr); 1.86 + } 1.87 + return gOSXVersion; 1.88 +} 1.89 + 1.90 +static bool OnLionOrLater() 1.91 +{ 1.92 + return (OSXVersion() >= MAC_OS_X_VERSION_10_7_HEX); 1.93 +} 1.94 +#endif 1.95 + 1.96 +static void 1.97 +my_malloc_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, 1.98 + uintptr_t result, uint32_t num_hot_frames_to_skip) 1.99 +{ 1.100 + static bool once = false; 1.101 + if (once) 1.102 + return; 1.103 + once = true; 1.104 + 1.105 + // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The 1.106 + // stack shows up as having two pthread_cond_wait$UNIX2003 frames. 1.107 + const char *name = "new_sem_from_pool"; 1.108 + NS_StackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0, 1.109 + const_cast<char*>(name), 0, nullptr); 1.110 +} 1.111 + 1.112 +// This is called from NS_LogInit() and from the stack walking functions, but 1.113 +// only the first call has any effect. We need to call this function from both 1.114 +// places because it must run before any mutexes are created, and also before 1.115 +// any objects whose refcounts we're logging are created. Running this 1.116 +// function during NS_LogInit() ensures that we meet the first criterion, and 1.117 +// running this function during the stack walking functions ensures we meet the 1.118 +// second criterion. 1.119 +void 1.120 +StackWalkInitCriticalAddress() 1.121 +{ 1.122 + if(gCriticalAddress.mInit) 1.123 + return; 1.124 + gCriticalAddress.mInit = true; 1.125 + // We must not do work when 'new_sem_from_pool' calls realloc, since 1.126 + // it holds a non-reentrant spin-lock and we will quickly deadlock. 1.127 + // new_sem_from_pool is not directly accessible using dlsym, so 1.128 + // we force a situation where new_sem_from_pool is on the stack and 1.129 + // use dladdr to check the addresses. 1.130 + 1.131 + // malloc_logger can be set by external tools like 'Instruments' or 'leaks' 1.132 + malloc_logger_t *old_malloc_logger = malloc_logger; 1.133 + malloc_logger = my_malloc_logger; 1.134 + 1.135 + pthread_cond_t cond; 1.136 + int r = pthread_cond_init(&cond, 0); 1.137 + MOZ_ASSERT(r == 0); 1.138 + pthread_mutex_t mutex; 1.139 + r = pthread_mutex_init(&mutex,0); 1.140 + MOZ_ASSERT(r == 0); 1.141 + r = pthread_mutex_lock(&mutex); 1.142 + MOZ_ASSERT(r == 0); 1.143 + struct timespec abstime = {0, 1}; 1.144 + r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime); 1.145 + 1.146 + // restore the previous malloc logger 1.147 + malloc_logger = old_malloc_logger; 1.148 + 1.149 + // On Lion, malloc is no longer called from pthread_cond_*wait*. This prevents 1.150 + // us from finding the address, but that is fine, since with no call to malloc 1.151 + // there is no critical address. 1.152 + MOZ_ASSERT(OnLionOrLater() || gCriticalAddress.mAddr != nullptr); 1.153 + MOZ_ASSERT(r == ETIMEDOUT); 1.154 + r = pthread_mutex_unlock(&mutex); 1.155 + MOZ_ASSERT(r == 0); 1.156 + r = pthread_mutex_destroy(&mutex); 1.157 + MOZ_ASSERT(r == 0); 1.158 + r = pthread_cond_destroy(&cond); 1.159 + MOZ_ASSERT(r == 0); 1.160 +} 1.161 + 1.162 +static bool IsCriticalAddress(void* aPC) 1.163 +{ 1.164 + return gCriticalAddress.mAddr == aPC; 1.165 +} 1.166 +#else 1.167 +static bool IsCriticalAddress(void* aPC) 1.168 +{ 1.169 + return false; 1.170 +} 1.171 +// We still initialize gCriticalAddress.mInit so that this code behaves 1.172 +// the same on all platforms. Otherwise a failure to init would be visible 1.173 +// only on OS X. 1.174 +void 1.175 +StackWalkInitCriticalAddress() 1.176 +{ 1.177 + gCriticalAddress.mInit = true; 1.178 +} 1.179 +#endif 1.180 + 1.181 +#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code 1.182 + 1.183 +#include "nscore.h" 1.184 +#include <windows.h> 1.185 +#include <process.h> 1.186 +#include <stdio.h> 1.187 +#include <malloc.h> 1.188 +#include "plstr.h" 1.189 +#include "mozilla/ArrayUtils.h" 1.190 + 1.191 +#include "nspr.h" 1.192 +#include <imagehlp.h> 1.193 +// We need a way to know if we are building for WXP (or later), as if we are, we 1.194 +// need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill. 1.195 +// A value of 9 indicates we want to use the new APIs. 1.196 +#if API_VERSION_NUMBER < 9 1.197 +#error Too old imagehlp.h 1.198 +#endif 1.199 + 1.200 +// Define these as static pointers so that we can load the DLL on the 1.201 +// fly (and not introduce a link-time dependency on it). Tip o' the 1.202 +// hat to Matt Pietrick for this idea. See: 1.203 +// 1.204 +// http://msdn.microsoft.com/library/periodic/period97/F1/D3/S245C6.htm 1.205 +// 1.206 +extern "C" { 1.207 + 1.208 +extern HANDLE hStackWalkMutex; 1.209 + 1.210 +bool EnsureSymInitialized(); 1.211 + 1.212 +bool EnsureWalkThreadReady(); 1.213 + 1.214 +struct WalkStackData { 1.215 + uint32_t skipFrames; 1.216 + HANDLE thread; 1.217 + bool walkCallingThread; 1.218 + HANDLE process; 1.219 + HANDLE eventStart; 1.220 + HANDLE eventEnd; 1.221 + void **pcs; 1.222 + uint32_t pc_size; 1.223 + uint32_t pc_count; 1.224 + uint32_t pc_max; 1.225 + void **sps; 1.226 + uint32_t sp_size; 1.227 + uint32_t sp_count; 1.228 + void *platformData; 1.229 +}; 1.230 + 1.231 +void PrintError(char *prefix, WalkStackData* data); 1.232 +unsigned int WINAPI WalkStackThread(void* data); 1.233 +void WalkStackMain64(struct WalkStackData* data); 1.234 + 1.235 + 1.236 +DWORD gStackWalkThread; 1.237 +CRITICAL_SECTION gDbgHelpCS; 1.238 + 1.239 +} 1.240 + 1.241 +// Routine to print an error message to standard error. 1.242 +void PrintError(const char *prefix) 1.243 +{ 1.244 + LPVOID lpMsgBuf; 1.245 + DWORD lastErr = GetLastError(); 1.246 + FormatMessageA( 1.247 + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 1.248 + nullptr, 1.249 + lastErr, 1.250 + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language 1.251 + (LPSTR) &lpMsgBuf, 1.252 + 0, 1.253 + nullptr 1.254 + ); 1.255 + fprintf(stderr, "### ERROR: %s: %s", 1.256 + prefix, lpMsgBuf ? lpMsgBuf : "(null)\n"); 1.257 + fflush(stderr); 1.258 + LocalFree(lpMsgBuf); 1.259 +} 1.260 + 1.261 +bool 1.262 +EnsureWalkThreadReady() 1.263 +{ 1.264 + static bool walkThreadReady = false; 1.265 + static HANDLE stackWalkThread = nullptr; 1.266 + static HANDLE readyEvent = nullptr; 1.267 + 1.268 + if (walkThreadReady) 1.269 + return walkThreadReady; 1.270 + 1.271 + if (stackWalkThread == nullptr) { 1.272 + readyEvent = ::CreateEvent(nullptr, FALSE /* auto-reset*/, 1.273 + FALSE /* initially non-signaled */, 1.274 + nullptr); 1.275 + if (readyEvent == nullptr) { 1.276 + PrintError("CreateEvent"); 1.277 + return false; 1.278 + } 1.279 + 1.280 + unsigned int threadID; 1.281 + stackWalkThread = (HANDLE) 1.282 + _beginthreadex(nullptr, 0, WalkStackThread, (void*)readyEvent, 1.283 + 0, &threadID); 1.284 + if (stackWalkThread == nullptr) { 1.285 + PrintError("CreateThread"); 1.286 + ::CloseHandle(readyEvent); 1.287 + readyEvent = nullptr; 1.288 + return false; 1.289 + } 1.290 + gStackWalkThread = threadID; 1.291 + ::CloseHandle(stackWalkThread); 1.292 + } 1.293 + 1.294 + MOZ_ASSERT((stackWalkThread != nullptr && readyEvent != nullptr) || 1.295 + (stackWalkThread == nullptr && readyEvent == nullptr)); 1.296 + 1.297 + // The thread was created. Try to wait an arbitrary amount of time (1 second 1.298 + // should be enough) for its event loop to start before posting events to it. 1.299 + DWORD waitRet = ::WaitForSingleObject(readyEvent, 1000); 1.300 + if (waitRet == WAIT_TIMEOUT) { 1.301 + // We get a timeout if we're called during static initialization because 1.302 + // the thread will only start executing after we return so it couldn't 1.303 + // have signalled the event. If that is the case, give up for now and 1.304 + // try again next time we're called. 1.305 + return false; 1.306 + } 1.307 + ::CloseHandle(readyEvent); 1.308 + stackWalkThread = nullptr; 1.309 + readyEvent = nullptr; 1.310 + 1.311 + 1.312 + ::InitializeCriticalSection(&gDbgHelpCS); 1.313 + 1.314 + return walkThreadReady = true; 1.315 +} 1.316 + 1.317 +void 1.318 +WalkStackMain64(struct WalkStackData* data) 1.319 +{ 1.320 + // Get the context information for the thread. That way we will 1.321 + // know where our sp, fp, pc, etc. are and can fill in the 1.322 + // STACKFRAME64 with the initial values. 1.323 + CONTEXT context; 1.324 + HANDLE myProcess = data->process; 1.325 + HANDLE myThread = data->thread; 1.326 + DWORD64 addr; 1.327 + DWORD64 spaddr; 1.328 + STACKFRAME64 frame64; 1.329 + // skip our own stack walking frames 1.330 + int skip = (data->walkCallingThread ? 3 : 0) + data->skipFrames; 1.331 + BOOL ok; 1.332 + 1.333 + // Get a context for the specified thread. 1.334 + if (!data->platformData) { 1.335 + memset(&context, 0, sizeof(CONTEXT)); 1.336 + context.ContextFlags = CONTEXT_FULL; 1.337 + if (!GetThreadContext(myThread, &context)) { 1.338 + if (data->walkCallingThread) { 1.339 + PrintError("GetThreadContext"); 1.340 + } 1.341 + return; 1.342 + } 1.343 + } else { 1.344 + context = *static_cast<CONTEXT*>(data->platformData); 1.345 + } 1.346 + 1.347 + // Setup initial stack frame to walk from 1.348 + memset(&frame64, 0, sizeof(frame64)); 1.349 +#ifdef _M_IX86 1.350 + frame64.AddrPC.Offset = context.Eip; 1.351 + frame64.AddrStack.Offset = context.Esp; 1.352 + frame64.AddrFrame.Offset = context.Ebp; 1.353 +#elif defined _M_AMD64 1.354 + frame64.AddrPC.Offset = context.Rip; 1.355 + frame64.AddrStack.Offset = context.Rsp; 1.356 + frame64.AddrFrame.Offset = context.Rbp; 1.357 +#elif defined _M_IA64 1.358 + frame64.AddrPC.Offset = context.StIIP; 1.359 + frame64.AddrStack.Offset = context.SP; 1.360 + frame64.AddrFrame.Offset = context.RsBSP; 1.361 +#else 1.362 +#error "Should not have compiled this code" 1.363 +#endif 1.364 + frame64.AddrPC.Mode = AddrModeFlat; 1.365 + frame64.AddrStack.Mode = AddrModeFlat; 1.366 + frame64.AddrFrame.Mode = AddrModeFlat; 1.367 + frame64.AddrReturn.Mode = AddrModeFlat; 1.368 + 1.369 + // Now walk the stack 1.370 + while (1) { 1.371 + 1.372 + // debug routines are not threadsafe, so grab the lock. 1.373 + EnterCriticalSection(&gDbgHelpCS); 1.374 + ok = StackWalk64( 1.375 +#ifdef _M_AMD64 1.376 + IMAGE_FILE_MACHINE_AMD64, 1.377 +#elif defined _M_IA64 1.378 + IMAGE_FILE_MACHINE_IA64, 1.379 +#elif defined _M_IX86 1.380 + IMAGE_FILE_MACHINE_I386, 1.381 +#else 1.382 +#error "Should not have compiled this code" 1.383 +#endif 1.384 + myProcess, 1.385 + myThread, 1.386 + &frame64, 1.387 + &context, 1.388 + nullptr, 1.389 + SymFunctionTableAccess64, // function table access routine 1.390 + SymGetModuleBase64, // module base routine 1.391 + 0 1.392 + ); 1.393 + LeaveCriticalSection(&gDbgHelpCS); 1.394 + 1.395 + if (ok) { 1.396 + addr = frame64.AddrPC.Offset; 1.397 + spaddr = frame64.AddrStack.Offset; 1.398 + } else { 1.399 + addr = 0; 1.400 + spaddr = 0; 1.401 + if (data->walkCallingThread) { 1.402 + PrintError("WalkStack64"); 1.403 + } 1.404 + } 1.405 + 1.406 + if (!ok || (addr == 0)) { 1.407 + break; 1.408 + } 1.409 + 1.410 + if (skip-- > 0) { 1.411 + continue; 1.412 + } 1.413 + 1.414 + if (data->pc_count < data->pc_size) 1.415 + data->pcs[data->pc_count] = (void*)addr; 1.416 + ++data->pc_count; 1.417 + 1.418 + if (data->sp_count < data->sp_size) 1.419 + data->sps[data->sp_count] = (void*)spaddr; 1.420 + ++data->sp_count; 1.421 + 1.422 + if (data->pc_max != 0 && data->pc_count == data->pc_max) 1.423 + break; 1.424 + 1.425 + if (frame64.AddrReturn.Offset == 0) 1.426 + break; 1.427 + } 1.428 + return; 1.429 +} 1.430 + 1.431 + 1.432 +unsigned int WINAPI 1.433 +WalkStackThread(void* aData) 1.434 +{ 1.435 + BOOL msgRet; 1.436 + MSG msg; 1.437 + 1.438 + // Call PeekMessage to force creation of a message queue so that 1.439 + // other threads can safely post events to us. 1.440 + ::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE); 1.441 + 1.442 + // and tell the thread that created us that we're ready. 1.443 + HANDLE readyEvent = (HANDLE)aData; 1.444 + ::SetEvent(readyEvent); 1.445 + 1.446 + while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) { 1.447 + if (msgRet == -1) { 1.448 + PrintError("GetMessage"); 1.449 + } else { 1.450 + DWORD ret; 1.451 + 1.452 + struct WalkStackData *data = (WalkStackData *)msg.lParam; 1.453 + if (!data) 1.454 + continue; 1.455 + 1.456 + // Don't suspend the calling thread until it's waiting for 1.457 + // us; otherwise the number of frames on the stack could vary. 1.458 + ret = ::WaitForSingleObject(data->eventStart, INFINITE); 1.459 + if (ret != WAIT_OBJECT_0) 1.460 + PrintError("WaitForSingleObject"); 1.461 + 1.462 + // Suspend the calling thread, dump his stack, and then resume him. 1.463 + // He's currently waiting for us to finish so now should be a good time. 1.464 + ret = ::SuspendThread( data->thread ); 1.465 + if (ret == -1) { 1.466 + PrintError("ThreadSuspend"); 1.467 + } 1.468 + else { 1.469 + WalkStackMain64(data); 1.470 + 1.471 + ret = ::ResumeThread(data->thread); 1.472 + if (ret == -1) { 1.473 + PrintError("ThreadResume"); 1.474 + } 1.475 + } 1.476 + 1.477 + ::SetEvent(data->eventEnd); 1.478 + } 1.479 + } 1.480 + 1.481 + return 0; 1.482 +} 1.483 + 1.484 +/** 1.485 + * Walk the stack, translating PC's found into strings and recording the 1.486 + * chain in aBuffer. For this to work properly, the DLLs must be rebased 1.487 + * so that the address in the file agrees with the address in memory. 1.488 + * Otherwise StackWalk will return FALSE when it hits a frame in a DLL 1.489 + * whose in memory address doesn't match its in-file address. 1.490 + */ 1.491 + 1.492 +EXPORT_XPCOM_API(nsresult) 1.493 +NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames, 1.494 + uint32_t aMaxFrames, void *aClosure, uintptr_t aThread, 1.495 + void *aPlatformData) 1.496 +{ 1.497 + StackWalkInitCriticalAddress(); 1.498 + static HANDLE myProcess = nullptr; 1.499 + HANDLE myThread; 1.500 + DWORD walkerReturn; 1.501 + struct WalkStackData data; 1.502 + 1.503 + if (!EnsureWalkThreadReady()) 1.504 + return NS_ERROR_FAILURE; 1.505 + 1.506 + HANDLE targetThread = ::GetCurrentThread(); 1.507 + data.walkCallingThread = true; 1.508 + if (aThread) { 1.509 + HANDLE threadToWalk = reinterpret_cast<HANDLE> (aThread); 1.510 + // walkCallingThread indicates whether we are walking the caller's stack 1.511 + data.walkCallingThread = (threadToWalk == targetThread); 1.512 + targetThread = threadToWalk; 1.513 + } 1.514 + 1.515 + // We need to avoid calling fprintf and friends if we're walking the stack of 1.516 + // another thread, in order to avoid deadlocks. 1.517 + const bool shouldBeThreadSafe = !!aThread; 1.518 + 1.519 + // Have to duplicate handle to get a real handle. 1.520 + if (!myProcess) { 1.521 + if (!::DuplicateHandle(::GetCurrentProcess(), 1.522 + ::GetCurrentProcess(), 1.523 + ::GetCurrentProcess(), 1.524 + &myProcess, 1.525 + PROCESS_ALL_ACCESS, FALSE, 0)) { 1.526 + if (!shouldBeThreadSafe) { 1.527 + PrintError("DuplicateHandle (process)"); 1.528 + } 1.529 + return NS_ERROR_FAILURE; 1.530 + } 1.531 + } 1.532 + if (!::DuplicateHandle(::GetCurrentProcess(), 1.533 + targetThread, 1.534 + ::GetCurrentProcess(), 1.535 + &myThread, 1.536 + THREAD_ALL_ACCESS, FALSE, 0)) { 1.537 + if (!shouldBeThreadSafe) { 1.538 + PrintError("DuplicateHandle (thread)"); 1.539 + } 1.540 + return NS_ERROR_FAILURE; 1.541 + } 1.542 + 1.543 + data.skipFrames = aSkipFrames; 1.544 + data.thread = myThread; 1.545 + data.process = myProcess; 1.546 + void *local_pcs[1024]; 1.547 + data.pcs = local_pcs; 1.548 + data.pc_count = 0; 1.549 + data.pc_size = ArrayLength(local_pcs); 1.550 + data.pc_max = aMaxFrames; 1.551 + void *local_sps[1024]; 1.552 + data.sps = local_sps; 1.553 + data.sp_count = 0; 1.554 + data.sp_size = ArrayLength(local_sps); 1.555 + data.platformData = aPlatformData; 1.556 + 1.557 + if (aThread) { 1.558 + // If we're walking the stack of another thread, we don't need to 1.559 + // use a separate walker thread. 1.560 + WalkStackMain64(&data); 1.561 + 1.562 + if (data.pc_count > data.pc_size) { 1.563 + data.pcs = (void**) _alloca(data.pc_count * sizeof(void*)); 1.564 + data.pc_size = data.pc_count; 1.565 + data.pc_count = 0; 1.566 + data.sps = (void**) _alloca(data.sp_count * sizeof(void*)); 1.567 + data.sp_size = data.sp_count; 1.568 + data.sp_count = 0; 1.569 + WalkStackMain64(&data); 1.570 + } 1.571 + } else { 1.572 + data.eventStart = ::CreateEvent(nullptr, FALSE /* auto-reset*/, 1.573 + FALSE /* initially non-signaled */, nullptr); 1.574 + data.eventEnd = ::CreateEvent(nullptr, FALSE /* auto-reset*/, 1.575 + FALSE /* initially non-signaled */, nullptr); 1.576 + 1.577 + ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data); 1.578 + 1.579 + walkerReturn = ::SignalObjectAndWait(data.eventStart, 1.580 + data.eventEnd, INFINITE, FALSE); 1.581 + if (walkerReturn != WAIT_OBJECT_0 && !shouldBeThreadSafe) 1.582 + PrintError("SignalObjectAndWait (1)"); 1.583 + if (data.pc_count > data.pc_size) { 1.584 + data.pcs = (void**) _alloca(data.pc_count * sizeof(void*)); 1.585 + data.pc_size = data.pc_count; 1.586 + data.pc_count = 0; 1.587 + data.sps = (void**) _alloca(data.sp_count * sizeof(void*)); 1.588 + data.sp_size = data.sp_count; 1.589 + data.sp_count = 0; 1.590 + ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data); 1.591 + walkerReturn = ::SignalObjectAndWait(data.eventStart, 1.592 + data.eventEnd, INFINITE, FALSE); 1.593 + if (walkerReturn != WAIT_OBJECT_0 && !shouldBeThreadSafe) 1.594 + PrintError("SignalObjectAndWait (2)"); 1.595 + } 1.596 + 1.597 + ::CloseHandle(data.eventStart); 1.598 + ::CloseHandle(data.eventEnd); 1.599 + } 1.600 + 1.601 + ::CloseHandle(myThread); 1.602 + 1.603 + for (uint32_t i = 0; i < data.pc_count; ++i) 1.604 + (*aCallback)(data.pcs[i], data.sps[i], aClosure); 1.605 + 1.606 + return data.pc_count == 0 ? NS_ERROR_FAILURE : NS_OK; 1.607 +} 1.608 + 1.609 + 1.610 +static BOOL CALLBACK callbackEspecial64( 1.611 + PCSTR aModuleName, 1.612 + DWORD64 aModuleBase, 1.613 + ULONG aModuleSize, 1.614 + PVOID aUserContext) 1.615 +{ 1.616 + BOOL retval = TRUE; 1.617 + DWORD64 addr = *(DWORD64*)aUserContext; 1.618 + 1.619 + /* 1.620 + * You'll want to control this if we are running on an 1.621 + * architecture where the addresses go the other direction. 1.622 + * Not sure this is even a realistic consideration. 1.623 + */ 1.624 + const BOOL addressIncreases = TRUE; 1.625 + 1.626 + /* 1.627 + * If it falls in side the known range, load the symbols. 1.628 + */ 1.629 + if (addressIncreases 1.630 + ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize)) 1.631 + : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize)) 1.632 + ) { 1.633 + retval = !!SymLoadModule64(GetCurrentProcess(), nullptr, 1.634 + (PSTR)aModuleName, nullptr, 1.635 + aModuleBase, aModuleSize); 1.636 + if (!retval) 1.637 + PrintError("SymLoadModule64"); 1.638 + } 1.639 + 1.640 + return retval; 1.641 +} 1.642 + 1.643 +/* 1.644 + * SymGetModuleInfoEspecial 1.645 + * 1.646 + * Attempt to determine the module information. 1.647 + * Bug 112196 says this DLL may not have been loaded at the time 1.648 + * SymInitialize was called, and thus the module information 1.649 + * and symbol information is not available. 1.650 + * This code rectifies that problem. 1.651 + */ 1.652 + 1.653 +// New members were added to IMAGEHLP_MODULE64 (that show up in the 1.654 +// Platform SDK that ships with VC8, but not the Platform SDK that ships 1.655 +// with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to 1.656 +// use them, and it's useful to be able to function correctly with the 1.657 +// older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll 1.658 +// version 5.1.) Since Platform SDK version need not correspond to 1.659 +// compiler version, and the version number in debughlp.h was NOT bumped 1.660 +// when these changes were made, ifdef based on a constant that was 1.661 +// added between these versions. 1.662 +#ifdef SSRVOPT_SETCONTEXT 1.663 +#define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64)) 1.664 +#else 1.665 +#define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64) 1.666 +#endif 1.667 + 1.668 +BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr, PIMAGEHLP_MODULE64 aModuleInfo, PIMAGEHLP_LINE64 aLineInfo) 1.669 +{ 1.670 + BOOL retval = FALSE; 1.671 + 1.672 + /* 1.673 + * Init the vars if we have em. 1.674 + */ 1.675 + aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE; 1.676 + if (nullptr != aLineInfo) { 1.677 + aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64); 1.678 + } 1.679 + 1.680 + /* 1.681 + * Give it a go. 1.682 + * It may already be loaded. 1.683 + */ 1.684 + retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo); 1.685 + 1.686 + if (FALSE == retval) { 1.687 + BOOL enumRes = FALSE; 1.688 + 1.689 + /* 1.690 + * Not loaded, here's the magic. 1.691 + * Go through all the modules. 1.692 + */ 1.693 + // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the 1.694 + // constness of the first parameter of 1.695 + // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from 1.696 + // non-const to const over time). See bug 391848 and bug 1.697 + // 415426. 1.698 + enumRes = EnumerateLoadedModules64(aProcess, (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64, (PVOID)&aAddr); 1.699 + if (FALSE != enumRes) 1.700 + { 1.701 + /* 1.702 + * One final go. 1.703 + * If it fails, then well, we have other problems. 1.704 + */ 1.705 + retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo); 1.706 + } 1.707 + } 1.708 + 1.709 + /* 1.710 + * If we got module info, we may attempt line info as well. 1.711 + * We will not report failure if this does not work. 1.712 + */ 1.713 + if (FALSE != retval && nullptr != aLineInfo) { 1.714 + DWORD displacement = 0; 1.715 + BOOL lineRes = FALSE; 1.716 + lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo); 1.717 + if (!lineRes) { 1.718 + // Clear out aLineInfo to indicate that it's not valid 1.719 + memset(aLineInfo, 0, sizeof(*aLineInfo)); 1.720 + } 1.721 + } 1.722 + 1.723 + return retval; 1.724 +} 1.725 + 1.726 +bool 1.727 +EnsureSymInitialized() 1.728 +{ 1.729 + static bool gInitialized = false; 1.730 + bool retStat; 1.731 + 1.732 + if (gInitialized) 1.733 + return gInitialized; 1.734 + 1.735 + if (!EnsureWalkThreadReady()) 1.736 + return false; 1.737 + 1.738 + SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); 1.739 + retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE); 1.740 + if (!retStat) 1.741 + PrintError("SymInitialize"); 1.742 + 1.743 + gInitialized = retStat; 1.744 + /* XXX At some point we need to arrange to call SymCleanup */ 1.745 + 1.746 + return retStat; 1.747 +} 1.748 + 1.749 + 1.750 +EXPORT_XPCOM_API(nsresult) 1.751 +NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails) 1.752 +{ 1.753 + aDetails->library[0] = '\0'; 1.754 + aDetails->loffset = 0; 1.755 + aDetails->filename[0] = '\0'; 1.756 + aDetails->lineno = 0; 1.757 + aDetails->function[0] = '\0'; 1.758 + aDetails->foffset = 0; 1.759 + 1.760 + if (!EnsureSymInitialized()) 1.761 + return NS_ERROR_FAILURE; 1.762 + 1.763 + HANDLE myProcess = ::GetCurrentProcess(); 1.764 + BOOL ok; 1.765 + 1.766 + // debug routines are not threadsafe, so grab the lock. 1.767 + EnterCriticalSection(&gDbgHelpCS); 1.768 + 1.769 + // 1.770 + // Attempt to load module info before we attempt to resolve the symbol. 1.771 + // This just makes sure we get good info if available. 1.772 + // 1.773 + 1.774 + DWORD64 addr = (DWORD64)aPC; 1.775 + IMAGEHLP_MODULE64 modInfo; 1.776 + IMAGEHLP_LINE64 lineInfo; 1.777 + BOOL modInfoRes; 1.778 + modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo); 1.779 + 1.780 + if (modInfoRes) { 1.781 + PL_strncpyz(aDetails->library, modInfo.ModuleName, 1.782 + sizeof(aDetails->library)); 1.783 + aDetails->loffset = (char*) aPC - (char*) modInfo.BaseOfImage; 1.784 + 1.785 + if (lineInfo.FileName) { 1.786 + PL_strncpyz(aDetails->filename, lineInfo.FileName, 1.787 + sizeof(aDetails->filename)); 1.788 + aDetails->lineno = lineInfo.LineNumber; 1.789 + } 1.790 + } 1.791 + 1.792 + ULONG64 buffer[(sizeof(SYMBOL_INFO) + 1.793 + MAX_SYM_NAME*sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; 1.794 + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; 1.795 + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); 1.796 + pSymbol->MaxNameLen = MAX_SYM_NAME; 1.797 + 1.798 + DWORD64 displacement; 1.799 + ok = SymFromAddr(myProcess, addr, &displacement, pSymbol); 1.800 + 1.801 + if (ok) { 1.802 + PL_strncpyz(aDetails->function, pSymbol->Name, 1.803 + sizeof(aDetails->function)); 1.804 + aDetails->foffset = static_cast<ptrdiff_t>(displacement); 1.805 + } 1.806 + 1.807 + LeaveCriticalSection(&gDbgHelpCS); // release our lock 1.808 + return NS_OK; 1.809 +} 1.810 + 1.811 +EXPORT_XPCOM_API(nsresult) 1.812 +NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails, 1.813 + char *aBuffer, uint32_t aBufferSize) 1.814 +{ 1.815 + if (aDetails->function[0]) { 1.816 + _snprintf(aBuffer, aBufferSize, "%s+0x%08lX [%s +0x%016lX]", 1.817 + aDetails->function, aDetails->foffset, 1.818 + aDetails->library, aDetails->loffset); 1.819 + } else if (aDetails->library[0]) { 1.820 + _snprintf(aBuffer, aBufferSize, "UNKNOWN [%s +0x%016lX]", 1.821 + aDetails->library, aDetails->loffset); 1.822 + } else { 1.823 + _snprintf(aBuffer, aBufferSize, "UNKNOWN 0x%016lX", aPC); 1.824 + } 1.825 + 1.826 + aBuffer[aBufferSize - 1] = '\0'; 1.827 + 1.828 + uint32_t len = strlen(aBuffer); 1.829 + if (aDetails->filename[0]) { 1.830 + _snprintf(aBuffer + len, aBufferSize - len, " (%s, line %d)\n", 1.831 + aDetails->filename, aDetails->lineno); 1.832 + } else { 1.833 + aBuffer[len] = '\n'; 1.834 + if (++len != aBufferSize) 1.835 + aBuffer[len] = '\0'; 1.836 + } 1.837 + aBuffer[aBufferSize - 2] = '\n'; 1.838 + aBuffer[aBufferSize - 1] = '\0'; 1.839 + return NS_OK; 1.840 +} 1.841 + 1.842 +// WIN32 x86 stack walking code 1.843 +// i386 or PPC Linux stackwalking code or Solaris 1.844 +#elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || NSSTACKWALK_SUPPORTS_LINUX || NSSTACKWALK_SUPPORTS_SOLARIS || NSSTACKWALK_SUPPORTS_MACOSX) 1.845 + 1.846 +#include <stdlib.h> 1.847 +#include <string.h> 1.848 +#include "nscore.h" 1.849 +#include <stdio.h> 1.850 +#include "plstr.h" 1.851 + 1.852 +// On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed 1.853 +// if __USE_GNU is defined. I suppose its some kind of standards 1.854 +// adherence thing. 1.855 +// 1.856 +#if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU) 1.857 +#define __USE_GNU 1.858 +#endif 1.859 + 1.860 +// This thing is exported by libstdc++ 1.861 +// Yes, this is a gcc only hack 1.862 +#if defined(MOZ_DEMANGLE_SYMBOLS) 1.863 +#include <cxxabi.h> 1.864 +#endif // MOZ_DEMANGLE_SYMBOLS 1.865 + 1.866 +void DemangleSymbol(const char * aSymbol, 1.867 + char * aBuffer, 1.868 + int aBufLen) 1.869 +{ 1.870 + aBuffer[0] = '\0'; 1.871 + 1.872 +#if defined(MOZ_DEMANGLE_SYMBOLS) 1.873 + /* See demangle.h in the gcc source for the voodoo */ 1.874 + char * demangled = abi::__cxa_demangle(aSymbol,0,0,0); 1.875 + 1.876 + if (demangled) 1.877 + { 1.878 + PL_strncpyz(aBuffer,demangled,aBufLen); 1.879 + free(demangled); 1.880 + } 1.881 +#endif // MOZ_DEMANGLE_SYMBOLS 1.882 +} 1.883 + 1.884 + 1.885 +#if NSSTACKWALK_SUPPORTS_SOLARIS 1.886 + 1.887 +/* 1.888 + * Stack walking code for Solaris courtesy of Bart Smaalder's "memtrak". 1.889 + */ 1.890 + 1.891 +#include <synch.h> 1.892 +#include <ucontext.h> 1.893 +#include <sys/frame.h> 1.894 +#include <sys/regset.h> 1.895 +#include <sys/stack.h> 1.896 + 1.897 +static int load_address ( void * pc, void * arg ); 1.898 +static struct bucket * newbucket ( void * pc ); 1.899 +static struct frame * cs_getmyframeptr ( void ); 1.900 +static void cs_walk_stack ( void * (*read_func)(char * address), 1.901 + struct frame * fp, 1.902 + int (*operate_func)(void *, void *, void *), 1.903 + void * usrarg ); 1.904 +static void cs_operate ( void (*operate_func)(void *, void *, void *), 1.905 + void * usrarg ); 1.906 + 1.907 +#ifndef STACK_BIAS 1.908 +#define STACK_BIAS 0 1.909 +#endif /*STACK_BIAS*/ 1.910 + 1.911 +#define LOGSIZE 4096 1.912 + 1.913 +/* type of demangling function */ 1.914 +typedef int demf_t(const char *, char *, size_t); 1.915 + 1.916 +static demf_t *demf; 1.917 + 1.918 +static int initialized = 0; 1.919 + 1.920 +#if defined(sparc) || defined(__sparc) 1.921 +#define FRAME_PTR_REGISTER REG_SP 1.922 +#endif 1.923 + 1.924 +#if defined(i386) || defined(__i386) 1.925 +#define FRAME_PTR_REGISTER EBP 1.926 +#endif 1.927 + 1.928 +struct bucket { 1.929 + void * pc; 1.930 + int index; 1.931 + struct bucket * next; 1.932 +}; 1.933 + 1.934 +struct my_user_args { 1.935 + NS_WalkStackCallback callback; 1.936 + uint32_t skipFrames; 1.937 + uint32_t maxFrames; 1.938 + uint32_t numFrames; 1.939 + void *closure; 1.940 +}; 1.941 + 1.942 + 1.943 +static void myinit(); 1.944 + 1.945 +#pragma init (myinit) 1.946 + 1.947 +static void 1.948 +myinit() 1.949 +{ 1.950 + 1.951 + if (! initialized) { 1.952 +#ifndef __GNUC__ 1.953 + void *handle; 1.954 + const char *libdem = "libdemangle.so.1"; 1.955 + 1.956 + /* load libdemangle if we can and need to (only try this once) */ 1.957 + if ((handle = dlopen(libdem, RTLD_LAZY)) != nullptr) { 1.958 + demf = (demf_t *)dlsym(handle, 1.959 + "cplus_demangle"); /*lint !e611 */ 1.960 + /* 1.961 + * lint override above is to prevent lint from 1.962 + * complaining about "suspicious cast". 1.963 + */ 1.964 + } 1.965 +#endif /*__GNUC__*/ 1.966 + } 1.967 + initialized = 1; 1.968 +} 1.969 + 1.970 + 1.971 +static int 1.972 +load_address(void * pc, void * arg) 1.973 +{ 1.974 + static struct bucket table[2048]; 1.975 + static mutex_t lock; 1.976 + struct bucket * ptr; 1.977 + struct my_user_args * args = (struct my_user_args *) arg; 1.978 + 1.979 + unsigned int val = NS_PTR_TO_INT32(pc); 1.980 + 1.981 + ptr = table + ((val >> 2)&2047); 1.982 + 1.983 + mutex_lock(&lock); 1.984 + while (ptr->next) { 1.985 + if (ptr->next->pc == pc) 1.986 + break; 1.987 + ptr = ptr->next; 1.988 + } 1.989 + 1.990 + int stop = 0; 1.991 + if (ptr->next) { 1.992 + mutex_unlock(&lock); 1.993 + } else { 1.994 + (args->callback)(pc, args->closure); 1.995 + args->numFrames++; 1.996 + if (args->maxFrames != 0 && args->numFrames == args->maxFrames) 1.997 + stop = 1; // causes us to stop getting frames 1.998 + 1.999 + ptr->next = newbucket(pc); 1.1000 + mutex_unlock(&lock); 1.1001 + } 1.1002 + return stop; 1.1003 +} 1.1004 + 1.1005 + 1.1006 +static struct bucket * 1.1007 +newbucket(void * pc) 1.1008 +{ 1.1009 + struct bucket * ptr = (struct bucket *) malloc(sizeof (*ptr)); 1.1010 + static int index; /* protected by lock in caller */ 1.1011 + 1.1012 + ptr->index = index++; 1.1013 + ptr->next = nullptr; 1.1014 + ptr->pc = pc; 1.1015 + return (ptr); 1.1016 +} 1.1017 + 1.1018 + 1.1019 +static struct frame * 1.1020 +csgetframeptr() 1.1021 +{ 1.1022 + ucontext_t u; 1.1023 + struct frame *fp; 1.1024 + 1.1025 + (void) getcontext(&u); 1.1026 + 1.1027 + fp = (struct frame *) 1.1028 + ((char *)u.uc_mcontext.gregs[FRAME_PTR_REGISTER] + 1.1029 + STACK_BIAS); 1.1030 + 1.1031 + /* make sure to return parents frame pointer.... */ 1.1032 + 1.1033 + return ((struct frame *)((ulong_t)fp->fr_savfp + STACK_BIAS)); 1.1034 +} 1.1035 + 1.1036 + 1.1037 +static void 1.1038 +cswalkstack(struct frame *fp, int (*operate_func)(void *, void *, void *), 1.1039 + void *usrarg) 1.1040 +{ 1.1041 + 1.1042 + while (fp != 0 && fp->fr_savpc != 0) { 1.1043 + 1.1044 + if (operate_func((void *)fp->fr_savpc, nullptr, usrarg) != 0) 1.1045 + break; 1.1046 + /* 1.1047 + * watch out - libthread stacks look funny at the top 1.1048 + * so they may not have their STACK_BIAS set 1.1049 + */ 1.1050 + 1.1051 + fp = (struct frame *)((ulong_t)fp->fr_savfp + 1.1052 + (fp->fr_savfp?(ulong_t)STACK_BIAS:0)); 1.1053 + } 1.1054 +} 1.1055 + 1.1056 + 1.1057 +static void 1.1058 +cs_operate(int (*operate_func)(void *, void *, void *), void * usrarg) 1.1059 +{ 1.1060 + cswalkstack(csgetframeptr(), operate_func, usrarg); 1.1061 +} 1.1062 + 1.1063 +EXPORT_XPCOM_API(nsresult) 1.1064 +NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames, 1.1065 + uint32_t aMaxFrames, void *aClosure, uintptr_t aThread, 1.1066 + void *aPlatformData) 1.1067 +{ 1.1068 + MOZ_ASSERT(!aThread); 1.1069 + MOZ_ASSERT(!aPlatformData); 1.1070 + struct my_user_args args; 1.1071 + 1.1072 + StackWalkInitCriticalAddress(); 1.1073 + 1.1074 + if (!initialized) 1.1075 + myinit(); 1.1076 + 1.1077 + args.callback = aCallback; 1.1078 + args.skipFrames = aSkipFrames; /* XXX Not handled! */ 1.1079 + args.maxFrames = aMaxFrames; 1.1080 + args.numFrames = 0; 1.1081 + args.closure = aClosure; 1.1082 + cs_operate(load_address, &args); 1.1083 + return args.numFrames == 0 ? NS_ERROR_FAILURE : NS_OK; 1.1084 +} 1.1085 + 1.1086 +EXPORT_XPCOM_API(nsresult) 1.1087 +NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails) 1.1088 +{ 1.1089 + aDetails->library[0] = '\0'; 1.1090 + aDetails->loffset = 0; 1.1091 + aDetails->filename[0] = '\0'; 1.1092 + aDetails->lineno = 0; 1.1093 + aDetails->function[0] = '\0'; 1.1094 + aDetails->foffset = 0; 1.1095 + 1.1096 + char dembuff[4096]; 1.1097 + Dl_info info; 1.1098 + 1.1099 + if (dladdr(aPC, & info)) { 1.1100 + if (info.dli_fname) { 1.1101 + PL_strncpyz(aDetails->library, info.dli_fname, 1.1102 + sizeof(aDetails->library)); 1.1103 + aDetails->loffset = (char*)aPC - (char*)info.dli_fbase; 1.1104 + } 1.1105 + if (info.dli_sname) { 1.1106 + aDetails->foffset = (char*)aPC - (char*)info.dli_saddr; 1.1107 +#ifdef __GNUC__ 1.1108 + DemangleSymbol(info.dli_sname, dembuff, sizeof(dembuff)); 1.1109 +#else 1.1110 + if (!demf || demf(info.dli_sname, dembuff, sizeof (dembuff))) 1.1111 + dembuff[0] = 0; 1.1112 +#endif /*__GNUC__*/ 1.1113 + PL_strncpyz(aDetails->function, 1.1114 + (dembuff[0] != '\0') ? dembuff : info.dli_sname, 1.1115 + sizeof(aDetails->function)); 1.1116 + } 1.1117 + } 1.1118 + 1.1119 + return NS_OK; 1.1120 +} 1.1121 + 1.1122 +EXPORT_XPCOM_API(nsresult) 1.1123 +NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails, 1.1124 + char *aBuffer, uint32_t aBufferSize) 1.1125 +{ 1.1126 + snprintf(aBuffer, aBufferSize, "%p %s:%s+0x%lx\n", 1.1127 + aPC, 1.1128 + aDetails->library[0] ? aDetails->library : "??", 1.1129 + aDetails->function[0] ? aDetails->function : "??", 1.1130 + aDetails->foffset); 1.1131 + return NS_OK; 1.1132 +} 1.1133 + 1.1134 +#else // not __sun-specific 1.1135 + 1.1136 +#if __GLIBC__ > 2 || __GLIBC_MINOR > 1 1.1137 +#define HAVE___LIBC_STACK_END 1 1.1138 +#else 1.1139 +#define HAVE___LIBC_STACK_END 0 1.1140 +#endif 1.1141 + 1.1142 +#if HAVE___LIBC_STACK_END 1.1143 +extern void *__libc_stack_end; // from ld-linux.so 1.1144 +#endif 1.1145 +namespace mozilla { 1.1146 +nsresult 1.1147 +FramePointerStackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames, 1.1148 + uint32_t aMaxFrames, void *aClosure, void **bp, 1.1149 + void *aStackEnd) 1.1150 +{ 1.1151 + // Stack walking code courtesy Kipp's "leaky". 1.1152 + 1.1153 + int32_t skip = aSkipFrames; 1.1154 + uint32_t numFrames = 0; 1.1155 + while (1) { 1.1156 + void **next = (void**)*bp; 1.1157 + // bp may not be a frame pointer on i386 if code was compiled with 1.1158 + // -fomit-frame-pointer, so do some sanity checks. 1.1159 + // (bp should be a frame pointer on ppc(64) but checking anyway may help 1.1160 + // a little if the stack has been corrupted.) 1.1161 + // We don't need to check against the begining of the stack because 1.1162 + // we can assume that bp > sp 1.1163 + if (next <= bp || 1.1164 + next > aStackEnd || 1.1165 + (long(next) & 3)) { 1.1166 + break; 1.1167 + } 1.1168 +#if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__) 1.1169 + // ppc mac or powerpc64 linux 1.1170 + void *pc = *(bp+2); 1.1171 + bp += 3; 1.1172 +#else // i386 or powerpc32 linux 1.1173 + void *pc = *(bp+1); 1.1174 + bp += 2; 1.1175 +#endif 1.1176 + if (IsCriticalAddress(pc)) { 1.1177 + printf("Aborting stack trace, PC is critical\n"); 1.1178 + return NS_ERROR_UNEXPECTED; 1.1179 + } 1.1180 + if (--skip < 0) { 1.1181 + // Assume that the SP points to the BP of the function 1.1182 + // it called. We can't know the exact location of the SP 1.1183 + // but this should be sufficient for our use the SP 1.1184 + // to order elements on the stack. 1.1185 + (*aCallback)(pc, bp, aClosure); 1.1186 + numFrames++; 1.1187 + if (aMaxFrames != 0 && numFrames == aMaxFrames) 1.1188 + break; 1.1189 + } 1.1190 + bp = next; 1.1191 + } 1.1192 + return numFrames == 0 ? NS_ERROR_FAILURE : NS_OK; 1.1193 +} 1.1194 + 1.1195 +} 1.1196 + 1.1197 +#define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__)) 1.1198 +#if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code 1.1199 + 1.1200 +EXPORT_XPCOM_API(nsresult) 1.1201 +NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames, 1.1202 + uint32_t aMaxFrames, void *aClosure, uintptr_t aThread, 1.1203 + void *aPlatformData) 1.1204 +{ 1.1205 + MOZ_ASSERT(!aThread); 1.1206 + MOZ_ASSERT(!aPlatformData); 1.1207 + StackWalkInitCriticalAddress(); 1.1208 + 1.1209 + // Get the frame pointer 1.1210 + void **bp; 1.1211 +#if defined(__i386) 1.1212 + __asm__( "movl %%ebp, %0" : "=g"(bp)); 1.1213 +#else 1.1214 + // It would be nice if this worked uniformly, but at least on i386 and 1.1215 + // x86_64, it stopped working with gcc 4.1, because it points to the 1.1216 + // end of the saved registers instead of the start. 1.1217 + bp = (void**) __builtin_frame_address(0); 1.1218 +#endif 1.1219 + 1.1220 + void *stackEnd; 1.1221 +#if HAVE___LIBC_STACK_END 1.1222 + stackEnd = __libc_stack_end; 1.1223 +#else 1.1224 + stackEnd = reinterpret_cast<void*>(-1); 1.1225 +#endif 1.1226 + return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames, 1.1227 + aClosure, bp, stackEnd); 1.1228 + 1.1229 +} 1.1230 + 1.1231 +#elif defined(HAVE__UNWIND_BACKTRACE) 1.1232 + 1.1233 +// libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0 1.1234 +#include <unwind.h> 1.1235 + 1.1236 +struct unwind_info { 1.1237 + NS_WalkStackCallback callback; 1.1238 + int skip; 1.1239 + int maxFrames; 1.1240 + int numFrames; 1.1241 + bool isCriticalAbort; 1.1242 + void *closure; 1.1243 +}; 1.1244 + 1.1245 +static _Unwind_Reason_Code 1.1246 +unwind_callback (struct _Unwind_Context *context, void *closure) 1.1247 +{ 1.1248 + unwind_info *info = static_cast<unwind_info *>(closure); 1.1249 + void *pc = reinterpret_cast<void *>(_Unwind_GetIP(context)); 1.1250 + // TODO Use something like '_Unwind_GetGR()' to get the stack pointer. 1.1251 + if (IsCriticalAddress(pc)) { 1.1252 + printf("Aborting stack trace, PC is critical\n"); 1.1253 + info->isCriticalAbort = true; 1.1254 + // We just want to stop the walk, so any error code will do. Using 1.1255 + // _URC_NORMAL_STOP would probably be the most accurate, but it is not 1.1256 + // defined on Android for ARM. 1.1257 + return _URC_FOREIGN_EXCEPTION_CAUGHT; 1.1258 + } 1.1259 + if (--info->skip < 0) { 1.1260 + (*info->callback)(pc, nullptr, info->closure); 1.1261 + info->numFrames++; 1.1262 + if (info->maxFrames != 0 && info->numFrames == info->maxFrames) { 1.1263 + // Again, any error code that stops the walk will do. 1.1264 + return _URC_FOREIGN_EXCEPTION_CAUGHT; 1.1265 + } 1.1266 + } 1.1267 + return _URC_NO_REASON; 1.1268 +} 1.1269 + 1.1270 +EXPORT_XPCOM_API(nsresult) 1.1271 +NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames, 1.1272 + uint32_t aMaxFrames, void *aClosure, uintptr_t aThread, 1.1273 + void *aPlatformData) 1.1274 +{ 1.1275 + MOZ_ASSERT(!aThread); 1.1276 + MOZ_ASSERT(!aPlatformData); 1.1277 + StackWalkInitCriticalAddress(); 1.1278 + unwind_info info; 1.1279 + info.callback = aCallback; 1.1280 + info.skip = aSkipFrames + 1; 1.1281 + info.maxFrames = aMaxFrames; 1.1282 + info.numFrames = 0; 1.1283 + info.isCriticalAbort = false; 1.1284 + info.closure = aClosure; 1.1285 + 1.1286 + (void)_Unwind_Backtrace(unwind_callback, &info); 1.1287 + 1.1288 + // We ignore the return value from _Unwind_Backtrace and instead determine 1.1289 + // the outcome from |info|. There are two main reasons for this: 1.1290 + // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns 1.1291 + // _URC_FAILURE. See 1.1292 + // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110. 1.1293 + // - If aMaxFrames != 0, we want to stop early, and the only way to do that 1.1294 + // is to make unwind_callback return something other than _URC_NO_REASON, 1.1295 + // which causes _Unwind_Backtrace to return a non-success code. 1.1296 + if (info.isCriticalAbort) 1.1297 + return NS_ERROR_UNEXPECTED; 1.1298 + return info.numFrames == 0 ? NS_ERROR_FAILURE : NS_OK; 1.1299 +} 1.1300 + 1.1301 +#endif 1.1302 + 1.1303 +EXPORT_XPCOM_API(nsresult) 1.1304 +NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails) 1.1305 +{ 1.1306 + aDetails->library[0] = '\0'; 1.1307 + aDetails->loffset = 0; 1.1308 + aDetails->filename[0] = '\0'; 1.1309 + aDetails->lineno = 0; 1.1310 + aDetails->function[0] = '\0'; 1.1311 + aDetails->foffset = 0; 1.1312 + 1.1313 + Dl_info info; 1.1314 + int ok = dladdr(aPC, &info); 1.1315 + if (!ok) { 1.1316 + return NS_OK; 1.1317 + } 1.1318 + 1.1319 + PL_strncpyz(aDetails->library, info.dli_fname, sizeof(aDetails->library)); 1.1320 + aDetails->loffset = (char*)aPC - (char*)info.dli_fbase; 1.1321 + 1.1322 + const char * symbol = info.dli_sname; 1.1323 + if (!symbol || symbol[0] == '\0') { 1.1324 + return NS_OK; 1.1325 + } 1.1326 + 1.1327 + DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function)); 1.1328 + 1.1329 + if (aDetails->function[0] == '\0') { 1.1330 + // Just use the mangled symbol if demangling failed. 1.1331 + PL_strncpyz(aDetails->function, symbol, sizeof(aDetails->function)); 1.1332 + } 1.1333 + 1.1334 + aDetails->foffset = (char*)aPC - (char*)info.dli_saddr; 1.1335 + return NS_OK; 1.1336 +} 1.1337 + 1.1338 +EXPORT_XPCOM_API(nsresult) 1.1339 +NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails, 1.1340 + char *aBuffer, uint32_t aBufferSize) 1.1341 +{ 1.1342 + if (!aDetails->library[0]) { 1.1343 + snprintf(aBuffer, aBufferSize, "UNKNOWN %p\n", aPC); 1.1344 + } else if (!aDetails->function[0]) { 1.1345 + snprintf(aBuffer, aBufferSize, "UNKNOWN [%s +0x%08" PRIXPTR "]\n", 1.1346 + aDetails->library, aDetails->loffset); 1.1347 + } else { 1.1348 + snprintf(aBuffer, aBufferSize, "%s+0x%08" PRIXPTR 1.1349 + " [%s +0x%08" PRIXPTR "]\n", 1.1350 + aDetails->function, aDetails->foffset, 1.1351 + aDetails->library, aDetails->loffset); 1.1352 + } 1.1353 + return NS_OK; 1.1354 +} 1.1355 + 1.1356 +#endif 1.1357 + 1.1358 +#else // unsupported platform. 1.1359 + 1.1360 +EXPORT_XPCOM_API(nsresult) 1.1361 +NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames, 1.1362 + uint32_t aMaxFrames, void *aClosure, uintptr_t aThread, 1.1363 + void *aPlatformData) 1.1364 +{ 1.1365 + MOZ_ASSERT(!aThread); 1.1366 + MOZ_ASSERT(!aPlatformData); 1.1367 + return NS_ERROR_NOT_IMPLEMENTED; 1.1368 +} 1.1369 + 1.1370 +namespace mozilla { 1.1371 +nsresult 1.1372 +FramePointerStackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames, 1.1373 + void *aClosure, void **bp) 1.1374 +{ 1.1375 + return NS_ERROR_NOT_IMPLEMENTED; 1.1376 +} 1.1377 +} 1.1378 + 1.1379 +EXPORT_XPCOM_API(nsresult) 1.1380 +NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails) 1.1381 +{ 1.1382 + aDetails->library[0] = '\0'; 1.1383 + aDetails->loffset = 0; 1.1384 + aDetails->filename[0] = '\0'; 1.1385 + aDetails->lineno = 0; 1.1386 + aDetails->function[0] = '\0'; 1.1387 + aDetails->foffset = 0; 1.1388 + return NS_ERROR_NOT_IMPLEMENTED; 1.1389 +} 1.1390 + 1.1391 +EXPORT_XPCOM_API(nsresult) 1.1392 +NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails, 1.1393 + char *aBuffer, uint32_t aBufferSize) 1.1394 +{ 1.1395 + aBuffer[0] = '\0'; 1.1396 + return NS_ERROR_NOT_IMPLEMENTED; 1.1397 +} 1.1398 + 1.1399 +#endif