Bug 1513088 - Switch aarch64-windows MozStackWalk code to RtlVirtualUnwind. r=froydnj
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 19 Dec 2018 20:14:17 +0000
changeset 451475 7eb93bcd0ee33a56004853d12a0e0822dd168f10
parent 451474 0204dfb3145ffbb3b282259e1564bfddd514ff75
child 451476 c1c5fd1fed41ca11442fca2189ef7f9835472311
push id35241
push userebalazs@mozilla.com
push dateThu, 20 Dec 2018 15:24:32 +0000
treeherdermozilla-central@8e2a1751a0f5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1513088, 1515229
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1513088 - Switch aarch64-windows MozStackWalk code to RtlVirtualUnwind. r=froydnj As far as my stepping through WalkStackMain64 goes, it seems StackWalk64 doesn't work, even with more information added to the frame data it's given. Switching to the same code as for x86-64, however, works, albeit skipping too many frames, but all platforms are actually skipping too many frames, so let's ignore that for now and leave it to bug 1515229. Differential Revision: https://phabricator.services.mozilla.com/D14929
mozglue/misc/StackWalk.cpp
mozglue/misc/StackWalk_windows.h
--- a/mozglue/misc/StackWalk.cpp
+++ b/mozglue/misc/StackWalk.cpp
@@ -91,17 +91,17 @@ struct WalkStackData {
   void** sps;
   uint32_t sp_size;
   uint32_t sp_count;
   CONTEXT* context;
 };
 
 CRITICAL_SECTION gDbgHelpCS;
 
-#ifdef _M_AMD64
+#if defined(_M_AMD64) || defined(_M_ARM64)
 // Because various Win64 APIs acquire function-table locks, we need a way of
 // preventing stack walking while those APIs are being called. Otherwise, the
 // stack walker may suspend a thread holding such a lock, and deadlock when the
 // stack unwind code attempts to wait for that lock.
 //
 // We're using an atomic counter rather than a critical section because we
 // don't require mutual exclusion with the stack walker. If the stack walker
 // determines that it's safe to start unwinding the suspended thread (i.e.
@@ -141,17 +141,17 @@ MFBT_API void UnregisterJitCodeRegion(ui
   // Currently we can only handle one JIT code region at a time
   MOZ_RELEASE_ASSERT(sJitCodeRegionStart && sJitCodeRegionStart == aStart &&
                      sJitCodeRegionSize == aSize);
 
   sJitCodeRegionStart = nullptr;
   sJitCodeRegionSize = 0;
 }
 
-#endif  // _M_AMD64
+#endif  // _M_AMD64 || _M_ARM64
 
 // Routine to print an error message to standard error.
 static void PrintError(const char* aPrefix) {
   LPSTR lpMsgBuf;
   DWORD lastErr = GetLastError();
   FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
                      FORMAT_MESSAGE_IGNORE_INSERTS,
                  nullptr, lastErr,
@@ -184,78 +184,72 @@ static void WalkStackMain64(struct WalkS
       ::RtlCaptureContext(context);
     } else if (!GetThreadContext(aData->thread, context)) {
       return;
     }
   } else {
     context = aData->context;
   }
 
-#if defined(_M_IX86) || defined(_M_IA64) || defined(_M_ARM64)
+#if defined(_M_IX86) || defined(_M_IA64)
   // Setup initial stack frame to walk from.
   STACKFRAME64 frame64;
   memset(&frame64, 0, sizeof(frame64));
 #ifdef _M_IX86
   frame64.AddrPC.Offset = context->Eip;
   frame64.AddrStack.Offset = context->Esp;
   frame64.AddrFrame.Offset = context->Ebp;
 #elif defined _M_IA64
   frame64.AddrPC.Offset = context->StIIP;
   frame64.AddrStack.Offset = context->SP;
   frame64.AddrFrame.Offset = context->RsBSP;
-#elif defined _M_ARM64
-  frame64.AddrPC.Offset = context->Pc;
-  frame64.AddrStack.Offset = context->Sp;
-  frame64.AddrFrame.Offset = context->Fp;
 #endif
   frame64.AddrPC.Mode = AddrModeFlat;
   frame64.AddrStack.Mode = AddrModeFlat;
   frame64.AddrFrame.Mode = AddrModeFlat;
   frame64.AddrReturn.Mode = AddrModeFlat;
 #endif
 
-#ifdef _M_AMD64
+#if defined(_M_AMD64) || defined(_M_ARM64)
   // If there are any active suppressions, then at least one thread (we don't
   // know which) is holding a lock that can deadlock RtlVirtualUnwind. Since
   // that thread may be the one that we're trying to unwind, we can't proceed.
   //
   // But if there are no suppressions, then our target thread can't be holding
   // a lock, and it's safe to proceed. By virtue of being suspended, the target
   // thread can't acquire any new locks during the unwind process, so we only
   // need to do this check once. After that, sStackWalkSuppressions can be
   // changed by other threads while we're unwinding, and that's fine because
   // we can't deadlock with those threads.
   if (sStackWalkSuppressions) {
     return;
   }
 #endif
 
