1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* API for getting a stack trace of the C/C++ stack on the current thread */
8
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/IntegerPrintfMacros.h"
12 #include "mozilla/StackWalk.h"
13
14 #include <string.h>
15
16 using namespace mozilla;
17
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
21 {
22 void* mAddr;
23 bool mInit;
24 };
25 static CriticalAddress gCriticalAddress;
26
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)
30 #define _GNU_SOURCE
31 #endif
32
33 #if defined(HAVE_DLOPEN) || defined(XP_DARWIN)
34 #include <dlfcn.h>
35 #endif
36
37 #if (defined(XP_DARWIN) && \
38 (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
39 #define MOZ_STACKWALK_SUPPORTS_MACOSX 1
40 #else
41 #define MOZ_STACKWALK_SUPPORTS_MACOSX 0
42 #endif
43
44 #if (defined(linux) && \
45 ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
46 defined(HAVE__UNWIND_BACKTRACE)))
47 #define MOZ_STACKWALK_SUPPORTS_LINUX 1
48 #else
49 #define MOZ_STACKWALK_SUPPORTS_LINUX 0
50 #endif
51
52 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
53 #define HAVE___LIBC_STACK_END 1
54 #else
55 #define HAVE___LIBC_STACK_END 0
56 #endif
57
58 #if HAVE___LIBC_STACK_END
59 extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so
60 #endif
61
62 #ifdef ANDROID
63 #include <algorithm>
64 #include <unistd.h>
65 #include <pthread.h>
66 #endif
67
68 #if MOZ_STACKWALK_SUPPORTS_MACOSX
69 #include <pthread.h>
70 #include <sys/errno.h>
71 #ifdef MOZ_WIDGET_COCOA
72 #include <CoreServices/CoreServices.h>
73 #endif
74
75 typedef void
76 malloc_logger_t(uint32_t aType,
77 uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
78 uintptr_t aResult, uint32_t aNumHotFramesToSkip);
79 extern malloc_logger_t* malloc_logger;
80
81 static void
stack_callback(uint32_t aFrameNumber,void * aPc,void * aSp,void * aClosure)82 stack_callback(uint32_t aFrameNumber, void* aPc, void* aSp, void* aClosure)
83 {
84 const char* name = static_cast<char*>(aClosure);
85 Dl_info info;
86
87 // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
88 // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
89 // correct one is the first that we find on our way up, so the
90 // following check for gCriticalAddress.mAddr is critical.
91 if (gCriticalAddress.mAddr || dladdr(aPc, &info) == 0 ||
92 !info.dli_sname || strcmp(info.dli_sname, name) != 0) {
93 return;
94 }
95 gCriticalAddress.mAddr = aPc;
96 }
97
98 static void
my_malloc_logger(uint32_t aType,uintptr_t aArg1,uintptr_t aArg2,uintptr_t aArg3,uintptr_t aResult,uint32_t aNumHotFramesToSkip)99 my_malloc_logger(uint32_t aType,
100 uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
101 uintptr_t aResult, uint32_t aNumHotFramesToSkip)
102 {
103 static bool once = false;
104 if (once) {
105 return;
106 }
107 once = true;
108
109 // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
110 // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
111 const char* name = "new_sem_from_pool";
112 MozStackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0,
113 const_cast<char*>(name), 0, nullptr);
114 }
115
116 // This is called from NS_LogInit() and from the stack walking functions, but
117 // only the first call has any effect. We need to call this function from both
118 // places because it must run before any mutexes are created, and also before
119 // any objects whose refcounts we're logging are created. Running this
120 // function during NS_LogInit() ensures that we meet the first criterion, and
121 // running this function during the stack walking functions ensures we meet the
122 // second criterion.
123 MFBT_API void
StackWalkInitCriticalAddress()124 StackWalkInitCriticalAddress()
125 {
126 if (gCriticalAddress.mInit) {
127 return;
128 }
129 gCriticalAddress.mInit = true;
130 // We must not do work when 'new_sem_from_pool' calls realloc, since
131 // it holds a non-reentrant spin-lock and we will quickly deadlock.
132 // new_sem_from_pool is not directly accessible using dlsym, so
133 // we force a situation where new_sem_from_pool is on the stack and
134 // use dladdr to check the addresses.
135
136 // malloc_logger can be set by external tools like 'Instruments' or 'leaks'
137 malloc_logger_t* old_malloc_logger = malloc_logger;
138 malloc_logger = my_malloc_logger;
139
140 pthread_cond_t cond;
141 int r = pthread_cond_init(&cond, 0);
142 MOZ_ASSERT(r == 0);
143 pthread_mutex_t mutex;
144 r = pthread_mutex_init(&mutex, 0);
145 MOZ_ASSERT(r == 0);
146 r = pthread_mutex_lock(&mutex);
147 MOZ_ASSERT(r == 0);
148 struct timespec abstime = { 0, 1 };
149 r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime);
150
151 // restore the previous malloc logger
152 malloc_logger = old_malloc_logger;
153
154 MOZ_ASSERT(r == ETIMEDOUT);
155 r = pthread_mutex_unlock(&mutex);
156 MOZ_ASSERT(r == 0);
157 r = pthread_mutex_destroy(&mutex);
158 MOZ_ASSERT(r == 0);
159 r = pthread_cond_destroy(&cond);
160 MOZ_ASSERT(r == 0);
161 }
162
163 static bool
IsCriticalAddress(void * aPC)164 IsCriticalAddress(void* aPC)
165 {
166 return gCriticalAddress.mAddr == aPC;
167 }
168 #else
169 static bool
IsCriticalAddress(void * aPC)170 IsCriticalAddress(void* aPC)
171 {
172 return false;
173 }
174 // We still initialize gCriticalAddress.mInit so that this code behaves
175 // the same on all platforms. Otherwise a failure to init would be visible
176 // only on OS X.
177 MFBT_API void
StackWalkInitCriticalAddress()178 StackWalkInitCriticalAddress()
179 {
180 gCriticalAddress.mInit = true;
181 }
182 #endif
183
184 #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
185
186 #include <windows.h>
187 #include <process.h>
188 #include <stdio.h>
189 #include <malloc.h>
190 #include "mozilla/ArrayUtils.h"
191 #include "mozilla/StackWalk_windows.h"
192
193 #include <imagehlp.h>
194 // We need a way to know if we are building for WXP (or later), as if we are, we
195 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
196 // A value of 9 indicates we want to use the new APIs.
197 #if API_VERSION_NUMBER < 9
198 #error Too old imagehlp.h
199 #endif
200
201 struct WalkStackData
202 {
203 // Are we walking the stack of the calling thread? Note that we need to avoid
204 // calling fprintf and friends if this is false, in order to avoid deadlocks.
205 bool walkCallingThread;
206 uint32_t skipFrames;
207 HANDLE thread;
208 HANDLE process;
209 HANDLE eventStart;
210 HANDLE eventEnd;
211 void** pcs;
212 uint32_t pc_size;
213 uint32_t pc_count;
214 uint32_t pc_max;
215 void** sps;
216 uint32_t sp_size;
217 uint32_t sp_count;
218 void* platformData;
219 };
220
221 DWORD gStackWalkThread;
222 CRITICAL_SECTION gDbgHelpCS;
223
224 // Routine to print an error message to standard error.
225 static void
PrintError(const char * aPrefix)226 PrintError(const char* aPrefix)
227 {
228 LPSTR lpMsgBuf;
229 DWORD lastErr = GetLastError();
230 FormatMessageA(
231 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
232 nullptr,
233 lastErr,
234 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
235 (LPSTR)&lpMsgBuf,
236 0,
237 nullptr
238 );
239 fprintf(stderr, "### ERROR: %s: %s",
240 aPrefix, lpMsgBuf ? lpMsgBuf : "(null)\n");
241 fflush(stderr);
242 LocalFree(lpMsgBuf);
243 }
244
245 static unsigned int WINAPI WalkStackThread(void* aData);
246
247 static bool
EnsureWalkThreadReady()248 EnsureWalkThreadReady()
249 {
250 static bool walkThreadReady = false;
251 static HANDLE stackWalkThread = nullptr;
252 static HANDLE readyEvent = nullptr;
253
254 if (walkThreadReady) {
255 return walkThreadReady;
256 }
257
258 if (!stackWalkThread) {
259 readyEvent = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
260 FALSE /* initially non-signaled */,
261 nullptr);
262 if (!readyEvent) {
263 PrintError("CreateEvent");
264 return false;
265 }
266
267 unsigned int threadID;
268 stackWalkThread = (HANDLE)_beginthreadex(nullptr, 0, WalkStackThread,
269 (void*)readyEvent, 0, &threadID);
270 if (!stackWalkThread) {
271 PrintError("CreateThread");
272 ::CloseHandle(readyEvent);
273 readyEvent = nullptr;
274 return false;
275 }
276 gStackWalkThread = threadID;
277 ::CloseHandle(stackWalkThread);
278 }
279
280 MOZ_ASSERT((stackWalkThread && readyEvent) ||
281 (!stackWalkThread && !readyEvent));
282
283 // The thread was created. Try to wait an arbitrary amount of time (1 second
284 // should be enough) for its event loop to start before posting events to it.
285 DWORD waitRet = ::WaitForSingleObject(readyEvent, 1000);
286 if (waitRet == WAIT_TIMEOUT) {
287 // We get a timeout if we're called during static initialization because
288 // the thread will only start executing after we return so it couldn't
289 // have signalled the event. If that is the case, give up for now and
290 // try again next time we're called.
291 return false;
292 }
293 ::CloseHandle(readyEvent);
294 stackWalkThread = nullptr;
295 readyEvent = nullptr;
296
297
298 ::InitializeCriticalSection(&gDbgHelpCS);
299
300 return walkThreadReady = true;
301 }
302
303 static void
WalkStackMain64(struct WalkStackData * aData)304 WalkStackMain64(struct WalkStackData* aData)
305 {
306 // Get a context for the specified thread.
307 CONTEXT context;
308 if (!aData->platformData) {
309 memset(&context, 0, sizeof(CONTEXT));
310 context.ContextFlags = CONTEXT_FULL;
311 if (!GetThreadContext(aData->thread, &context)) {
312 if (aData->walkCallingThread) {
313 PrintError("GetThreadContext");
314 }
315 return;
316 }
317 } else {
318 context = *static_cast<CONTEXT*>(aData->platformData);
319 }
320
321 #if defined(_M_IX86) || defined(_M_IA64)
322 // Setup initial stack frame to walk from.
323 STACKFRAME64 frame64;
324 memset(&frame64, 0, sizeof(frame64));
325 #ifdef _M_IX86
326 frame64.AddrPC.Offset = context.Eip;
327 frame64.AddrStack.Offset = context.Esp;
328 frame64.AddrFrame.Offset = context.Ebp;
329 #elif defined _M_IA64
330 frame64.AddrPC.Offset = context.StIIP;
331 frame64.AddrStack.Offset = context.SP;
332 frame64.AddrFrame.Offset = context.RsBSP;
333 #endif
334 frame64.AddrPC.Mode = AddrModeFlat;
335 frame64.AddrStack.Mode = AddrModeFlat;
336 frame64.AddrFrame.Mode = AddrModeFlat;
337 frame64.AddrReturn.Mode = AddrModeFlat;
338 #endif
339
340 // Skip our own stack walking frames.
341 int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
342
343 // Now walk the stack.
344 while (true) {
345 DWORD64 addr;
346 DWORD64 spaddr;
347
348 #if defined(_M_IX86) || defined(_M_IA64)
349 // 32-bit frame unwinding.
350 // Debug routines are not threadsafe, so grab the lock.
351 EnterCriticalSection(&gDbgHelpCS);
352 BOOL ok = StackWalk64(
353 #if defined _M_IA64
354 IMAGE_FILE_MACHINE_IA64,
355 #elif defined _M_IX86
356 IMAGE_FILE_MACHINE_I386,
357 #endif
358 aData->process,
359 aData->thread,
360 &frame64,
361 &context,
362 nullptr,
363 SymFunctionTableAccess64, // function table access routine
364 SymGetModuleBase64, // module base routine
365 0
366 );
367 LeaveCriticalSection(&gDbgHelpCS);
368
369 if (ok) {
370 addr = frame64.AddrPC.Offset;
371 spaddr = frame64.AddrStack.Offset;
372 } else {
373 addr = 0;
374 spaddr = 0;
375 if (aData->walkCallingThread) {
376 PrintError("WalkStack64");
377 }
378 }
379
380 if (!ok) {
381 break;
382 }
383
384 #elif defined(_M_AMD64)
385 // 64-bit frame unwinding.
386 // Try to look up unwind metadata for the current function.
387 ULONG64 imageBase;
388 PRUNTIME_FUNCTION runtimeFunction =
389 RtlLookupFunctionEntry(context.Rip, &imageBase, NULL);
390
391 if (!runtimeFunction) {
392 // Alas, this is probably a JIT frame, for which we don't generate unwind
393 // info and so we have to give up.
394 break;
395 }
396
397 PVOID dummyHandlerData;
398 ULONG64 dummyEstablisherFrame;
399 RtlVirtualUnwind(UNW_FLAG_NHANDLER,
400 imageBase,
401 context.Rip,
402 runtimeFunction,
403 &context,
404 &dummyHandlerData,
405 &dummyEstablisherFrame,
406 nullptr);
407
408 addr = context.Rip;
409 spaddr = context.Rsp;
410
411 #else
412 #error "unknown platform"
413 #endif
414
415 if (addr == 0) {
416 break;
417 }
418
419 if (skip-- > 0) {
420 continue;
421 }
422
423 if (aData->pc_count < aData->pc_size) {
424 aData->pcs[aData->pc_count] = (void*)addr;
425 }
426 ++aData->pc_count;
427
428 if (aData->sp_count < aData->sp_size) {
429 aData->sps[aData->sp_count] = (void*)spaddr;
430 }
431 ++aData->sp_count;
432
433 if (aData->pc_max != 0 && aData->pc_count == aData->pc_max) {
434 break;
435 }
436
437 #if defined(_M_IX86) || defined(_M_IA64)
438 if (frame64.AddrReturn.Offset == 0) {
439 break;
440 }
441 #endif
442 }
443 }
444
445 // The JIT needs to allocate executable memory. Because of the inanity of
446 // the win64 APIs, this requires locks that stalk walkers also need. Provide
447 // another lock to allow synchronization around these resources.
448 #ifdef _M_AMD64
449
450 struct CriticalSectionAutoInitializer {
451 CRITICAL_SECTION lock;
452
CriticalSectionAutoInitializerCriticalSectionAutoInitializer453 CriticalSectionAutoInitializer() {
454 InitializeCriticalSection(&lock);
455 }
456 };
457
458 static CriticalSectionAutoInitializer gWorkaroundLock;
459
460 #endif // _M_AMD64
461
462 MFBT_API void
AcquireStackWalkWorkaroundLock()463 AcquireStackWalkWorkaroundLock()
464 {
465 #ifdef _M_AMD64
466 EnterCriticalSection(&gWorkaroundLock.lock);
467 #endif
468 }
469
470 MFBT_API bool
TryAcquireStackWalkWorkaroundLock()471 TryAcquireStackWalkWorkaroundLock()
472 {
473 #ifdef _M_AMD64
474 return TryEnterCriticalSection(&gWorkaroundLock.lock);
475 #else
476 return true;
477 #endif
478 }
479
480 MFBT_API void
ReleaseStackWalkWorkaroundLock()481 ReleaseStackWalkWorkaroundLock()
482 {
483 #ifdef _M_AMD64
484 LeaveCriticalSection(&gWorkaroundLock.lock);
485 #endif
486 }
487
488 static unsigned int WINAPI
WalkStackThread(void * aData)489 WalkStackThread(void* aData)
490 {
491 BOOL msgRet;
492 MSG msg;
493
494 // Call PeekMessage to force creation of a message queue so that
495 // other threads can safely post events to us.
496 ::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
497
498 // and tell the thread that created us that we're ready.
499 HANDLE readyEvent = (HANDLE)aData;
500 ::SetEvent(readyEvent);
501
502 while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) {
503 if (msgRet == -1) {
504 PrintError("GetMessage");
505 } else {
506 DWORD ret;
507
508 struct WalkStackData* data = (WalkStackData*)msg.lParam;
509 if (!data) {
510 continue;
511 }
512
513 // Don't suspend the calling thread until it's waiting for
514 // us; otherwise the number of frames on the stack could vary.
515 ret = ::WaitForSingleObject(data->eventStart, INFINITE);
516 if (ret != WAIT_OBJECT_0) {
517 PrintError("WaitForSingleObject");
518 }
519
520 // Suspend the calling thread, dump his stack, and then resume him.
521 // He's currently waiting for us to finish so now should be a good time.
522 ret = ::SuspendThread(data->thread);
523 if (ret == -1) {
524 PrintError("ThreadSuspend");
525 } else {
526 WalkStackMain64(data);
527
528 ret = ::ResumeThread(data->thread);
529 if (ret == -1) {
530 PrintError("ThreadResume");
531 }
532 }
533
534 ::SetEvent(data->eventEnd);
535 }
536 }
537
538 return 0;
539 }
540
541 /**
542 * Walk the stack, translating PC's found into strings and recording the
543 * chain in aBuffer. For this to work properly, the DLLs must be rebased
544 * so that the address in the file agrees with the address in memory.
545 * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
546 * whose in memory address doesn't match its in-file address.
547 */
548
549 MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure,uintptr_t aThread,void * aPlatformData)550 MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
551 uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
552 void* aPlatformData)
553 {
554 StackWalkInitCriticalAddress();
555 static HANDLE myProcess = nullptr;
556 HANDLE myThread;
557 DWORD walkerReturn;
558 struct WalkStackData data;
559
560 if (!EnsureWalkThreadReady()) {
561 return false;
562 }
563
564 HANDLE currentThread = ::GetCurrentThread();
565 HANDLE targetThread =
566 aThread ? reinterpret_cast<HANDLE>(aThread) : currentThread;
567 data.walkCallingThread = (targetThread == currentThread);
568
569 // Have to duplicate handle to get a real handle.
570 if (!myProcess) {
571 if (!::DuplicateHandle(::GetCurrentProcess(),
572 ::GetCurrentProcess(),
573 ::GetCurrentProcess(),
574 &myProcess,
575 PROCESS_ALL_ACCESS, FALSE, 0)) {
576 if (data.walkCallingThread) {
577 PrintError("DuplicateHandle (process)");
578 }
579 return false;
580 }
581 }
582 if (!::DuplicateHandle(::GetCurrentProcess(),
583 targetThread,
584 ::GetCurrentProcess(),
585 &myThread,
586 THREAD_ALL_ACCESS, FALSE, 0)) {
587 if (data.walkCallingThread) {
588 PrintError("DuplicateHandle (thread)");
589 }
590 return false;
591 }
592
593 data.skipFrames = aSkipFrames;
594 data.thread = myThread;
595 data.process = myProcess;
596 void* local_pcs[1024];
597 data.pcs = local_pcs;
598 data.pc_count = 0;
599 data.pc_size = ArrayLength(local_pcs);
600 data.pc_max = aMaxFrames;
601 void* local_sps[1024];
602 data.sps = local_sps;
603 data.sp_count = 0;
604 data.sp_size = ArrayLength(local_sps);
605 data.platformData = aPlatformData;
606
607 if (aThread) {
608 // If we're walking the stack of another thread, we don't need to
609 // use a separate walker thread.
610 WalkStackMain64(&data);
611
612 if (data.pc_count > data.pc_size) {
613 data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
614 data.pc_size = data.pc_count;
615 data.pc_count = 0;
616 data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
617 data.sp_size = data.sp_count;
618 data.sp_count = 0;
619 WalkStackMain64(&data);
620 }
621 } else {
622 data.eventStart = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
623 FALSE /* initially non-signaled */, nullptr);
624 data.eventEnd = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
625 FALSE /* initially non-signaled */, nullptr);
626
627 ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
628
629 walkerReturn = ::SignalObjectAndWait(data.eventStart,
630 data.eventEnd, INFINITE, FALSE);
631 if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
632 PrintError("SignalObjectAndWait (1)");
633 }
634 if (data.pc_count > data.pc_size) {
635 data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
636 data.pc_size = data.pc_count;
637 data.pc_count = 0;
638 data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
639 data.sp_size = data.sp_count;
640 data.sp_count = 0;
641 ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
642 walkerReturn = ::SignalObjectAndWait(data.eventStart,
643 data.eventEnd, INFINITE, FALSE);
644 if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
645 PrintError("SignalObjectAndWait (2)");
646 }
647 }
648
649 ::CloseHandle(data.eventStart);
650 ::CloseHandle(data.eventEnd);
651 }
652
653 ::CloseHandle(myThread);
654
655 for (uint32_t i = 0; i < data.pc_count; ++i) {
656 (*aCallback)(i + 1, data.pcs[i], data.sps[i], aClosure);
657 }
658
659 return data.pc_count != 0;
660 }
661
662
663 static BOOL CALLBACK
callbackEspecial64(PCSTR aModuleName,DWORD64 aModuleBase,ULONG aModuleSize,PVOID aUserContext)664 callbackEspecial64(
665 PCSTR aModuleName,
666 DWORD64 aModuleBase,
667 ULONG aModuleSize,
668 PVOID aUserContext)
669 {
670 BOOL retval = TRUE;
671 DWORD64 addr = *(DWORD64*)aUserContext;
672
673 /*
674 * You'll want to control this if we are running on an
675 * architecture where the addresses go the other direction.
676 * Not sure this is even a realistic consideration.
677 */
678 const BOOL addressIncreases = TRUE;
679
680 /*
681 * If it falls in side the known range, load the symbols.
682 */
683 if (addressIncreases
684 ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize))
685 : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))
686 ) {
687 retval = !!SymLoadModule64(GetCurrentProcess(), nullptr,
688 (PSTR)aModuleName, nullptr,
689 aModuleBase, aModuleSize);
690 if (!retval) {
691 PrintError("SymLoadModule64");
692 }
693 }
694
695 return retval;
696 }
697
698 /*
699 * SymGetModuleInfoEspecial
700 *
701 * Attempt to determine the module information.
702 * Bug 112196 says this DLL may not have been loaded at the time
703 * SymInitialize was called, and thus the module information
704 * and symbol information is not available.
705 * This code rectifies that problem.
706 */
707
708 // New members were added to IMAGEHLP_MODULE64 (that show up in the
709 // Platform SDK that ships with VC8, but not the Platform SDK that ships
710 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
711 // use them, and it's useful to be able to function correctly with the
712 // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
713 // version 5.1.) Since Platform SDK version need not correspond to
714 // compiler version, and the version number in debughlp.h was NOT bumped
715 // when these changes were made, ifdef based on a constant that was
716 // added between these versions.
717 #ifdef SSRVOPT_SETCONTEXT
718 #define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64))
719 #else
720 #define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
721 #endif
722
SymGetModuleInfoEspecial64(HANDLE aProcess,DWORD64 aAddr,PIMAGEHLP_MODULE64 aModuleInfo,PIMAGEHLP_LINE64 aLineInfo)723 BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr,
724 PIMAGEHLP_MODULE64 aModuleInfo,
725 PIMAGEHLP_LINE64 aLineInfo)
726 {
727 BOOL retval = FALSE;
728
729 /*
730 * Init the vars if we have em.
731 */
732 aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE;
733 if (aLineInfo) {
734 aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
735 }
736
737 /*
738 * Give it a go.
739 * It may already be loaded.
740 */
741 retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
742 if (retval == FALSE) {
743 /*
744 * Not loaded, here's the magic.
745 * Go through all the modules.
746 */
747 // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
748 // constness of the first parameter of
749 // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
750 // non-const to const over time). See bug 391848 and bug
751 // 415426.
752 BOOL enumRes = EnumerateLoadedModules64(
753 aProcess,
754 (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64,
755 (PVOID)&aAddr);
756 if (enumRes != FALSE) {
757 /*
758 * One final go.
759 * If it fails, then well, we have other problems.
760 */
761 retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
762 }
763 }
764
765 /*
766 * If we got module info, we may attempt line info as well.
767 * We will not report failure if this does not work.
768 */
769 if (retval != FALSE && aLineInfo) {
770 DWORD displacement = 0;
771 BOOL lineRes = FALSE;
772 lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo);
773 if (!lineRes) {
774 // Clear out aLineInfo to indicate that it's not valid
775 memset(aLineInfo, 0, sizeof(*aLineInfo));
776 }
777 }
778
779 return retval;
780 }
781
782 static bool
EnsureSymInitialized()783 EnsureSymInitialized()
784 {
785 static bool gInitialized = false;
786 bool retStat;
787
788 if (gInitialized) {
789 return gInitialized;
790 }
791
792 if (!EnsureWalkThreadReady()) {
793 return false;
794 }
795
796 SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
797 retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
798 if (!retStat) {
799 PrintError("SymInitialize");
800 }
801
802 gInitialized = retStat;
803 /* XXX At some point we need to arrange to call SymCleanup */
804
805 return retStat;
806 }
807
808
809 MFBT_API bool
MozDescribeCodeAddress(void * aPC,MozCodeAddressDetails * aDetails)810 MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
811 {
812 aDetails->library[0] = '\0';
813 aDetails->loffset = 0;
814 aDetails->filename[0] = '\0';
815 aDetails->lineno = 0;
816 aDetails->function[0] = '\0';
817 aDetails->foffset = 0;
818
819 if (!EnsureSymInitialized()) {
820 return false;
821 }
822
823 HANDLE myProcess = ::GetCurrentProcess();
824 BOOL ok;
825
826 // debug routines are not threadsafe, so grab the lock.
827 EnterCriticalSection(&gDbgHelpCS);
828
829 //
830 // Attempt to load module info before we attempt to resolve the symbol.
831 // This just makes sure we get good info if available.
832 //
833
834 DWORD64 addr = (DWORD64)aPC;
835 IMAGEHLP_MODULE64 modInfo;
836 IMAGEHLP_LINE64 lineInfo;
837 BOOL modInfoRes;
838 modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo);
839
840 if (modInfoRes) {
841 strncpy(aDetails->library, modInfo.LoadedImageName,
842 sizeof(aDetails->library));
843 aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
844 aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage;
845
846 if (lineInfo.FileName) {
847 strncpy(aDetails->filename, lineInfo.FileName,
848 sizeof(aDetails->filename));
849 aDetails->filename[mozilla::ArrayLength(aDetails->filename) - 1] = '\0';
850 aDetails->lineno = lineInfo.LineNumber;
851 }
852 }
853
854 ULONG64 buffer[(sizeof(SYMBOL_INFO) +
855 MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
856 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
857 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
858 pSymbol->MaxNameLen = MAX_SYM_NAME;
859
860 DWORD64 displacement;
861 ok = SymFromAddr(myProcess, addr, &displacement, pSymbol);
862
863 if (ok) {
864 strncpy(aDetails->function, pSymbol->Name,
865 sizeof(aDetails->function));
866 aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
867 aDetails->foffset = static_cast<ptrdiff_t>(displacement);
868 }
869
870 LeaveCriticalSection(&gDbgHelpCS); // release our lock
871 return true;
872 }
873
874 // i386 or PPC Linux stackwalking code
875 #elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || MOZ_STACKWALK_SUPPORTS_MACOSX)
876
877 #include <stdlib.h>
878 #include <string.h>
879 #include <stdio.h>
880
881 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
882 // if __USE_GNU is defined. I suppose its some kind of standards
883 // adherence thing.
884 //
885 #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
886 #define __USE_GNU
887 #endif
888
889 // This thing is exported by libstdc++
890 // Yes, this is a gcc only hack
891 #if defined(MOZ_DEMANGLE_SYMBOLS)
892 #include <cxxabi.h>
893 #endif // MOZ_DEMANGLE_SYMBOLS
894
DemangleSymbol(const char * aSymbol,char * aBuffer,int aBufLen)895 void DemangleSymbol(const char* aSymbol,
896 char* aBuffer,
897 int aBufLen)
898 {
899 aBuffer[0] = '\0';
900
901 #if defined(MOZ_DEMANGLE_SYMBOLS)
902 /* See demangle.h in the gcc source for the voodoo */
903 char* demangled = abi::__cxa_demangle(aSymbol, 0, 0, 0);
904
905 if (demangled) {
906 strncpy(aBuffer, demangled, aBufLen);
907 aBuffer[aBufLen - 1] = '\0';
908 free(demangled);
909 }
910 #endif // MOZ_DEMANGLE_SYMBOLS
911 }
912
913 // {x86, ppc} x {Linux, Mac} stackwalking code.
914 #if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
915 (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
916
917 MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure,uintptr_t aThread,void * aPlatformData)918 MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
919 uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
920 void* aPlatformData)
921 {
922 MOZ_ASSERT(!aThread);
923 MOZ_ASSERT(!aPlatformData);
924 StackWalkInitCriticalAddress();
925
926 // Get the frame pointer
927 void** bp = (void**)__builtin_frame_address(0);
928
929 void* stackEnd;
930 #if HAVE___LIBC_STACK_END
931 stackEnd = __libc_stack_end;
932 #elif defined(XP_DARWIN)
933 stackEnd = pthread_get_stackaddr_np(pthread_self());
934 #elif defined(ANDROID)
935 pthread_attr_t sattr;
936 pthread_attr_init(&sattr);
937 pthread_getattr_np(pthread_self(), &sattr);
938 void* stackBase = stackEnd = nullptr;
939 size_t stackSize = 0;
940 if (gettid() != getpid()) {
941 // bionic's pthread_attr_getstack doesn't tell the truth for the main
942 // thread (see bug 846670). So don't use it for the main thread.
943 if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) {
944 stackEnd = static_cast<char*>(stackBase) + stackSize;
945 } else {
946 stackEnd = nullptr;
947 }
948 }
949 if (!stackEnd) {
950 // So consider the current frame pointer + an arbitrary size of 8MB
951 // (modulo overflow ; not really arbitrary as it's the default stack
952 // size for the main thread) if pthread_attr_getstack failed for
953 // some reason (or was skipped).
954 static const uintptr_t kMaxStackSize = 8 * 1024 * 1024;
955 uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize;
956 uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp));
957 stackEnd = reinterpret_cast<void*>(stackStart + kMaxStackSize);
958 }
959 #else
960 # error Unsupported configuration
961 #endif
962 return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames,
963 aClosure, bp, stackEnd);
964 }
965
966 #elif defined(HAVE__UNWIND_BACKTRACE)
967
968 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
969 #include <unwind.h>
970
971 struct unwind_info
972 {
973 MozWalkStackCallback callback;
974 int skip;
975 int maxFrames;
976 int numFrames;
977 bool isCriticalAbort;
978 void* closure;
979 };
980
981 static _Unwind_Reason_Code
unwind_callback(struct _Unwind_Context * context,void * closure)982 unwind_callback(struct _Unwind_Context* context, void* closure)
983 {
984 unwind_info* info = static_cast<unwind_info*>(closure);
985 void* pc = reinterpret_cast<void*>(_Unwind_GetIP(context));
986 // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
987 if (IsCriticalAddress(pc)) {
988 info->isCriticalAbort = true;
989 // We just want to stop the walk, so any error code will do. Using
990 // _URC_NORMAL_STOP would probably be the most accurate, but it is not
991 // defined on Android for ARM.
992 return _URC_FOREIGN_EXCEPTION_CAUGHT;
993 }
994 if (--info->skip < 0) {
995 info->numFrames++;
996 (*info->callback)(info->numFrames, pc, nullptr, info->closure);
997 if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
998 // Again, any error code that stops the walk will do.
999 return _URC_FOREIGN_EXCEPTION_CAUGHT;
1000 }
1001 }
1002 return _URC_NO_REASON;
1003 }
1004
1005 MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure,uintptr_t aThread,void * aPlatformData)1006 MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
1007 uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
1008 void* aPlatformData)
1009 {
1010 MOZ_ASSERT(!aThread);
1011 MOZ_ASSERT(!aPlatformData);
1012 StackWalkInitCriticalAddress();
1013 unwind_info info;
1014 info.callback = aCallback;
1015 info.skip = aSkipFrames + 1;
1016 info.maxFrames = aMaxFrames;
1017 info.numFrames = 0;
1018 info.isCriticalAbort = false;
1019 info.closure = aClosure;
1020
1021 (void)_Unwind_Backtrace(unwind_callback, &info);
1022
1023 // We ignore the return value from _Unwind_Backtrace and instead determine
1024 // the outcome from |info|. There are two main reasons for this:
1025 // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
1026 // _URC_FAILURE. See
1027 // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
1028 // - If aMaxFrames != 0, we want to stop early, and the only way to do that
1029 // is to make unwind_callback return something other than _URC_NO_REASON,
1030 // which causes _Unwind_Backtrace to return a non-success code.
1031 if (info.isCriticalAbort) {
1032 return false;
1033 }
1034 return info.numFrames != 0;
1035 }
1036
1037 #endif
1038
1039 bool MFBT_API
MozDescribeCodeAddress(void * aPC,MozCodeAddressDetails * aDetails)1040 MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
1041 {
1042 aDetails->library[0] = '\0';
1043 aDetails->loffset = 0;
1044 aDetails->filename[0] = '\0';
1045 aDetails->lineno = 0;
1046 aDetails->function[0] = '\0';
1047 aDetails->foffset = 0;
1048
1049 Dl_info info;
1050 int ok = dladdr(aPC, &info);
1051 if (!ok) {
1052 return true;
1053 }
1054
1055 strncpy(aDetails->library, info.dli_fname, sizeof(aDetails->library));
1056 aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
1057 aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
1058
1059 const char* symbol = info.dli_sname;
1060 if (!symbol || symbol[0] == '\0') {
1061 return true;
1062 }
1063
1064 DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function));
1065
1066 if (aDetails->function[0] == '\0') {
1067 // Just use the mangled symbol if demangling failed.
1068 strncpy(aDetails->function, symbol, sizeof(aDetails->function));
1069 aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
1070 }
1071
1072 aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
1073 return true;
1074 }
1075
1076 #else // unsupported platform.
1077
1078 MFBT_API bool
MozStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure,uintptr_t aThread,void * aPlatformData)1079 MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
1080 uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
1081 void* aPlatformData)
1082 {
1083 MOZ_ASSERT(!aThread);
1084 MOZ_ASSERT(!aPlatformData);
1085 return false;
1086 }
1087
1088 MFBT_API bool
MozDescribeCodeAddress(void * aPC,MozCodeAddressDetails * aDetails)1089 MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
1090 {
1091 aDetails->library[0] = '\0';
1092 aDetails->loffset = 0;
1093 aDetails->filename[0] = '\0';
1094 aDetails->lineno = 0;
1095 aDetails->function[0] = '\0';
1096 aDetails->foffset = 0;
1097 return false;
1098 }
1099
1100 #endif
1101
1102 #if defined(XP_WIN) || defined (XP_MACOSX) || defined (XP_LINUX)
1103 namespace mozilla {
1104 bool
FramePointerStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure,void ** bp,void * aStackEnd)1105 FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
1106 uint32_t aMaxFrames, void* aClosure, void** bp,
1107 void* aStackEnd)
1108 {
1109 // Stack walking code courtesy Kipp's "leaky".
1110
1111 int32_t skip = aSkipFrames;
1112 uint32_t numFrames = 0;
1113 while (bp) {
1114 void** next = (void**)*bp;
1115 // bp may not be a frame pointer on i386 if code was compiled with
1116 // -fomit-frame-pointer, so do some sanity checks.
1117 // (bp should be a frame pointer on ppc(64) but checking anyway may help
1118 // a little if the stack has been corrupted.)
1119 // We don't need to check against the begining of the stack because
1120 // we can assume that bp > sp
1121 if (next <= bp ||
1122 next > aStackEnd ||
1123 (uintptr_t(next) & 3)) {
1124 break;
1125 }
1126 #if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
1127 // ppc mac or powerpc64 linux
1128 void* pc = *(bp + 2);
1129 bp += 3;
1130 #else // i386 or powerpc32 linux
1131 void* pc = *(bp + 1);
1132 bp += 2;
1133 #endif
1134 if (IsCriticalAddress(pc)) {
1135 return false;
1136 }
1137 if (--skip < 0) {
1138 // Assume that the SP points to the BP of the function
1139 // it called. We can't know the exact location of the SP
1140 // but this should be sufficient for our use the SP
1141 // to order elements on the stack.
1142 numFrames++;
1143 (*aCallback)(numFrames, pc, bp, aClosure);
1144 if (aMaxFrames != 0 && numFrames == aMaxFrames) {
1145 break;
1146 }
1147 }
1148 bp = next;
1149 }
1150 return numFrames != 0;
1151 }
1152 } // namespace mozilla
1153
1154 #else
1155
1156 namespace mozilla {
1157 MFBT_API bool
FramePointerStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,void * aClosure,void ** aBp)1158 FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
1159 void* aClosure, void** aBp)
1160 {
1161 return false;
1162 }
1163 }
1164
1165 #endif
1166
1167 MFBT_API void
MozFormatCodeAddressDetails(char * aBuffer,uint32_t aBufferSize,uint32_t aFrameNumber,void * aPC,const MozCodeAddressDetails * aDetails)1168 MozFormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
1169 uint32_t aFrameNumber, void* aPC,
1170 const MozCodeAddressDetails* aDetails)
1171 {
1172 MozFormatCodeAddress(aBuffer, aBufferSize,
1173 aFrameNumber, aPC, aDetails->function,
1174 aDetails->library, aDetails->loffset,
1175 aDetails->filename, aDetails->lineno);
1176 }
1177
1178 MFBT_API void
MozFormatCodeAddress(char * aBuffer,uint32_t aBufferSize,uint32_t aFrameNumber,const void * aPC,const char * aFunction,const char * aLibrary,ptrdiff_t aLOffset,const char * aFileName,uint32_t aLineNo)1179 MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
1180 const void* aPC, const char* aFunction,
1181 const char* aLibrary, ptrdiff_t aLOffset,
1182 const char* aFileName, uint32_t aLineNo)
1183 {
1184 const char* function = aFunction && aFunction[0] ? aFunction : "???";
1185 if (aFileName && aFileName[0]) {
1186 // We have a filename and (presumably) a line number. Use them.
1187 snprintf(aBuffer, aBufferSize,
1188 "#%02u: %s (%s:%u)",
1189 aFrameNumber, function, aFileName, aLineNo);
1190 } else if (aLibrary && aLibrary[0]) {
1191 // We have no filename, but we do have a library name. Use it and the
1192 // library offset, and print them in a way that scripts like
1193 // fix_{linux,macosx}_stacks.py can easily post-process.
1194 snprintf(aBuffer, aBufferSize,
1195 "#%02u: %s[%s +0x%" PRIxPTR "]",
1196 aFrameNumber, function, aLibrary, static_cast<uintptr_t>(aLOffset));
1197 } else {
1198 // We have nothing useful to go on. (The format string is split because
1199 // '??)' is a trigraph and causes a warning, sigh.)
1200 snprintf(aBuffer, aBufferSize,
1201 "#%02u: ??? (???:???" ")",
1202 aFrameNumber);
1203 }
1204 }
1205