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_DLFCN_H) || 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 __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
48 # define HAVE___LIBC_STACK_END 1
50 # define HAVE___LIBC_STACK_END 0
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
59 # define MOZ_STACKWALK_SUPPORTS_LINUX 0
62 #if HAVE___LIBC_STACK_END
63 extern MOZ_EXPORT
void* __libc_stack_end
; // from ld-linux.so
74 constexpr FrameSkipper() : mSkipUntilAddr(0) {}
75 static uintptr_t AddressFromPC(const void* aPC
) {
77 // On 32-bit ARM, mask off the thumb bit to get the instruction address.
78 return uintptr_t(aPC
) & ~1;
80 return uintptr_t(aPC
);
83 bool ShouldSkipPC(void* aPC
) {
84 // Skip frames until we encounter the one we were initialized with,
85 // and then never skip again.
86 uintptr_t instructionAddress
= AddressFromPC(aPC
);
87 if (mSkipUntilAddr
!= 0) {
88 if (mSkipUntilAddr
!= instructionAddress
) {
95 explicit FrameSkipper(const void* aPC
) : mSkipUntilAddr(AddressFromPC(aPC
)) {}
98 uintptr_t mSkipUntilAddr
;
103 # include <windows.h>
104 # include <process.h>
107 # include "mozilla/ArrayUtils.h"
108 # include "mozilla/Atomics.h"
109 # include "mozilla/StackWalk_windows.h"
110 # include "mozilla/WindowsVersion.h"
112 # include <imagehlp.h>
113 // We need a way to know if we are building for WXP (or later), as if we are, we
114 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
115 // A value of 9 indicates we want to use the new APIs.
116 # if API_VERSION_NUMBER < 9
117 # error Too old imagehlp.h
120 // DbgHelp functions are not thread-safe and should therefore be protected by
121 // using this critical section. Only use the critical section after a
122 // successful call to InitializeDbgHelp().
123 CRITICAL_SECTION gDbgHelpCS
;
125 # if defined(_M_AMD64) || defined(_M_ARM64)
126 // Because various Win64 APIs acquire function-table locks, we need a way of
127 // preventing stack walking while those APIs are being called. Otherwise, the
128 // stack walker may suspend a thread holding such a lock, and deadlock when the
129 // stack unwind code attempts to wait for that lock.
131 // We're using an atomic counter rather than a critical section because we
132 // don't require mutual exclusion with the stack walker. If the stack walker
133 // determines that it's safe to start unwinding the suspended thread (i.e.
134 // there are no suppressions when the unwind begins), then it's safe to
135 // continue unwinding that thread even if other threads request suppressions
136 // in the meantime, because we can't deadlock with those other threads.
138 // XXX: This global variable is a larger-than-necessary hammer. A more scoped
139 // solution would be to maintain a counter per thread, but then it would be
140 // more difficult for WalkStackMain64 to read the suspended thread's counter.
141 static Atomic
<size_t> sStackWalkSuppressions
;
143 void SuppressStackWalking() { ++sStackWalkSuppressions
; }
145 void DesuppressStackWalking() {
146 auto previousValue
= sStackWalkSuppressions
--;
147 // We should never desuppress from 0. See bug 1687510 comment 10 for an
148 // example in which this occured.
149 MOZ_RELEASE_ASSERT(previousValue
);
153 AutoSuppressStackWalking::AutoSuppressStackWalking() { SuppressStackWalking(); }
156 AutoSuppressStackWalking::~AutoSuppressStackWalking() {
157 DesuppressStackWalking();
160 static uint8_t* sJitCodeRegionStart
;
161 static size_t sJitCodeRegionSize
;
162 uint8_t* sMsMpegJitCodeRegionStart
;
163 size_t sMsMpegJitCodeRegionSize
;
165 MFBT_API
void RegisterJitCodeRegion(uint8_t* aStart
, size_t aSize
) {
166 // Currently we can only handle one JIT code region at a time
167 MOZ_RELEASE_ASSERT(!sJitCodeRegionStart
);
169 sJitCodeRegionStart
= aStart
;
170 sJitCodeRegionSize
= aSize
;
173 MFBT_API
void UnregisterJitCodeRegion(uint8_t* aStart
, size_t aSize
) {
174 // Currently we can only handle one JIT code region at a time
175 MOZ_RELEASE_ASSERT(sJitCodeRegionStart
&& sJitCodeRegionStart
== aStart
&&
176 sJitCodeRegionSize
== aSize
);
178 sJitCodeRegionStart
= nullptr;
179 sJitCodeRegionSize
= 0;
182 # endif // _M_AMD64 || _M_ARM64
184 // Routine to print an error message to standard error.
185 static void PrintError(const char* aPrefix
) {
187 DWORD lastErr
= GetLastError();
188 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|
189 FORMAT_MESSAGE_IGNORE_INSERTS
,
191 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
192 (LPSTR
)&lpMsgBuf
, 0, nullptr);
193 fprintf(stderr
, "### ERROR: %s: %s", aPrefix
,
194 lpMsgBuf
? lpMsgBuf
: "(null)\n");
199 enum class DbgHelpInitFlags
: bool {
204 // This function ensures that DbgHelp.dll is loaded in the current process,
205 // and initializes the gDbgHelpCS critical section that we use to protect calls
206 // to DbgHelp functions. If DbgHelpInitFlags::WithSymbolSupport is set, we
207 // additionally call the symbol initialization functions from DbgHelp so that
208 // symbol-related functions can be used.
210 // This function is thread-safe and reentrancy-safe. In debug and fuzzing
211 // builds, MOZ_ASSERT and MOZ_CRASH walk the stack to print it before actually
212 // crashing. Hence *any* MOZ_ASSERT or MOZ_CRASH failure reached from
213 // InitializeDbgHelp() leads to rentrancy (see bug 1869997 for an example).
214 // Such failures can occur indirectly when we load dbghelp.dll, because we
215 // override various Microsoft-internal functions that are called upon DLL
217 [[nodiscard
]] static bool InitializeDbgHelp(
218 DbgHelpInitFlags aInitFlags
= DbgHelpInitFlags::BasicInit
) {
219 // In the code below, it is only safe to reach MOZ_ASSERT or MOZ_CRASH while
220 // sInitializationThreadId is set to the current thread id.
221 static Atomic
<DWORD
> sInitializationThreadId
{0};
222 DWORD currentThreadId
= ::GetCurrentThreadId();
224 // This code relies on Windows never giving us a current thread ID of zero.
225 // We make this assumption explicit, by failing if that should ever occur.
226 if (!currentThreadId
) {
230 if (sInitializationThreadId
== currentThreadId
) {
231 // This is a reentrant call and we must abort here.
235 static const bool sHasInitializedDbgHelp
= [currentThreadId
]() {
236 sInitializationThreadId
= currentThreadId
;
238 ::InitializeCriticalSection(&gDbgHelpCS
);
239 bool dbgHelpLoaded
= static_cast<bool>(::LoadLibraryW(L
"dbghelp.dll"));
241 MOZ_ASSERT(dbgHelpLoaded
);
242 sInitializationThreadId
= 0;
243 return dbgHelpLoaded
;
246 // If we don't need symbol initialization, we are done. If we need it, we
247 // can only proceed if DbgHelp initialization was successful.
248 if (aInitFlags
== DbgHelpInitFlags::BasicInit
|| !sHasInitializedDbgHelp
) {
249 return sHasInitializedDbgHelp
;
252 static const bool sHasInitializedSymbols
= [currentThreadId
]() {
253 sInitializationThreadId
= currentThreadId
;
255 EnterCriticalSection(&gDbgHelpCS
);
256 SymSetOptions(SYMOPT_LOAD_LINES
| SYMOPT_UNDNAME
);
257 bool symbolsInitialized
= SymInitialize(GetCurrentProcess(), nullptr, TRUE
);
258 /* XXX At some point we need to arrange to call SymCleanup */
259 LeaveCriticalSection(&gDbgHelpCS
);
261 if (!symbolsInitialized
) {
262 PrintError("SymInitialize");
265 MOZ_ASSERT(symbolsInitialized
);
266 sInitializationThreadId
= 0;
267 return symbolsInitialized
;
270 return sHasInitializedSymbols
;
273 // Wrapper around a reference to a CONTEXT, to simplify access to main
274 // platform-specific execution registers.
275 // It also avoids using CONTEXT* nullable pointers.
276 class CONTEXTGenericAccessors
{
278 explicit CONTEXTGenericAccessors(CONTEXT
& aCONTEXT
) : mCONTEXT(aCONTEXT
) {}
280 CONTEXT
* CONTEXTPtr() { return &mCONTEXT
; }
283 # if defined(_M_AMD64)
285 # elif defined(_M_ARM64)
287 # elif defined(_M_IX86)
290 # error "unknown platform"
295 # if defined(_M_AMD64)
297 # elif defined(_M_ARM64)
299 # elif defined(_M_IX86)
302 # error "unknown platform"
307 # if defined(_M_AMD64)
309 # elif defined(_M_ARM64)
311 # elif defined(_M_IX86)
314 # error "unknown platform"
323 * Walk the stack, translating PC's found into strings and recording the
324 * chain in aBuffer. For this to work properly, the DLLs must be rebased
325 * so that the address in the file agrees with the address in memory.
326 * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
327 * whose in memory address doesn't match its in-file address.
330 static void DoMozStackWalkThread(MozWalkStackCallback aCallback
,
331 const void* aFirstFramePC
, uint32_t aMaxFrames
,
332 void* aClosure
, HANDLE aThread
,
334 # if defined(_M_IX86)
335 if (!InitializeDbgHelp()) {
340 HANDLE targetThread
= aThread
;
341 bool walkCallingThread
;
343 targetThread
= ::GetCurrentThread();
344 walkCallingThread
= true;
346 DWORD targetThreadId
= ::GetThreadId(targetThread
);
347 DWORD currentThreadId
= ::GetCurrentThreadId();
348 walkCallingThread
= (targetThreadId
== currentThreadId
);
351 // If not already provided, get a context for the specified thread.
354 memset(&context_buf
, 0, sizeof(CONTEXT
));
355 context_buf
.ContextFlags
= CONTEXT_FULL
;
356 if (walkCallingThread
) {
357 ::RtlCaptureContext(&context_buf
);
358 } else if (!GetThreadContext(targetThread
, &context_buf
)) {
362 CONTEXTGenericAccessors context
{aContext
? *aContext
: context_buf
};
364 # if defined(_M_IX86)
365 // Setup initial stack frame to walk from.
366 STACKFRAME64 frame64
;
367 memset(&frame64
, 0, sizeof(frame64
));
368 frame64
.AddrPC
.Offset
= context
.PC();
369 frame64
.AddrStack
.Offset
= context
.SP();
370 frame64
.AddrFrame
.Offset
= context
.BP();
371 frame64
.AddrPC
.Mode
= AddrModeFlat
;
372 frame64
.AddrStack
.Mode
= AddrModeFlat
;
373 frame64
.AddrFrame
.Mode
= AddrModeFlat
;
374 frame64
.AddrReturn
.Mode
= AddrModeFlat
;
377 # if defined(_M_AMD64) || defined(_M_ARM64)
378 // If there are any active suppressions, then at least one thread (we don't
379 // know which) is holding a lock that can deadlock RtlVirtualUnwind. Since
380 // that thread may be the one that we're trying to unwind, we can't proceed.
382 // But if there are no suppressions, then our target thread can't be holding
383 // a lock, and it's safe to proceed. By virtue of being suspended, the target
384 // thread can't acquire any new locks during the unwind process, so we only
385 // need to do this check once. After that, sStackWalkSuppressions can be
386 // changed by other threads while we're unwinding, and that's fine because
387 // we can't deadlock with those threads.
388 if (sStackWalkSuppressions
) {
392 bool firstFrame
= true;
395 FrameSkipper
skipper(aFirstFramePC
);
399 // Now walk the stack.
404 # if defined(_M_IX86)
405 // 32-bit frame unwinding.
406 // Debug routines are not threadsafe, so grab the lock.
407 EnterCriticalSection(&gDbgHelpCS
);
409 StackWalk64(IMAGE_FILE_MACHINE_I386
, ::GetCurrentProcess(),
410 targetThread
, &frame64
, context
.CONTEXTPtr(), nullptr,
411 SymFunctionTableAccess64
, // function table access routine
412 SymGetModuleBase64
, // module base routine
414 LeaveCriticalSection(&gDbgHelpCS
);
417 addr
= frame64
.AddrPC
.Offset
;
418 spaddr
= frame64
.AddrStack
.Offset
;
422 if (walkCallingThread
) {
423 PrintError("WalkStack64");
431 # elif defined(_M_AMD64) || defined(_M_ARM64)
433 auto currentInstr
= context
.PC();
435 // If we reach a frame in JIT code, we don't have enough information to
436 // unwind, so we have to give up.
437 if (sJitCodeRegionStart
&& (uint8_t*)currentInstr
>= sJitCodeRegionStart
&&
438 (uint8_t*)currentInstr
< sJitCodeRegionStart
+ sJitCodeRegionSize
) {
442 // We must also avoid msmpeg2vdec.dll's JIT region: they don't generate
443 // unwind data, so their JIT unwind callback just throws up its hands and
444 // terminates the process.
445 if (sMsMpegJitCodeRegionStart
&&
446 (uint8_t*)currentInstr
>= sMsMpegJitCodeRegionStart
&&
447 (uint8_t*)currentInstr
<
448 sMsMpegJitCodeRegionStart
+ sMsMpegJitCodeRegionSize
) {
452 // 64-bit frame unwinding.
453 // Try to look up unwind metadata for the current function.
455 PRUNTIME_FUNCTION runtimeFunction
=
456 RtlLookupFunctionEntry(currentInstr
, &imageBase
, NULL
);
458 if (runtimeFunction
) {
459 PVOID dummyHandlerData
;
460 ULONG64 dummyEstablisherFrame
;
461 RtlVirtualUnwind(UNW_FLAG_NHANDLER
, imageBase
, currentInstr
,
462 runtimeFunction
, context
.CONTEXTPtr(), &dummyHandlerData
,
463 &dummyEstablisherFrame
, nullptr);
464 } else if (firstFrame
) {
465 // Leaf functions can be unwound by hand.
466 context
.PC() = *reinterpret_cast<DWORD64
*>(context
.SP());
467 context
.SP() += sizeof(void*);
469 // Something went wrong.
474 spaddr
= context
.SP();
477 # error "unknown platform"
484 if (skipper
.ShouldSkipPC((void*)addr
)) {
488 aCallback(++frames
, (void*)addr
, (void*)spaddr
, aClosure
);
490 if (aMaxFrames
!= 0 && frames
== aMaxFrames
) {
494 # if defined(_M_IX86)
495 if (frame64
.AddrReturn
.Offset
== 0) {
502 MFBT_API
void MozStackWalkThread(MozWalkStackCallback aCallback
,
503 uint32_t aMaxFrames
, void* aClosure
,
504 HANDLE aThread
, CONTEXT
* aContext
) {
505 // We don't pass a aFirstFramePC because we walk the stack for another
507 DoMozStackWalkThread(aCallback
, nullptr, aMaxFrames
, aClosure
, aThread
,
511 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
512 const void* aFirstFramePC
, uint32_t aMaxFrames
,
514 DoMozStackWalkThread(aCallback
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
515 aMaxFrames
, aClosure
, nullptr, nullptr);
518 static BOOL CALLBACK
callbackEspecial64(PCSTR aModuleName
, DWORD64 aModuleBase
,
519 ULONG aModuleSize
, PVOID aUserContext
) {
521 DWORD64 addr
= *(DWORD64
*)aUserContext
;
524 * You'll want to control this if we are running on an
525 * architecture where the addresses go the other direction.
526 * Not sure this is even a realistic consideration.
528 const BOOL addressIncreases
= TRUE
;
531 * If it falls in side the known range, load the symbols.
534 ? (addr
>= aModuleBase
&& addr
<= (aModuleBase
+ aModuleSize
))
535 : (addr
<= aModuleBase
&& addr
>= (aModuleBase
- aModuleSize
))) {
536 retval
= !!SymLoadModule64(GetCurrentProcess(), nullptr, (PSTR
)aModuleName
,
537 nullptr, aModuleBase
, aModuleSize
);
539 PrintError("SymLoadModule64");
547 * SymGetModuleInfoEspecial
549 * Attempt to determine the module information.
550 * Bug 112196 says this DLL may not have been loaded at the time
551 * SymInitialize was called, and thus the module information
552 * and symbol information is not available.
553 * This code rectifies that problem.
556 // New members were added to IMAGEHLP_MODULE64 (that show up in the
557 // Platform SDK that ships with VC8, but not the Platform SDK that ships
558 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
559 // use them, and it's useful to be able to function correctly with the
560 // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
561 // version 5.1.) Since Platform SDK version need not correspond to
562 // compiler version, and the version number in debughlp.h was NOT bumped
563 // when these changes were made, ifdef based on a constant that was
564 // added between these versions.
565 # ifdef SSRVOPT_SETCONTEXT
566 # define NS_IMAGEHLP_MODULE64_SIZE \
567 (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / \
571 # define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
574 BOOL
SymGetModuleInfoEspecial64(HANDLE aProcess
, DWORD64 aAddr
,
575 PIMAGEHLP_MODULE64 aModuleInfo
,
576 PIMAGEHLP_LINE64 aLineInfo
) {
580 * Init the vars if we have em.
582 aModuleInfo
->SizeOfStruct
= NS_IMAGEHLP_MODULE64_SIZE
;
584 aLineInfo
->SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
589 * It may already be loaded.
591 retval
= SymGetModuleInfo64(aProcess
, aAddr
, aModuleInfo
);
592 if (retval
== FALSE
) {
594 * Not loaded, here's the magic.
595 * Go through all the modules.
597 // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
598 // constness of the first parameter of
599 // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
600 // non-const to const over time). See bug 391848 and bug
602 BOOL enumRes
= EnumerateLoadedModules64(
603 aProcess
, (PENUMLOADED_MODULES_CALLBACK64
)callbackEspecial64
,
605 if (enumRes
!= FALSE
) {
608 * If it fails, then well, we have other problems.
610 retval
= SymGetModuleInfo64(aProcess
, aAddr
, aModuleInfo
);
615 * If we got module info, we may attempt line info as well.
616 * We will not report failure if this does not work.
618 if (retval
!= FALSE
&& aLineInfo
) {
619 DWORD displacement
= 0;
620 BOOL lineRes
= FALSE
;
621 lineRes
= SymGetLineFromAddr64(aProcess
, aAddr
, &displacement
, aLineInfo
);
623 // Clear out aLineInfo to indicate that it's not valid
624 memset(aLineInfo
, 0, sizeof(*aLineInfo
));
631 MFBT_API
bool MozDescribeCodeAddress(void* aPC
,
632 MozCodeAddressDetails
* aDetails
) {
633 aDetails
->library
[0] = '\0';
634 aDetails
->loffset
= 0;
635 aDetails
->filename
[0] = '\0';
636 aDetails
->lineno
= 0;
637 aDetails
->function
[0] = '\0';
638 aDetails
->foffset
= 0;
640 if (!InitializeDbgHelp(DbgHelpInitFlags::WithSymbolSupport
)) {
644 HANDLE myProcess
= ::GetCurrentProcess();
647 // debug routines are not threadsafe, so grab the lock.
648 EnterCriticalSection(&gDbgHelpCS
);
651 // Attempt to load module info before we attempt to resolve the symbol.
652 // This just makes sure we get good info if available.
655 DWORD64 addr
= (DWORD64
)aPC
;
656 IMAGEHLP_MODULE64 modInfo
;
657 IMAGEHLP_LINE64 lineInfo
;
659 modInfoRes
= SymGetModuleInfoEspecial64(myProcess
, addr
, &modInfo
, &lineInfo
);
662 strncpy(aDetails
->library
, modInfo
.LoadedImageName
,
663 sizeof(aDetails
->library
));
664 aDetails
->library
[mozilla::ArrayLength(aDetails
->library
) - 1] = '\0';
665 aDetails
->loffset
= (char*)aPC
- (char*)modInfo
.BaseOfImage
;
667 if (lineInfo
.FileName
) {
668 strncpy(aDetails
->filename
, lineInfo
.FileName
,
669 sizeof(aDetails
->filename
));
670 aDetails
->filename
[mozilla::ArrayLength(aDetails
->filename
) - 1] = '\0';
671 aDetails
->lineno
= lineInfo
.LineNumber
;
675 ULONG64 buffer
[(sizeof(SYMBOL_INFO
) + MAX_SYM_NAME
* sizeof(TCHAR
) +
676 sizeof(ULONG64
) - 1) /
678 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)buffer
;
679 pSymbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
680 pSymbol
->MaxNameLen
= MAX_SYM_NAME
;
682 DWORD64 displacement
;
683 ok
= SymFromAddr(myProcess
, addr
, &displacement
, pSymbol
);
686 strncpy(aDetails
->function
, pSymbol
->Name
, sizeof(aDetails
->function
));
687 aDetails
->function
[mozilla::ArrayLength(aDetails
->function
) - 1] = '\0';
688 aDetails
->foffset
= static_cast<ptrdiff_t>(displacement
);
691 LeaveCriticalSection(&gDbgHelpCS
); // release our lock
695 // i386 or PPC Linux stackwalking code
697 // Changes to to OS/Architecture support here should be reflected in
698 // build/moz.configure/memory.configure
699 #elif HAVE_DLADDR && \
700 (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || \
701 MOZ_STACKWALK_SUPPORTS_MACOSX)
706 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
707 // if __USE_GNU is defined. I suppose its some kind of standards
710 # if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
714 // This thing is exported by libstdc++
715 // Yes, this is a gcc only hack
716 # if defined(MOZ_DEMANGLE_SYMBOLS)
718 # endif // MOZ_DEMANGLE_SYMBOLS
722 void DemangleSymbol(const char* aSymbol
, char* aBuffer
, int aBufLen
) {
725 # if defined(MOZ_DEMANGLE_SYMBOLS)
726 /* See demangle.h in the gcc source for the voodoo */
727 char* demangled
= abi::__cxa_demangle(aSymbol
, 0, 0, 0);
730 strncpy(aBuffer
, demangled
, aBufLen
);
731 aBuffer
[aBufLen
- 1] = '\0';
734 # endif // MOZ_DEMANGLE_SYMBOLS
737 } // namespace mozilla
739 // {x86, ppc} x {Linux, Mac} stackwalking code.
741 // Changes to to OS/Architecture support here should be reflected in
742 // build/moz.configure/memory.configure
743 # if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
744 (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
746 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback
,
747 const void* aFirstFramePC
,
748 uint32_t aMaxFrames
, void* aClosure
,
749 void** aBp
, void* aStackEnd
);
751 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
752 const void* aFirstFramePC
, uint32_t aMaxFrames
,
754 // Get the frame pointer
755 void** bp
= (void**)__builtin_frame_address(0);
758 # if HAVE___LIBC_STACK_END
759 stackEnd
= __libc_stack_end
;
760 # elif defined(XP_DARWIN)
761 stackEnd
= pthread_get_stackaddr_np(pthread_self());
762 # elif defined(ANDROID)
763 pthread_attr_t sattr
;
764 pthread_attr_init(&sattr
);
765 pthread_getattr_np(pthread_self(), &sattr
);
766 void* stackBase
= stackEnd
= nullptr;
767 size_t stackSize
= 0;
768 if (gettid() != getpid()) {
769 // bionic's pthread_attr_getstack doesn't tell the truth for the main
770 // thread (see bug 846670). So don't use it for the main thread.
771 if (!pthread_attr_getstack(&sattr
, &stackBase
, &stackSize
)) {
772 stackEnd
= static_cast<char*>(stackBase
) + stackSize
;
778 // So consider the current frame pointer + an arbitrary size of 8MB
779 // (modulo overflow ; not really arbitrary as it's the default stack
780 // size for the main thread) if pthread_attr_getstack failed for
781 // some reason (or was skipped).
782 static const uintptr_t kMaxStackSize
= 8 * 1024 * 1024;
783 uintptr_t maxStackStart
= uintptr_t(-1) - kMaxStackSize
;
784 uintptr_t stackStart
= std::max(maxStackStart
, uintptr_t(bp
));
785 stackEnd
= reinterpret_cast<void*>(stackStart
+ kMaxStackSize
);
788 # error Unsupported configuration
790 DoFramePointerStackWalk(aCallback
, aFirstFramePC
, aMaxFrames
, aClosure
, bp
,
794 # elif defined(HAVE__UNWIND_BACKTRACE)
796 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
800 MozWalkStackCallback callback
;
801 FrameSkipper skipper
;
807 static _Unwind_Reason_Code
unwind_callback(struct _Unwind_Context
* context
,
809 unwind_info
* info
= static_cast<unwind_info
*>(closure
);
810 void* pc
= reinterpret_cast<void*>(_Unwind_GetIP(context
));
811 // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
812 if (!info
->skipper
.ShouldSkipPC(pc
)) {
814 (*info
->callback
)(info
->numFrames
, pc
, nullptr, info
->closure
);
815 if (info
->maxFrames
!= 0 && info
->numFrames
== info
->maxFrames
) {
816 // Again, any error code that stops the walk will do.
817 return _URC_FOREIGN_EXCEPTION_CAUGHT
;
820 return _URC_NO_REASON
;
823 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
824 const void* aFirstFramePC
, uint32_t aMaxFrames
,
827 info
.callback
= aCallback
;
828 info
.skipper
= FrameSkipper(aFirstFramePC
? aFirstFramePC
: CallerPC());
829 info
.maxFrames
= aMaxFrames
;
831 info
.closure
= aClosure
;
833 // We ignore the return value from _Unwind_Backtrace. There are three main
835 // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
837 // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
838 // - If aMaxFrames != 0, we want to stop early, and the only way to do that
839 // is to make unwind_callback return something other than _URC_NO_REASON,
840 // which causes _Unwind_Backtrace to return a non-success code.
841 // - MozStackWalk doesn't have a return value anyway.
842 (void)_Unwind_Backtrace(unwind_callback
, &info
);
847 bool MFBT_API
MozDescribeCodeAddress(void* aPC
,
848 MozCodeAddressDetails
* aDetails
) {
849 aDetails
->library
[0] = '\0';
850 aDetails
->loffset
= 0;
851 aDetails
->filename
[0] = '\0';
852 aDetails
->lineno
= 0;
853 aDetails
->function
[0] = '\0';
854 aDetails
->foffset
= 0;
858 # if defined(ANDROID) && defined(MOZ_LINKER)
859 int ok
= __wrap_dladdr(aPC
, &info
);
861 int ok
= dladdr(aPC
, &info
);
868 strncpy(aDetails
->library
, info
.dli_fname
, sizeof(aDetails
->library
));
869 aDetails
->library
[mozilla::ArrayLength(aDetails
->library
) - 1] = '\0';
870 aDetails
->loffset
= (char*)aPC
- (char*)info
.dli_fbase
;
872 # if !defined(XP_FREEBSD)
873 // On FreeBSD, dli_sname is unusably bad, it often returns things like
874 // 'gtk_xtbin_new' or 'XRE_GetBootstrap' instead of long C++ symbols. Just let
875 // GetFunction do the lookup directly in the ELF image.
877 const char* symbol
= info
.dli_sname
;
878 if (!symbol
|| symbol
[0] == '\0') {
882 DemangleSymbol(symbol
, aDetails
->function
, sizeof(aDetails
->function
));
884 if (aDetails
->function
[0] == '\0') {
885 // Just use the mangled symbol if demangling failed.
886 strncpy(aDetails
->function
, symbol
, sizeof(aDetails
->function
));
887 aDetails
->function
[mozilla::ArrayLength(aDetails
->function
) - 1] = '\0';
890 aDetails
->foffset
= (char*)aPC
- (char*)info
.dli_saddr
;
896 #else // unsupported platform.
898 MFBT_API
void MozStackWalk(MozWalkStackCallback aCallback
,
899 const void* aFirstFramePC
, uint32_t aMaxFrames
,
902 MFBT_API
bool MozDescribeCodeAddress(void* aPC
,
903 MozCodeAddressDetails
* aDetails
) {
904 aDetails
->library
[0] = '\0';
905 aDetails
->loffset
= 0;
906 aDetails
->filename
[0] = '\0';
907 aDetails
->lineno
= 0;
908 aDetails
->function
[0] = '\0';
909 aDetails
->foffset
= 0;
915 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
917 # if defined(XP_MACOSX) && defined(__aarch64__)
918 // On macOS arm64, system libraries are arm64e binaries, and arm64e can do
919 // pointer authentication: The low bits of the pointer are the actual pointer
920 // value, and the high bits are an encrypted hash. During stackwalking, we need
921 // to strip off this hash. In theory, ptrauth_strip would be the right function
922 // to call for this. However, that function is a no-op unless it's called from
923 // code which also builds as arm64e - which we do not. So we cannot use it. So
924 // for now, we hardcode a mask that seems to work today: 40 bits for the pointer
925 // and 24 bits for the hash seems to do the trick. We can worry about
926 // dynamically computing the correct mask if this ever stops working.
927 const uintptr_t kPointerMask
=
928 (uintptr_t(1) << 40) - 1; // 40 bits pointer, 24 bit PAC
930 const uintptr_t kPointerMask
= ~uintptr_t(0);
934 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback
,
935 const void* aFirstFramePC
,
936 uint32_t aMaxFrames
, void* aClosure
,
937 void** aBp
, void* aStackEnd
) {
938 // Stack walking code courtesy Kipp's "leaky".
940 FrameSkipper
skipper(aFirstFramePC
);
941 uint32_t numFrames
= 0;
943 // Sanitize the given aBp. Assume that something reasonably close to
944 // but before the stack end is going be a valid frame pointer. Also
945 // check that it is an aligned address. This increases the chances
946 // that if the pointer is not valid (which might happen if the caller
947 // called __builtin_frame_address(1) and its frame is busted for some
948 // reason), we won't read it, leading to a crash. Because the calling
949 // code is not using frame pointers when returning, it might actually
950 // recover just fine.
951 static const uintptr_t kMaxStackSize
= 8 * 1024 * 1024;
952 if (uintptr_t(aBp
) < uintptr_t(aStackEnd
) -
953 std::min(kMaxStackSize
, uintptr_t(aStackEnd
)) ||
954 aBp
>= aStackEnd
|| (uintptr_t(aBp
) & 3)) {
959 void** next
= (void**)*aBp
;
960 // aBp may not be a frame pointer on i386 if code was compiled with
961 // -fomit-frame-pointer, so do some sanity checks.
962 // (aBp should be a frame pointer on ppc(64) but checking anyway may help
963 // a little if the stack has been corrupted.)
964 // We don't need to check against the begining of the stack because
965 // we can assume that aBp > sp
966 if (next
<= aBp
|| next
>= aStackEnd
|| (uintptr_t(next
) & 3)) {
969 # if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
970 // ppc mac or powerpc64 linux
971 void* pc
= *(aBp
+ 2);
973 # else // i386 or powerpc32 linux
974 void* pc
= *(aBp
+ 1);
978 // Strip off pointer authentication hash, if present. For now, it looks
979 // like only return addresses require stripping, and stack pointers do
980 // not. This might change in the future.
981 pc
= (void*)((uintptr_t)pc
& kPointerMask
);
983 if (!skipper
.ShouldSkipPC(pc
)) {
984 // Assume that the SP points to the BP of the function
985 // it called. We can't know the exact location of the SP
986 // but this should be sufficient for our use the SP
987 // to order elements on the stack.
989 (*aCallback
)(numFrames
, pc
, aBp
, aClosure
);
990 if (aMaxFrames
!= 0 && numFrames
== aMaxFrames
) {
1000 MFBT_API
void FramePointerStackWalk(MozWalkStackCallback aCallback
,
1001 uint32_t aMaxFrames
, void* aClosure
,
1002 void** aBp
, void* aStackEnd
) {
1003 // We don't pass a aFirstFramePC because we start walking the stack from the
1005 DoFramePointerStackWalk(aCallback
, nullptr, aMaxFrames
, aClosure
, aBp
,
1009 } // namespace mozilla
1014 MFBT_API
void FramePointerStackWalk(MozWalkStackCallback aCallback
,
1015 uint32_t aMaxFrames
, void* aClosure
,
1016 void** aBp
, void* aStackEnd
) {}
1017 } // namespace mozilla
1021 MFBT_API
int MozFormatCodeAddressDetails(
1022 char* aBuffer
, uint32_t aBufferSize
, uint32_t aFrameNumber
, void* aPC
,
1023 const MozCodeAddressDetails
* aDetails
) {
1024 return MozFormatCodeAddress(aBuffer
, aBufferSize
, aFrameNumber
, aPC
,
1025 aDetails
->function
, aDetails
->library
,
1026 aDetails
->loffset
, aDetails
->filename
,
1030 MFBT_API
int MozFormatCodeAddress(char* aBuffer
, uint32_t aBufferSize
,
1031 uint32_t aFrameNumber
, const void* aPC
,
1032 const char* aFunction
, const char* aLibrary
,
1033 ptrdiff_t aLOffset
, const char* aFileName
,
1035 const char* function
= aFunction
&& aFunction
[0] ? aFunction
: "???";
1036 if (aFileName
&& aFileName
[0]) {
1037 // We have a filename and (presumably) a line number. Use them.
1038 return SprintfBuf(aBuffer
, aBufferSize
, "#%02u: %s (%s:%u)", aFrameNumber
,
1039 function
, aFileName
, aLineNo
);
1040 } else if (aLibrary
&& aLibrary
[0]) {
1041 // We have no filename, but we do have a library name. Use it and the
1042 // library offset, and print them in a way that `fix_stacks.py` can
1044 return SprintfBuf(aBuffer
, aBufferSize
, "#%02u: %s[%s +0x%" PRIxPTR
"]",
1045 aFrameNumber
, function
, aLibrary
,
1046 static_cast<uintptr_t>(aLOffset
));
1048 // We have nothing useful to go on. (The format string is split because
1049 // '??)' is a trigraph and causes a warning, sigh.)
1050 return SprintfBuf(aBuffer
, aBufferSize
,
1051 "#%02u: ??? (???:???"
1057 static void EnsureWrite(FILE* aStream
, const char* aBuf
, size_t aLen
) {
1059 int fd
= _fileno(aStream
);
1061 int fd
= fileno(aStream
);
1065 auto written
= _write(fd
, aBuf
, aLen
);
1067 auto written
= write(fd
, aBuf
, aLen
);
1069 if (written
<= 0 || size_t(written
) > aLen
) {
1078 static int PrintStackFrameBuf(char (&aBuf
)[N
], uint32_t aFrameNumber
, void* aPC
,
1080 MozCodeAddressDetails details
;
1081 MozDescribeCodeAddress(aPC
, &details
);
1083 MozFormatCodeAddressDetails(aBuf
, N
- 1, aFrameNumber
, aPC
, &details
);
1084 len
= std::min(len
, N
- 2);
1090 static void PrintStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
1092 FILE* stream
= (FILE*)aClosure
;
1093 char buf
[1025]; // 1024 + 1 for trailing '\n'
1094 int len
= PrintStackFrameBuf(buf
, aFrameNumber
, aPC
, aSP
);
1096 EnsureWrite(stream
, buf
, len
);
1099 static bool WalkTheStackEnabled() {
1100 static bool result
= [] {
1101 char* value
= getenv("MOZ_DISABLE_WALKTHESTACK");
1102 return !(value
&& value
[0]);
1107 MFBT_API
void MozWalkTheStack(FILE* aStream
, const void* aFirstFramePC
,
1108 uint32_t aMaxFrames
) {
1109 if (WalkTheStackEnabled()) {
1110 MozStackWalk(PrintStackFrame
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
1111 aMaxFrames
, aStream
);
1115 static void WriteStackFrame(uint32_t aFrameNumber
, void* aPC
, void* aSP
,
1117 auto writer
= (void (*)(const char*))aClosure
;
1119 PrintStackFrameBuf(buf
, aFrameNumber
, aPC
, aSP
);
1123 MFBT_API
void MozWalkTheStackWithWriter(void (*aWriter
)(const char*),
1124 const void* aFirstFramePC
,
1125 uint32_t aMaxFrames
) {
1126 if (WalkTheStackEnabled()) {
1127 MozStackWalk(WriteStackFrame
, aFirstFramePC
? aFirstFramePC
: CallerPC(),
1128 aMaxFrames
, (void*)aWriter
);