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/. */
7 /* API for getting a stack trace of the C/C++ stack on the current thread */
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/StackWalk.h"
13 # include "mozilla/StackWalkThread.h"
18 #include "mozilla/Sprintf.h"
22 #if defined(ANDROID) && defined(MOZ_LINKER)
24 # include <android/log.h>
27 using namespace mozilla
;
29 // for _Unwind_Backtrace from libcxxrt or libunwind
30 // cxxabi.h from libcxxrt implicitly includes unwind.h first
31 #if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE)
35 #if defined(HAVE_DLOPEN) || defined(XP_DARWIN)
39 #if (defined(XP_DARWIN) && \
40 (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
41 # define MOZ_STACKWALK_SUPPORTS_MACOSX 1
43 # define MOZ_STACKWALK_SUPPORTS_MACOSX 0
46 #if (defined(linux) && \
47 ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
48 defined(HAVE__UNWIND_BACKTRACE)))
49 # define MOZ_STACKWALK_SUPPORTS_LINUX 1
51 # define MOZ_STACKWALK_SUPPORTS_LINUX 0
54 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
55 # define HAVE___LIBC_STACK_END 1
57 # define HAVE___LIBC_STACK_END 0
60 #if HAVE___LIBC_STACK_END
61 extern MOZ_EXPORT
void* __libc_stack_end
; // from ld-linux.so
72 constexpr FrameSkipper() : mPc(0) {}
73 bool ShouldSkipPC(void* aPC
) {
74 // Skip frames until we encounter the one we were initialized with,
75 // and then never skip again.
77 if (mPc
!= uintptr_t(aPC
)) {
84 explicit FrameSkipper(const void* aPc
) : mPc(uintptr_t(aPc
)) {}
96 # include "mozilla/ArrayUtils.h"
97 # include "mozilla/Atomics.h"
98 # include "mozilla/StackWalk_windows.h"
99 # include "mozilla/WindowsVersion.h"
101 # include <imagehlp.h>
102 // We need a way to know if we are building for WXP (or later), as if we are, we
103 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
104 // A value of 9 indicates we want to use the new APIs.
105 # if API_VERSION_NUMBER < 9
106 # error Too old imagehlp.h
109 CRITICAL_SECTION gDbgHelpCS
;
111 # if defined(_M_AMD64) || defined(_M_ARM64)
112 // Because various Win64 APIs acquire function-table locks, we need a way of
113 // preventing stack walking while those APIs are being called. Otherwise, the
114 // stack walker may suspend a thread holding such a lock, and deadlock when the
115 // stack unwind code attempts to wait for that lock.
117 // We're using an atomic counter rather than a critical section because we
118 // don't require mutual exclusion with the stack walker. If the stack walker
119 // determines that it's safe to start unwinding the suspended thread (i.e.
120 // there are no suppressions when the unwind begins), then it's safe to
121 // continue unwinding that thread even if other threads request suppressions
122 // in the meantime, because we can't deadlock with those other threads.
124 // XXX: This global variable is a larger-than-necessary hammer. A more scoped
125 // solution would be to maintain a counter per thread, but then it would be
126 // more difficult for WalkStackMain64 to read the suspended thread's counter.
127 static Atomic
<size_t> sStackWalkSuppressions
;
129 void SuppressStackWalking() { ++sStackWalkSuppressions
; }
131 void DesuppressStackWalking() {
132 auto previousValue
= sStackWalkSuppressions
--;
133 // We should never desuppress from 0. See bug 1687510 comment 10 for an
134 // example in which this occured.
135 MOZ_RELEASE_ASSERT(previousValue
);
139 AutoSuppressStackWalking::AutoSuppressStackWalking() { SuppressStackWalking(); }
142 AutoSuppressStackWalking::~AutoSuppressStackWalking() {
143 DesuppressStackWalking();
146 static uint8_t* sJitCodeRegionStart
;
147 static size_t sJitCodeRegionSize
;
148 uint8_t* sMsMpegJitCodeRegionStart
;
149 size_t sMsMpegJitCodeRegionSize
;
151 MFBT_API
void RegisterJitCodeRegion(uint8_t* aStart
, size_t aSize
) {
152 // Currently we can only handle one JIT code region at a time
153 MOZ_RELEASE_ASSERT(!sJitCodeRegionStart
);
155 sJitCodeRegionStart
= aStart
;
156 sJitCodeRegionSize
= aSize
;
159 MFBT_API
void UnregisterJitCodeRegion(uint8_t* aStart
, size_t aSize
) {
160 // Currently we can only handle one JIT code region at a time
161 MOZ_RELEASE_ASSERT(sJitCodeRegionStart
&& sJitCodeRegionStart
== aStart
&&
162 sJitCodeRegionSize
== aSize
);
164 sJitCodeRegionStart
= nullptr;
165 sJitCodeRegionSize
= 0;
168 # endif // _M_AMD64 || _M_ARM64
170 // Routine to print an error message to standard error.
171 static void PrintError(const char* aPrefix
) {
173 DWORD lastErr
= GetLastError();
174 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|
175 FORMAT_MESSAGE_IGNORE_INSERTS
,
177 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
178 (LPSTR
)&lpMsgBuf
, 0, nullptr);
179 fprintf(stderr
, "### ERROR: %s: %s", aPrefix
,
180 lpMsgBuf
? lpMsgBuf
: "(null)\n");
185 static bool EnsureDbgHelpInitialized() {
186 static bool sInitialized
= []() {
187 // We ensure that the critical section is always initialized only once,
188 // which avoids undefined behavior.
189 ::InitializeCriticalSection(&gDbgHelpCS
);
190 auto success
= static_cast<bool>(::LoadLibraryW(L
"dbghelp.dll"));
197 // Wrapper around a reference to a CONTEXT, to simplify access to main
198 // platform-specific execution registers.
199 // It also avoids using CONTEXT* nullable pointers.
200 class CONTEXTGenericAccessors
{
202 explicit CONTEXTGenericAccessors(CONTEXT
& aCONTEXT
) : mCONTEXT(aCONTEXT
) {}
204 CONTEXT
* CONTEXTPtr() { return &mCONTEXT
; }
207 # if defined(_M_AMD64)
209 # elif defined(_M_ARM64)
211 # elif defined(_M_IX86)
214 # error "unknown platform"
219 # if defined(_M_AMD64)
221 # elif defined(_M_ARM64)
223 # elif defined(_M_IX86)
226 # error "unknown platform"
231 # if defined(_M_AMD64)
233 # elif defined(_M_ARM64)
235 # elif defined(_M_IX86)
238 # error "unknown platform"
247 * Walk the stack, translating PC's found into strings and recording the
248 * chain in aBuffer. For this to work properly, the DLLs must be rebased
249 * so that the address in the file agrees with the address in memory.
250 * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
251 * whose in memory address doesn't match its in-file address.
254 static void DoMozStackWalkThread(MozWalkStackCallback aCallback
,
255 const void* aFirstFramePC
, uint32_t aMaxFrames
,
256 void* aClosure
, HANDLE aThread
,
258 # if defined(_M_IX86)
259 if (!EnsureDbgHelpInitialized()) {
264 HANDLE targetThread
= aThread
;
265 bool walkCallingThread
;
267 targetThread
= ::GetCurrentThread();
268 walkCallingThread
= true;
270 DWORD targetThreadId
= ::GetThreadId(targetThread
);
271 DWORD currentThreadId
= ::GetCurrentThreadId();
272 walkCallingThread
= (targetThreadId
== currentThreadId
);
275 // If not already provided, get a context for the specified thread.
278 memset(&context_buf
, 0, sizeof(CONTEXT
));
279 context_buf
.ContextFlags
= CONTEXT_FULL
;
280 if (walkCallingThread
) {
281 ::RtlCaptureContext(&context_buf
);
282 } else if (!GetThreadContext(targetThread
, &context_buf
)) {
286 CONTEXTGenericAccessors context
{aContext
? *aContext
: context_buf
};
288 # if defined(_M_IX86)
289 // Setup initial stack frame to walk from.
290 STACKFRAME64 frame64
;
291 memset(&frame64
, 0, sizeof(frame64
));
292 frame64
.AddrPC
.Offset
= context
.PC();
293 frame64
.AddrStack
.Offset
= context
.SP();
294 frame64
.AddrFrame
.Offset
= context
.BP();
295 frame64
.AddrPC
.Mode
= AddrModeFlat
;
296 frame64
.AddrStack
.Mode
= AddrModeFlat
;
297 frame64
.AddrFrame
.Mode
= AddrModeFlat
;
298 frame64
.AddrReturn
.Mode
= AddrModeFlat
;
301 # if defined(_M_AMD64) || defined(_M_ARM64)
302 // If there are any active suppressions, then at least one thread (we don't
303 // know which) is holding a lock that can deadlock RtlVirtualUnwind. Since
304 // that thread may be the one that we're trying to unwind, we can't proceed.
306 // But if there are no suppressions, then our target thread can't be holding
307 // a lock, and it's safe to proceed. By virtue of being suspended, the target
308 // thread can't acquire any new locks during the unwind process, so we only
309 // need to do this check once. After that, sStackWalkSuppressions can be
310 // changed by other threads while we're unwinding, and that's fine because
311 // we can't deadlock with those threads.
312 if (sStackWalkSuppressions
) {
316 bool firstFrame
= true;
319 FrameSkipper
skipper(aFirstFramePC
);
323 // Now walk the stack.
328 # if defined(_M_IX86)
329 // 32-bit frame unwinding.
330 // Debug routines are not threadsafe, so grab the lock.
331 EnterCriticalSection(&gDbgHelpCS
);
333 StackWalk64(IMAGE_FILE_MACHINE_I386
, ::GetCurrentProcess(),
334 targetThread
, &frame64
, context
.CONTEXTPtr(), nullptr,
335 SymFunctionTableAccess64
, // function table access routine
336 SymGetModuleBase64
, // module base routine
338 LeaveCriticalSection(&gDbgHelpCS
);
341 addr
= frame64
.AddrPC
.Offset
;
342 spaddr
= frame64
.AddrStack
.Offset
;
346 if (walkCallingThread
) {
347 PrintError("WalkStack64");
355 # elif defined(_M_AMD64) || defined(_M_ARM64)
357 auto currentInstr
= context
.PC();
359 // If we reach a frame in JIT code, we don't have enough information to
360 // unwind, so we have to give up.
361 if (sJitCodeRegionStart
&& (uint8_t*)currentInstr
>= sJitCodeRegionStart
&&
362 (uint8_t*)currentInstr
< sJitCodeRegionStart
+ sJitCodeRegionSize
) {
366 // We must also avoid msmpeg2vdec.dll's JIT region: they don't generate
367 // unwind data, so their JIT unwind callback just throws up its hands and
368 // terminates the process.
369 if (sMsMpegJitCodeRegionStart
&&
370 (uint8_t*)currentInstr
>= sMsMpegJitCodeRegionStart
&&
371 (uint8_t*)currentInstr
<
372 sMsMpegJitCodeRegionStart
+ sMsMpegJitCodeRegionSize
) {
376 // 64-bit frame unwinding.
377 // Try to look up unwind metadata for the current function.
379 PRUNTIME_FUNCTION runtimeFunction
=
380 RtlLookupFunctionEntry(currentInstr
, &imageBase
, NULL
);
382 if (runtimeFunction
) {
383 PVOID dummyHandlerData
;
384 ULONG64 dummyEstablisherFrame
;
385 RtlVirtualUnwind(UNW_FLAG_NHANDLER
, imageBase
, currentInstr
,
386 runtimeFunction
, context
.CONTEXTPtr(), &dummyHandlerData
,
387 &dummyEstablisherFrame
, nullptr);
388 } else if (firstFrame
) {
389 // Leaf functions can be unwound by hand.
390 context
.PC() = *reinterpret_cast<DWORD64
*>(context
.SP());
391 context
.SP() += sizeof(void*);
393 // Something went wrong.
398 spaddr
= context
.SP();
401 # error "unknown platform"
408 if (skipper
.ShouldSkipPC((void*)addr
)) {
412 aCallback(++frames
, (void*)addr
, (void*)spaddr
, aClosure
);
414 if (aMaxFrames
!= 0 && frames
== aMaxFrames
) {
418 # if defined(_M_IX86)
419 if (frame64
.AddrReturn
.Offset
== 0) {
426 MFBT_API
void MozStackWalkThread(MozWalkStackCallback aCallback
,
427 uint32_t aMaxFrames
, void* aClosure
,
428 HANDLE aThread
, CONTEXT
* aContext
) {
429 // We don't pass a aFirstFramePC because we walk the stack for another
431 DoMozStackWalkThread(aCallback
, nullptr, aMaxFrames
, aClosure
, aThread
,
435 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
436 const void* aFirstFramePC
, uint32_t aMaxFrames
,
438 DoMozStackWalkThread(aCallback
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
439 aMaxFrames
, aClosure
, nullptr, nullptr);
442 static BOOL CALLBACK
callbackEspecial64(PCSTR aModuleName
, DWORD64 aModuleBase
,
443 ULONG aModuleSize
, PVOID aUserContext
) {
445 DWORD64 addr
= *(DWORD64
*)aUserContext
;
448 * You'll want to control this if we are running on an
449 * architecture where the addresses go the other direction.
450 * Not sure this is even a realistic consideration.
452 const BOOL addressIncreases
= TRUE
;
455 * If it falls in side the known range, load the symbols.
458 ? (addr
>= aModuleBase
&& addr
<= (aModuleBase
+ aModuleSize
))
459 : (addr
<= aModuleBase
&& addr
>= (aModuleBase
- aModuleSize
))) {
460 retval
= !!SymLoadModule64(GetCurrentProcess(), nullptr, (PSTR
)aModuleName
,
461 nullptr, aModuleBase
, aModuleSize
);
463 PrintError("SymLoadModule64");
471 * SymGetModuleInfoEspecial
473 * Attempt to determine the module information.
474 * Bug 112196 says this DLL may not have been loaded at the time
475 * SymInitialize was called, and thus the module information
476 * and symbol information is not available.
477 * This code rectifies that problem.
480 // New members were added to IMAGEHLP_MODULE64 (that show up in the
481 // Platform SDK that ships with VC8, but not the Platform SDK that ships
482 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
483 // use them, and it's useful to be able to function correctly with the
484 // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
485 // version 5.1.) Since Platform SDK version need not correspond to
486 // compiler version, and the version number in debughlp.h was NOT bumped
487 // when these changes were made, ifdef based on a constant that was
488 // added between these versions.
489 # ifdef SSRVOPT_SETCONTEXT
490 # define NS_IMAGEHLP_MODULE64_SIZE \
491 (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / \
495 # define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
498 BOOL
SymGetModuleInfoEspecial64(HANDLE aProcess
, DWORD64 aAddr
,
499 PIMAGEHLP_MODULE64 aModuleInfo
,
500 PIMAGEHLP_LINE64 aLineInfo
) {
504 * Init the vars if we have em.
506 aModuleInfo
->SizeOfStruct
= NS_IMAGEHLP_MODULE64_SIZE
;
508 aLineInfo
->SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
513 * It may already be loaded.
515 retval
= SymGetModuleInfo64(aProcess
, aAddr
, aModuleInfo
);
516 if (retval
== FALSE
) {
518 * Not loaded, here's the magic.
519 * Go through all the modules.
521 // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
522 // constness of the first parameter of
523 // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
524 // non-const to const over time). See bug 391848 and bug
526 BOOL enumRes
= EnumerateLoadedModules64(
527 aProcess
, (PENUMLOADED_MODULES_CALLBACK64
)callbackEspecial64
,
529 if (enumRes
!= FALSE
) {
532 * If it fails, then well, we have other problems.
534 retval
= SymGetModuleInfo64(aProcess
, aAddr
, aModuleInfo
);
539 * If we got module info, we may attempt line info as well.
540 * We will not report failure if this does not work.
542 if (retval
!= FALSE
&& aLineInfo
) {
543 DWORD displacement
= 0;
544 BOOL lineRes
= FALSE
;
545 lineRes
= SymGetLineFromAddr64(aProcess
, aAddr
, &displacement
, aLineInfo
);
547 // Clear out aLineInfo to indicate that it's not valid
548 memset(aLineInfo
, 0, sizeof(*aLineInfo
));
555 static bool EnsureSymInitialized() {
556 static bool sInitialized
= []() {
557 if (!EnsureDbgHelpInitialized()) {
561 EnterCriticalSection(&gDbgHelpCS
);
562 SymSetOptions(SYMOPT_LOAD_LINES
| SYMOPT_UNDNAME
);
563 bool success
= SymInitialize(GetCurrentProcess(), nullptr, TRUE
);
564 /* XXX At some point we need to arrange to call SymCleanup */
565 LeaveCriticalSection(&gDbgHelpCS
);
568 PrintError("SymInitialize");
577 MFBT_API
bool MozDescribeCodeAddress(void* aPC
,
578 MozCodeAddressDetails
* aDetails
) {
579 aDetails
->library
[0] = '\0';
580 aDetails
->loffset
= 0;
581 aDetails
->filename
[0] = '\0';
582 aDetails
->lineno
= 0;
583 aDetails
->function
[0] = '\0';
584 aDetails
->foffset
= 0;
586 if (!EnsureSymInitialized()) {
590 HANDLE myProcess
= ::GetCurrentProcess();
593 // debug routines are not threadsafe, so grab the lock.
594 EnterCriticalSection(&gDbgHelpCS
);
597 // Attempt to load module info before we attempt to resolve the symbol.
598 // This just makes sure we get good info if available.
601 DWORD64 addr
= (DWORD64
)aPC
;
602 IMAGEHLP_MODULE64 modInfo
;
603 IMAGEHLP_LINE64 lineInfo
;
605 modInfoRes
= SymGetModuleInfoEspecial64(myProcess
, addr
, &modInfo
, &lineInfo
);
608 strncpy(aDetails
->library
, modInfo
.LoadedImageName
,
609 sizeof(aDetails
->library
));
610 aDetails
->library
[mozilla::ArrayLength(aDetails
->library
) - 1] = '\0';
611 aDetails
->loffset
= (char*)aPC
- (char*)modInfo
.BaseOfImage
;
613 if (lineInfo
.FileName
) {
614 strncpy(aDetails
->filename
, lineInfo
.FileName
,
615 sizeof(aDetails
->filename
));
616 aDetails
->filename
[mozilla::ArrayLength(aDetails
->filename
) - 1] = '\0';
617 aDetails
->lineno
= lineInfo
.LineNumber
;
621 ULONG64 buffer
[(sizeof(SYMBOL_INFO
) + MAX_SYM_NAME
* sizeof(TCHAR
) +
622 sizeof(ULONG64
) - 1) /
624 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)buffer
;
625 pSymbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
626 pSymbol
->MaxNameLen
= MAX_SYM_NAME
;
628 DWORD64 displacement
;
629 ok
= SymFromAddr(myProcess
, addr
, &displacement
, pSymbol
);
632 strncpy(aDetails
->function
, pSymbol
->Name
, sizeof(aDetails
->function
));
633 aDetails
->function
[mozilla::ArrayLength(aDetails
->function
) - 1] = '\0';
634 aDetails
->foffset
= static_cast<ptrdiff_t>(displacement
);
637 LeaveCriticalSection(&gDbgHelpCS
); // release our lock
641 // i386 or PPC Linux stackwalking code
643 // Changes to to OS/Architecture support here should be reflected in
644 // build/moz.configure/memory.configure
645 #elif HAVE_DLADDR && \
646 (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || \
647 MOZ_STACKWALK_SUPPORTS_MACOSX)
652 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
653 // if __USE_GNU is defined. I suppose its some kind of standards
656 # if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
660 // This thing is exported by libstdc++
661 // Yes, this is a gcc only hack
662 # if defined(MOZ_DEMANGLE_SYMBOLS)
664 # endif // MOZ_DEMANGLE_SYMBOLS
668 void DemangleSymbol(const char* aSymbol
, char* aBuffer
, int aBufLen
) {
671 # if defined(MOZ_DEMANGLE_SYMBOLS)
672 /* See demangle.h in the gcc source for the voodoo */
673 char* demangled
= abi::__cxa_demangle(aSymbol
, 0, 0, 0);
676 strncpy(aBuffer
, demangled
, aBufLen
);
677 aBuffer
[aBufLen
- 1] = '\0';
680 # endif // MOZ_DEMANGLE_SYMBOLS
683 } // namespace mozilla
685 // {x86, ppc} x {Linux, Mac} stackwalking code.
687 // Changes to to OS/Architecture support here should be reflected in
688 // build/moz.configure/memory.configure
689 # if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
690 (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
692 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback
,
693 const void* aFirstFramePC
,
694 uint32_t aMaxFrames
, void* aClosure
,
695 void** aBp
, void* aStackEnd
);
697 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
698 const void* aFirstFramePC
, uint32_t aMaxFrames
,
700 // Get the frame pointer
701 void** bp
= (void**)__builtin_frame_address(0);
704 # if HAVE___LIBC_STACK_END
705 stackEnd
= __libc_stack_end
;
706 # elif defined(XP_DARWIN)
707 stackEnd
= pthread_get_stackaddr_np(pthread_self());
708 # elif defined(ANDROID)
709 pthread_attr_t sattr
;
710 pthread_attr_init(&sattr
);
711 pthread_getattr_np(pthread_self(), &sattr
);
712 void* stackBase
= stackEnd
= nullptr;
713 size_t stackSize
= 0;
714 if (gettid() != getpid()) {
715 // bionic's pthread_attr_getstack doesn't tell the truth for the main
716 // thread (see bug 846670). So don't use it for the main thread.
717 if (!pthread_attr_getstack(&sattr
, &stackBase
, &stackSize
)) {
718 stackEnd
= static_cast<char*>(stackBase
) + stackSize
;
724 // So consider the current frame pointer + an arbitrary size of 8MB
725 // (modulo overflow ; not really arbitrary as it's the default stack
726 // size for the main thread) if pthread_attr_getstack failed for
727 // some reason (or was skipped).
728 static const uintptr_t kMaxStackSize
= 8 * 1024 * 1024;
729 uintptr_t maxStackStart
= uintptr_t(-1) - kMaxStackSize
;
730 uintptr_t stackStart
= std::max(maxStackStart
, uintptr_t(bp
));
731 stackEnd
= reinterpret_cast<void*>(stackStart
+ kMaxStackSize
);
734 # error Unsupported configuration
736 DoFramePointerStackWalk(aCallback
, aFirstFramePC
, aMaxFrames
, aClosure
, bp
,
740 # elif defined(HAVE__UNWIND_BACKTRACE)
742 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
746 MozWalkStackCallback callback
;
747 FrameSkipper skipper
;
753 static _Unwind_Reason_Code
unwind_callback(struct _Unwind_Context
* context
,
755 unwind_info
* info
= static_cast<unwind_info
*>(closure
);
756 void* pc
= reinterpret_cast<void*>(_Unwind_GetIP(context
));
757 // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
758 if (!info
->skipper
.ShouldSkipPC(pc
)) {
760 (*info
->callback
)(info
->numFrames
, pc
, nullptr, info
->closure
);
761 if (info
->maxFrames
!= 0 && info
->numFrames
== info
->maxFrames
) {
762 // Again, any error code that stops the walk will do.
763 return _URC_FOREIGN_EXCEPTION_CAUGHT
;
766 return _URC_NO_REASON
;
769 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
770 const void* aFirstFramePC
, uint32_t aMaxFrames
,
773 info
.callback
= aCallback
;
774 info
.skipper
= FrameSkipper(aFirstFramePC
? aFirstFramePC
: CallerPC());
775 info
.maxFrames
= aMaxFrames
;
777 info
.closure
= aClosure
;
779 // We ignore the return value from _Unwind_Backtrace. There are three main
781 // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
783 // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
784 // - If aMaxFrames != 0, we want to stop early, and the only way to do that
785 // is to make unwind_callback return something other than _URC_NO_REASON,
786 // which causes _Unwind_Backtrace to return a non-success code.
787 // - MozStackWalk doesn't have a return value anyway.
788 (void)_Unwind_Backtrace(unwind_callback
, &info
);
793 bool MFBT_API
MozDescribeCodeAddress(void* aPC
,
794 MozCodeAddressDetails
* aDetails
) {
795 aDetails
->library
[0] = '\0';
796 aDetails
->loffset
= 0;
797 aDetails
->filename
[0] = '\0';
798 aDetails
->lineno
= 0;
799 aDetails
->function
[0] = '\0';
800 aDetails
->foffset
= 0;
804 # if defined(ANDROID) && defined(MOZ_LINKER)
805 int ok
= __wrap_dladdr(aPC
, &info
);
807 int ok
= dladdr(aPC
, &info
);
814 strncpy(aDetails
->library
, info
.dli_fname
, sizeof(aDetails
->library
));
815 aDetails
->library
[mozilla::ArrayLength(aDetails
->library
) - 1] = '\0';
816 aDetails
->loffset
= (char*)aPC
- (char*)info
.dli_fbase
;
818 # if !defined(XP_FREEBSD)
819 // On FreeBSD, dli_sname is unusably bad, it often returns things like
820 // 'gtk_xtbin_new' or 'XRE_GetBootstrap' instead of long C++ symbols. Just let
821 // GetFunction do the lookup directly in the ELF image.
823 const char* symbol
= info
.dli_sname
;
824 if (!symbol
|| symbol
[0] == '\0') {
828 DemangleSymbol(symbol
, aDetails
->function
, sizeof(aDetails
->function
));
830 if (aDetails
->function
[0] == '\0') {
831 // Just use the mangled symbol if demangling failed.
832 strncpy(aDetails
->function
, symbol
, sizeof(aDetails
->function
));
833 aDetails
->function
[mozilla::ArrayLength(aDetails
->function
) - 1] = '\0';
836 aDetails
->foffset
= (char*)aPC
- (char*)info
.dli_saddr
;
842 #else // unsupported platform.
844 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
845 const void* aFirstFramePC
, uint32_t aMaxFrames
,
848 MFBT_API
bool MozDescribeCodeAddress(void* aPC
,
849 MozCodeAddressDetails
* aDetails
) {
850 aDetails
->library
[0] = '\0';
851 aDetails
->loffset
= 0;
852 aDetails
->filename
[0] = '\0';
853 aDetails
->lineno
= 0;
854 aDetails
->function
[0] = '\0';
855 aDetails
->foffset
= 0;
861 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
863 # if defined(XP_MACOSX) && defined(__aarch64__)
864 // On macOS arm64, system libraries are arm64e binaries, and arm64e can do
865 // pointer authentication: The low bits of the pointer are the actual pointer
866 // value, and the high bits are an encrypted hash. During stackwalking, we need
867 // to strip off this hash. In theory, ptrauth_strip would be the right function
868 // to call for this. However, that function is a no-op unless it's called from
869 // code which also builds as arm64e - which we do not. So we cannot use it. So
870 // for now, we hardcode a mask that seems to work today: 40 bits for the pointer
871 // and 24 bits for the hash seems to do the trick. We can worry about
872 // dynamically computing the correct mask if this ever stops working.
873 const uintptr_t kPointerMask
=
874 (uintptr_t(1) << 40) - 1; // 40 bits pointer, 24 bit PAC
876 const uintptr_t kPointerMask
= ~uintptr_t(0);
880 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback
,
881 const void* aFirstFramePC
,
882 uint32_t aMaxFrames
, void* aClosure
,
883 void** aBp
, void* aStackEnd
) {
884 // Stack walking code courtesy Kipp's "leaky".
886 FrameSkipper
skipper(aFirstFramePC
);
887 uint32_t numFrames
= 0;
889 // Sanitize the given aBp. Assume that something reasonably close to
890 // but before the stack end is going be a valid frame pointer. Also
891 // check that it is an aligned address. This increases the chances
892 // that if the pointer is not valid (which might happen if the caller
893 // called __builtin_frame_address(1) and its frame is busted for some
894 // reason), we won't read it, leading to a crash. Because the calling
895 // code is not using frame pointers when returning, it might actually
896 // recover just fine.
897 static const uintptr_t kMaxStackSize
= 8 * 1024 * 1024;
898 if (uintptr_t(aBp
) < uintptr_t(aStackEnd
) -
899 std::min(kMaxStackSize
, uintptr_t(aStackEnd
)) ||
900 aBp
>= aStackEnd
|| (uintptr_t(aBp
) & 3)) {
905 void** next
= (void**)*aBp
;
906 // aBp may not be a frame pointer on i386 if code was compiled with
907 // -fomit-frame-pointer, so do some sanity checks.
908 // (aBp should be a frame pointer on ppc(64) but checking anyway may help
909 // a little if the stack has been corrupted.)
910 // We don't need to check against the begining of the stack because
911 // we can assume that aBp > sp
912 if (next
<= aBp
|| next
>= aStackEnd
|| (uintptr_t(next
) & 3)) {
915 # if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
916 // ppc mac or powerpc64 linux
917 void* pc
= *(aBp
+ 2);
919 # else // i386 or powerpc32 linux
920 void* pc
= *(aBp
+ 1);
924 // Strip off pointer authentication hash, if present. For now, it looks
925 // like only return addresses require stripping, and stack pointers do
926 // not. This might change in the future.
927 pc
= (void*)((uintptr_t)pc
& kPointerMask
);
929 if (!skipper
.ShouldSkipPC(pc
)) {
930 // Assume that the SP points to the BP of the function
931 // it called. We can't know the exact location of the SP
932 // but this should be sufficient for our use the SP
933 // to order elements on the stack.
935 (*aCallback
)(numFrames
, pc
, aBp
, aClosure
);
936 if (aMaxFrames
!= 0 && numFrames
== aMaxFrames
) {
946 MFBT_API
void FramePointerStackWalk(MozWalkStackCallback aCallback
,
947 uint32_t aMaxFrames
, void* aClosure
,
948 void** aBp
, void* aStackEnd
) {
949 // We don't pass a aFirstFramePC because we start walking the stack from the
951 DoFramePointerStackWalk(aCallback
, nullptr, aMaxFrames
, aClosure
, aBp
,
955 } // namespace mozilla
960 MFBT_API
void FramePointerStackWalk(MozWalkStackCallback aCallback
,
961 uint32_t aMaxFrames
, void* aClosure
,
962 void** aBp
, void* aStackEnd
) {}
963 } // namespace mozilla
967 MFBT_API
int MozFormatCodeAddressDetails(
968 char* aBuffer
, uint32_t aBufferSize
, uint32_t aFrameNumber
, void* aPC
,
969 const MozCodeAddressDetails
* aDetails
) {
970 return MozFormatCodeAddress(aBuffer
, aBufferSize
, aFrameNumber
, aPC
,
971 aDetails
->function
, aDetails
->library
,
972 aDetails
->loffset
, aDetails
->filename
,
976 MFBT_API
int MozFormatCodeAddress(char* aBuffer
, uint32_t aBufferSize
,
977 uint32_t aFrameNumber
, const void* aPC
,
978 const char* aFunction
, const char* aLibrary
,
979 ptrdiff_t aLOffset
, const char* aFileName
,
981 const char* function
= aFunction
&& aFunction
[0] ? aFunction
: "???";
982 if (aFileName
&& aFileName
[0]) {
983 // We have a filename and (presumably) a line number. Use them.
984 return SprintfBuf(aBuffer
, aBufferSize
, "#%02u: %s (%s:%u)", aFrameNumber
,
985 function
, aFileName
, aLineNo
);
986 } else if (aLibrary
&& aLibrary
[0]) {
987 // We have no filename, but we do have a library name. Use it and the
988 // library offset, and print them in a way that `fix_stacks.py` can
990 return SprintfBuf(aBuffer
, aBufferSize
, "#%02u: %s[%s +0x%" PRIxPTR
"]",
991 aFrameNumber
, function
, aLibrary
,
992 static_cast<uintptr_t>(aLOffset
));
994 // We have nothing useful to go on. (The format string is split because
995 // '??)' is a trigraph and causes a warning, sigh.)
996 return SprintfBuf(aBuffer
, aBufferSize
,
997 "#%02u: ??? (???:???"
1003 static void EnsureWrite(FILE* aStream
, const char* aBuf
, size_t aLen
) {
1005 int fd
= _fileno(aStream
);
1007 int fd
= fileno(aStream
);
1011 auto written
= _write(fd
, aBuf
, aLen
);
1013 auto written
= write(fd
, aBuf
, aLen
);
1015 if (written
<= 0 || size_t(written
) > aLen
) {
1024 static int PrintStackFrameBuf(char (&aBuf
)[N
], uint32_t aFrameNumber
, void* aPC
,
1026 MozCodeAddressDetails details
;
1027 MozDescribeCodeAddress(aPC
, &details
);
1029 MozFormatCodeAddressDetails(aBuf
, N
- 1, aFrameNumber
, aPC
, &details
);
1030 len
= std::min(len
, N
- 2);
1036 static void PrintStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
1038 FILE* stream
= (FILE*)aClosure
;
1039 char buf
[1025]; // 1024 + 1 for trailing '\n'
1040 int len
= PrintStackFrameBuf(buf
, aFrameNumber
, aPC
, aSP
);
1042 EnsureWrite(stream
, buf
, len
);
1045 static bool WalkTheStackEnabled() {
1046 static bool result
= [] {
1047 char* value
= getenv("MOZ_DISABLE_WALKTHESTACK");
1048 return !(value
&& value
[0]);
1053 MFBT_API
void MozWalkTheStack(FILE* aStream
, const void* aFirstFramePC
,
1054 uint32_t aMaxFrames
) {
1055 if (WalkTheStackEnabled()) {
1056 MozStackWalk(PrintStackFrame
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
1057 aMaxFrames
, aStream
);
1061 static void WriteStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
1063 auto writer
= (void (*)(const char*))aClosure
;
1065 PrintStackFrameBuf(buf
, aFrameNumber
, aPC
, aSP
);
1069 MFBT_API
void MozWalkTheStackWithWriter(void (*aWriter
)(const char*),
1070 const void* aFirstFramePC
,
1071 uint32_t aMaxFrames
) {
1072 if (WalkTheStackEnabled()) {
1073 MozStackWalk(WriteStackFrame
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
1074 aMaxFrames
, (void*)aWriter
);