Implement out of context stack unwinder (#13302)
[platform/upstream/coreclr.git] / src / ToolBox / SOS / Strike / strike.cpp
index 7e01635..f9d02c5 100644 (file)
@@ -331,7 +331,7 @@ DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
 AMD64_CONTEXT g_X64FrameContexts[MAX_STACK_FRAMES];
 
 static HRESULT
-GetContextStackTrace(PULONG pnumFrames)
+GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames)
 {
     PDEBUG_CONTROL4 debugControl4;
     HRESULT hr;
@@ -339,6 +339,14 @@ GetContextStackTrace(PULONG pnumFrames)
     // Do we have advanced capability?
     if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK)
     {
+        ULONG oldId, id;
+        g_ExtSystem->GetCurrentThreadId(&oldId);
+
+        if ((hr = g_ExtSystem->GetThreadIdBySystemId(osThreadId, &id)) != S_OK) {
+            return hr;
+        }
+        g_ExtSystem->SetCurrentThreadId(id);
+
         // GetContextStackTrace fills g_X64FrameContexts as an array of 
         // contexts packed as target architecture contexts. We cannot 
         // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since 
@@ -353,6 +361,7 @@ GetContextStackTrace(PULONG pnumFrames)
             g_targetMachine->GetContextSize(),
             pnumFrames);
 
+        g_ExtSystem->SetCurrentThreadId(oldId);
         debugControl4->Release();
     }
     return hr;
@@ -418,6 +427,9 @@ void DumpStackInternal(DumpStackFlag *pDSFlag)
     DumpStackWorker(*pDSFlag);
 }
 
