Bug 1088343 (part 1) - Get NS_StackWalk working on Win64. r=aklotz.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 08 Jan 2015 16:00:07 -0800
changeset 235992 01a2e2efa1683b39495476d533c6033c3112bb1b
parent 235991 14bb2f5eed924ce5a8a83b3a567d19f3e6c7d795
child 235993 3eb9a4e2bab0cbca1eeebba75ee0e5feaed157c5
push id384
push usermartin.thomson@gmail.com
push dateFri, 09 Jan 2015 21:26:39 +0000
reviewersaklotz
bugs1088343
milestone37.0a1
Bug 1088343 (part 1) - Get NS_StackWalk working on Win64. r=aklotz.
xpcom/base/nsStackWalk.cpp
--- a/xpcom/base/nsStackWalk.cpp
+++ b/xpcom/base/nsStackWalk.cpp
@@ -300,82 +300,70 @@ EnsureWalkThreadReady()
   ::InitializeCriticalSection(&gDbgHelpCS);
 
   return walkThreadReady = true;
 }
 
 static void
 WalkStackMain64(struct WalkStackData* aData)
 {
-  // Get the context information for the thread. That way we will
-  // know where our sp, fp, pc, etc. are and can fill in the
-  // STACKFRAME64 with the initial values.
+  // Get a context for the specified thread.
   CONTEXT context;
-  HANDLE myProcess = aData->process;
-  HANDLE myThread = aData->thread;
-  DWORD64 addr;
-  DWORD64 spaddr;
-  STACKFRAME64 frame64;
-  // skip our own stack walking frames
-  int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
-  BOOL ok;
-
-  // Get a context for the specified thread.
   if (!aData->platformData) {
     memset(&context, 0, sizeof(CONTEXT));
     context.ContextFlags = CONTEXT_FULL;
-    if (!GetThreadContext(myThread, &context)) {
+    if (!GetThreadContext(aData->thread, &context)) {
       if (aData->walkCallingThread) {
         PrintError("GetThreadContext");
       }
       return;
     }
   } else {
     context = *static_cast<CONTEXT*>(aData->platformData);
   }
 
-  // Setup initial stack frame to walk from
+#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_AMD64
-  frame64.AddrPC.Offset    = context.Rip;
-  frame64.AddrStack.Offset = context.Rsp;
-  frame64.AddrFrame.Offset = context.Rbp;
 #elif defined _M_IA64
   frame64.AddrPC.Offset    = context.StIIP;
   frame64.AddrStack.Offset = context.SP;
   frame64.AddrFrame.Offset = context.RsBSP;
-#else
-#error "Should not have compiled this code"
 #endif
   frame64.AddrPC.Mode      = AddrModeFlat;
   frame64.AddrStack.Mode   = AddrModeFlat;
   frame64.AddrFrame.Mode   = AddrModeFlat;
   frame64.AddrReturn.Mode  = AddrModeFlat;
+#endif
 
-  // Now walk the stack
-  while (1) {
+  // Skip our own stack walking frames.
+  int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
 
-    // debug routines are not threadsafe, so grab the lock.
+  // Now walk the stack.
+  while (true) {
+    DWORD64 addr;
+    DWORD64 spaddr;
+
+#if defined(_M_IX86) || defined(_M_IA64)
+    // 32-bit frame unwinding.
+    // Debug routines are not threadsafe, so grab the lock.
     EnterCriticalSection(&gDbgHelpCS);
-    ok = StackWalk64(
-#ifdef _M_AMD64
-      IMAGE_FILE_MACHINE_AMD64,
-#elif defined _M_IA64
+    BOOL ok = StackWalk64(
+#if defined _M_IA64
       IMAGE_FILE_MACHINE_IA64,
 #elif defined _M_IX86
       IMAGE_FILE_MACHINE_I386,
-#else
-#error "Should not have compiled this code"
 #endif
-      myProcess,
-      myThread,
+      aData->process,
+      aData->thread,
       &frame64,
       &context,
       nullptr,
       SymFunctionTableAccess64, // function table access routine
       SymGetModuleBase64,       // module base routine
       0
     );
     LeaveCriticalSection(&gDbgHelpCS);
@@ -386,17 +374,52 @@ WalkStackMain64(struct WalkStackData* aD
     } else {
       addr = 0;
       spaddr = 0;
       if (aData->walkCallingThread) {
         PrintError("WalkStack64");
       }
     }
 
-    if (!ok || (addr == 0)) {
+    if (!ok) {
+      break;
+    }
+
+#elif defined(_M_AMD64)
+    // 64-bit frame unwinding.
+    // Try to look up unwind metadata for the current function.
+    ULONG64 imageBase;
+    PRUNTIME_FUNCTION runtimeFunction =
+      RtlLookupFunctionEntry(context.Rip, &imageBase, NULL);
+
+    if (!runtimeFunction) {
+      // Alas, this is probably a JIT frame, for which we don't generate unwind
+      // info and so we have to give up.
+      break;
+    }
+
+    PVOID dummyHandlerData;
+    ULONG64 dummyEstablisherFrame;
+    RtlVirtualUnwind(UNW_FLAG_NHANDLER,
+                     imageBase,
+                     context.Rip,
+                     runtimeFunction,
+                     &context,
+                     &dummyHandlerData,
+                     &dummyEstablisherFrame,
+                     nullptr);
+
+    addr = context.Rip;
+    spaddr = context.Rsp;
+
+#else
+#error "unknown platform"
+#endif
+
+    if (addr == 0) {
       break;
     }
 
     if (skip-- > 0) {
       continue;
     }
 
     if (aData->pc_count < aData->pc_size) {
@@ -408,21 +431,22 @@ WalkStackMain64(struct WalkStackData* aD
       aData->sps[aData->sp_count] = (void*)spaddr;
     }
     ++aData->sp_count;
 
     if (aData->pc_max != 0 && aData->pc_count == aData->pc_max) {
       break;
     }
 
+#if defined(_M_IX86) || defined(_M_IA64)
     if (frame64.AddrReturn.Offset == 0) {
       break;
     }
+#endif
   }
-  return;
 }
 
 static unsigned int WINAPI
 WalkStackThread(void* aData)
 {
   BOOL msgRet;
   MSG msg;