StackWalk.cpp (46882B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* API for getting a stack trace of the C/C++ stack on the current thread */ 8 9 #include "mozilla/Array.h" 10 #include "mozilla/Atomics.h" 11 #include "mozilla/Attributes.h" 12 #include "mozilla/StackWalk.h" 13 #ifdef XP_WIN 14 # include "mozilla/StackWalkThread.h" 15 # include <io.h> 16 #else 17 # include <unistd.h> 18 #endif 19 #include "mozilla/Sprintf.h" 20 21 #include <string.h> 22 23 #if defined(ANDROID) && defined(MOZ_LINKER) 24 # include "Linker.h" 25 # include <android/log.h> 26 #endif 27 28 using namespace mozilla; 29 30 // for _Unwind_Backtrace from libcxxrt or libunwind 31 // cxxabi.h from libcxxrt implicitly includes unwind.h first 32 #if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE) 33 # define _GNU_SOURCE 34 #endif 35 36 #if defined(HAVE_DLFCN_H) || defined(XP_DARWIN) 37 # include <dlfcn.h> 38 #endif 39 40 #if (defined(XP_DARWIN) && \ 41 (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE))) 42 # define MOZ_STACKWALK_SUPPORTS_MACOSX 1 43 #else 44 # define MOZ_STACKWALK_SUPPORTS_MACOSX 0 45 #endif 46 47 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) 48 # define HAVE___LIBC_STACK_END 1 49 #else 50 # define HAVE___LIBC_STACK_END 0 51 #endif 52 53 #if (defined(linux) && \ 54 ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \ 55 defined(HAVE__UNWIND_BACKTRACE)) && \ 56 (HAVE___LIBC_STACK_END || ANDROID)) 57 # define MOZ_STACKWALK_SUPPORTS_LINUX 1 58 #else 59 # define MOZ_STACKWALK_SUPPORTS_LINUX 0 60 #endif 61 62 #if HAVE___LIBC_STACK_END 63 extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so 64 # ifdef __aarch64__ 65 static Atomic<uintptr_t> ldso_base; 66 # endif 67 #endif 68 69 #ifdef ANDROID 70 # include <algorithm> 71 # include <unistd.h> 72 # include <pthread.h> 73 #endif 74 75 class FrameSkipper { 76 public: 77 constexpr FrameSkipper() : mSkipUntilAddr(0) {} 78 static uintptr_t AddressFromPC(const void* aPC) { 79 #ifdef __arm__ 80 // On 32-bit ARM, mask off the thumb bit to get the instruction address. 81 return uintptr_t(aPC) & ~1; 82 #else 83 return uintptr_t(aPC); 84 #endif 85 } 86 bool ShouldSkipPC(void* aPC) { 87 // Skip frames until we encounter the one we were initialized with, 88 // and then never skip again. 89 uintptr_t instructionAddress = AddressFromPC(aPC); 90 if (mSkipUntilAddr != 0) { 91 if (mSkipUntilAddr != instructionAddress) { 92 return true; 93 } 94 mSkipUntilAddr = 0; 95 } 96 return false; 97 } 98 explicit FrameSkipper(const void* aPC) : mSkipUntilAddr(AddressFromPC(aPC)) {} 99 100 private: 101 uintptr_t mSkipUntilAddr; 102 }; 103 104 #ifdef XP_WIN 105 106 # include <windows.h> 107 # include <process.h> 108 # include <stdio.h> 109 # include <malloc.h> 110 # include "mozilla/Atomics.h" 111 # include "mozilla/StackWalk_windows.h" 112 113 # include <imagehlp.h> 114 // We need a way to know if we are building for WXP (or later), as if we are, we 115 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill. 116 // A value of 9 indicates we want to use the new APIs. 117 # if API_VERSION_NUMBER < 9 118 # error Too old imagehlp.h 119 # endif 120 121 # if defined(_M_AMD64) || defined(_M_ARM64) 122 // We must use RtlLookupFunctionEntry to do stack walking on x86-64 and arm64, 123 // but internally this function does a blocking shared acquire of SRW locks 124 // that live in ntdll and are not exported. This is problematic when we want to 125 // suspend a thread and walk its stack, like we do in the profiler and the 126 // background hang reporter. If the suspended thread happens to hold one of the 127 // locks exclusively while suspended, then the stack walking thread will 128 // deadlock if it calls RtlLookupFunctionEntry. 129 // 130 // Note that we only care about deadlocks between the stack walking thread and 131 // the suspended thread. Any other deadlock scenario is considered out of 132 // scope, because they are unlikely to be our fault -- these other scenarios 133 // imply that some thread that we did not suspend is stuck holding one of the 134 // locks exclusively, and exclusive acquisition of these locks only happens for 135 // a brief time during Microsoft API calls (e.g. LdrLoadDll, LdrUnloadDll). 136 // 137 // We use one of two alternative strategies to gracefully fail to capture a 138 // stack instead of running into a deadlock: 139 // (1) collect pointers to the ntdll internal locks at stack walk 140 // initialization, then try to acquire them non-blockingly before 141 // initiating any stack walk; 142 // or (2) mark all code paths that can potentially end up doing an exclusive 143 // acquisition of the locks as stack walk suppression paths, then check 144 // if any thread is currently on a stack walk suppression path before 145 // initiating any stack walk; 146 // 147 // Strategy (2) can only avoid all deadlocks under the easily wronged 148 // assumption that we have correctly identified all existing paths that should 149 // be stack suppression paths. With strategy (2) we cannot collect stacks e.g. 150 // during the whole duration of a DLL load happening on any thread so the 151 // profiling results are worse. 152 // 153 // Strategy (1) guarantees no deadlock. It also gives better profiling results 154 // because it is more fine-grained. Therefore we always prefer strategy (1), 155 // and we only use strategy (2) as a fallback. 156 157 // Strategy (1): Ntdll Internal Locks 158 // 159 // The external stack walk initialization code will feed us pointers to the 160 // ntdll internal locks. Once we have them, we no longer need to rely on 161 // strategy (2). 162 static Atomic<bool> sStackWalkLocksInitialized; 163 static Array<SRWLOCK*, 2> sStackWalkLocks; 164 165 MFBT_API 166 void InitializeStackWalkLocks(const Array<void*, 2>& aStackWalkLocks) { 167 sStackWalkLocks[0] = reinterpret_cast<SRWLOCK*>(aStackWalkLocks[0]); 168 sStackWalkLocks[1] = reinterpret_cast<SRWLOCK*>(aStackWalkLocks[1]); 169 sStackWalkLocksInitialized = true; 170 } 171 172 // Strategy (2): Stack Walk Suppressions 173 // 174 // We're using an atomic counter rather than a critical section because we 175 // don't require mutual exclusion with the stack walker. If the stack walker 176 // determines that it's safe to start unwinding the suspended thread (i.e. 177 // there are no suppressions when the unwind begins), then it's safe to 178 // continue unwinding that thread even if other threads request suppressions 179 // in the meantime, because we can't deadlock with those other threads. 180 // 181 // XXX: This global variable is a larger-than-necessary hammer. A more scoped 182 // solution would be to maintain a counter per thread, but then it would be 183 // more difficult for WalkStackMain64 to read the suspended thread's counter. 184 static Atomic<size_t> sStackWalkSuppressions; 185 186 void SuppressStackWalking() { ++sStackWalkSuppressions; } 187 188 void DesuppressStackWalking() { 189 auto previousValue = sStackWalkSuppressions--; 190 // We should never desuppress from 0. See bug 1687510 comment 10 for an 191 // example in which this occured. 192 MOZ_RELEASE_ASSERT(previousValue); 193 } 194 195 MFBT_API 196 AutoSuppressStackWalking::AutoSuppressStackWalking() { SuppressStackWalking(); } 197 198 MFBT_API 199 AutoSuppressStackWalking::~AutoSuppressStackWalking() { 200 DesuppressStackWalking(); 201 } 202 203 bool IsStackWalkingSafe() { 204 // Use strategy (1), if initialized. 205 if (sStackWalkLocksInitialized) { 206 bool isSafe = false; 207 if (::TryAcquireSRWLockShared(sStackWalkLocks[0])) { 208 if (::TryAcquireSRWLockShared(sStackWalkLocks[1])) { 209 isSafe = true; 210 ::ReleaseSRWLockShared(sStackWalkLocks[1]); 211 } 212 ::ReleaseSRWLockShared(sStackWalkLocks[0]); 213 } 214 return isSafe; 215 } 216 217 // Otherwise, fall back to strategy (2). 218 return sStackWalkSuppressions == 0; 219 } 220 221 static uint8_t* sJitCodeRegionStart; 222 static size_t sJitCodeRegionSize; 223 uint8_t* sMsMpegJitCodeRegionStart; 224 size_t sMsMpegJitCodeRegionSize; 225 226 MFBT_API void RegisterJitCodeRegion(uint8_t* aStart, size_t aSize) { 227 // Currently we can only handle one JIT code region at a time 228 MOZ_RELEASE_ASSERT(!sJitCodeRegionStart); 229 230 sJitCodeRegionStart = aStart; 231 sJitCodeRegionSize = aSize; 232 } 233 234 MFBT_API void UnregisterJitCodeRegion(uint8_t* aStart, size_t aSize) { 235 // Currently we can only handle one JIT code region at a time 236 MOZ_RELEASE_ASSERT(sJitCodeRegionStart && sJitCodeRegionStart == aStart && 237 sJitCodeRegionSize == aSize); 238 239 sJitCodeRegionStart = nullptr; 240 sJitCodeRegionSize = 0; 241 } 242 243 # endif // _M_AMD64 || _M_ARM64 244 245 // Routine to print an error message to standard error. 246 static void PrintError(const char* aPrefix) { 247 LPSTR lpMsgBuf; 248 DWORD lastErr = GetLastError(); 249 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 250 FORMAT_MESSAGE_IGNORE_INSERTS, 251 nullptr, lastErr, 252 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language 253 (LPSTR)&lpMsgBuf, 0, nullptr); 254 fprintf(stderr, "### ERROR: %s: %s", aPrefix, 255 lpMsgBuf ? lpMsgBuf : "(null)\n"); 256 fflush(stderr); 257 LocalFree(lpMsgBuf); 258 } 259 260 class MOZ_RAII AutoCriticalSection { 261 public: 262 explicit inline AutoCriticalSection(LPCRITICAL_SECTION aCriticalSection) 263 : mCriticalSection{aCriticalSection} { 264 ::EnterCriticalSection(mCriticalSection); 265 } 266 inline ~AutoCriticalSection() { ::LeaveCriticalSection(mCriticalSection); } 267 268 AutoCriticalSection(AutoCriticalSection&& other) = delete; 269 AutoCriticalSection operator=(AutoCriticalSection&& other) = delete; 270 AutoCriticalSection(const AutoCriticalSection&) = delete; 271 AutoCriticalSection operator=(const AutoCriticalSection&) = delete; 272 273 private: 274 LPCRITICAL_SECTION mCriticalSection; 275 }; 276 277 // A thread-safe safe object interface for Microsoft's DbgHelp.dll. DbgHelp 278 // APIs are not thread-safe and they require the use of a unique HANDLE value 279 // as an identifier for the current session. All this is handled internally by 280 // DbgHelpWrapper. 281 class DbgHelpWrapper { 282 public: 283 explicit inline DbgHelpWrapper() : DbgHelpWrapper(InitFlag::BasicInit) {} 284 DbgHelpWrapper(DbgHelpWrapper&& other) = delete; 285 DbgHelpWrapper operator=(DbgHelpWrapper&& other) = delete; 286 DbgHelpWrapper(const DbgHelpWrapper&) = delete; 287 DbgHelpWrapper operator=(const DbgHelpWrapper&) = delete; 288 289 inline bool ReadyToUse() { return mInitSuccess; } 290 291 inline BOOL StackWalk64( 292 DWORD aMachineType, HANDLE aThread, LPSTACKFRAME64 aStackFrame, 293 PVOID aContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 aReadMemoryRoutine, 294 PFUNCTION_TABLE_ACCESS_ROUTINE64 aFunctionTableAccessRoutine, 295 PGET_MODULE_BASE_ROUTINE64 aGetModuleBaseRoutine, 296 PTRANSLATE_ADDRESS_ROUTINE64 aTranslateAddress) { 297 if (!ReadyToUse()) { 298 return FALSE; 299 } 300 301 AutoCriticalSection guard(&sCriticalSection); 302 return ::StackWalk64(aMachineType, sSessionId, aThread, aStackFrame, 303 aContextRecord, aReadMemoryRoutine, 304 aFunctionTableAccessRoutine, aGetModuleBaseRoutine, 305 aTranslateAddress); 306 } 307 308 protected: 309 enum class InitFlag : bool { 310 BasicInit, 311 WithSymbolSupport, 312 }; 313 314 explicit inline DbgHelpWrapper(InitFlag initFlag) { 315 mInitSuccess = Initialize(initFlag); 316 } 317 318 // DbgHelp functions are not thread-safe and should therefore be protected 319 // by using this critical section through a AutoCriticalSection. 320 static CRITICAL_SECTION sCriticalSection; 321 322 // DbgHelp functions require a unique HANDLE hProcess that should be the same 323 // throughout the current session. We refer to this handle as a session id. 324 // Ideally the session id should be a valid HANDLE to the target process, 325 // which in our case is the current process. 326 // 327 // However, in order to avoid conflicts with other sessions, the session id 328 // should be unique and therefore not just GetCurrentProcess(), which other 329 // pieces of code tend to already use (see bug 1699328). 330 // 331 // We therefore define sSessionId as a duplicate of the current process 332 // handle, a solution that meets all the requirements listed above. 333 static HANDLE sSessionId; 334 335 private: 336 bool mInitSuccess; 337 338 // This function initializes sCriticalSection, sSessionId and loads DbgHelp. 339 // It also calls SymInitialize if called with aInitFlag::WithSymbolSupport. 340 // It is thread-safe and reentrancy-safe. 341 [[nodiscard]] static bool Initialize(InitFlag aInitFlag); 342 343 // In debug and fuzzing builds, MOZ_ASSERT and MOZ_CRASH walk the stack to 344 // print it before actually crashing. This code path uses a DbgHelpWrapper 345 // object, hence *any* MOZ_ASSERT or MOZ_CRASH failure reached from 346 // Initialize() leads to rentrancy (see bug 1869997 for an example). Such 347 // failures can occur indirectly when we load dbghelp.dll, because we 348 // override various Microsoft-internal functions that are called upon DLL 349 // loading. We protect against reentrancy by keeping track of the ID of the 350 // thread that runs the initialization code. 351 static Atomic<DWORD> sInitializationThreadId; 352 }; 353 354 CRITICAL_SECTION DbgHelpWrapper::sCriticalSection{}; 355 Atomic<DWORD> DbgHelpWrapper::sInitializationThreadId{0}; 356 HANDLE DbgHelpWrapper::sSessionId{nullptr}; 357 358 // Thread-safety here is ensured by the C++ standard: scoped static 359 // initialization is thread-safe. sInitializationThreadId is used to protect 360 // against reentrancy -- and only for that purpose. 361 [[nodiscard]] /* static */ bool DbgHelpWrapper::Initialize( 362 DbgHelpWrapper::InitFlag aInitFlag) { 363 // In the code below, it is only safe to reach MOZ_ASSERT or MOZ_CRASH while 364 // sInitializationThreadId is set to the current thread id. 365 static Atomic<DWORD> sInitializationThreadId{0}; 366 DWORD currentThreadId = ::GetCurrentThreadId(); 367 368 // This code relies on Windows never giving us a current thread ID of zero. 369 // We make this assumption explicit, by failing if that should ever occur. 370 if (!currentThreadId) { 371 return false; 372 } 373 374 if (sInitializationThreadId == currentThreadId) { 375 // This is a reentrant call and we must abort here. 376 return false; 377 } 378 379 static const bool sHasInitializedDbgHelp = [currentThreadId]() { 380 // Per the C++ standard, only one thread evers reaches this path. 381 sInitializationThreadId = currentThreadId; 382 383 ::InitializeCriticalSection(&sCriticalSection); 384 385 if (!::DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), 386 GetCurrentProcess(), &sSessionId, 0, FALSE, 387 DUPLICATE_SAME_ACCESS)) { 388 return false; 389 } 390 391 bool dbgHelpLoaded = static_cast<bool>(::LoadLibraryW(L"dbghelp.dll")); 392 393 MOZ_ASSERT(dbgHelpLoaded); 394 sInitializationThreadId = 0; 395 return dbgHelpLoaded; 396 }(); 397 398 // If we don't need symbol initialization, we are done. If we need it, we 399 // can only proceed if DbgHelp initialization was successful. 400 if (aInitFlag == InitFlag::BasicInit || !sHasInitializedDbgHelp) { 401 return sHasInitializedDbgHelp; 402 } 403 404 static const bool sHasInitializedSymbols = [currentThreadId]() { 405 // Per the C++ standard, only one thread evers reaches this path. 406 sInitializationThreadId = currentThreadId; 407 408 bool symbolsInitialized = false; 409 410 { 411 AutoCriticalSection guard(&sCriticalSection); 412 ::SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); 413 symbolsInitialized = 414 static_cast<bool>(::SymInitializeW(sSessionId, nullptr, TRUE)); 415 /* XXX At some point we need to arrange to call SymCleanup */ 416 } 417 418 if (!symbolsInitialized) { 419 PrintError("SymInitialize"); 420 } 421 422 MOZ_ASSERT(symbolsInitialized); 423 sInitializationThreadId = 0; 424 return symbolsInitialized; 425 }(); 426 427 return sHasInitializedSymbols; 428 } 429 430 // Some APIs such as SymFromAddr also require that the session id has gone 431 // through SymInitialize. This is handled by child class DbgHelpWrapperSym. 432 class DbgHelpWrapperSym : public DbgHelpWrapper { 433 public: 434 explicit DbgHelpWrapperSym() : DbgHelpWrapper(InitFlag::WithSymbolSupport) {} 435 436 inline BOOL SymFromAddr(DWORD64 aAddress, PDWORD64 aDisplacement, 437 PSYMBOL_INFO aSymbol) { 438 if (!ReadyToUse()) { 439 return FALSE; 440 } 441 442 AutoCriticalSection guard(&sCriticalSection); 443 return ::SymFromAddr(sSessionId, aAddress, aDisplacement, aSymbol); 444 } 445 446 BOOL SymGetModuleInfoEspecial64(DWORD64 aAddr, PIMAGEHLP_MODULE64 aModuleInfo, 447 PIMAGEHLP_LINE64 aLineInfo); 448 449 private: 450 // Helpers for SymGetModuleInfoEspecial64 451 struct CallbackEspecial64UserContext { 452 HANDLE mSessionId; 453 DWORD64 mAddr; 454 }; 455 456 static BOOL CALLBACK CallbackEspecial64(PCSTR aModuleName, 457 DWORD64 aModuleBase, 458 ULONG aModuleSize, 459 PVOID aUserContext); 460 }; 461 462 // Wrapper around a reference to a CONTEXT, to simplify access to main 463 // platform-specific execution registers. 464 // It also avoids using CONTEXT* nullable pointers. 465 class CONTEXTGenericAccessors { 466 public: 467 explicit CONTEXTGenericAccessors(CONTEXT& aCONTEXT) : mCONTEXT(aCONTEXT) {} 468 469 CONTEXT* CONTEXTPtr() { return &mCONTEXT; } 470 471 inline auto& PC() { 472 # if defined(_M_AMD64) 473 return mCONTEXT.Rip; 474 # elif defined(_M_ARM64) 475 return mCONTEXT.Pc; 476 # elif defined(_M_IX86) 477 return mCONTEXT.Eip; 478 # else 479 # error "unknown platform" 480 # endif 481 } 482 483 inline auto& SP() { 484 # if defined(_M_AMD64) 485 return mCONTEXT.Rsp; 486 # elif defined(_M_ARM64) 487 return mCONTEXT.Sp; 488 # elif defined(_M_IX86) 489 return mCONTEXT.Esp; 490 # else 491 # error "unknown platform" 492 # endif 493 } 494 495 inline auto& BP() { 496 # if defined(_M_AMD64) 497 return mCONTEXT.Rbp; 498 # elif defined(_M_ARM64) 499 return mCONTEXT.Fp; 500 # elif defined(_M_IX86) 501 return mCONTEXT.Ebp; 502 # else 503 # error "unknown platform" 504 # endif 505 } 506 507 private: 508 CONTEXT& mCONTEXT; 509 }; 510 511 /** 512 * Walk the stack, translating PC's found into strings and recording the 513 * chain in aBuffer. For this to work properly, the DLLs must be rebased 514 * so that the address in the file agrees with the address in memory. 515 * Otherwise StackWalk will return FALSE when it hits a frame in a DLL 516 * whose in memory address doesn't match its in-file address. 517 */ 518 519 static void DoMozStackWalkThread(MozWalkStackCallback aCallback, 520 const void* aFirstFramePC, uint32_t aMaxFrames, 521 void* aClosure, HANDLE aThread, 522 CONTEXT* aContext) { 523 # if defined(_M_IX86) 524 DbgHelpWrapper dbgHelp; 525 if (!dbgHelp.ReadyToUse()) { 526 return; 527 } 528 # endif 529 530 HANDLE targetThread = aThread; 531 bool walkCallingThread; 532 if (!targetThread) { 533 targetThread = ::GetCurrentThread(); 534 walkCallingThread = true; 535 } else { 536 DWORD targetThreadId = ::GetThreadId(targetThread); 537 DWORD currentThreadId = ::GetCurrentThreadId(); 538 walkCallingThread = (targetThreadId == currentThreadId); 539 } 540 541 // If not already provided, get a context for the specified thread. 542 CONTEXT context_buf; 543 if (!aContext) { 544 memset(&context_buf, 0, sizeof(CONTEXT)); 545 context_buf.ContextFlags = CONTEXT_FULL; 546 if (walkCallingThread) { 547 ::RtlCaptureContext(&context_buf); 548 } else if (!GetThreadContext(targetThread, &context_buf)) { 549 return; 550 } 551 } 552 CONTEXTGenericAccessors context{aContext ? *aContext : context_buf}; 553 554 # if defined(_M_IX86) 555 // Setup initial stack frame to walk from. 556 STACKFRAME64 frame64; 557 memset(&frame64, 0, sizeof(frame64)); 558 frame64.AddrPC.Offset = context.PC(); 559 frame64.AddrStack.Offset = context.SP(); 560 frame64.AddrFrame.Offset = context.BP(); 561 frame64.AddrPC.Mode = AddrModeFlat; 562 frame64.AddrStack.Mode = AddrModeFlat; 563 frame64.AddrFrame.Mode = AddrModeFlat; 564 frame64.AddrReturn.Mode = AddrModeFlat; 565 # endif 566 567 # if defined(_M_AMD64) || defined(_M_ARM64) 568 // If at least one thread (we don't know which) may be holding a lock that 569 // can deadlock RtlLookupFunctionEntry, we can't proceed because that thread 570 // may be the one that we're trying to walk the stack of. 571 // 572 // But if there is no such thread by this point, then our target thread can't 573 // be holding a lock, so it's safe to proceed. By virtue of being suspended, 574 // the target thread can't acquire any new locks during our stack walking, so 575 // we only need to do this check once. Other threads may temporarily acquire 576 // the locks while we're walking the stack, but that's mostly fine -- calling 577 // RtlLookupFunctionEntry will make us wait for them to release the locks, 578 // but at least we won't deadlock. 579 if (!IsStackWalkingSafe()) { 580 return; 581 } 582 583 bool firstFrame = true; 584 # endif 585 586 FrameSkipper skipper(aFirstFramePC); 587 588 uint32_t frames = 0; 589 590 // Now walk the stack. 591 while (true) { 592 DWORD64 addr; 593 DWORD64 spaddr; 594 595 # if defined(_M_IX86) 596 // 32-bit frame unwinding. 597 BOOL ok = dbgHelp.StackWalk64( 598 IMAGE_FILE_MACHINE_I386, targetThread, &frame64, context.CONTEXTPtr(), 599 nullptr, 600 ::SymFunctionTableAccess64, // function table access routine 601 ::SymGetModuleBase64, // module base routine 602 0); 603 604 if (ok) { 605 addr = frame64.AddrPC.Offset; 606 spaddr = frame64.AddrStack.Offset; 607 } else { 608 addr = 0; 609 spaddr = 0; 610 if (walkCallingThread) { 611 PrintError("WalkStack64"); 612 } 613 } 614 615 if (!ok) { 616 break; 617 } 618 619 # elif defined(_M_AMD64) || defined(_M_ARM64) 620 621 auto currentInstr = context.PC(); 622 623 // If we reach a frame in JIT code, we don't have enough information to 624 // unwind, so we have to give up. 625 if (sJitCodeRegionStart && (uint8_t*)currentInstr >= sJitCodeRegionStart && 626 (uint8_t*)currentInstr < sJitCodeRegionStart + sJitCodeRegionSize) { 627 break; 628 } 629 630 // We must also avoid msmpeg2vdec.dll's JIT region: they don't generate 631 // unwind data, so their JIT unwind callback just throws up its hands and 632 // terminates the process. 633 if (sMsMpegJitCodeRegionStart && 634 (uint8_t*)currentInstr >= sMsMpegJitCodeRegionStart && 635 (uint8_t*)currentInstr < 636 sMsMpegJitCodeRegionStart + sMsMpegJitCodeRegionSize) { 637 break; 638 } 639 640 // 64-bit frame unwinding. 641 // Try to look up unwind metadata for the current function. 642 ULONG64 imageBase; 643 PRUNTIME_FUNCTION runtimeFunction = 644 RtlLookupFunctionEntry(currentInstr, &imageBase, NULL); 645 646 if (runtimeFunction) { 647 PVOID dummyHandlerData; 648 ULONG64 dummyEstablisherFrame; 649 RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, currentInstr, 650 runtimeFunction, context.CONTEXTPtr(), &dummyHandlerData, 651 &dummyEstablisherFrame, nullptr); 652 } else if (firstFrame) { 653 // Leaf functions can be unwound by hand. 654 context.PC() = *reinterpret_cast<DWORD64*>(context.SP()); 655 context.SP() += sizeof(void*); 656 } else { 657 // Something went wrong. 658 break; 659 } 660 661 addr = context.PC(); 662 spaddr = context.SP(); 663 firstFrame = false; 664 # else 665 # error "unknown platform" 666 # endif 667 668 if (addr == 0) { 669 break; 670 } 671 672 if (skipper.ShouldSkipPC((void*)addr)) { 673 continue; 674 } 675 676 aCallback(++frames, (void*)addr, (void*)spaddr, aClosure); 677 678 if (aMaxFrames != 0 && frames == aMaxFrames) { 679 break; 680 } 681 682 # if defined(_M_IX86) 683 if (frame64.AddrReturn.Offset == 0) { 684 break; 685 } 686 # endif 687 } 688 } 689 690 MFBT_API void MozStackWalkThread(MozWalkStackCallback aCallback, 691 uint32_t aMaxFrames, void* aClosure, 692 HANDLE aThread, CONTEXT* aContext) { 693 // We don't pass a aFirstFramePC because we walk the stack for another 694 // thread. 695 DoMozStackWalkThread(aCallback, nullptr, aMaxFrames, aClosure, aThread, 696 aContext); 697 } 698 699 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback, 700 const void* aFirstFramePC, uint32_t aMaxFrames, 701 void* aClosure) { 702 DoMozStackWalkThread(aCallback, aFirstFramePC ? aFirstFramePC : CallerPC(), 703 aMaxFrames, aClosure, nullptr, nullptr); 704 } 705 706 /* static */ BOOL CALLBACK 707 DbgHelpWrapperSym::CallbackEspecial64(PCSTR aModuleName, DWORD64 aModuleBase, 708 ULONG aModuleSize, PVOID aUserContext) { 709 BOOL retval = TRUE; 710 auto context = reinterpret_cast<CallbackEspecial64UserContext*>(aUserContext); 711 DWORD64 addr = context->mAddr; 712 713 /* 714 * You'll want to control this if we are running on an 715 * architecture where the addresses go the other direction. 716 * Not sure this is even a realistic consideration. 717 */ 718 const BOOL addressIncreases = TRUE; 719 720 /* 721 * If it falls in side the known range, load the symbols. 722 */ 723 if (addressIncreases 724 ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize)) 725 : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))) { 726 retval = !!::SymLoadModule64(context->mSessionId, nullptr, aModuleName, 727 nullptr, aModuleBase, aModuleSize); 728 if (!retval) { 729 PrintError("SymLoadModule64"); 730 } 731 } 732 733 return retval; 734 } 735 736 /* 737 * SymGetModuleInfoEspecial 738 * 739 * Attempt to determine the module information. 740 * Bug 112196 says this DLL may not have been loaded at the time 741 * SymInitialize was called, and thus the module information 742 * and symbol information is not available. 743 * This code rectifies that problem. 744 */ 745 746 // New members were added to IMAGEHLP_MODULE64 (that show up in the 747 // Platform SDK that ships with VC8, but not the Platform SDK that ships 748 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to 749 // use them, and it's useful to be able to function correctly with the 750 // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll 751 // version 5.1.) Since Platform SDK version need not correspond to 752 // compiler version, and the version number in debughlp.h was NOT bumped 753 // when these changes were made, ifdef based on a constant that was 754 // added between these versions. 755 # ifdef SSRVOPT_SETCONTEXT 756 # define NS_IMAGEHLP_MODULE64_SIZE \ 757 (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / \ 758 sizeof(DWORD64)) * \ 759 sizeof(DWORD64)) 760 # else 761 # define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64) 762 # endif 763 764 BOOL DbgHelpWrapperSym::SymGetModuleInfoEspecial64( 765 DWORD64 aAddr, PIMAGEHLP_MODULE64 aModuleInfo, PIMAGEHLP_LINE64 aLineInfo) { 766 if (!ReadyToUse()) { 767 return FALSE; 768 } 769 770 AutoCriticalSection guard(&sCriticalSection); 771 BOOL retval = FALSE; 772 773 /* 774 * Init the vars if we have em. 775 */ 776 aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE; 777 if (aLineInfo) { 778 aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64); 779 } 780 781 /* 782 * Give it a go. 783 * It may already be loaded. 784 */ 785 retval = ::SymGetModuleInfo64(sSessionId, aAddr, aModuleInfo); 786 if (retval == FALSE) { 787 /* 788 * Not loaded, here's the magic. 789 * Go through all the modules. 790 */ 791 CallbackEspecial64UserContext context{ 792 .mSessionId = sSessionId, 793 .mAddr = aAddr, 794 }; 795 BOOL enumRes = ::EnumerateLoadedModules64( 796 sSessionId, CallbackEspecial64, reinterpret_cast<PVOID>(&context)); 797 if (enumRes != FALSE) { 798 /* 799 * One final go. 800 * If it fails, then well, we have other problems. 801 */ 802 retval = ::SymGetModuleInfo64(sSessionId, aAddr, aModuleInfo); 803 } 804 } 805 806 /* 807 * If we got module info, we may attempt line info as well. 808 * We will not report failure if this does not work. 809 */ 810 if (retval != FALSE && aLineInfo) { 811 DWORD displacement = 0; 812 BOOL lineRes = FALSE; 813 lineRes = 814 ::SymGetLineFromAddr64(sSessionId, aAddr, &displacement, aLineInfo); 815 if (!lineRes) { 816 // Clear out aLineInfo to indicate that it's not valid 817 memset(aLineInfo, 0, sizeof(*aLineInfo)); 818 } 819 } 820 821 return retval; 822 } 823 824 MFBT_API bool MozDescribeCodeAddress(void* aPC, 825 MozCodeAddressDetails* aDetails) { 826 aDetails->library[0] = '\0'; 827 aDetails->loffset = 0; 828 aDetails->filename[0] = '\0'; 829 aDetails->lineno = 0; 830 aDetails->function[0] = '\0'; 831 aDetails->foffset = 0; 832 833 DbgHelpWrapperSym dbgHelp; 834 if (!dbgHelp.ReadyToUse()) { 835 return false; 836 } 837 838 // Attempt to load module info before we attempt to resolve the symbol. 839 // This just makes sure we get good info if available. 840 DWORD64 addr = (DWORD64)aPC; 841 IMAGEHLP_MODULE64 modInfo; 842 IMAGEHLP_LINE64 lineInfo; 843 BOOL modInfoRes; 844 modInfoRes = dbgHelp.SymGetModuleInfoEspecial64(addr, &modInfo, &lineInfo); 845 846 if (modInfoRes) { 847 strncpy(aDetails->library, modInfo.LoadedImageName, 848 sizeof(aDetails->library)); 849 aDetails->library[std::size(aDetails->library) - 1] = '\0'; 850 aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage; 851 852 if (lineInfo.FileName) { 853 strncpy(aDetails->filename, lineInfo.FileName, 854 sizeof(aDetails->filename)); 855 aDetails->filename[std::size(aDetails->filename) - 1] = '\0'; 856 aDetails->lineno = lineInfo.LineNumber; 857 } 858 } 859 860 ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + 861 sizeof(ULONG64) - 1) / 862 sizeof(ULONG64)]; 863 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; 864 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); 865 pSymbol->MaxNameLen = MAX_SYM_NAME; 866 867 DWORD64 displacement; 868 BOOL ok = dbgHelp.SymFromAddr(addr, &displacement, pSymbol); 869 870 if (ok) { 871 strncpy(aDetails->function, pSymbol->Name, sizeof(aDetails->function)); 872 aDetails->function[std::size(aDetails->function) - 1] = '\0'; 873 aDetails->foffset = static_cast<ptrdiff_t>(displacement); 874 } 875 876 return true; 877 } 878 879 // i386 or PPC Linux stackwalking code 880 // 881 // Changes to to OS/Architecture support here should be reflected in 882 // build/moz.configure/memory.configure 883 #elif HAVE_DLADDR && \ 884 (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || \ 885 MOZ_STACKWALK_SUPPORTS_MACOSX) 886 887 # include <stdlib.h> 888 # include <stdio.h> 889 890 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed 891 // if __USE_GNU is defined. I suppose its some kind of standards 892 // adherence thing. 893 // 894 # if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU) 895 # define __USE_GNU 896 # endif 897 898 // This thing is exported by libstdc++ 899 // Yes, this is a gcc only hack 900 # if defined(MOZ_DEMANGLE_SYMBOLS) 901 # include <cxxabi.h> 902 # endif // MOZ_DEMANGLE_SYMBOLS 903 904 namespace mozilla { 905 906 void DemangleSymbol(const char* aSymbol, char* aBuffer, int aBufLen) { 907 aBuffer[0] = '\0'; 908 909 # if defined(MOZ_DEMANGLE_SYMBOLS) 910 /* See demangle.h in the gcc source for the voodoo */ 911 char* demangled = abi::__cxa_demangle(aSymbol, 0, 0, 0); 912 913 if (demangled) { 914 strncpy(aBuffer, demangled, aBufLen); 915 aBuffer[aBufLen - 1] = '\0'; 916 free(demangled); 917 } 918 # endif // MOZ_DEMANGLE_SYMBOLS 919 } 920 921 } // namespace mozilla 922 923 // {x86, ppc} x {Linux, Mac} stackwalking code. 924 // 925 // Changes to to OS/Architecture support here should be reflected in 926 // build/moz.configure/memory.configure 927 # if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \ 928 (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX)) 929 930 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback, 931 const void* aFirstFramePC, 932 uint32_t aMaxFrames, void* aClosure, 933 void** aBp, void* aStackEnd); 934 935 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback, 936 const void* aFirstFramePC, uint32_t aMaxFrames, 937 void* aClosure) { 938 // Get the frame pointer 939 void** bp = (void**)__builtin_frame_address(0); 940 941 void* stackEnd; 942 # if HAVE___LIBC_STACK_END 943 stackEnd = __libc_stack_end; 944 # elif defined(XP_DARWIN) 945 stackEnd = pthread_get_stackaddr_np(pthread_self()); 946 # elif defined(ANDROID) 947 pthread_attr_t sattr; 948 pthread_attr_init(&sattr); 949 int rc = pthread_getattr_np(pthread_self(), &sattr); 950 MOZ_RELEASE_ASSERT(rc == 0, "pthread_getattr_np failed"); 951 void* stackBase = stackEnd = nullptr; 952 size_t stackSize = 0; 953 if (gettid() != getpid()) { 954 // bionic's pthread_attr_getstack doesn't tell the truth for the main 955 // thread (see bug 846670). So don't use it for the main thread. 956 if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) { 957 stackEnd = static_cast<char*>(stackBase) + stackSize; 958 } else { 959 stackEnd = nullptr; 960 } 961 } 962 if (!stackEnd) { 963 // So consider the current frame pointer + an arbitrary size of 8MB 964 // (modulo overflow ; not really arbitrary as it's the default stack 965 // size for the main thread) if pthread_attr_getstack failed for 966 // some reason (or was skipped). 967 static const uintptr_t kMaxStackSize = 8 * 1024 * 1024; 968 uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize; 969 uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp)); 970 stackEnd = reinterpret_cast<void*>(stackStart + kMaxStackSize); 971 } 972 # else 973 # error Unsupported configuration 974 # endif 975 DoFramePointerStackWalk(aCallback, aFirstFramePC, aMaxFrames, aClosure, bp, 976 stackEnd); 977 } 978 979 # elif defined(HAVE__UNWIND_BACKTRACE) 980 981 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0 982 # include <unwind.h> 983 984 struct unwind_info { 985 MozWalkStackCallback callback; 986 FrameSkipper skipper; 987 int maxFrames; 988 int numFrames; 989 void* closure; 990 }; 991 992 static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context, 993 void* closure) { 994 _Unwind_Reason_Code ret = _URC_NO_REASON; 995 unwind_info* info = static_cast<unwind_info*>(closure); 996 void* pc = reinterpret_cast<void*>(_Unwind_GetIP(context)); 997 # if HAVE___LIBC_STACK_END && defined(__aarch64__) 998 // Work around https://sourceware.org/bugzilla/show_bug.cgi?id=32612 999 // The _dl_tlsdesc_dynamic function can't be unwound through with 1000 // _Unwind_Backtrace when glibc is built with aarch64 PAC (that leads 1001 // to a crash). 1002 // Unfortunately, we can't get the address of that specific function, so 1003 // we just disallow all of ld-linux-aarch64.so.1: when we hit an address 1004 // in there, we make _Unwind_Backtrace stop. 1005 // In the case of _dl_tlsdesc_dynamic, this would stop the stackwalk at 1006 // tls_get_addr_tail, which is enough information to know the stack comes 1007 // from ld.so, and we even get inlining info giving us malloc, 1008 // allocate_dtv_entry and allocate_and_init, which is plenty enough and 1009 // better than nothing^Hcrashing. 1010 // To figure out whether the frame falls into ld-linux-aarch64.so.1, we 1011 // use __libc_stack_end (which lives there and is .data) as upper bound 1012 // (assuming .data comes after .text), and get the base address of the 1013 // library via dladdr. 1014 if (!ldso_base) { 1015 Dl_info info; 1016 dladdr(&__libc_stack_end, &info); 1017 ldso_base = (uintptr_t)info.dli_fbase; 1018 } 1019 if (ldso_base && ((uintptr_t)pc > ldso_base) && 1020 (uintptr_t)pc < (uintptr_t)&__libc_stack_end) { 1021 // Any error code will do, we just want to stop the walk even when 1022 // we haven't reached the limit. 1023 ret = _URC_FOREIGN_EXCEPTION_CAUGHT; 1024 } 1025 # endif 1026 // TODO Use something like '_Unwind_GetGR()' to get the stack pointer. 1027 if (!info->skipper.ShouldSkipPC(pc)) { 1028 info->numFrames++; 1029 (*info->callback)(info->numFrames, pc, nullptr, info->closure); 1030 if (info->maxFrames != 0 && info->numFrames == info->maxFrames) { 1031 // Again, any error code that stops the walk will do. 1032 return _URC_FOREIGN_EXCEPTION_CAUGHT; 1033 } 1034 } 1035 return ret; 1036 } 1037 1038 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback, 1039 const void* aFirstFramePC, uint32_t aMaxFrames, 1040 void* aClosure) { 1041 unwind_info info; 1042 info.callback = aCallback; 1043 info.skipper = FrameSkipper(aFirstFramePC ? aFirstFramePC : CallerPC()); 1044 info.maxFrames = aMaxFrames; 1045 info.numFrames = 0; 1046 info.closure = aClosure; 1047 1048 // We ignore the return value from _Unwind_Backtrace. There are three main 1049 // reasons for this. 1050 // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns 1051 // _URC_FAILURE. See 1052 // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110. 1053 // - If aMaxFrames != 0, we want to stop early, and the only way to do that 1054 // is to make unwind_callback return something other than _URC_NO_REASON, 1055 // which causes _Unwind_Backtrace to return a non-success code. 1056 // - MozStackWalk doesn't have a return value anyway. 1057 (void)_Unwind_Backtrace(unwind_callback, &info); 1058 } 1059 1060 # endif 1061 1062 bool MFBT_API MozDescribeCodeAddress(void* aPC, 1063 MozCodeAddressDetails* aDetails) { 1064 aDetails->library[0] = '\0'; 1065 aDetails->loffset = 0; 1066 aDetails->filename[0] = '\0'; 1067 aDetails->lineno = 0; 1068 aDetails->function[0] = '\0'; 1069 aDetails->foffset = 0; 1070 1071 Dl_info info; 1072 1073 # if defined(ANDROID) && defined(MOZ_LINKER) 1074 int ok = __wrap_dladdr(aPC, &info); 1075 # else 1076 int ok = dladdr(aPC, &info); 1077 # endif 1078 1079 if (!ok) { 1080 return true; 1081 } 1082 1083 strncpy(aDetails->library, info.dli_fname, sizeof(aDetails->library)); 1084 aDetails->library[std::size(aDetails->library) - 1] = '\0'; 1085 aDetails->loffset = (char*)aPC - (char*)info.dli_fbase; 1086 1087 # if !defined(XP_FREEBSD) 1088 // On FreeBSD, dli_sname is unusably bad, it often returns things like 1089 // 'gtk_xtbin_new' or 'XRE_GetBootstrap' instead of long C++ symbols. Just let 1090 // GetFunction do the lookup directly in the ELF image. 1091 1092 const char* symbol = info.dli_sname; 1093 if (!symbol || symbol[0] == '\0') { 1094 return true; 1095 } 1096 1097 DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function)); 1098 1099 if (aDetails->function[0] == '\0') { 1100 // Just use the mangled symbol if demangling failed. 1101 strncpy(aDetails->function, symbol, sizeof(aDetails->function)); 1102 aDetails->function[std::size(aDetails->function) - 1] = '\0'; 1103 } 1104 1105 aDetails->foffset = (char*)aPC - (char*)info.dli_saddr; 1106 # endif 1107 1108 return true; 1109 } 1110 1111 #else // unsupported platform. 1112 1113 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback, 1114 const void* aFirstFramePC, uint32_t aMaxFrames, 1115 void* aClosure) {} 1116 1117 MFBT_API bool MozDescribeCodeAddress(void* aPC, 1118 MozCodeAddressDetails* aDetails) { 1119 aDetails->library[0] = '\0'; 1120 aDetails->loffset = 0; 1121 aDetails->filename[0] = '\0'; 1122 aDetails->lineno = 0; 1123 aDetails->function[0] = '\0'; 1124 aDetails->foffset = 0; 1125 return false; 1126 } 1127 1128 #endif 1129 1130 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) 1131 1132 # if defined(XP_MACOSX) && defined(__aarch64__) 1133 // On macOS arm64, system libraries are arm64e binaries, and arm64e can do 1134 // pointer authentication: The low bits of the pointer are the actual pointer 1135 // value, and the high bits are an encrypted hash. During stackwalking, we need 1136 // to strip off this hash. In theory, ptrauth_strip would be the right function 1137 // to call for this. However, that function is a no-op unless it's called from 1138 // code which also builds as arm64e - which we do not. So we cannot use it. So 1139 // for now, we hardcode a mask that seems to work today: 40 bits for the pointer 1140 // and 24 bits for the hash seems to do the trick. We can worry about 1141 // dynamically computing the correct mask if this ever stops working. 1142 const uintptr_t kPointerMask = 1143 (uintptr_t(1) << 40) - 1; // 40 bits pointer, 24 bit PAC 1144 # else 1145 const uintptr_t kPointerMask = ~uintptr_t(0); 1146 # endif 1147 1148 MOZ_ASAN_IGNORE 1149 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback, 1150 const void* aFirstFramePC, 1151 uint32_t aMaxFrames, void* aClosure, 1152 void** aBp, void* aStackEnd) { 1153 // Stack walking code courtesy Kipp's "leaky". 1154 1155 FrameSkipper skipper(aFirstFramePC); 1156 uint32_t numFrames = 0; 1157 1158 // Sanitize the given aBp. Assume that something reasonably close to 1159 // but before the stack end is going be a valid frame pointer. Also 1160 // check that it is an aligned address. This increases the chances 1161 // that if the pointer is not valid (which might happen if the caller 1162 // called __builtin_frame_address(1) and its frame is busted for some 1163 // reason), we won't read it, leading to a crash. Because the calling 1164 // code is not using frame pointers when returning, it might actually 1165 // recover just fine. 1166 static const uintptr_t kMaxStackSize = 8 * 1024 * 1024; 1167 if (uintptr_t(aBp) < uintptr_t(aStackEnd) - 1168 std::min(kMaxStackSize, uintptr_t(aStackEnd)) || 1169 aBp >= aStackEnd || (uintptr_t(aBp) & 3)) { 1170 return; 1171 } 1172 1173 while (aBp) { 1174 void** next = (void**)*aBp; 1175 // aBp may not be a frame pointer on i386 if code was compiled with 1176 // -fomit-frame-pointer, so do some sanity checks. 1177 // (aBp should be a frame pointer on ppc(64) but checking anyway may help 1178 // a little if the stack has been corrupted.) 1179 // We don't need to check against the begining of the stack because 1180 // we can assume that aBp > sp 1181 if (next <= aBp || next >= aStackEnd || (uintptr_t(next) & 3)) { 1182 break; 1183 } 1184 # if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__) 1185 // ppc mac or powerpc64 linux 1186 void* pc = *(aBp + 2); 1187 aBp += 3; 1188 # else // i386 or powerpc32 linux 1189 void* pc = *(aBp + 1); 1190 aBp += 2; 1191 # endif 1192 1193 // Strip off pointer authentication hash, if present. For now, it looks 1194 // like only return addresses require stripping, and stack pointers do 1195 // not. This might change in the future. 1196 pc = (void*)((uintptr_t)pc & kPointerMask); 1197 1198 if (!skipper.ShouldSkipPC(pc)) { 1199 // Assume that the SP points to the BP of the function 1200 // it called. We can't know the exact location of the SP 1201 // but this should be sufficient for our use the SP 1202 // to order elements on the stack. 1203 numFrames++; 1204 (*aCallback)(numFrames, pc, aBp, aClosure); 1205 if (aMaxFrames != 0 && numFrames == aMaxFrames) { 1206 break; 1207 } 1208 } 1209 aBp = next; 1210 } 1211 } 1212 1213 namespace mozilla { 1214 1215 MFBT_API void FramePointerStackWalk(MozWalkStackCallback aCallback, 1216 uint32_t aMaxFrames, void* aClosure, 1217 void** aBp, void* aStackEnd) { 1218 // We don't pass a aFirstFramePC because we start walking the stack from the 1219 // frame at aBp. 1220 DoFramePointerStackWalk(aCallback, nullptr, aMaxFrames, aClosure, aBp, 1221 aStackEnd); 1222 } 1223 1224 } // namespace mozilla 1225 1226 #else 1227 1228 namespace mozilla { 1229 MFBT_API void FramePointerStackWalk(MozWalkStackCallback aCallback, 1230 uint32_t aMaxFrames, void* aClosure, 1231 void** aBp, void* aStackEnd) {} 1232 } // namespace mozilla 1233 1234 #endif 1235 1236 MFBT_API int MozFormatCodeAddressDetails( 1237 char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber, void* aPC, 1238 const MozCodeAddressDetails* aDetails) { 1239 return MozFormatCodeAddress(aBuffer, aBufferSize, aFrameNumber, aPC, 1240 aDetails->function, aDetails->library, 1241 aDetails->loffset, aDetails->filename, 1242 aDetails->lineno); 1243 } 1244 1245 MFBT_API int MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, 1246 uint32_t aFrameNumber, const void* aPC, 1247 const char* aFunction, const char* aLibrary, 1248 ptrdiff_t aLOffset, const char* aFileName, 1249 uint32_t aLineNo) { 1250 const char* function = aFunction && aFunction[0] ? aFunction : "???"; 1251 if (aFileName && aFileName[0]) { 1252 // We have a filename and (presumably) a line number. Use them. 1253 return SprintfBuf(aBuffer, aBufferSize, "#%02u: %s (%s:%u)", aFrameNumber, 1254 function, aFileName, aLineNo); 1255 } else if (aLibrary && aLibrary[0]) { 1256 // We have no filename, but we do have a library name. Use it and the 1257 // library offset, and print them in a way that `fix_stacks.py` can 1258 // post-process. 1259 return SprintfBuf(aBuffer, aBufferSize, "#%02u: %s[%s +0x%" PRIxPTR "]", 1260 aFrameNumber, function, aLibrary, 1261 static_cast<uintptr_t>(aLOffset)); 1262 } else { 1263 // We have nothing useful to go on. (The format string is split because 1264 // '??)' is a trigraph and causes a warning, sigh.) 1265 return SprintfBuf(aBuffer, aBufferSize, 1266 "#%02u: ??? (???:???" 1267 ")", 1268 aFrameNumber); 1269 } 1270 } 1271 1272 static void EnsureWrite(FILE* aStream, const char* aBuf, size_t aLen) { 1273 #ifdef XP_WIN 1274 int fd = _fileno(aStream); 1275 #else 1276 int fd = fileno(aStream); 1277 #endif 1278 while (aLen > 0) { 1279 #ifdef XP_WIN 1280 auto written = _write(fd, aBuf, aLen); 1281 #else 1282 auto written = write(fd, aBuf, aLen); 1283 #endif 1284 if (written <= 0 || size_t(written) > aLen) { 1285 break; 1286 } 1287 aBuf += written; 1288 aLen -= written; 1289 } 1290 } 1291 1292 template <int N> 1293 static int PrintStackFrameBuf(char (&aBuf)[N], uint32_t aFrameNumber, void* aPC, 1294 void* aSP) { 1295 MozCodeAddressDetails details; 1296 MozDescribeCodeAddress(aPC, &details); 1297 int len = 1298 MozFormatCodeAddressDetails(aBuf, N - 1, aFrameNumber, aPC, &details); 1299 len = std::min(len, N - 2); 1300 aBuf[len++] = '\n'; 1301 aBuf[len] = '\0'; 1302 return len; 1303 } 1304 1305 static void PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP, 1306 void* aClosure) { 1307 FILE* stream = (FILE*)aClosure; 1308 char buf[1025]; // 1024 + 1 for trailing '\n' 1309 int len = PrintStackFrameBuf(buf, aFrameNumber, aPC, aSP); 1310 fflush(stream); 1311 EnsureWrite(stream, buf, len); 1312 } 1313 1314 static bool WalkTheStackEnabled() { 1315 static bool result = [] { 1316 char* value = getenv("MOZ_DISABLE_WALKTHESTACK"); 1317 return !(value && value[0]); 1318 }(); 1319 return result; 1320 } 1321 1322 MFBT_API void MozWalkTheStack(FILE* aStream, const void* aFirstFramePC, 1323 uint32_t aMaxFrames) { 1324 if (WalkTheStackEnabled()) { 1325 MozStackWalk(PrintStackFrame, aFirstFramePC ? aFirstFramePC : CallerPC(), 1326 aMaxFrames, aStream); 1327 } 1328 } 1329 1330 static void WriteStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP, 1331 void* aClosure) { 1332 auto writer = (void (*)(const char*))aClosure; 1333 char buf[1024]; 1334 PrintStackFrameBuf(buf, aFrameNumber, aPC, aSP); 1335 writer(buf); 1336 } 1337 1338 MFBT_API void MozWalkTheStackWithWriter(void (*aWriter)(const char*), 1339 const void* aFirstFramePC, 1340 uint32_t aMaxFrames) { 1341 if (WalkTheStackEnabled()) { 1342 MozStackWalk(WriteStackFrame, aFirstFramePC ? aFirstFramePC : CallerPC(), 1343 aMaxFrames, (void*)aWriter); 1344 } 1345 }