+#if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
+static BOOL UnwindStackFrames(ULONG32 osThreadId);
+#endif
 
 DECLARE_API(DumpStack)
 {
@@ -431,11 +443,13 @@ DECLARE_API(DumpStack)
     DSFlag.top = 0;
     DSFlag.end = 0;
 
+    BOOL unwind = FALSE;
     BOOL dml = FALSE;
     CMDOption option[] = {
         // name, vptr, type, hasValue
         {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
         {"-n",  &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
+        {"-unwind",  &unwind, COBOOL, FALSE},
 #ifndef FEATURE_PAL
         {"/d", &dml, COBOOL, FALSE}
 #endif
@@ -459,13 +473,22 @@ DECLARE_API(DumpStack)
 
     EnableDMLHolder enabledml(dml);
 
-    ULONG id = 0;
-    g_ExtSystem->GetCurrentThreadSystemId(&id);
-    ExtOut("OS Thread Id: 0x%x ", id);
+    ULONG sysId = 0, id = 0;
+    g_ExtSystem->GetCurrentThreadSystemId(&sysId);
+    ExtOut("OS Thread Id: 0x%x ", sysId);
     g_ExtSystem->GetCurrentThreadId(&id);
     ExtOut("(%d)\n", id);
 
-    DumpStackInternal(&DSFlag);
+#if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
+    if (unwind)
+    {
+        UnwindStackFrames(sysId);
+    }
+    else
+#endif
+    {
+        DumpStackInternal(&DSFlag);
+    }
     return Status;
 }
 
@@ -12045,7 +12068,7 @@ public:
         ULONG numNativeFrames = 0;
         if (bFull)
         {
-            hr = GetContextStackTrace(&numNativeFrames);
+            hr = GetContextStackTrace(osID, &numNativeFrames);
             if (FAILED(hr))
             {
                 ExtOut("Failed to get native stack frames: %lx\n", hr);
@@ -12282,15 +12305,43 @@ public:
         
         PrintThread(osid, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
     }
-private: 
 
+    static void PrintAllThreads(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
+    {
+        HRESULT Status;
+
+        DacpThreadStoreData ThreadStore;
+        if ((Status = ThreadStore.Request(g_sos)) != S_OK)
+        {
+            ExtErr("Failed to request ThreadStore\n");
+            return;
+        }
+
+        DacpThreadData Thread;
+        CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+        while (CurThread != 0)
+        {
+            if (IsInterrupt())
+                break;
+
+            if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
+            {
+                ExtErr("Failed to request thread at %p\n", CurThread);
+                return;
+            }
+            ExtOut("OS Thread Id: 0x%x\n", Thread.osThreadId);
+            PrintThread(Thread.osThreadId, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
+            CurThread = Thread.nextThread;
+        }
+    }
+
+private: 
     static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
     {
         HRESULT hr = S_OK;
         ToRelease<IXCLRDataTask> pTask;
 
-        if ((hr = g_ExtSystem->GetCurrentThreadSystemId(&osID)) != S_OK ||
-            (hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
+        if ((hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
         {
             ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
             ExtOut("managed thread. You can run !threads to get a list of managed threads in\n");
@@ -12693,6 +12744,7 @@ DECLARE_API(ClrStack)
     BOOL dml = FALSE;
     BOOL bFull = FALSE;
     BOOL bDisplayRegVals = FALSE;
+    BOOL bAllThreads = FALSE;    
     DWORD frameToDumpVariablesFor = -1;
     StringHolder cvariableName;
     ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
@@ -12708,6 +12760,7 @@ DECLARE_API(ClrStack)
     CMDOption option[] = 
     {   // name, vptr, type, hasValue
         {"-a", &bAll, COBOOL, FALSE},
+        {"-all", &bAllThreads, COBOOL, FALSE},
         {"-p", &bParams, COBOOL, FALSE},
         {"-l", &bLocals, COBOOL, FALSE},
         {"-n", &bSuppressLines, COBOOL, FALSE},
@@ -12765,7 +12818,12 @@ DECLARE_API(ClrStack)
         return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
     }
     
-    ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
+    if (bAllThreads) {
+        ClrStackImpl::PrintAllThreads(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
+    }
+    else {
+        ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
+    }
     
     return S_OK;
 }
@@ -13297,7 +13355,7 @@ HRESULT CALLBACK ImplementEFNStackTrace(
     ULONG numFrames = 0;
     BOOL bInNative = TRUE;
 
-    Status = GetContextStackTrace(&numFrames);
+    Status = GetContextStackTrace(ThreadId, &numFrames);
     if (FAILED(Status))
     {
         goto Exit;
@@ -14597,3 +14655,86 @@ DECLARE_API(Help)
     
     return S_OK;
 }
+
+#if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
+
+static BOOL 
+ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
+{
+    ULONG fetched;
+    HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(address), buffer, size, &fetched);
+    return SUCCEEDED(hr);
+}
+
+static BOOL
+GetStackFrame(CONTEXT* context, ULONG numNativeFrames)
+{
+    KNONVOLATILE_CONTEXT_POINTERS contextPointers;
+    memset(&contextPointers, 0, sizeof(contextPointers));
+
+    ULONG64 baseAddress;
+    HRESULT hr = g_ExtSymbols->GetModuleByOffset(context->Rip, 0, NULL, &baseAddress);
+    if (FAILED(hr))
+    {
+        PDEBUG_STACK_FRAME frame = &g_Frames[0];
+        for (int i = 0; i < numNativeFrames; i++, frame++) {
+            if (frame->InstructionOffset == context->Rip)
+            {
+                if ((i + 1) >= numNativeFrames) {
+                    return FALSE;
+                }
+                memcpy(context, &(g_X64FrameContexts[i + 1]), sizeof(*context));
+                return TRUE;
+            }
+        }
+        return FALSE;
+    }
+    if (!PAL_VirtualUnwindOutOfProc(context, &contextPointers, baseAddress, ReadMemoryAdapter))
+    {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static BOOL
+UnwindStackFrames(ULONG32 osThreadId)
+{
+    ULONG numNativeFrames = 0;
+    HRESULT hr = GetContextStackTrace(osThreadId, &numNativeFrames);
+    if (FAILED(hr))
+    {
+        return FALSE;
+    }
+    CONTEXT context;
+    memset(&context, 0, sizeof(context));
+    context.ContextFlags = CONTEXT_FULL;
+
+    hr = g_ExtSystem->GetThreadContextById(osThreadId, CONTEXT_FULL, sizeof(context), (PBYTE)&context);
+    if (FAILED(hr))
+    {
+        return FALSE;
+    }
+    TableOutput out(3, POINTERSIZE_HEX, AlignRight);
+    out.WriteRow("RSP", "RIP", "Call Site");
+
+    DEBUG_STACK_FRAME nativeFrame;
+    memset(&nativeFrame, 0, sizeof(nativeFrame));
+
+    do 
+    {
+        if (context.Rip == 0)
+        {
+            break;
+        }
+        nativeFrame.InstructionOffset = context.Rip;
+        nativeFrame.ReturnOffset = context.Rip;
+        nativeFrame.FrameOffset = context.Rbp;
+        nativeFrame.StackOffset = context.Rsp;
+        ClrStackImpl::PrintNativeStackFrame(out, &nativeFrame, FALSE);
+
+    } while (GetStackFrame(&context, numNativeFrames));
+
+    return TRUE;
+}
+
+#endif // FEATURE_PAL && _TARGET_WIN64_