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