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/Attributes.h"
12 #include "mozilla/IntegerPrintfMacros.h"
13 #include "mozilla/StackWalk.h"
14
15 #include <string.h>
16
17 #if defined(ANDROID) && defined(MOZ_LINKER)
18 # include "Linker.h"
19 # include <android/log.h>
20 #endif
21
22 using namespace mozilla;
23
24 // for _Unwind_Backtrace from libcxxrt or libunwind
25 // cxxabi.h from libcxxrt implicitly includes unwind.h first
26 #if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE)
27 # define _GNU_SOURCE
28 #endif
29
30 #if defined(HAVE_DLOPEN) || defined(XP_DARWIN)
31 # include <dlfcn.h>
32 #endif
33
34 #if (defined(XP_DARWIN) && \
35 (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
36 # define MOZ_STACKWALK_SUPPORTS_MACOSX 1
37 #else
38 # define MOZ_STACKWALK_SUPPORTS_MACOSX 0
39 #endif
40
41 #if (defined(linux) && \
42 ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
43 defined(HAVE__UNWIND_BACKTRACE)))
44 # define MOZ_STACKWALK_SUPPORTS_LINUX 1
45 #else
46 # define MOZ_STACKWALK_SUPPORTS_LINUX 0
47 #endif
48
49 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
50 # define HAVE___LIBC_STACK_END 1
51 #else
52 # define HAVE___LIBC_STACK_END 0
53 #endif
54
55 #if HAVE___LIBC_STACK_END
56 extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so
57 #endif
58
59 #ifdef ANDROID
60 # include <algorithm>
61 # include <unistd.h>
62 # include <pthread.h>
63 #endif
64
65 #if MOZ_STACKWALK_SUPPORTS_WINDOWS
66
67 # include <windows.h>
68 # include <process.h>
69 # include <stdio.h>
70 # include <malloc.h>
71 # include "mozilla/ArrayUtils.h"
72 # include "mozilla/Atomics.h"
73 # include "mozilla/StackWalk_windows.h"
74 # include "mozilla/WindowsVersion.h"
75
76 # include <imagehlp.h>
77 // We need a way to know if we are building for WXP (or later), as if we are, we
78 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
79 // A value of 9 indicates we want to use the new APIs.
80 # if API_VERSION_NUMBER < 9
81 # error Too old imagehlp.h
82 # endif
83
84 struct WalkStackData {
85 // Are we walking the stack of the calling thread? Note that we need to avoid
86 // calling fprintf and friends if this is false, in order to avoid deadlocks.
87 bool walkCallingThread;
88 uint32_t skipFrames;
89 HANDLE thread;
90 HANDLE process;
91 HANDLE eventStart;
92 HANDLE eventEnd;
93 void** pcs;
94 uint32_t pc_size;
95 uint32_t pc_count;
96 uint32_t pc_max;
97 void** sps;
98 uint32_t sp_size;
99 uint32_t sp_count;
100 CONTEXT* context;
101 };
102
103 CRITICAL_SECTION gDbgHelpCS;
104
105 # if defined(_M_AMD64) || defined(_M_ARM64)
106 // Because various Win64 APIs acquire function-table locks, we need a way of
107 // preventing stack walking while those APIs are being called. Otherwise, the
108 // stack walker may suspend a thread holding such a lock, and deadlock when the
109 // stack unwind code attempts to wait for that lock.
110 //
111 // We're using an atomic counter rather than a critical section because we
112 // don't require mutual exclusion with the stack walker. If the stack walker
113 // determines that it's safe to start unwinding the suspended thread (i.e.
114 // there are no suppressions when the unwind begins), then it's safe to
115 // continue unwinding that thread even if other threads request suppressions
116 // in the meantime, because we can't deadlock with those other threads.
117 //
118 // XXX: This global variable is a larger-than-necessary hammer. A more scoped
119 // solution would be to maintain a counter per thread, but then it would be
120 // more difficult for WalkStackMain64 to read the suspended thread's counter.
121 static Atomic<size_t> sStackWalkSuppressions;
122
SuppressStackWalking()123 void SuppressStackWalking() { ++sStackWalkSuppressions; }
124
DesuppressStackWalking()125 void DesuppressStackWalking() { --sStackWalkSuppressions; }
126
127 MFBT_API
AutoSuppressStackWalking()128 AutoSuppressStackWalking::AutoSuppressStackWalking() { SuppressStackWalking(); }
129
130 MFBT_API
~AutoSuppressStackWalking()131 AutoSuppressStackWalking::~AutoSuppressStackWalking() {
132 DesuppressStackWalking();
133 }
134
135 static uint8_t* sJitCodeRegionStart;
136 static size_t sJitCodeRegionSize;
137 uint8_t* sMsMpegJitCodeRegionStart;
138 size_t sMsMpegJitCodeRegionSize;
139
RegisterJitCodeRegion(uint8_t * aStart,size_t aSize)140 MFBT_API void RegisterJitCodeRegion(uint8_t* aStart, size_t aSize) {
141 // Currently we can only handle one JIT code region at a time
142 MOZ_RELEASE_ASSERT(!sJitCodeRegionStart);
143
144 sJitCodeRegionStart = aStart;
145 sJitCodeRegionSize = aSize;
146 }
147
UnregisterJitCodeRegion(uint8_t * aStart,size_t aSize)148 MFBT_API void UnregisterJitCodeRegion(uint8_t* aStart, size_t aSize) {
149 // Currently we can only handle one JIT code region at a time
150 MOZ_RELEASE_ASSERT(sJitCodeRegionStart && sJitCodeRegionStart == aStart &&
151 sJitCodeRegionSize == aSize);
152
153 sJitCodeRegionStart = nullptr;
154 sJitCodeRegionSize = 0;
155 }
156
157 # endif // _M_AMD64 || _M_ARM64
158
159 // Routine to print an error message to standard error.
PrintError(const char * aPrefix)160 static void PrintError(const char* aPrefix) {
161 LPSTR lpMsgBuf;
162 DWORD lastErr = GetLastError();
163 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
164 FORMAT_MESSAGE_IGNORE_INSERTS,
165 nullptr, lastErr,
166 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
167 (LPSTR)&lpMsgBuf, 0, nullptr);
168 fprintf(stderr, "### ERROR: %s: %s", aPrefix,
169 lpMsgBuf ? lpMsgBuf : "(null)\n");
170 fflush(stderr);
171 LocalFree(lpMsgBuf);
172 }
173
InitializeDbgHelpCriticalSection()174 static void InitializeDbgHelpCriticalSection() {
175 static bool initialized = false;
176 if (initialized) {
177 return;
178 }
179 ::InitializeCriticalSection(&gDbgHelpCS);
180 initialized = true;
181 }
182
WalkStackMain64(struct WalkStackData * aData)183 static void WalkStackMain64(struct WalkStackData* aData) {
184 // Get a context for the specified thread.
185 CONTEXT context_buf;
186 CONTEXT* context;
187 if (!aData->context) {
188 context = &context_buf;
189 memset(context, 0, sizeof(CONTEXT));
190 context->ContextFlags = CONTEXT_FULL;
191 if (aData->walkCallingThread) {
192 ::RtlCaptureContext(context);
193 } else if (!GetThreadContext(aData->thread, context)) {
194 return;
195 }
196 } else {
197 context = aData->context;
198 }
199
200 # if defined(_M_IX86) || defined(_M_IA64)
201 // Setup initial stack frame to walk from.
202 STACKFRAME64 frame64;
203 memset(&frame64, 0, sizeof(frame64));
204 # ifdef _M_IX86
205 frame64.AddrPC.Offset = context->Eip;
206 frame64.AddrStack.Offset = context->Esp;
207 frame64.AddrFrame.Offset = context->Ebp;
208 # elif defined _M_IA64
209 frame64.AddrPC.Offset = context->StIIP;
210 frame64.AddrStack.Offset = context->SP;
211 frame64.AddrFrame.Offset = context->RsBSP;
212 # endif
213 frame64.AddrPC.Mode = AddrModeFlat;
214 frame64.AddrStack.Mode = AddrModeFlat;
215 frame64.AddrFrame.Mode = AddrModeFlat;
216 frame64.AddrReturn.Mode = AddrModeFlat;
217 # endif
218
219 # if defined(_M_AMD64) || defined(_M_ARM64)
220 // If there are any active suppressions, then at least one thread (we don't
221 // know which) is holding a lock that can deadlock RtlVirtualUnwind. Since
222 // that thread may be the one that we're trying to unwind, we can't proceed.
223 //
224 // But if there are no suppressions, then our target thread can't be holding
225 // a lock, and it's safe to proceed. By virtue of being suspended, the target
226 // thread can't acquire any new locks during the unwind process, so we only
227 // need to do this check once. After that, sStackWalkSuppressions can be
228 // changed by other threads while we're unwinding, and that's fine because
229 // we can't deadlock with those threads.
230 if (sStackWalkSuppressions) {
231 return;
232 }
233 # endif
234
235 # if defined(_M_AMD64) || defined(_M_ARM64)
236 bool firstFrame = true;
237 # endif
238
239 // Skip our own stack walking frames.
240 int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
241
242 // Now walk the stack.
243 while (true) {
244 DWORD64 addr;
245 DWORD64 spaddr;
246
247 # if defined(_M_IX86) || defined(_M_IA64)
248 // 32-bit frame unwinding.
249 // Debug routines are not threadsafe, so grab the lock.
250 EnterCriticalSection(&gDbgHelpCS);
251 BOOL ok = StackWalk64(
252 # if defined _M_IA64
253 IMAGE_FILE_MACHINE_IA64,
254 # elif defined _M_IX86
255 IMAGE_FILE_MACHINE_I386,
256 # endif
257 aData->process, aData->thread, &frame64, context, nullptr,
258 SymFunctionTableAccess64, // function table access routine
259 SymGetModuleBase64, // module base routine
260 0);
261 LeaveCriticalSection(&gDbgHelpCS);
262
263 if (ok) {
264 addr = frame64.AddrPC.Offset;
265 spaddr = frame64.AddrStack.Offset;
266 } else {
267 addr = 0;
268 spaddr = 0;
269 if (aData->walkCallingThread) {
270 PrintError("WalkStack64");
271 }
272 }
273
274 if (!ok) {
275 break;
276 }
277
278 # elif defined(_M_AMD64) || defined(_M_ARM64)
279
280 # if defined(_M_AMD64)
281 auto currentInstr = context->Rip;
282 # elif defined(_M_ARM64)
283 auto currentInstr = context->Pc;
284 # endif
285
286 // If we reach a frame in JIT code, we don't have enough information to
287 // unwind, so we have to give up.
288 if (sJitCodeRegionStart && (uint8_t*)currentInstr >= sJitCodeRegionStart &&
289 (uint8_t*)currentInstr < sJitCodeRegionStart + sJitCodeRegionSize) {
290 break;
291 }
292
293 // We must also avoid msmpeg2vdec.dll's JIT region: they don't generate
294 // unwind data, so their JIT unwind callback just throws up its hands and
295 // terminates the process.
296 if (sMsMpegJitCodeRegionStart &&
297 (uint8_t*)currentInstr >= sMsMpegJitCodeRegionStart &&
298 (uint8_t*)currentInstr <
299 sMsMpegJitCodeRegionStart + sMsMpegJitCodeRegionSize) {
300 break;
301 }
302
303 // 64-bit frame unwinding.
304 // Try to look up unwind metadata for the current function.
305 ULONG64 imageBase;
306 PRUNTIME_FUNCTION runtimeFunction =
307 RtlLookupFunctionEntry(currentInstr, &imageBase, NULL);
308
309 if (runtimeFunction) {
310 PVOID dummyHandlerData;
311 ULONG64 dummyEstablisherFrame;
312 RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, currentInstr,
313 runtimeFunction, context, &dummyHandlerData,
314 &dummyEstablisherFrame, nullptr);
315 } else if (firstFrame) {
316 // Leaf functions can be unwound by hand.
317 # if defined(_M_AMD64)
318 context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
319 context->Rsp += sizeof(void*);
320 # elif defined(_M_ARM64)
321 context->Pc = *reinterpret_cast<DWORD64*>(context->Sp);
322 context->Sp += sizeof(void*);
323 # endif
324 } else {
325 // Something went wrong.
326 break;
327 }
328
329 # if defined(_M_AMD64)
330 addr = context->Rip;
331 spaddr = context->Rsp;
332 # elif defined(_M_ARM64)
333 addr = context->Pc;
334 spaddr = context->Sp;
335 # endif
336 firstFrame = false;
337 # else
338 # error "unknown platform"
339 # endif
340
341 if (addr == 0) {
342 break;
343 }
344
345 if (skip-- > 0) {
346 continue;
347 }
348
349 if (aData->pc_count < aData->pc_size) {
350 aData->pcs[aData->pc_count] = (void*)addr;
351 }
352 ++aData->pc_count;
353
354 if (aData->sp_count < aData->sp_size) {
355 aData->sps[aData->sp_count] = (void*)spaddr;
356 }
357 ++aData->sp_count;
358
359 if (aData->pc_max != 0 && aData->pc_count == aData->pc_max) {
360 break;
361 }
362
363 # if defined(_M_IX86) || defined(_M_IA64)
364 if (frame64.AddrReturn.Offset == 0) {
365 break;
366 }
367 # endif
368 }
369 }
370
371 /**
372 * Walk the stack, translating PC's found into strings and recording the
373 * chain in aBuffer. For this to work properly, the DLLs must be rebased
374 * so that the address in the file agrees with the address in memory.
375 * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
376 * whose in memory address doesn't match its in-file address.
377 */
378
MozStackWalkThread(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure,HANDLE aThread,CONTEXT * aContext)379 MFBT_API void MozStackWalkThread(MozWalkStackCallback aCallback,
380 uint32_t aSkipFrames, uint32_t aMaxFrames,
381 void* aClosure, HANDLE aThread,
382 CONTEXT* aContext) {
383 struct WalkStackData data;
384
385 InitializeDbgHelpCriticalSection();
386
387 HANDLE targetThread = aThread;
388 if (!aThread) {
389 targetThread = ::GetCurrentThread();
390 data.walkCallingThread = true;
391 } else {
392 DWORD threadId = ::GetThreadId(aThread);
393 DWORD currentThreadId = ::GetCurrentThreadId();
394 data.walkCallingThread = (threadId == currentThreadId);
395 }
396
397 data.skipFrames = aSkipFrames;
398 data.thread = targetThread;
399 data.process = ::GetCurrentProcess();
400 void* local_pcs[1024];
401 data.pcs = local_pcs;
402 data.pc_count = 0;
403 data.pc_size = ArrayLength(local_pcs);
404 data.pc_max = aMaxFrames;
405 void* local_sps[1024];
406 data.sps = local_sps;
407 data.sp_count = 0;
408 data.sp_size = ArrayLength(local_sps);
409 data.context = aContext;
410
411 WalkStackMain64(&data);
412
413 if (data.pc_count > data.pc_size) {
414 data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
415 data.pc_size = data.pc_count;
416 data.pc_count = 0;
417 data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
418 data.sp_size = data.sp_count;
419 data.sp_count = 0;
420 WalkStackMain64(&data);
421 }
422
423 for (uint32_t i = 0; i < data.pc_count; ++i) {
424 (*aCallback)(i + 1, data.pcs[i], data.sps[i], aClosure);
425 }
426 }
427
MozStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure)428 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
429 uint32_t aMaxFrames, void* aClosure) {
430 MozStackWalkThread(aCallback, aSkipFrames, aMaxFrames, aClosure, nullptr,
431 nullptr);
432 }
433
callbackEspecial64(PCSTR aModuleName,DWORD64 aModuleBase,ULONG aModuleSize,PVOID aUserContext)434 static BOOL CALLBACK callbackEspecial64(PCSTR aModuleName, DWORD64 aModuleBase,
435 ULONG aModuleSize, PVOID aUserContext) {
436 BOOL retval = TRUE;
437 DWORD64 addr = *(DWORD64*)aUserContext;
438
439 /*
440 * You'll want to control this if we are running on an
441 * architecture where the addresses go the other direction.
442 * Not sure this is even a realistic consideration.
443 */
444 const BOOL addressIncreases = TRUE;
445
446 /*
447 * If it falls in side the known range, load the symbols.
448 */
449 if (addressIncreases
450 ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize))
451 : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))) {
452 retval = !!SymLoadModule64(GetCurrentProcess(), nullptr, (PSTR)aModuleName,
453 nullptr, aModuleBase, aModuleSize);
454 if (!retval) {
455 PrintError("SymLoadModule64");
456 }
457 }
458
459 return retval;
460 }
461
462 /*
463 * SymGetModuleInfoEspecial
464 *
465 * Attempt to determine the module information.
466 * Bug 112196 says this DLL may not have been loaded at the time
467 * SymInitialize was called, and thus the module information
468 * and symbol information is not available.
469 * This code rectifies that problem.
470 */
471
472 // New members were added to IMAGEHLP_MODULE64 (that show up in the
473 // Platform SDK that ships with VC8, but not the Platform SDK that ships
474 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
475 // use them, and it's useful to be able to function correctly with the
476 // older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
477 // version 5.1.) Since Platform SDK version need not correspond to
478 // compiler version, and the version number in debughlp.h was NOT bumped
479 // when these changes were made, ifdef based on a constant that was
480 // added between these versions.
481 # ifdef SSRVOPT_SETCONTEXT
482 # define NS_IMAGEHLP_MODULE64_SIZE \
483 (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / \
484 sizeof(DWORD64)) * \
485 sizeof(DWORD64))
486 # else
487 # define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
488 # endif
489
SymGetModuleInfoEspecial64(HANDLE aProcess,DWORD64 aAddr,PIMAGEHLP_MODULE64 aModuleInfo,PIMAGEHLP_LINE64 aLineInfo)490 BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr,
491 PIMAGEHLP_MODULE64 aModuleInfo,
492 PIMAGEHLP_LINE64 aLineInfo) {
493 BOOL retval = FALSE;
494
495 /*
496 * Init the vars if we have em.
497 */
498 aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE;
499 if (aLineInfo) {
500 aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
501 }
502
503 /*
504 * Give it a go.
505 * It may already be loaded.
506 */
507 retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
508 if (retval == FALSE) {
509 /*
510 * Not loaded, here's the magic.
511 * Go through all the modules.
512 */
513 // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
514 // constness of the first parameter of
515 // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
516 // non-const to const over time). See bug 391848 and bug
517 // 415426.
518 BOOL enumRes = EnumerateLoadedModules64(
519 aProcess, (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64,
520 (PVOID)&aAddr);
521 if (enumRes != FALSE) {
522 /*
523 * One final go.
524 * If it fails, then well, we have other problems.
525 */
526 retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
527 }
528 }
529
530 /*
531 * If we got module info, we may attempt line info as well.
532 * We will not report failure if this does not work.
533 */
534 if (retval != FALSE && aLineInfo) {
535 DWORD displacement = 0;
536 BOOL lineRes = FALSE;
537 lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo);
538 if (!lineRes) {
539 // Clear out aLineInfo to indicate that it's not valid
540 memset(aLineInfo, 0, sizeof(*aLineInfo));
541 }
542 }
543
544 return retval;
545 }
546
EnsureSymInitialized()547 static bool EnsureSymInitialized() {
548 static bool gInitialized = false;
549 bool retStat;
550
551 if (gInitialized) {
552 return gInitialized;
553 }
554
555 InitializeDbgHelpCriticalSection();
556
557 SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
558 retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
559 if (!retStat) {
560 PrintError("SymInitialize");
561 }
562
563 gInitialized = retStat;
564 /* XXX At some point we need to arrange to call SymCleanup */
565
566 return retStat;
567 }
568
MozDescribeCodeAddress(void * aPC,MozCodeAddressDetails * aDetails)569 MFBT_API bool MozDescribeCodeAddress(void* aPC,
570 MozCodeAddressDetails* aDetails) {
571 aDetails->library[0] = '\0';
572 aDetails->loffset = 0;
573 aDetails->filename[0] = '\0';
574 aDetails->lineno = 0;
575 aDetails->function[0] = '\0';
576 aDetails->foffset = 0;
577
578 if (!EnsureSymInitialized()) {
579 return false;
580 }
581
582 HANDLE myProcess = ::GetCurrentProcess();
583 BOOL ok;
584
585 // debug routines are not threadsafe, so grab the lock.
586 EnterCriticalSection(&gDbgHelpCS);
587
588 //
589 // Attempt to load module info before we attempt to resolve the symbol.
590 // This just makes sure we get good info if available.
591 //
592
593 DWORD64 addr = (DWORD64)aPC;
594 IMAGEHLP_MODULE64 modInfo;
595 IMAGEHLP_LINE64 lineInfo;
596 BOOL modInfoRes;
597 modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo);
598
599 if (modInfoRes) {
600 strncpy(aDetails->library, modInfo.LoadedImageName,
601 sizeof(aDetails->library));
602 aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
603 aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage;
604
605 if (lineInfo.FileName) {
606 strncpy(aDetails->filename, lineInfo.FileName,
607 sizeof(aDetails->filename));
608 aDetails->filename[mozilla::ArrayLength(aDetails->filename) - 1] = '\0';
609 aDetails->lineno = lineInfo.LineNumber;
610 }
611 }
612
613 ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) +
614 sizeof(ULONG64) - 1) /
615 sizeof(ULONG64)];
616 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
617 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
618 pSymbol->MaxNameLen = MAX_SYM_NAME;
619
620 DWORD64 displacement;
621 ok = SymFromAddr(myProcess, addr, &displacement, pSymbol);
622
623 if (ok) {
624 strncpy(aDetails->function, pSymbol->Name, sizeof(aDetails->function));
625 aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
626 aDetails->foffset = static_cast<ptrdiff_t>(displacement);
627 }
628
629 LeaveCriticalSection(&gDbgHelpCS); // release our lock
630 return true;
631 }
632
633 // i386 or PPC Linux stackwalking code
634 #elif HAVE_DLADDR && \
635 (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || \
636 MOZ_STACKWALK_SUPPORTS_MACOSX)
637
638 # include <stdlib.h>
639 # include <string.h>
640 # include <stdio.h>
641
642 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
643 // if __USE_GNU is defined. I suppose its some kind of standards
644 // adherence thing.
645 //
646 # if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
647 # define __USE_GNU
648 # endif
649
650 // This thing is exported by libstdc++
651 // Yes, this is a gcc only hack
652 # if defined(MOZ_DEMANGLE_SYMBOLS)
653 # include <cxxabi.h>
654 # endif // MOZ_DEMANGLE_SYMBOLS
655
656 namespace mozilla {
657
DemangleSymbol(const char * aSymbol,char * aBuffer,int aBufLen)658 void DemangleSymbol(const char* aSymbol, char* aBuffer, int aBufLen) {
659 aBuffer[0] = '\0';
660
661 # if defined(MOZ_DEMANGLE_SYMBOLS)
662 /* See demangle.h in the gcc source for the voodoo */
663 char* demangled = abi::__cxa_demangle(aSymbol, 0, 0, 0);
664
665 if (demangled) {
666 strncpy(aBuffer, demangled, aBufLen);
667 aBuffer[aBufLen - 1] = '\0';
668 free(demangled);
669 }
670 # endif // MOZ_DEMANGLE_SYMBOLS
671 }
672
673 } // namespace mozilla
674
675 // {x86, ppc} x {Linux, Mac} stackwalking code.
676 # if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
677 (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
678
MozStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure)679 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
680 uint32_t aMaxFrames, void* aClosure) {
681 // Get the frame pointer
682 void** bp = (void**)__builtin_frame_address(0);
683
684 void* stackEnd;
685 # if HAVE___LIBC_STACK_END
686 stackEnd = __libc_stack_end;
687 # elif defined(XP_DARWIN)
688 stackEnd = pthread_get_stackaddr_np(pthread_self());
689 # elif defined(ANDROID)
690 pthread_attr_t sattr;
691 pthread_attr_init(&sattr);
692 pthread_getattr_np(pthread_self(), &sattr);
693 void* stackBase = stackEnd = nullptr;
694 size_t stackSize = 0;
695 if (gettid() != getpid()) {
696 // bionic's pthread_attr_getstack doesn't tell the truth for the main
697 // thread (see bug 846670). So don't use it for the main thread.
698 if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) {
699 stackEnd = static_cast<char*>(stackBase) + stackSize;
700 } else {
701 stackEnd = nullptr;
702 }
703 }
704 if (!stackEnd) {
705 // So consider the current frame pointer + an arbitrary size of 8MB
706 // (modulo overflow ; not really arbitrary as it's the default stack
707 // size for the main thread) if pthread_attr_getstack failed for
708 // some reason (or was skipped).
709 static const uintptr_t kMaxStackSize = 8 * 1024 * 1024;
710 uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize;
711 uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp));
712 stackEnd = reinterpret_cast<void*>(stackStart + kMaxStackSize);
713 }
714 # else
715 # error Unsupported configuration
716 # endif
717 FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames, aClosure, bp,
718 stackEnd);
719 }
720
721 # elif defined(HAVE__UNWIND_BACKTRACE)
722
723 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
724 # include <unwind.h>
725
726 struct unwind_info {
727 MozWalkStackCallback callback;
728 int skip;
729 int maxFrames;
730 int numFrames;
731 void* closure;
732 };
733
unwind_callback(struct _Unwind_Context * context,void * closure)734 static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context,
735 void* closure) {
736 unwind_info* info = static_cast<unwind_info*>(closure);
737 void* pc = reinterpret_cast<void*>(_Unwind_GetIP(context));
738 // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
739 if (--info->skip < 0) {
740 info->numFrames++;
741 (*info->callback)(info->numFrames, pc, nullptr, info->closure);
742 if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
743 // Again, any error code that stops the walk will do.
744 return _URC_FOREIGN_EXCEPTION_CAUGHT;
745 }
746 }
747 return _URC_NO_REASON;
748 }
749
MozStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure)750 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
751 uint32_t aMaxFrames, void* aClosure) {
752 unwind_info info;
753 info.callback = aCallback;
754 info.skip = aSkipFrames + 1;
755 info.maxFrames = aMaxFrames;
756 info.numFrames = 0;
757 info.closure = aClosure;
758
759 // We ignore the return value from _Unwind_Backtrace. There are three main
760 // reasons for this.
761 // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
762 // _URC_FAILURE. See
763 // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
764 // - If aMaxFrames != 0, we want to stop early, and the only way to do that
765 // is to make unwind_callback return something other than _URC_NO_REASON,
766 // which causes _Unwind_Backtrace to return a non-success code.
767 // - MozStackWalk doesn't have a return value anyway.
768 (void)_Unwind_Backtrace(unwind_callback, &info);
769 }
770
771 # endif
772
MozDescribeCodeAddress(void * aPC,MozCodeAddressDetails * aDetails)773 bool MFBT_API MozDescribeCodeAddress(void* aPC,
774 MozCodeAddressDetails* 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;
781
782 Dl_info info;
783
784 # if defined(ANDROID) && defined(MOZ_LINKER)
785 int ok = __wrap_dladdr(aPC, &info);
786 # else
787 int ok = dladdr(aPC, &info);
788 # endif
789
790 if (!ok) {
791 return true;
792 }
793
794 strncpy(aDetails->library, info.dli_fname, sizeof(aDetails->library));
795 aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
796 aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
797
798 # if !defined(XP_FREEBSD)
799 // On FreeBSD, dli_sname is unusably bad, it often returns things like
800 // 'gtk_xtbin_new' or 'XRE_GetBootstrap' instead of long C++ symbols. Just let
801 // GetFunction do the lookup directly in the ELF image.
802
803 const char* symbol = info.dli_sname;
804 if (!symbol || symbol[0] == '\0') {
805 return true;
806 }
807
808 DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function));
809
810 if (aDetails->function[0] == '\0') {
811 // Just use the mangled symbol if demangling failed.
812 strncpy(aDetails->function, symbol, sizeof(aDetails->function));
813 aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
814 }
815
816 aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
817 # endif
818
819 return true;
820 }
821
822 #else // unsupported platform.
823
MozStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure)824 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
825 uint32_t aMaxFrames, void* aClosure) {}
826
MozDescribeCodeAddress(void * aPC,MozCodeAddressDetails * aDetails)827 MFBT_API bool MozDescribeCodeAddress(void* aPC,
828 MozCodeAddressDetails* aDetails) {
829 aDetails->library[0] = '\0';
830 aDetails->loffset = 0;
831 aDetails->filename[0] = '\0';
832 aDetails->lineno = 0;
833 aDetails->function[0] = '\0';
834 aDetails->foffset = 0;
835 return false;
836 }
837
838 #endif
839
840 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
841 namespace mozilla {
842 MOZ_ASAN_BLACKLIST
FramePointerStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure,void ** aBp,void * aStackEnd)843 void FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
844 uint32_t aMaxFrames, void* aClosure, void** aBp,
845 void* aStackEnd) {
846 // Stack walking code courtesy Kipp's "leaky".
847
848 int32_t skip = aSkipFrames;
849 uint32_t numFrames = 0;
850 while (aBp) {
851 void** next = (void**)*aBp;
852 // aBp may not be a frame pointer on i386 if code was compiled with
853 // -fomit-frame-pointer, so do some sanity checks.
854 // (aBp should be a frame pointer on ppc(64) but checking anyway may help
855 // a little if the stack has been corrupted.)
856 // We don't need to check against the begining of the stack because
857 // we can assume that aBp > sp
858 if (next <= aBp || next >= aStackEnd || (uintptr_t(next) & 3)) {
859 break;
860 }
861 # if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
862 // ppc mac or powerpc64 linux
863 void* pc = *(aBp + 2);
864 aBp += 3;
865 # else // i386 or powerpc32 linux
866 void* pc = *(aBp + 1);
867 aBp += 2;
868 # endif
869 if (--skip < 0) {
870 // Assume that the SP points to the BP of the function
871 // it called. We can't know the exact location of the SP
872 // but this should be sufficient for our use the SP
873 // to order elements on the stack.
874 numFrames++;
875 (*aCallback)(numFrames, pc, aBp, aClosure);
876 if (aMaxFrames != 0 && numFrames == aMaxFrames) {
877 break;
878 }
879 }
880 aBp = next;
881 }
882 }
883 } // namespace mozilla
884
885 #else
886
887 namespace mozilla {
FramePointerStackWalk(MozWalkStackCallback aCallback,uint32_t aSkipFrames,uint32_t aMaxFrames,void * aClosure,void ** aBp,void * aStackEnd)888 MFBT_API void FramePointerStackWalk(MozWalkStackCallback aCallback,
889 uint32_t aSkipFrames, uint32_t aMaxFrames,
890 void* aClosure, void** aBp,
891 void* aStackEnd) {}
892 } // namespace mozilla
893
894 #endif
895
MozFormatCodeAddressDetails(char * aBuffer,uint32_t aBufferSize,uint32_t aFrameNumber,void * aPC,const MozCodeAddressDetails * aDetails)896 MFBT_API void MozFormatCodeAddressDetails(
897 char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber, void* aPC,
898 const MozCodeAddressDetails* aDetails) {
899 MozFormatCodeAddress(aBuffer, aBufferSize, aFrameNumber, aPC,
900 aDetails->function, aDetails->library, aDetails->loffset,
901 aDetails->filename, aDetails->lineno);
902 }
903
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)904 MFBT_API void MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize,
905 uint32_t aFrameNumber, const void* aPC,
906 const char* aFunction, const char* aLibrary,
907 ptrdiff_t aLOffset, const char* aFileName,
908 uint32_t aLineNo) {
909 const char* function = aFunction && aFunction[0] ? aFunction : "???";
910 if (aFileName && aFileName[0]) {
911 // We have a filename and (presumably) a line number. Use them.
912 snprintf(aBuffer, aBufferSize, "#%02u: %s (%s:%u)", aFrameNumber, function,
913 aFileName, aLineNo);
914 } else if (aLibrary && aLibrary[0]) {
915 // We have no filename, but we do have a library name. Use it and the
916 // library offset, and print them in a way that `fix_stacks.py` can
917 // post-process.
918 snprintf(aBuffer, aBufferSize, "#%02u: %s[%s +0x%" PRIxPTR "]",
919 aFrameNumber, function, aLibrary,
920 static_cast<uintptr_t>(aLOffset));
921 } else {
922 // We have nothing useful to go on. (The format string is split because
923 // '??)' is a trigraph and causes a warning, sigh.)
924 snprintf(aBuffer, aBufferSize,
925 "#%02u: ??? (???:???"
926 ")",
927 aFrameNumber);
928 }
929 }
930