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