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