-#ifdef _M_AMD64
+#if defined(_M_AMD64) || defined(_M_ARM64)
   bool firstFrame = true;
 #endif
 
   // Skip our own stack walking frames.
   int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
 
   // Now walk the stack.
   while (true) {
     DWORD64 addr;
     DWORD64 spaddr;
 
-#if defined(_M_IX86) || defined(_M_IA64) || defined(_M_ARM64)
+#if defined(_M_IX86) || defined(_M_IA64)
     // 32-bit frame unwinding.
     // Debug routines are not threadsafe, so grab the lock.
     EnterCriticalSection(&gDbgHelpCS);
     BOOL ok = StackWalk64(
 #if defined _M_IA64
         IMAGE_FILE_MACHINE_IA64,
 #elif defined _M_IX86
         IMAGE_FILE_MACHINE_I386,
-#elif defined _M_ARM64
-        IMAGE_FILE_MACHINE_ARM64,
 #endif
         aData->process, aData->thread, &frame64, context, nullptr,
         SymFunctionTableAccess64,  // function table access routine
         SymGetModuleBase64,        // module base routine
         0);
     LeaveCriticalSection(&gDbgHelpCS);
 
     if (ok) {
@@ -268,57 +262,74 @@ static void WalkStackMain64(struct WalkS
         PrintError("WalkStack64");
       }
     }
 
     if (!ok) {
       break;
     }
 
-#elif defined(_M_AMD64)
+#elif defined(_M_AMD64) || defined(_M_ARM64)
+
+#if defined(_M_AMD64)
+    auto currentInstr = context->Rip;
+#elif defined(_M_ARM64)
+    auto currentInstr = context->Pc;
+#endif
+
     // If we reach a frame in JIT code, we don't have enough information to
     // unwind, so we have to give up.
-    if (sJitCodeRegionStart && (uint8_t*)context->Rip >= sJitCodeRegionStart &&
-        (uint8_t*)context->Rip < sJitCodeRegionStart + sJitCodeRegionSize) {
+    if (sJitCodeRegionStart && (uint8_t*)currentInstr >= sJitCodeRegionStart &&
+        (uint8_t*)currentInstr < sJitCodeRegionStart + sJitCodeRegionSize) {
       break;
     }
 
     // We must also avoid msmpeg2vdec.dll's JIT region: they don't generate
     // unwind data, so their JIT unwind callback just throws up its hands and
     // terminates the process.
     if (sMsMpegJitCodeRegionStart &&
-        (uint8_t*)context->Rip >= sMsMpegJitCodeRegionStart &&
-        (uint8_t*)context->Rip <
+        (uint8_t*)currentInstr >= sMsMpegJitCodeRegionStart &&
+        (uint8_t*)currentInstr <
             sMsMpegJitCodeRegionStart + sMsMpegJitCodeRegionSize) {
       break;
     }
 
     // 64-bit frame unwinding.
     // Try to look up unwind metadata for the current function.
     ULONG64 imageBase;
     PRUNTIME_FUNCTION runtimeFunction =
-        RtlLookupFunctionEntry(context->Rip, &imageBase, NULL);
+        RtlLookupFunctionEntry(currentInstr, &imageBase, NULL);
 
     if (runtimeFunction) {
       PVOID dummyHandlerData;
       ULONG64 dummyEstablisherFrame;
-      RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, context->Rip,
+      RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, currentInstr,
                        runtimeFunction, context, &dummyHandlerData,
                        &dummyEstablisherFrame, nullptr);
     } else if (firstFrame) {
       // Leaf functions can be unwound by hand.
+#if defined(_M_AMD64)
       context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
       context->Rsp += sizeof(void*);
+#elif defined(_M_ARM64)
+      context->Pc = *reinterpret_cast<DWORD64*>(context->Sp);
+      context->Sp += sizeof(void*);
+#endif
     } else {
       // Something went wrong.
       break;
     }
 
+#if defined(_M_AMD64)
     addr = context->Rip;
     spaddr = context->Rsp;
+#elif defined(_M_ARM64)
+    addr = context->Pc;
+    spaddr = context->Sp;
+#endif
     firstFrame = false;
 #else
 #error "unknown platform"
 #endif
 
     if (addr == 0) {
       break;
     }
--- a/mozglue/misc/StackWalk_windows.h
+++ b/mozglue/misc/StackWalk_windows.h
@@ -1,14 +1,14 @@
 #ifndef mozilla_StackWalk_windows_h
 #define mozilla_StackWalk_windows_h
 
 #include "mozilla/Types.h"
 
-#ifdef _M_AMD64
+#if defined(_M_AMD64) || defined(_M_ARM64)
 /**
  * Allow stack walkers to work around the egregious win64 dynamic lookup table
  * list API by locking around SuspendThread to avoid deadlock.
  *
  * See comment in StackWalk.cpp
  */
 struct MOZ_RAII AutoSuppressStackWalking {
   MFBT_API AutoSuppressStackWalking();