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/Atomics.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/StackWalk.h"
14 # include "mozilla/StackWalkThread.h"
19 #include "mozilla/Sprintf.h"
23 #if defined(ANDROID) && defined(MOZ_LINKER)
25 # include <android/log.h>
28 using namespace mozilla
;
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)
36 #if defined(HAVE_DLOPEN) || defined(XP_DARWIN)
40 #if (defined(XP_DARWIN) && \
41 (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
42 # define MOZ_STACKWALK_SUPPORTS_MACOSX 1
44 # define MOZ_STACKWALK_SUPPORTS_MACOSX 0
47 #if (defined(linux) && \
48 ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
49 defined(HAVE__UNWIND_BACKTRACE)))
50 # define MOZ_STACKWALK_SUPPORTS_LINUX 1
52 # define MOZ_STACKWALK_SUPPORTS_LINUX 0
55 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
56 # define HAVE___LIBC_STACK_END 1
58 # define HAVE___LIBC_STACK_END 0
61 #if HAVE___LIBC_STACK_END
62 extern MOZ_EXPORT
void* __libc_stack_end
; // from ld-linux.so
73 constexpr FrameSkipper() : mSkipUntilAddr(0) {}
74 static uintptr_t AddressFromPC(const void* aPC
) {
76 // On 32-bit ARM, mask off the thumb bit to get the instruction address.
77 return uintptr_t(aPC
) & ~1;
79 return uintptr_t(aPC
);
82 bool ShouldSkipPC(void* aPC
) {
83 // Skip frames until we encounter the one we were initialized with,
84 // and then never skip again.
85 uintptr_t instructionAddress
= AddressFromPC(aPC
);
86 if (mSkipUntilAddr
!= 0) {
87 if (mSkipUntilAddr
!= instructionAddress
) {
94 explicit FrameSkipper(const void* aPC
) : mSkipUntilAddr(AddressFromPC(aPC
)) {}
97 uintptr_t mSkipUntilAddr
;
102 # include <windows.h>
103 # include <process.h>
106 # include "mozilla/ArrayUtils.h"
107 # include "mozilla/Atomics.h"
108 # include "mozilla/StackWalk_windows.h"
109 # include "mozilla/WindowsVersion.h"
111 # include <imagehlp.h>
112 // We need a way to know if we are building for WXP (or later), as if we are, we
113 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
114 // A value of 9 indicates we want to use the new APIs.
115 # if API_VERSION_NUMBER < 9
116 # error Too old imagehlp.h
119 // DbgHelp functions are not thread-safe and should therefore be protected by
120 // using this critical section. Only use the critical section after a
121 // successful call to InitializeDbgHelp().
122 CRITICAL_SECTION gDbgHelpCS
;
124 # if defined(_M_AMD64) || defined(_M_ARM64)
125 // Because various Win64 APIs acquire function-table locks, we need a way of
126 // preventing stack walking while those APIs are being called. Otherwise, the
127 // stack walker may suspend a thread holding such a lock, and deadlock when the
128 // stack unwind code attempts to wait for that lock.
130 // We're using an atomic counter rather than a critical section because we
131 // don't require mutual exclusion with the stack walker. If the stack walker
132 // determines that it's safe to start unwinding the suspended thread (i.e.
133 // there are no suppressions when the unwind begins), then it's safe to
134 // continue unwinding that thread even if other threads request suppressions
135 // in the meantime, because we can't deadlock with those other threads.
137 // XXX: This global variable is a larger-than-necessary hammer. A more scoped
138 // solution would be to maintain a counter per thread, but then it would be
139 // more difficult for WalkStackMain64 to read the suspended thread's counter.
140 static Atomic
<size_t> sStackWalkSuppressions
;
142 void SuppressStackWalking() { ++sStackWalkSuppressions
; }
144 void DesuppressStackWalking() {
145 auto previousValue
= sStackWalkSuppressions
--;
146 // We should never desuppress from 0. See bug 1687510 comment 10 for an
147 // example in which this occured.
148 MOZ_RELEASE_ASSERT(previousValue
);
152 AutoSuppressStackWalking::AutoSuppressStackWalking() { SuppressStackWalking(); }
155 AutoSuppressStackWalking::~AutoSuppressStackWalking() {
156 DesuppressStackWalking();
159 static uint8_t* sJitCodeRegionStart
;
160 static size_t sJitCodeRegionSize
;
161 uint8_t* sMsMpegJitCodeRegionStart
;
162 size_t sMsMpegJitCodeRegionSize
;
164 MFBT_API
void RegisterJitCodeRegion(uint8_t* aStart
, size_t aSize
) {
165 // Currently we can only handle one JIT code region at a time
166 MOZ_RELEASE_ASSERT(!sJitCodeRegionStart
);
168 sJitCodeRegionStart
= aStart
;
169 sJitCodeRegionSize
= aSize
;
172 MFBT_API
void UnregisterJitCodeRegion(uint8_t* aStart
, size_t aSize
) {
173 // Currently we can only handle one JIT code region at a time
174 MOZ_RELEASE_ASSERT(sJitCodeRegionStart
&& sJitCodeRegionStart
== aStart
&&
175 sJitCodeRegionSize
== aSize
);
177 sJitCodeRegionStart
= nullptr;
178 sJitCodeRegionSize
= 0;
181 # endif // _M_AMD64 || _M_ARM64
183 // Routine to print an error message to standard error.
184 static void PrintError(const char* aPrefix
) {
186 DWORD lastErr
= GetLastError();
187 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|
188 FORMAT_MESSAGE_IGNORE_INSERTS
,
190 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
191 (LPSTR
)&lpMsgBuf
, 0, nullptr);
192 fprintf(stderr
, "### ERROR: %s: %s", aPrefix
,
193 lpMsgBuf
? lpMsgBuf
: "(null)\n");
198 enum class DbgHelpInitFlags
: bool {
203 // This function ensures that DbgHelp.dll is loaded in the current process,
204 // and initializes the gDbgHelpCS critical section that we use to protect calls
205 // to DbgHelp functions. If DbgHelpInitFlags::WithSymbolSupport is set, we
206 // additionally call the symbol initialization functions from DbgHelp so that
207 // symbol-related functions can be used.
209 // This function is thread-safe and reentrancy-safe. In debug and fuzzing
210 // builds, MOZ_ASSERT and MOZ_CRASH walk the stack to print it before actually
211 // crashing. Hence *any* MOZ_ASSERT or MOZ_CRASH failure reached from
212 // InitializeDbgHelp() leads to rentrancy (see bug 1869997 for an example).
213 // Such failures can occur indirectly when we load dbghelp.dll, because we
214 // override various Microsoft-internal functions that are called upon DLL
216 [[nodiscard
]] static bool InitializeDbgHelp(
217 DbgHelpInitFlags aInitFlags
= DbgHelpInitFlags::BasicInit
) {
218 // In the code below, it is only safe to reach MOZ_ASSERT or MOZ_CRASH while
219 // sInitializationThreadId is set to the current thread id.
220 static Atomic
<DWORD
> sInitializationThreadId
{0};
221 DWORD currentThreadId
= ::GetCurrentThreadId();
223 // This code relies on Windows never giving us a current thread ID of zero.
224 // We make this assumption explicit, by failing if that should ever occur.
225 if (!currentThreadId
) {
229 if (sInitializationThreadId
== currentThreadId
) {
230 // This is a reentrant call and we must abort here.
234 static const bool sHasInitializedDbgHelp
= [currentThreadId
]() {
235 sInitializationThreadId
= currentThreadId
;
237 ::InitializeCriticalSection(&gDbgHelpCS
);
238 bool dbgHelpLoaded
= static_cast<bool>(::LoadLibraryW(L
"dbghelp.dll"));
240 MOZ_ASSERT(dbgHelpLoaded
);
241 sInitializationThreadId
= 0;
242 return dbgHelpLoaded
;
245 // If we don't need symbol initialization, we are done. If we need it, we
246 // can only proceed if DbgHelp initialization was successful.
247 if (aInitFlags
== DbgHelpInitFlags::BasicInit
|| !sHasInitializedDbgHelp
) {
248 return sHasInitializedDbgHelp
;
251 static const bool sHasInitializedSymbols
= [currentThreadId
]() {
252 sInitializationThreadId
= currentThreadId
;
254 EnterCriticalSection(&gDbgHelpCS
);
255 SymSetOptions(SYMOPT_LOAD_LINES
| SYMOPT_UNDNAME
);
256 bool symbolsInitialized
= SymInitialize(GetCurrentProcess(), nullptr, TRUE
);
257 /* XXX At some point we need to arrange to call SymCleanup */
258 LeaveCriticalSection(&gDbgHelpCS
);
260 if (!symbolsInitialized
) {
261 PrintError("SymInitialize");
264 MOZ_ASSERT(symbolsInitialized
);
265 sInitializationThreadId
= 0;
266 return symbolsInitialized
;
269 return sHasInitializedSymbols
;
272 // Wrapper around a reference to a CONTEXT, to simplify access to main
273 // platform-specific execution registers.
274 // It also avoids using CONTEXT* nullable pointers.
275 class CONTEXTGenericAccessors
{
277 explicit CONTEXTGenericAccessors(CONTEXT
& aCONTEXT
) : mCONTEXT(aCONTEXT
) {}
279 CONTEXT
* CONTEXTPtr() { return &mCONTEXT
; }
282 # if defined(_M_AMD64)
284 # elif defined(_M_ARM64)
286 # elif defined(_M_IX86)
289 # error "unknown platform"
294 # if defined(_M_AMD64)
296 # elif defined(_M_ARM64)
298 # elif defined(_M_IX86)
301 # error "unknown platform"
306 # if defined(_M_AMD64)
308 # elif defined(_M_ARM64)
310 # elif defined(_M_IX86)
313 # error "unknown platform"
322 * Walk the stack, translating PC's found into strings and recording the
323 * chain in aBuffer. For this to work properly, the DLLs must be rebased
324 * so that the address in the file agrees with the address in memory.
325 * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
326 * whose in memory address doesn't match its in-file address.
329 static void DoMozStackWalkThread(MozWalkStackCallback aCallback
,
330 const void* aFirstFramePC
, uint32_t aMaxFrames
,
331 void* aClosure
, HANDLE aThread
,
333 # if defined(_M_IX86)
334 if (!InitializeDbgHelp()) {
339 HANDLE targetThread
= aThread
;
340 bool walkCallingThread
;
342 targetThread
= ::GetCurrentThread();
343 walkCallingThread
= true;
345 DWORD targetThreadId
= ::GetThreadId(targetThread
);
346 DWORD currentThreadId
= ::GetCurrentThreadId();
347 walkCallingThread
= (targetThreadId
== currentThreadId
);
350 // If not already provided, get a context for the specified thread.
353 memset(&context_buf
, 0, sizeof(CONTEXT
));
354 context_buf
.ContextFlags
= CONTEXT_FULL
;
355 if (walkCallingThread
) {
356 ::RtlCaptureContext(&context_buf
);
357 } else if (!GetThreadContext(targetThread
, &context_buf
)) {
361 CONTEXTGenericAccessors context
{aContext
? *aContext
: context_buf
};
363 # if defined(_M_IX86)
364 // Setup initial stack frame to walk from.
365 STACKFRAME64 frame64
;
366 memset(&frame64
, 0, sizeof(frame64
));
367 frame64
.AddrPC
.Offset
= context
.PC();
368 frame64
.AddrStack
.Offset
= context
.SP();
369 frame64
.AddrFrame
.Offset
= context
.BP();
370 frame64
.AddrPC
.Mode
= AddrModeFlat
;
371 frame64
.AddrStack
.Mode
= AddrModeFlat
;
372 frame64
.AddrFrame
.Mode
= AddrModeFlat
;
373 frame64
.AddrReturn
.Mode
= AddrModeFlat
;
376 # if defined(_M_AMD64) || defined(_M_ARM64)
377 // If there are any active suppressions, then at least one thread (we don't
378 // know which) is holding a lock that can deadlock RtlVirtualUnwind. Since
379 // that thread may be the one that we're trying to unwind, we can't proceed.
381 // But if there are no suppressions, then our target thread can't be holding
382 // a lock, and it's safe to proceed. By virtue of being suspended, the target
383 // thread can't acquire any new locks during the unwind process, so we only
384 // need to do this check once. After that, sStackWalkSuppressions can be
385 // changed by other threads while we're unwinding, and that's fine because
386 // we can't deadlock with those threads.
387 if (sStackWalkSuppressions
) {
391 bool firstFrame
= true;
394 FrameSkipper
skipper(aFirstFramePC
);
398 // Now walk the stack.
403 # if defined(_M_IX86)
404 // 32-bit frame unwinding.
405 // Debug routines are not threadsafe, so grab the lock.
406 EnterCriticalSection(&gDbgHelpCS
);
408 StackWalk64(IMAGE_FILE_MACHINE_I386
, ::GetCurrentProcess(),
409 targetThread
, &frame64
, context
.CONTEXTPtr(), nullptr,
410 SymFunctionTableAccess64
, // function table access routine
411 SymGetModuleBase64
, // module base routine
413 LeaveCriticalSection(&gDbgHelpCS
);
416 addr
= frame64
.AddrPC
.Offset
;
417 spaddr
= frame64
.AddrStack
.Offset
;
421 if (walkCallingThread
) {
422 PrintError("WalkStack64");
430 # elif defined(_M_AMD64) || defined(_M_ARM64)
432 auto currentInstr
= context
.PC();
434 // If we reach a frame in JIT code, we don't have enough information to
435 // unwind, so we have to give up.
436 if (sJitCodeRegionStart
&& (uint8_t*)currentInstr
>= sJitCodeRegionStart
&&
437 (uint8_t*)currentInstr
< sJitCodeRegionStart
+ sJitCodeRegionSize
) {
441 // We must also avoid msmpeg2vdec.dll's JIT region: they don't generate
442 // unwind data, so their JIT unwind callback just throws up its hands and
443 // terminates the process.
444 if (sMsMpegJitCodeRegionStart
&&
445 (uint8_t*)currentInstr
>= sMsMpegJitCodeRegionStart
&&
446 (uint8_t*)currentInstr
<
447 sMsMpegJitCodeRegionStart
+ sMsMpegJitCodeRegionSize
) {
451 // 64-bit frame unwinding.
452 // Try to look up unwind metadata for the current function.
454 PRUNTIME_FUNCTION runtimeFunction
=
455 RtlLookupFunctionEntry(currentInstr
, &imageBase
, NULL
);
457 if (runtimeFunction
) {
458 PVOID dummyHandlerData
;
459 ULONG64 dummyEstablisherFrame
;
460 RtlVirtualUnwind(UNW_FLAG_NHANDLER
, imageBase
, currentInstr
,
461 runtimeFunction
, context
.CONTEXTPtr(), &dummyHandlerData
,
462 &dummyEstablisherFrame
, nullptr);
463 } else if (firstFrame
) {
464 // Leaf functions can be unwound by hand.
465 context
.PC() = *reinterpret_cast<DWORD64
*>(context
.SP());
466 context
.SP() += sizeof(void*);
468 // Something went wrong.
473 spaddr
= context
.SP();
476 # error "unknown platform"
483 if (skipper
.ShouldSkipPC((void*)addr
)) {
487 aCallback(++frames
, (void*)addr
, (void*)spaddr
, aClosure
);
489 if (aMaxFrames
!= 0 && frames
== aMaxFrames
) {
493 # if defined(_M_IX86)
494 if (frame64
.AddrReturn
.Offset
== 0) {
501 MFBT_API
void MozStackWalkThread(MozWalkStackCallback aCallback
,
502 uint32_t aMaxFrames
, void* aClosure
,
503 HANDLE aThread
, CONTEXT
* aContext
) {
504 // We don't pass a aFirstFramePC because we walk the stack for another
506 DoMozStackWalkThread(aCallback
, nullptr, aMaxFrames
, aClosure
, aThread
,
510 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
511 const void* aFirstFramePC
, uint32_t aMaxFrames
,
513 DoMozStackWalkThread(aCallback
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
514 aMaxFrames
, aClosure
, nullptr, nullptr);
517 static BOOL CALLBACK
callbackEspecial64(PCSTR aModuleName
, DWORD64 aModuleBase
,
518 ULONG aModuleSize
, PVOID aUserContext
) {
520 DWORD64 addr
= *(DWORD64
*)aUserContext
;
523 * You'll want to control this if we are running on an
524 * architecture where the addresses go the other direction.
525 * Not sure this is even a realistic consideration.
527 const BOOL addressIncreases
= TRUE
;
530 * If it falls in side the known range, load the symbols.
533 ? (addr
>= aModuleBase
&& addr
<= (aModuleBase
+ aModuleSize
))
534 : (addr
<= aModuleBase
&& addr
>= (aModuleBase
- aModuleSize
))) {
535 retval
= !!SymLoadModule64(GetCurrentProcess(), nullptr, (PSTR
)aModuleName
,
536 nullptr, aModuleBase
, aModuleSize
);
538 PrintError("SymLoadModule64");
546 * SymGetModuleInfoEspecial
548 * Attempt to determine the module information.
549 * Bug 112196 says this DLL may not have been loaded at the time
550 * SymInitialize was called, and thus the module information
551 * and symbol information is not available.
552 * This code rectifies that problem.
555 // New members were added to IMAGEHLP_MODULE64 (that show up in the
556 // Platform SDK that ships with VC8, but not the Platform SDK that ships
557 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
558 // use them, and it's useful to be able to function correctly with the
559 // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
560 // version 5.1.) Since Platform SDK version need not correspond to
561 // compiler version, and the version number in debughlp.h was NOT bumped
562 // when these changes were made, ifdef based on a constant that was
563 // added between these versions.
564 # ifdef SSRVOPT_SETCONTEXT
565 # define NS_IMAGEHLP_MODULE64_SIZE \
566 (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / \
570 # define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
573 BOOL
SymGetModuleInfoEspecial64(HANDLE aProcess
, DWORD64 aAddr
,
574 PIMAGEHLP_MODULE64 aModuleInfo
,
575 PIMAGEHLP_LINE64 aLineInfo
) {
579 * Init the vars if we have em.
581 aModuleInfo
->SizeOfStruct
= NS_IMAGEHLP_MODULE64_SIZE
;
583 aLineInfo
->SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
588 * It may already be loaded.
590 retval
= SymGetModuleInfo64(aProcess
, aAddr
, aModuleInfo
);
591 if (retval
== FALSE
) {
593 * Not loaded, here's the magic.
594 * Go through all the modules.
596 // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
597 // constness of the first parameter of
598 // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
599 // non-const to const over time). See bug 391848 and bug
601 BOOL enumRes
= EnumerateLoadedModules64(
602 aProcess
, (PENUMLOADED_MODULES_CALLBACK64
)callbackEspecial64
,
604 if (enumRes
!= FALSE
) {
607 * If it fails, then well, we have other problems.
609 retval
= SymGetModuleInfo64(aProcess
, aAddr
, aModuleInfo
);
614 * If we got module info, we may attempt line info as well.
615 * We will not report failure if this does not work.
617 if (retval
!= FALSE
&& aLineInfo
) {
618 DWORD displacement
= 0;
619 BOOL lineRes
= FALSE
;
620 lineRes
= SymGetLineFromAddr64(aProcess
, aAddr
, &displacement
, aLineInfo
);
622 // Clear out aLineInfo to indicate that it's not valid
623 memset(aLineInfo
, 0, sizeof(*aLineInfo
));
630 MFBT_API
bool MozDescribeCodeAddress(void* aPC
,
631 MozCodeAddressDetails
* aDetails
) {
632 aDetails
->library
[0] = '\0';
633 aDetails
->loffset
= 0;
634 aDetails
->filename
[0] = '\0';
635 aDetails
->lineno
= 0;
636 aDetails
->function
[0] = '\0';
637 aDetails
->foffset
= 0;
639 if (!InitializeDbgHelp(DbgHelpInitFlags::WithSymbolSupport
)) {
643 HANDLE myProcess
= ::GetCurrentProcess();
646 // debug routines are not threadsafe, so grab the lock.
647 EnterCriticalSection(&gDbgHelpCS
);
650 // Attempt to load module info before we attempt to resolve the symbol.
651 // This just makes sure we get good info if available.
654 DWORD64 addr
= (DWORD64
)aPC
;
655 IMAGEHLP_MODULE64 modInfo
;
656 IMAGEHLP_LINE64 lineInfo
;
658 modInfoRes
= SymGetModuleInfoEspecial64(myProcess
, addr
, &modInfo
, &lineInfo
);
661 strncpy(aDetails
->library
, modInfo
.LoadedImageName
,
662 sizeof(aDetails
->library
));
663 aDetails
->library
[mozilla::ArrayLength(aDetails
->library
) - 1] = '\0';
664 aDetails
->loffset
= (char*)aPC
- (char*)modInfo
.BaseOfImage
;
666 if (lineInfo
.FileName
) {
667 strncpy(aDetails
->filename
, lineInfo
.FileName
,
668 sizeof(aDetails
->filename
));
669 aDetails
->filename
[mozilla::ArrayLength(aDetails
->filename
) - 1] = '\0';
670 aDetails
->lineno
= lineInfo
.LineNumber
;
674 ULONG64 buffer
[(sizeof(SYMBOL_INFO
) + MAX_SYM_NAME
* sizeof(TCHAR
) +
675 sizeof(ULONG64
) - 1) /
677 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)buffer
;
678 pSymbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
679 pSymbol
->MaxNameLen
= MAX_SYM_NAME
;
681 DWORD64 displacement
;
682 ok
= SymFromAddr(myProcess
, addr
, &displacement
, pSymbol
);
685 strncpy(aDetails
->function
, pSymbol
->Name
, sizeof(aDetails
->function
));
686 aDetails
->function
[mozilla::ArrayLength(aDetails
->function
) - 1] = '\0';
687 aDetails
->foffset
= static_cast<ptrdiff_t>(displacement
);
690 LeaveCriticalSection(&gDbgHelpCS
); // release our lock
694 // i386 or PPC Linux stackwalking code
696 // Changes to to OS/Architecture support here should be reflected in
697 // build/moz.configure/memory.configure
698 #elif HAVE_DLADDR && \
699 (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || \
700 MOZ_STACKWALK_SUPPORTS_MACOSX)
705 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
706 // if __USE_GNU is defined. I suppose its some kind of standards
709 # if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
713 // This thing is exported by libstdc++
714 // Yes, this is a gcc only hack
715 # if defined(MOZ_DEMANGLE_SYMBOLS)
717 # endif // MOZ_DEMANGLE_SYMBOLS
721 void DemangleSymbol(const char* aSymbol
, char* aBuffer
, int aBufLen
) {
724 # if defined(MOZ_DEMANGLE_SYMBOLS)
725 /* See demangle.h in the gcc source for the voodoo */
726 char* demangled
= abi::__cxa_demangle(aSymbol
, 0, 0, 0);
729 strncpy(aBuffer
, demangled
, aBufLen
);
730 aBuffer
[aBufLen
- 1] = '\0';
733 # endif // MOZ_DEMANGLE_SYMBOLS
736 } // namespace mozilla
738 // {x86, ppc} x {Linux, Mac} stackwalking code.
740 // Changes to to OS/Architecture support here should be reflected in
741 // build/moz.configure/memory.configure
742 # if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
743 (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
745 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback
,
746 const void* aFirstFramePC
,
747 uint32_t aMaxFrames
, void* aClosure
,
748 void** aBp
, void* aStackEnd
);
750 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
751 const void* aFirstFramePC
, uint32_t aMaxFrames
,
753 // Get the frame pointer
754 void** bp
= (void**)__builtin_frame_address(0);
757 # if HAVE___LIBC_STACK_END
758 stackEnd
= __libc_stack_end
;
759 # elif defined(XP_DARWIN)
760 stackEnd
= pthread_get_stackaddr_np(pthread_self());
761 # elif defined(ANDROID)
762 pthread_attr_t sattr
;
763 pthread_attr_init(&sattr
);
764 pthread_getattr_np(pthread_self(), &sattr
);
765 void* stackBase
= stackEnd
= nullptr;
766 size_t stackSize
= 0;
767 if (gettid() != getpid()) {
768 // bionic's pthread_attr_getstack doesn't tell the truth for the main
769 // thread (see bug 846670). So don't use it for the main thread.
770 if (!pthread_attr_getstack(&sattr
, &stackBase
, &stackSize
)) {
771 stackEnd
= static_cast<char*>(stackBase
) + stackSize
;
777 // So consider the current frame pointer + an arbitrary size of 8MB
778 // (modulo overflow ; not really arbitrary as it's the default stack
779 // size for the main thread) if pthread_attr_getstack failed for
780 // some reason (or was skipped).
781 static const uintptr_t kMaxStackSize
= 8 * 1024 * 1024;
782 uintptr_t maxStackStart
= uintptr_t(-1) - kMaxStackSize
;
783 uintptr_t stackStart
= std::max(maxStackStart
, uintptr_t(bp
));
784 stackEnd
= reinterpret_cast<void*>(stackStart
+ kMaxStackSize
);
787 # error Unsupported configuration
789 DoFramePointerStackWalk(aCallback
, aFirstFramePC
, aMaxFrames
, aClosure
, bp
,
793 # elif defined(HAVE__UNWIND_BACKTRACE)
795 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
799 MozWalkStackCallback callback
;
800 FrameSkipper skipper
;
806 static _Unwind_Reason_Code
unwind_callback(struct _Unwind_Context
* context
,
808 unwind_info
* info
= static_cast<unwind_info
*>(closure
);
809 void* pc
= reinterpret_cast<void*>(_Unwind_GetIP(context
));
810 // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
811 if (!info
->skipper
.ShouldSkipPC(pc
)) {
813 (*info
->callback
)(info
->numFrames
, pc
, nullptr, info
->closure
);
814 if (info
->maxFrames
!= 0 && info
->numFrames
== info
->maxFrames
) {
815 // Again, any error code that stops the walk will do.
816 return _URC_FOREIGN_EXCEPTION_CAUGHT
;
819 return _URC_NO_REASON
;
822 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
823 const void* aFirstFramePC
, uint32_t aMaxFrames
,
826 info
.callback
= aCallback
;
827 info
.skipper
= FrameSkipper(aFirstFramePC
? aFirstFramePC
: CallerPC());
828 info
.maxFrames
= aMaxFrames
;
830 info
.closure
= aClosure
;
832 // We ignore the return value from _Unwind_Backtrace. There are three main
834 // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
836 // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
837 // - If aMaxFrames != 0, we want to stop early, and the only way to do that
838 // is to make unwind_callback return something other than _URC_NO_REASON,
839 // which causes _Unwind_Backtrace to return a non-success code.
840 // - MozStackWalk doesn't have a return value anyway.
841 (void)_Unwind_Backtrace(unwind_callback
, &info
);
846 bool MFBT_API
MozDescribeCodeAddress(void* aPC
,
847 MozCodeAddressDetails
* aDetails
) {
848 aDetails
->library
[0] = '\0';
849 aDetails
->loffset
= 0;
850 aDetails
->filename
[0] = '\0';
851 aDetails
->lineno
= 0;
852 aDetails
->function
[0] = '\0';
853 aDetails
->foffset
= 0;
857 # if defined(ANDROID) && defined(MOZ_LINKER)
858 int ok
= __wrap_dladdr(aPC
, &info
);
860 int ok
= dladdr(aPC
, &info
);
867 strncpy(aDetails
->library
, info
.dli_fname
, sizeof(aDetails
->library
));
868 aDetails
->library
[mozilla::ArrayLength(aDetails
->library
) - 1] = '\0';
869 aDetails
->loffset
= (char*)aPC
- (char*)info
.dli_fbase
;
871 # if !defined(XP_FREEBSD)
872 // On FreeBSD, dli_sname is unusably bad, it often returns things like
873 // 'gtk_xtbin_new' or 'XRE_GetBootstrap' instead of long C++ symbols. Just let
874 // GetFunction do the lookup directly in the ELF image.
876 const char* symbol
= info
.dli_sname
;
877 if (!symbol
|| symbol
[0] == '\0') {
881 DemangleSymbol(symbol
, aDetails
->function
, sizeof(aDetails
->function
));
883 if (aDetails
->function
[0] == '\0') {
884 // Just use the mangled symbol if demangling failed.
885 strncpy(aDetails
->function
, symbol
, sizeof(aDetails
->function
));
886 aDetails
->function
[mozilla::ArrayLength(aDetails
->function
) - 1] = '\0';
889 aDetails
->foffset
= (char*)aPC
- (char*)info
.dli_saddr
;
895 #else // unsupported platform.
897 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
898 const void* aFirstFramePC
, uint32_t aMaxFrames
,
901 MFBT_API
bool MozDescribeCodeAddress(void* aPC
,
902 MozCodeAddressDetails
* aDetails
) {
903 aDetails
->library
[0] = '\0';
904 aDetails
->loffset
= 0;
905 aDetails
->filename
[0] = '\0';
906 aDetails
->lineno
= 0;
907 aDetails
->function
[0] = '\0';
908 aDetails
->foffset
= 0;
914 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
916 # if defined(XP_MACOSX) && defined(__aarch64__)
917 // On macOS arm64, system libraries are arm64e binaries, and arm64e can do
918 // pointer authentication: The low bits of the pointer are the actual pointer
919 // value, and the high bits are an encrypted hash. During stackwalking, we need
920 // to strip off this hash. In theory, ptrauth_strip would be the right function
921 // to call for this. However, that function is a no-op unless it's called from
922 // code which also builds as arm64e - which we do not. So we cannot use it. So
923 // for now, we hardcode a mask that seems to work today: 40 bits for the pointer
924 // and 24 bits for the hash seems to do the trick. We can worry about
925 // dynamically computing the correct mask if this ever stops working.
926 const uintptr_t kPointerMask
=
927 (uintptr_t(1) << 40) - 1; // 40 bits pointer, 24 bit PAC
929 const uintptr_t kPointerMask
= ~uintptr_t(0);
933 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback
,
934 const void* aFirstFramePC
,
935 uint32_t aMaxFrames
, void* aClosure
,
936 void** aBp
, void* aStackEnd
) {
937 // Stack walking code courtesy Kipp's "leaky".
939 FrameSkipper
skipper(aFirstFramePC
);
940 uint32_t numFrames
= 0;
942 // Sanitize the given aBp. Assume that something reasonably close to
943 // but before the stack end is going be a valid frame pointer. Also
944 // check that it is an aligned address. This increases the chances
945 // that if the pointer is not valid (which might happen if the caller
946 // called __builtin_frame_address(1) and its frame is busted for some
947 // reason), we won't read it, leading to a crash. Because the calling
948 // code is not using frame pointers when returning, it might actually
949 // recover just fine.
950 static const uintptr_t kMaxStackSize
= 8 * 1024 * 1024;
951 if (uintptr_t(aBp
) < uintptr_t(aStackEnd
) -
952 std::min(kMaxStackSize
, uintptr_t(aStackEnd
)) ||
953 aBp
>= aStackEnd
|| (uintptr_t(aBp
) & 3)) {
958 void** next
= (void**)*aBp
;
959 // aBp may not be a frame pointer on i386 if code was compiled with
960 // -fomit-frame-pointer, so do some sanity checks.
961 // (aBp should be a frame pointer on ppc(64) but checking anyway may help
962 // a little if the stack has been corrupted.)
963 // We don't need to check against the begining of the stack because
964 // we can assume that aBp > sp
965 if (next
<= aBp
|| next
>= aStackEnd
|| (uintptr_t(next
) & 3)) {
968 # if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
969 // ppc mac or powerpc64 linux
970 void* pc
= *(aBp
+ 2);
972 # else // i386 or powerpc32 linux
973 void* pc
= *(aBp
+ 1);
977 // Strip off pointer authentication hash, if present. For now, it looks
978 // like only return addresses require stripping, and stack pointers do
979 // not. This might change in the future.
980 pc
= (void*)((uintptr_t)pc
& kPointerMask
);
982 if (!skipper
.ShouldSkipPC(pc
)) {
983 // Assume that the SP points to the BP of the function
984 // it called. We can't know the exact location of the SP
985 // but this should be sufficient for our use the SP
986 // to order elements on the stack.
988 (*aCallback
)(numFrames
, pc
, aBp
, aClosure
);
989 if (aMaxFrames
!= 0 && numFrames
== aMaxFrames
) {
999 MFBT_API
void FramePointerStackWalk(MozWalkStackCallback aCallback
,
1000 uint32_t aMaxFrames
, void* aClosure
,
1001 void** aBp
, void* aStackEnd
) {
1002 // We don't pass a aFirstFramePC because we start walking the stack from the
1004 DoFramePointerStackWalk(aCallback
, nullptr, aMaxFrames
, aClosure
, aBp
,
1008 } // namespace mozilla
1013 MFBT_API
void FramePointerStackWalk(MozWalkStackCallback aCallback
,
1014 uint32_t aMaxFrames
, void* aClosure
,
1015 void** aBp
, void* aStackEnd
) {}
1016 } // namespace mozilla
1020 MFBT_API
int MozFormatCodeAddressDetails(
1021 char* aBuffer
, uint32_t aBufferSize
, uint32_t aFrameNumber
, void* aPC
,
1022 const MozCodeAddressDetails
* aDetails
) {
1023 return MozFormatCodeAddress(aBuffer
, aBufferSize
, aFrameNumber
, aPC
,
1024 aDetails
->function
, aDetails
->library
,
1025 aDetails
->loffset
, aDetails
->filename
,
1029 MFBT_API
int MozFormatCodeAddress(char* aBuffer
, uint32_t aBufferSize
,
1030 uint32_t aFrameNumber
, const void* aPC
,
1031 const char* aFunction
, const char* aLibrary
,
1032 ptrdiff_t aLOffset
, const char* aFileName
,
1034 const char* function
= aFunction
&& aFunction
[0] ? aFunction
: "???";
1035 if (aFileName
&& aFileName
[0]) {
1036 // We have a filename and (presumably) a line number. Use them.
1037 return SprintfBuf(aBuffer
, aBufferSize
, "#%02u: %s (%s:%u)", aFrameNumber
,
1038 function
, aFileName
, aLineNo
);
1039 } else if (aLibrary
&& aLibrary
[0]) {
1040 // We have no filename, but we do have a library name. Use it and the
1041 // library offset, and print them in a way that `fix_stacks.py` can
1043 return SprintfBuf(aBuffer
, aBufferSize
, "#%02u: %s[%s +0x%" PRIxPTR
"]",
1044 aFrameNumber
, function
, aLibrary
,
1045 static_cast<uintptr_t>(aLOffset
));
1047 // We have nothing useful to go on. (The format string is split because
1048 // '??)' is a trigraph and causes a warning, sigh.)
1049 return SprintfBuf(aBuffer
, aBufferSize
,
1050 "#%02u: ??? (???:???"
1056 static void EnsureWrite(FILE* aStream
, const char* aBuf
, size_t aLen
) {
1058 int fd
= _fileno(aStream
);
1060 int fd
= fileno(aStream
);
1064 auto written
= _write(fd
, aBuf
, aLen
);
1066 auto written
= write(fd
, aBuf
, aLen
);
1068 if (written
<= 0 || size_t(written
) > aLen
) {
1077 static int PrintStackFrameBuf(char (&aBuf
)[N
], uint32_t aFrameNumber
, void* aPC
,
1079 MozCodeAddressDetails details
;
1080 MozDescribeCodeAddress(aPC
, &details
);
1082 MozFormatCodeAddressDetails(aBuf
, N
- 1, aFrameNumber
, aPC
, &details
);
1083 len
= std::min(len
, N
- 2);
1089 static void PrintStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
1091 FILE* stream
= (FILE*)aClosure
;
1092 char buf
[1025]; // 1024 + 1 for trailing '\n'
1093 int len
= PrintStackFrameBuf(buf
, aFrameNumber
, aPC
, aSP
);
1095 EnsureWrite(stream
, buf
, len
);
1098 static bool WalkTheStackEnabled() {
1099 static bool result
= [] {
1100 char* value
= getenv("MOZ_DISABLE_WALKTHESTACK");
1101 return !(value
&& value
[0]);
1106 MFBT_API
void MozWalkTheStack(FILE* aStream
, const void* aFirstFramePC
,
1107 uint32_t aMaxFrames
) {
1108 if (WalkTheStackEnabled()) {
1109 MozStackWalk(PrintStackFrame
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
1110 aMaxFrames
, aStream
);
1114 static void WriteStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
1116 auto writer
= (void (*)(const char*))aClosure
;
1118 PrintStackFrameBuf(buf
, aFrameNumber
, aPC
, aSP
);
1122 MFBT_API
void MozWalkTheStackWithWriter(void (*aWriter
)(const char*),
1123 const void* aFirstFramePC
,
1124 uint32_t aMaxFrames
) {
1125 if (WalkTheStackEnabled()) {
1126 MozStackWalk(WriteStackFrame
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
1127 aMaxFrames
, (void*)aWriter
);