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/Assertions.h"
10 #include "mozilla/IntegerPrintfMacros.h"
11 #include "mozilla/StackWalk.h"
12 #include "nsStackWalkPrivate.h"
14 #include "nsStackWalk.h"
16 using namespace mozilla
;
18 // The presence of this address is the stack must stop the stack walk. If
19 // there is no such address, the structure will be {nullptr, true}.
20 struct CriticalAddress
25 static CriticalAddress gCriticalAddress
;
27 // for _Unwind_Backtrace from libcxxrt or libunwind
28 // cxxabi.h from libcxxrt implicitly includes unwind.h first
29 #if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE)
33 #if defined(HAVE_DLOPEN) || defined(XP_MACOSX)
37 #define NSSTACKWALK_SUPPORTS_MACOSX \
38 (defined(XP_MACOSX) && \
39 (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
41 #define NSSTACKWALK_SUPPORTS_LINUX \
43 ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
44 defined(HAVE__UNWIND_BACKTRACE)))
46 #define NSSTACKWALK_SUPPORTS_SOLARIS \
48 (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386)))
50 #if NSSTACKWALK_SUPPORTS_MACOSX
52 #include <CoreServices/CoreServices.h>
55 malloc_logger_t(uint32_t aType
,
56 uintptr_t aArg1
, uintptr_t aArg2
, uintptr_t aArg3
,
57 uintptr_t aResult
, uint32_t aNumHotFramesToSkip
);
58 extern malloc_logger_t
* malloc_logger
;
61 stack_callback(void* aPc
, void* aSp
, void* aClosure
)
63 const char* name
= static_cast<char*>(aClosure
);
66 // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
67 // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
68 // correct one is the first that we find on our way up, so the
69 // following check for gCriticalAddress.mAddr is critical.
70 if (gCriticalAddress
.mAddr
|| dladdr(aPc
, &info
) == 0 ||
71 !info
.dli_sname
|| strcmp(info
.dli_sname
, name
) != 0) {
74 gCriticalAddress
.mAddr
= aPc
;
78 #define MAC_OS_X_VERSION_10_7_HEX 0x00001070
80 static int32_t OSXVersion()
82 static int32_t gOSXVersion
= 0x0;
83 if (gOSXVersion
== 0x0) {
84 OSErr err
= ::Gestalt(gestaltSystemVersion
, (SInt32
*)&gOSXVersion
);
85 MOZ_ASSERT(err
== noErr
);
90 static bool OnLionOrLater()
92 return (OSXVersion() >= MAC_OS_X_VERSION_10_7_HEX
);
97 my_malloc_logger(uint32_t aType
,
98 uintptr_t aArg1
, uintptr_t aArg2
, uintptr_t aArg3
,
99 uintptr_t aResult
, uint32_t aNumHotFramesToSkip
)
101 static bool once
= false;
107 // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
108 // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
109 const char* name
= "new_sem_from_pool";
110 NS_StackWalk(stack_callback
, /* skipFrames */ 0, /* maxFrames */ 0,
111 const_cast<char*>(name
), 0, nullptr);
114 // This is called from NS_LogInit() and from the stack walking functions, but
115 // only the first call has any effect. We need to call this function from both
116 // places because it must run before any mutexes are created, and also before
117 // any objects whose refcounts we're logging are created. Running this
118 // function during NS_LogInit() ensures that we meet the first criterion, and
119 // running this function during the stack walking functions ensures we meet the
122 StackWalkInitCriticalAddress()
124 if (gCriticalAddress
.mInit
) {
127 gCriticalAddress
.mInit
= true;
128 // We must not do work when 'new_sem_from_pool' calls realloc, since
129 // it holds a non-reentrant spin-lock and we will quickly deadlock.
130 // new_sem_from_pool is not directly accessible using dlsym, so
131 // we force a situation where new_sem_from_pool is on the stack and
132 // use dladdr to check the addresses.
134 // malloc_logger can be set by external tools like 'Instruments' or 'leaks'
135 malloc_logger_t
* old_malloc_logger
= malloc_logger
;
136 malloc_logger
= my_malloc_logger
;
139 int r
= pthread_cond_init(&cond
, 0);
141 pthread_mutex_t mutex
;
142 r
= pthread_mutex_init(&mutex
, 0);
144 r
= pthread_mutex_lock(&mutex
);
146 struct timespec abstime
= { 0, 1 };
147 r
= pthread_cond_timedwait_relative_np(&cond
, &mutex
, &abstime
);
149 // restore the previous malloc logger
150 malloc_logger
= old_malloc_logger
;
152 // On Lion, malloc is no longer called from pthread_cond_*wait*. This prevents
153 // us from finding the address, but that is fine, since with no call to malloc
154 // there is no critical address.
155 MOZ_ASSERT(OnLionOrLater() || gCriticalAddress
.mAddr
!= nullptr);
156 MOZ_ASSERT(r
== ETIMEDOUT
);
157 r
= pthread_mutex_unlock(&mutex
);
159 r
= pthread_mutex_destroy(&mutex
);
161 r
= pthread_cond_destroy(&cond
);
166 IsCriticalAddress(void* aPC
)
168 return gCriticalAddress
.mAddr
== aPC
;
172 IsCriticalAddress(void* aPC
)
176 // We still initialize gCriticalAddress.mInit so that this code behaves
177 // the same on all platforms. Otherwise a failure to init would be visible
180 StackWalkInitCriticalAddress()
182 gCriticalAddress
.mInit
= true;
186 #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
194 #include "mozilla/ArrayUtils.h"
197 #include <imagehlp.h>
198 // We need a way to know if we are building for WXP (or later), as if we are, we
199 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
200 // A value of 9 indicates we want to use the new APIs.
201 #if API_VERSION_NUMBER < 9
202 #error Too old imagehlp.h
205 // Define these as static pointers so that we can load the DLL on the
206 // fly (and not introduce a link-time dependency on it). Tip o' the
207 // hat to Matt Pietrick for this idea. See:
209 // http://msdn.microsoft.com/library/periodic/period97/F1/D3/S245C6.htm
213 extern HANDLE hStackWalkMutex
;
215 bool EnsureSymInitialized();
217 bool EnsureWalkThreadReady();
223 bool walkCallingThread
;
237 void PrintError(char* aPrefix
, WalkStackData
* aData
);
238 unsigned int WINAPI
WalkStackThread(void* aData
);
239 void WalkStackMain64(struct WalkStackData
* aData
);
242 DWORD gStackWalkThread
;
243 CRITICAL_SECTION gDbgHelpCS
;
247 // Routine to print an error message to standard error.
249 PrintError(const char* aPrefix
)
252 DWORD lastErr
= GetLastError();
254 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
,
257 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
262 fprintf(stderr
, "### ERROR: %s: %s",
263 aPrefix
, lpMsgBuf
? lpMsgBuf
: "(null)\n");
269 EnsureWalkThreadReady()
271 static bool walkThreadReady
= false;
272 static HANDLE stackWalkThread
= nullptr;
273 static HANDLE readyEvent
= nullptr;
275 if (walkThreadReady
) {
276 return walkThreadReady
;
279 if (!stackWalkThread
) {
280 readyEvent
= ::CreateEvent(nullptr, FALSE
/* auto-reset*/,
281 FALSE
/* initially non-signaled */,
284 PrintError("CreateEvent");
288 unsigned int threadID
;
289 stackWalkThread
= (HANDLE
)_beginthreadex(nullptr, 0, WalkStackThread
,
290 (void*)readyEvent
, 0, &threadID
);
291 if (!stackWalkThread
) {
292 PrintError("CreateThread");
293 ::CloseHandle(readyEvent
);
294 readyEvent
= nullptr;
297 gStackWalkThread
= threadID
;
298 ::CloseHandle(stackWalkThread
);
301 MOZ_ASSERT((stackWalkThread
&& readyEvent
) ||
302 (!stackWalkThread
&& !readyEvent
));
304 // The thread was created. Try to wait an arbitrary amount of time (1 second
305 // should be enough) for its event loop to start before posting events to it.
306 DWORD waitRet
= ::WaitForSingleObject(readyEvent
, 1000);
307 if (waitRet
== WAIT_TIMEOUT
) {
308 // We get a timeout if we're called during static initialization because
309 // the thread will only start executing after we return so it couldn't
310 // have signalled the event. If that is the case, give up for now and
311 // try again next time we're called.
314 ::CloseHandle(readyEvent
);
315 stackWalkThread
= nullptr;
316 readyEvent
= nullptr;
319 ::InitializeCriticalSection(&gDbgHelpCS
);
321 return walkThreadReady
= true;
325 WalkStackMain64(struct WalkStackData
* aData
)
327 // Get the context information for the thread. That way we will
328 // know where our sp, fp, pc, etc. are and can fill in the
329 // STACKFRAME64 with the initial values.
331 HANDLE myProcess
= aData
->process
;
332 HANDLE myThread
= aData
->thread
;
335 STACKFRAME64 frame64
;
336 // skip our own stack walking frames
337 int skip
= (aData
->walkCallingThread
? 3 : 0) + aData
->skipFrames
;
340 // Get a context for the specified thread.
341 if (!aData
->platformData
) {
342 memset(&context
, 0, sizeof(CONTEXT
));
343 context
.ContextFlags
= CONTEXT_FULL
;
344 if (!GetThreadContext(myThread
, &context
)) {
345 if (aData
->walkCallingThread
) {
346 PrintError("GetThreadContext");
351 context
= *static_cast<CONTEXT
*>(aData
->platformData
);
354 // Setup initial stack frame to walk from
355 memset(&frame64
, 0, sizeof(frame64
));
357 frame64
.AddrPC
.Offset
= context
.Eip
;
358 frame64
.AddrStack
.Offset
= context
.Esp
;
359 frame64
.AddrFrame
.Offset
= context
.Ebp
;
360 #elif defined _M_AMD64
361 frame64
.AddrPC
.Offset
= context
.Rip
;
362 frame64
.AddrStack
.Offset
= context
.Rsp
;
363 frame64
.AddrFrame
.Offset
= context
.Rbp
;
364 #elif defined _M_IA64
365 frame64
.AddrPC
.Offset
= context
.StIIP
;
366 frame64
.AddrStack
.Offset
= context
.SP
;
367 frame64
.AddrFrame
.Offset
= context
.RsBSP
;
369 #error "Should not have compiled this code"
371 frame64
.AddrPC
.Mode
= AddrModeFlat
;
372 frame64
.AddrStack
.Mode
= AddrModeFlat
;
373 frame64
.AddrFrame
.Mode
= AddrModeFlat
;
374 frame64
.AddrReturn
.Mode
= AddrModeFlat
;
376 // Now walk the stack
379 // debug routines are not threadsafe, so grab the lock.
380 EnterCriticalSection(&gDbgHelpCS
);
383 IMAGE_FILE_MACHINE_AMD64
,
384 #elif defined _M_IA64
385 IMAGE_FILE_MACHINE_IA64
,
386 #elif defined _M_IX86
387 IMAGE_FILE_MACHINE_I386
,
389 #error "Should not have compiled this code"
396 SymFunctionTableAccess64
, // function table access routine
397 SymGetModuleBase64
, // module base routine
400 LeaveCriticalSection(&gDbgHelpCS
);
403 addr
= frame64
.AddrPC
.Offset
;
404 spaddr
= frame64
.AddrStack
.Offset
;
408 if (aData
->walkCallingThread
) {
409 PrintError("WalkStack64");
413 if (!ok
|| (addr
== 0)) {
421 if (aData
->pc_count
< aData
->pc_size
) {
422 aData
->pcs
[aData
->pc_count
] = (void*)addr
;
426 if (aData
->sp_count
< aData
->sp_size
) {
427 aData
->sps
[aData
->sp_count
] = (void*)spaddr
;
431 if (aData
->pc_max
!= 0 && aData
->pc_count
== aData
->pc_max
) {
435 if (frame64
.AddrReturn
.Offset
== 0) {
444 WalkStackThread(void* aData
)
449 // Call PeekMessage to force creation of a message queue so that
450 // other threads can safely post events to us.
451 ::PeekMessage(&msg
, nullptr, WM_USER
, WM_USER
, PM_NOREMOVE
);
453 // and tell the thread that created us that we're ready.
454 HANDLE readyEvent
= (HANDLE
)aData
;
455 ::SetEvent(readyEvent
);
457 while ((msgRet
= ::GetMessage(&msg
, (HWND
)-1, 0, 0)) != 0) {
459 PrintError("GetMessage");
463 struct WalkStackData
* data
= (WalkStackData
*)msg
.lParam
;
468 // Don't suspend the calling thread until it's waiting for
469 // us; otherwise the number of frames on the stack could vary.
470 ret
= ::WaitForSingleObject(data
->eventStart
, INFINITE
);
471 if (ret
!= WAIT_OBJECT_0
) {
472 PrintError("WaitForSingleObject");
475 // Suspend the calling thread, dump his stack, and then resume him.
476 // He's currently waiting for us to finish so now should be a good time.
477 ret
= ::SuspendThread(data
->thread
);
479 PrintError("ThreadSuspend");
481 WalkStackMain64(data
);
483 ret
= ::ResumeThread(data
->thread
);
485 PrintError("ThreadResume");
489 ::SetEvent(data
->eventEnd
);
497 * Walk the stack, translating PC's found into strings and recording the
498 * chain in aBuffer. For this to work properly, the DLLs must be rebased
499 * so that the address in the file agrees with the address in memory.
500 * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
501 * whose in memory address doesn't match its in-file address.
504 EXPORT_XPCOM_API(nsresult
)
505 NS_StackWalk(NS_WalkStackCallback aCallback
, uint32_t aSkipFrames
,
506 uint32_t aMaxFrames
, void* aClosure
, uintptr_t aThread
,
509 StackWalkInitCriticalAddress();
510 static HANDLE myProcess
= nullptr;
513 struct WalkStackData data
;
515 if (!EnsureWalkThreadReady()) {
516 return NS_ERROR_FAILURE
;
519 HANDLE targetThread
= ::GetCurrentThread();
520 data
.walkCallingThread
= true;
522 HANDLE threadToWalk
= reinterpret_cast<HANDLE
>(aThread
);
523 // walkCallingThread indicates whether we are walking the caller's stack
524 data
.walkCallingThread
= (threadToWalk
== targetThread
);
525 targetThread
= threadToWalk
;
528 // We need to avoid calling fprintf and friends if we're walking the stack of
529 // another thread, in order to avoid deadlocks.
530 const bool shouldBeThreadSafe
= !!aThread
;
532 // Have to duplicate handle to get a real handle.
534 if (!::DuplicateHandle(::GetCurrentProcess(),
535 ::GetCurrentProcess(),
536 ::GetCurrentProcess(),
538 PROCESS_ALL_ACCESS
, FALSE
, 0)) {
539 if (!shouldBeThreadSafe
) {
540 PrintError("DuplicateHandle (process)");
542 return NS_ERROR_FAILURE
;
545 if (!::DuplicateHandle(::GetCurrentProcess(),
547 ::GetCurrentProcess(),
549 THREAD_ALL_ACCESS
, FALSE
, 0)) {
550 if (!shouldBeThreadSafe
) {
551 PrintError("DuplicateHandle (thread)");
553 return NS_ERROR_FAILURE
;
556 data
.skipFrames
= aSkipFrames
;
557 data
.thread
= myThread
;
558 data
.process
= myProcess
;
559 void* local_pcs
[1024];
560 data
.pcs
= local_pcs
;
562 data
.pc_size
= ArrayLength(local_pcs
);
563 data
.pc_max
= aMaxFrames
;
564 void* local_sps
[1024];
565 data
.sps
= local_sps
;
567 data
.sp_size
= ArrayLength(local_sps
);
568 data
.platformData
= aPlatformData
;
571 // If we're walking the stack of another thread, we don't need to
572 // use a separate walker thread.
573 WalkStackMain64(&data
);
575 if (data
.pc_count
> data
.pc_size
) {
576 data
.pcs
= (void**)_alloca(data
.pc_count
* sizeof(void*));
577 data
.pc_size
= data
.pc_count
;
579 data
.sps
= (void**)_alloca(data
.sp_count
* sizeof(void*));
580 data
.sp_size
= data
.sp_count
;
582 WalkStackMain64(&data
);
585 data
.eventStart
= ::CreateEvent(nullptr, FALSE
/* auto-reset*/,
586 FALSE
/* initially non-signaled */, nullptr);
587 data
.eventEnd
= ::CreateEvent(nullptr, FALSE
/* auto-reset*/,
588 FALSE
/* initially non-signaled */, nullptr);
590 ::PostThreadMessage(gStackWalkThread
, WM_USER
, 0, (LPARAM
)&data
);
592 walkerReturn
= ::SignalObjectAndWait(data
.eventStart
,
593 data
.eventEnd
, INFINITE
, FALSE
);
594 if (walkerReturn
!= WAIT_OBJECT_0
&& !shouldBeThreadSafe
) {
595 PrintError("SignalObjectAndWait (1)");
597 if (data
.pc_count
> data
.pc_size
) {
598 data
.pcs
= (void**)_alloca(data
.pc_count
* sizeof(void*));
599 data
.pc_size
= data
.pc_count
;
601 data
.sps
= (void**)_alloca(data
.sp_count
* sizeof(void*));
602 data
.sp_size
= data
.sp_count
;
604 ::PostThreadMessage(gStackWalkThread
, WM_USER
, 0, (LPARAM
)&data
);
605 walkerReturn
= ::SignalObjectAndWait(data
.eventStart
,
606 data
.eventEnd
, INFINITE
, FALSE
);
607 if (walkerReturn
!= WAIT_OBJECT_0
&& !shouldBeThreadSafe
) {
608 PrintError("SignalObjectAndWait (2)");
612 ::CloseHandle(data
.eventStart
);
613 ::CloseHandle(data
.eventEnd
);
616 ::CloseHandle(myThread
);
618 for (uint32_t i
= 0; i
< data
.pc_count
; ++i
) {
619 (*aCallback
)(data
.pcs
[i
], data
.sps
[i
], aClosure
);
622 return data
.pc_count
== 0 ? NS_ERROR_FAILURE
: NS_OK
;
634 DWORD64 addr
= *(DWORD64
*)aUserContext
;
637 * You'll want to control this if we are running on an
638 * architecture where the addresses go the other direction.
639 * Not sure this is even a realistic consideration.
641 const BOOL addressIncreases
= TRUE
;
644 * If it falls in side the known range, load the symbols.
647 ? (addr
>= aModuleBase
&& addr
<= (aModuleBase
+ aModuleSize
))
648 : (addr
<= aModuleBase
&& addr
>= (aModuleBase
- aModuleSize
))
650 retval
= !!SymLoadModule64(GetCurrentProcess(), nullptr,
651 (PSTR
)aModuleName
, nullptr,
652 aModuleBase
, aModuleSize
);
654 PrintError("SymLoadModule64");
662 * SymGetModuleInfoEspecial
664 * Attempt to determine the module information.
665 * Bug 112196 says this DLL may not have been loaded at the time
666 * SymInitialize was called, and thus the module information
667 * and symbol information is not available.
668 * This code rectifies that problem.
671 // New members were added to IMAGEHLP_MODULE64 (that show up in the
672 // Platform SDK that ships with VC8, but not the Platform SDK that ships
673 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
674 // use them, and it's useful to be able to function correctly with the
675 // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
676 // version 5.1.) Since Platform SDK version need not correspond to
677 // compiler version, and the version number in debughlp.h was NOT bumped
678 // when these changes were made, ifdef based on a constant that was
679 // added between these versions.
680 #ifdef SSRVOPT_SETCONTEXT
681 #define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64))
683 #define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
686 BOOL
SymGetModuleInfoEspecial64(HANDLE aProcess
, DWORD64 aAddr
,
687 PIMAGEHLP_MODULE64 aModuleInfo
,
688 PIMAGEHLP_LINE64 aLineInfo
)
693 * Init the vars if we have em.
695 aModuleInfo
->SizeOfStruct
= NS_IMAGEHLP_MODULE64_SIZE
;
697 aLineInfo
->SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
702 * It may already be loaded.
704 retval
= SymGetModuleInfo64(aProcess
, aAddr
, aModuleInfo
);
705 if (retval
== FALSE
) {
707 * Not loaded, here's the magic.
708 * Go through all the modules.
710 // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
711 // constness of the first parameter of
712 // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
713 // non-const to const over time). See bug 391848 and bug
715 BOOL enumRes
= EnumerateLoadedModules64(
717 (PENUMLOADED_MODULES_CALLBACK64
)callbackEspecial64
,
719 if (enumRes
!= FALSE
) {
722 * If it fails, then well, we have other problems.
724 retval
= SymGetModuleInfo64(aProcess
, aAddr
, aModuleInfo
);
729 * If we got module info, we may attempt line info as well.
730 * We will not report failure if this does not work.
732 if (retval
!= FALSE
&& aLineInfo
) {
733 DWORD displacement
= 0;
734 BOOL lineRes
= FALSE
;
735 lineRes
= SymGetLineFromAddr64(aProcess
, aAddr
, &displacement
, aLineInfo
);
737 // Clear out aLineInfo to indicate that it's not valid
738 memset(aLineInfo
, 0, sizeof(*aLineInfo
));
746 EnsureSymInitialized()
748 static bool gInitialized
= false;
755 if (!EnsureWalkThreadReady()) {
759 SymSetOptions(SYMOPT_LOAD_LINES
| SYMOPT_UNDNAME
);
760 retStat
= SymInitialize(GetCurrentProcess(), nullptr, TRUE
);
762 PrintError("SymInitialize");
765 gInitialized
= retStat
;
766 /* XXX At some point we need to arrange to call SymCleanup */
772 EXPORT_XPCOM_API(nsresult
)
773 NS_DescribeCodeAddress(void* aPC
, nsCodeAddressDetails
* aDetails
)
775 aDetails
->library
[0] = '\0';
776 aDetails
->loffset
= 0;
777 aDetails
->filename
[0] = '\0';
778 aDetails
->lineno
= 0;
779 aDetails
->function
[0] = '\0';
780 aDetails
->foffset
= 0;
782 if (!EnsureSymInitialized()) {
783 return NS_ERROR_FAILURE
;
786 HANDLE myProcess
= ::GetCurrentProcess();
789 // debug routines are not threadsafe, so grab the lock.
790 EnterCriticalSection(&gDbgHelpCS
);
793 // Attempt to load module info before we attempt to resolve the symbol.
794 // This just makes sure we get good info if available.
797 DWORD64 addr
= (DWORD64
)aPC
;
798 IMAGEHLP_MODULE64 modInfo
;
799 IMAGEHLP_LINE64 lineInfo
;
801 modInfoRes
= SymGetModuleInfoEspecial64(myProcess
, addr
, &modInfo
, &lineInfo
);
804 PL_strncpyz(aDetails
->library
, modInfo
.ModuleName
,
805 sizeof(aDetails
->library
));
806 aDetails
->loffset
= (char*)aPC
- (char*)modInfo
.BaseOfImage
;
808 if (lineInfo
.FileName
) {
809 PL_strncpyz(aDetails
->filename
, lineInfo
.FileName
,
810 sizeof(aDetails
->filename
));
811 aDetails
->lineno
= lineInfo
.LineNumber
;
815 ULONG64 buffer
[(sizeof(SYMBOL_INFO
) +
816 MAX_SYM_NAME
* sizeof(TCHAR
) + sizeof(ULONG64
) - 1) / sizeof(ULONG64
)];
817 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)buffer
;
818 pSymbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
819 pSymbol
->MaxNameLen
= MAX_SYM_NAME
;
821 DWORD64 displacement
;
822 ok
= SymFromAddr(myProcess
, addr
, &displacement
, pSymbol
);
825 PL_strncpyz(aDetails
->function
, pSymbol
->Name
,
826 sizeof(aDetails
->function
));
827 aDetails
->foffset
= static_cast<ptrdiff_t>(displacement
);
830 LeaveCriticalSection(&gDbgHelpCS
); // release our lock
834 EXPORT_XPCOM_API(nsresult
)
835 NS_FormatCodeAddressDetails(void* aPC
, const nsCodeAddressDetails
* aDetails
,
836 char* aBuffer
, uint32_t aBufferSize
)
838 if (aDetails
->function
[0]) {
839 _snprintf(aBuffer
, aBufferSize
, "%s+0x%08lX [%s +0x%016lX]",
840 aDetails
->function
, aDetails
->foffset
,
841 aDetails
->library
, aDetails
->loffset
);
842 } else if (aDetails
->library
[0]) {
843 _snprintf(aBuffer
, aBufferSize
, "UNKNOWN [%s +0x%016lX]",
844 aDetails
->library
, aDetails
->loffset
);
846 _snprintf(aBuffer
, aBufferSize
, "UNKNOWN 0x%016lX", aPC
);
849 aBuffer
[aBufferSize
- 1] = '\0';
851 uint32_t len
= strlen(aBuffer
);
852 if (aDetails
->filename
[0]) {
853 _snprintf(aBuffer
+ len
, aBufferSize
- len
, " (%s, line %d)\n",
854 aDetails
->filename
, aDetails
->lineno
);
857 if (++len
!= aBufferSize
) {
861 aBuffer
[aBufferSize
- 2] = '\n';
862 aBuffer
[aBufferSize
- 1] = '\0';
866 // WIN32 x86 stack walking code
867 // i386 or PPC Linux stackwalking code or Solaris
868 #elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || NSSTACKWALK_SUPPORTS_LINUX || NSSTACKWALK_SUPPORTS_SOLARIS || NSSTACKWALK_SUPPORTS_MACOSX)
876 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
877 // if __USE_GNU is defined. I suppose its some kind of standards
880 #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
884 // This thing is exported by libstdc++
885 // Yes, this is a gcc only hack
886 #if defined(MOZ_DEMANGLE_SYMBOLS)
888 #endif // MOZ_DEMANGLE_SYMBOLS
890 void DemangleSymbol(const char* aSymbol
,
896 #if defined(MOZ_DEMANGLE_SYMBOLS)
897 /* See demangle.h in the gcc source for the voodoo */
898 char* demangled
= abi::__cxa_demangle(aSymbol
, 0, 0, 0);
901 PL_strncpyz(aBuffer
, demangled
, aBufLen
);
904 #endif // MOZ_DEMANGLE_SYMBOLS
908 #if NSSTACKWALK_SUPPORTS_SOLARIS
911 * Stack walking code for Solaris courtesy of Bart Smaalder's "memtrak".
915 #include <ucontext.h>
916 #include <sys/frame.h>
917 #include <sys/regset.h>
918 #include <sys/stack.h>
920 static int load_address ( void * pc
, void * arg
);
921 static struct bucket
* newbucket ( void * pc
);
922 static struct frame
* cs_getmyframeptr ( void );
923 static void cs_walk_stack ( void * (*read_func
)(char * address
),
925 int (*operate_func
)(void *, void *, void *),
927 static void cs_operate ( void (*operate_func
)(void *, void *, void *),
932 #endif /*STACK_BIAS*/
936 /* type of demangling function */
937 typedef int demf_t(const char *, char *, size_t);
941 static int initialized
= 0;
943 #if defined(sparc) || defined(__sparc)
944 #define FRAME_PTR_REGISTER REG_SP
947 #if defined(i386) || defined(__i386)
948 #define FRAME_PTR_REGISTER EBP
954 struct bucket
* next
;
957 struct my_user_args
{
958 NS_WalkStackCallback callback
;
966 static void myinit();
968 #pragma init (myinit)
977 const char *libdem
= "libdemangle.so.1";
979 /* load libdemangle if we can and need to (only try this once) */
980 if ((handle
= dlopen(libdem
, RTLD_LAZY
)) != nullptr) {
981 demf
= (demf_t
*)dlsym(handle
,
982 "cplus_demangle"); /*lint !e611 */
984 * lint override above is to prevent lint from
985 * complaining about "suspicious cast".
995 load_address(void * pc
, void * arg
)
997 static struct bucket table
[2048];
1000 struct my_user_args
* args
= (struct my_user_args
*) arg
;
1002 unsigned int val
= NS_PTR_TO_INT32(pc
);
1004 ptr
= table
+ ((val
>> 2)&2047);
1008 if (ptr
->next
->pc
== pc
)
1015 mutex_unlock(&lock
);
1017 (args
->callback
)(pc
, args
->closure
);
1019 if (args
->maxFrames
!= 0 && args
->numFrames
== args
->maxFrames
)
1020 stop
= 1; // causes us to stop getting frames
1022 ptr
->next
= newbucket(pc
);
1023 mutex_unlock(&lock
);
1029 static struct bucket
*
1030 newbucket(void * pc
)
1032 struct bucket
* ptr
= (struct bucket
*)malloc(sizeof(*ptr
));
1033 static int index
; /* protected by lock in caller */
1035 ptr
->index
= index
++;
1036 ptr
->next
= nullptr;
1042 static struct frame
*
1048 (void) getcontext(&u
);
1050 fp
= (struct frame
*)
1051 ((char *)u
.uc_mcontext
.gregs
[FRAME_PTR_REGISTER
] +
1054 /* make sure to return parents frame pointer.... */
1056 return ((struct frame
*)((ulong_t
)fp
->fr_savfp
+ STACK_BIAS
));
1061 cswalkstack(struct frame
*fp
, int (*operate_func
)(void *, void *, void *),
1065 while (fp
!= 0 && fp
->fr_savpc
!= 0) {
1067 if (operate_func((void *)fp
->fr_savpc
, nullptr, usrarg
) != 0)
1070 * watch out - libthread stacks look funny at the top
1071 * so they may not have their STACK_BIAS set
1074 fp
= (struct frame
*)((ulong_t
)fp
->fr_savfp
+
1075 (fp
->fr_savfp
?(ulong_t
)STACK_BIAS
:0));
1081 cs_operate(int (*operate_func
)(void *, void *, void *), void * usrarg
)
1083 cswalkstack(csgetframeptr(), operate_func
, usrarg
);
1086 EXPORT_XPCOM_API(nsresult
)
1087 NS_StackWalk(NS_WalkStackCallback aCallback
, uint32_t aSkipFrames
,
1088 uint32_t aMaxFrames
, void* aClosure
, uintptr_t aThread
,
1089 void* aPlatformData
)
1091 MOZ_ASSERT(!aThread
);
1092 MOZ_ASSERT(!aPlatformData
);
1093 struct my_user_args args
;
1095 StackWalkInitCriticalAddress();
1101 args
.callback
= aCallback
;
1102 args
.skipFrames
= aSkipFrames
; /* XXX Not handled! */
1103 args
.maxFrames
= aMaxFrames
;
1105 args
.closure
= aClosure
;
1106 cs_operate(load_address
, &args
);
1107 return args
.numFrames
== 0 ? NS_ERROR_FAILURE
: NS_OK
;
1110 EXPORT_XPCOM_API(nsresult
)
1111 NS_DescribeCodeAddress(void* aPC
, nsCodeAddressDetails
* aDetails
)
1113 aDetails
->library
[0] = '\0';
1114 aDetails
->loffset
= 0;
1115 aDetails
->filename
[0] = '\0';
1116 aDetails
->lineno
= 0;
1117 aDetails
->function
[0] = '\0';
1118 aDetails
->foffset
= 0;
1123 if (dladdr(aPC
, & info
)) {
1124 if (info
.dli_fname
) {
1125 PL_strncpyz(aDetails
->library
, info
.dli_fname
,
1126 sizeof(aDetails
->library
));
1127 aDetails
->loffset
= (char*)aPC
- (char*)info
.dli_fbase
;
1129 if (info
.dli_sname
) {
1130 aDetails
->foffset
= (char*)aPC
- (char*)info
.dli_saddr
;
1132 DemangleSymbol(info
.dli_sname
, dembuff
, sizeof(dembuff
));
1134 if (!demf
|| demf(info
.dli_sname
, dembuff
, sizeof(dembuff
))) {
1138 PL_strncpyz(aDetails
->function
,
1139 (dembuff
[0] != '\0') ? dembuff
: info
.dli_sname
,
1140 sizeof(aDetails
->function
));
1147 EXPORT_XPCOM_API(nsresult
)
1148 NS_FormatCodeAddressDetails(void* aPC
, const nsCodeAddressDetails
* aDetails
,
1149 char* aBuffer
, uint32_t aBufferSize
)
1151 snprintf(aBuffer
, aBufferSize
, "%p %s:%s+0x%lx\n",
1153 aDetails
->library
[0] ? aDetails
->library
: "??",
1154 aDetails
->function
[0] ? aDetails
->function
: "??",
1159 #else // not __sun-specific
1161 #if __GLIBC__ > 2 || __GLIBC_MINOR > 1
1162 #define HAVE___LIBC_STACK_END 1
1164 #define HAVE___LIBC_STACK_END 0
1167 #if HAVE___LIBC_STACK_END
1168 extern void* __libc_stack_end
; // from ld-linux.so
1172 FramePointerStackWalk(NS_WalkStackCallback aCallback
, uint32_t aSkipFrames
,
1173 uint32_t aMaxFrames
, void* aClosure
, void** bp
,
1176 // Stack walking code courtesy Kipp's "leaky".
1178 int32_t skip
= aSkipFrames
;
1179 uint32_t numFrames
= 0;
1181 void** next
= (void**)*bp
;
1182 // bp may not be a frame pointer on i386 if code was compiled with
1183 // -fomit-frame-pointer, so do some sanity checks.
1184 // (bp should be a frame pointer on ppc(64) but checking anyway may help
1185 // a little if the stack has been corrupted.)
1186 // We don't need to check against the begining of the stack because
1187 // we can assume that bp > sp
1193 #if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
1194 // ppc mac or powerpc64 linux
1195 void* pc
= *(bp
+ 2);
1197 #else // i386 or powerpc32 linux
1198 void* pc
= *(bp
+ 1);
1201 if (IsCriticalAddress(pc
)) {
1202 return NS_ERROR_UNEXPECTED
;
1205 // Assume that the SP points to the BP of the function
1206 // it called. We can't know the exact location of the SP
1207 // but this should be sufficient for our use the SP
1208 // to order elements on the stack.
1209 (*aCallback
)(pc
, bp
, aClosure
);
1211 if (aMaxFrames
!= 0 && numFrames
== aMaxFrames
) {
1217 return numFrames
== 0 ? NS_ERROR_FAILURE
: NS_OK
;
1222 #define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__))
1223 #if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code
1225 EXPORT_XPCOM_API(nsresult
)
1226 NS_StackWalk(NS_WalkStackCallback aCallback
, uint32_t aSkipFrames
,
1227 uint32_t aMaxFrames
, void* aClosure
, uintptr_t aThread
,
1228 void* aPlatformData
)
1230 MOZ_ASSERT(!aThread
);
1231 MOZ_ASSERT(!aPlatformData
);
1232 StackWalkInitCriticalAddress();
1234 // Get the frame pointer
1237 __asm__("movl %%ebp, %0" : "=g"(bp
));
1239 // It would be nice if this worked uniformly, but at least on i386 and
1240 // x86_64, it stopped working with gcc 4.1, because it points to the
1241 // end of the saved registers instead of the start.
1242 bp
= (void**)__builtin_frame_address(0);
1246 #if HAVE___LIBC_STACK_END
1247 stackEnd
= __libc_stack_end
;
1249 stackEnd
= reinterpret_cast<void*>(-1);
1251 return FramePointerStackWalk(aCallback
, aSkipFrames
, aMaxFrames
,
1252 aClosure
, bp
, stackEnd
);
1256 #elif defined(HAVE__UNWIND_BACKTRACE)
1258 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
1263 NS_WalkStackCallback callback
;
1267 bool isCriticalAbort
;
1271 static _Unwind_Reason_Code
1272 unwind_callback(struct _Unwind_Context
* context
, void* closure
)
1274 unwind_info
* info
= static_cast<unwind_info
*>(closure
);
1275 void* pc
= reinterpret_cast<void*>(_Unwind_GetIP(context
));
1276 // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
1277 if (IsCriticalAddress(pc
)) {
1278 info
->isCriticalAbort
= true;
1279 // We just want to stop the walk, so any error code will do. Using
1280 // _URC_NORMAL_STOP would probably be the most accurate, but it is not
1281 // defined on Android for ARM.
1282 return _URC_FOREIGN_EXCEPTION_CAUGHT
;
1284 if (--info
->skip
< 0) {
1285 (*info
->callback
)(pc
, nullptr, info
->closure
);
1287 if (info
->maxFrames
!= 0 && info
->numFrames
== info
->maxFrames
) {
1288 // Again, any error code that stops the walk will do.
1289 return _URC_FOREIGN_EXCEPTION_CAUGHT
;
1292 return _URC_NO_REASON
;
1295 EXPORT_XPCOM_API(nsresult
)
1296 NS_StackWalk(NS_WalkStackCallback aCallback
, uint32_t aSkipFrames
,
1297 uint32_t aMaxFrames
, void* aClosure
, uintptr_t aThread
,
1298 void* aPlatformData
)
1300 MOZ_ASSERT(!aThread
);
1301 MOZ_ASSERT(!aPlatformData
);
1302 StackWalkInitCriticalAddress();
1304 info
.callback
= aCallback
;
1305 info
.skip
= aSkipFrames
+ 1;
1306 info
.maxFrames
= aMaxFrames
;
1308 info
.isCriticalAbort
= false;
1309 info
.closure
= aClosure
;
1311 (void)_Unwind_Backtrace(unwind_callback
, &info
);
1313 // We ignore the return value from _Unwind_Backtrace and instead determine
1314 // the outcome from |info|. There are two main reasons for this:
1315 // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
1316 // _URC_FAILURE. See
1317 // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
1318 // - If aMaxFrames != 0, we want to stop early, and the only way to do that
1319 // is to make unwind_callback return something other than _URC_NO_REASON,
1320 // which causes _Unwind_Backtrace to return a non-success code.
1321 if (info
.isCriticalAbort
) {
1322 return NS_ERROR_UNEXPECTED
;
1324 return info
.numFrames
== 0 ? NS_ERROR_FAILURE
: NS_OK
;
1329 EXPORT_XPCOM_API(nsresult
)
1330 NS_DescribeCodeAddress(void* aPC
, nsCodeAddressDetails
* aDetails
)
1332 aDetails
->library
[0] = '\0';
1333 aDetails
->loffset
= 0;
1334 aDetails
->filename
[0] = '\0';
1335 aDetails
->lineno
= 0;
1336 aDetails
->function
[0] = '\0';
1337 aDetails
->foffset
= 0;
1340 int ok
= dladdr(aPC
, &info
);
1345 PL_strncpyz(aDetails
->library
, info
.dli_fname
, sizeof(aDetails
->library
));
1346 aDetails
->loffset
= (char*)aPC
- (char*)info
.dli_fbase
;
1348 const char* symbol
= info
.dli_sname
;
1349 if (!symbol
|| symbol
[0] == '\0') {
1353 DemangleSymbol(symbol
, aDetails
->function
, sizeof(aDetails
->function
));
1355 if (aDetails
->function
[0] == '\0') {
1356 // Just use the mangled symbol if demangling failed.
1357 PL_strncpyz(aDetails
->function
, symbol
, sizeof(aDetails
->function
));
1360 aDetails
->foffset
= (char*)aPC
- (char*)info
.dli_saddr
;
1364 EXPORT_XPCOM_API(nsresult
)
1365 NS_FormatCodeAddressDetails(void* aPC
, const nsCodeAddressDetails
* aDetails
,
1366 char* aBuffer
, uint32_t aBufferSize
)
1368 if (!aDetails
->library
[0]) {
1369 snprintf(aBuffer
, aBufferSize
, "UNKNOWN %p\n", aPC
);
1370 } else if (!aDetails
->function
[0]) {
1371 snprintf(aBuffer
, aBufferSize
, "UNKNOWN [%s +0x%08" PRIXPTR
"]\n",
1372 aDetails
->library
, aDetails
->loffset
);
1374 snprintf(aBuffer
, aBufferSize
, "%s+0x%08" PRIXPTR
1375 " [%s +0x%08" PRIXPTR
"]\n",
1376 aDetails
->function
, aDetails
->foffset
,
1377 aDetails
->library
, aDetails
->loffset
);
1384 #else // unsupported platform.
1386 EXPORT_XPCOM_API(nsresult
)
1387 NS_StackWalk(NS_WalkStackCallback aCallback
, uint32_t aSkipFrames
,
1388 uint32_t aMaxFrames
, void* aClosure
, uintptr_t aThread
,
1389 void* aPlatformData
)
1391 MOZ_ASSERT(!aThread
);
1392 MOZ_ASSERT(!aPlatformData
);
1393 return NS_ERROR_NOT_IMPLEMENTED
;
1398 FramePointerStackWalk(NS_WalkStackCallback aCallback
, uint32_t aSkipFrames
,
1399 void* aClosure
, void** aBp
)
1401 return NS_ERROR_NOT_IMPLEMENTED
;
1405 EXPORT_XPCOM_API(nsresult
)
1406 NS_DescribeCodeAddress(void* aPC
, nsCodeAddressDetails
* aDetails
)
1408 aDetails
->library
[0] = '\0';
1409 aDetails
->loffset
= 0;
1410 aDetails
->filename
[0] = '\0';
1411 aDetails
->lineno
= 0;
1412 aDetails
->function
[0] = '\0';
1413 aDetails
->foffset
= 0;
1414 return NS_ERROR_NOT_IMPLEMENTED
;
1417 EXPORT_XPCOM_API(nsresult
)
1418 NS_FormatCodeAddressDetails(void* aPC
, const nsCodeAddressDetails
* aDetails
,
1419 char* aBuffer
, uint32_t aBufferSize
)
1422 return NS_ERROR_NOT_IMPLEMENTED
;