APIs to allow profilers to use DoStackSnapShot on Linux (#24968)
authorDavid Mason <davmason@microsoft.com>
Thu, 6 Jun 2019 00:30:46 +0000 (17:30 -0700)
committerGitHub <noreply@github.com>
Thu, 6 Jun 2019 00:30:46 +0000 (17:30 -0700)
src/inc/corerror.xml
src/inc/corprof.idl
src/inc/profilepriv.h
src/inc/profilepriv.inl
src/pal/prebuilt/inc/corerror.h
src/pal/prebuilt/inc/corprof.h
src/vm/profilingenumerators.cpp
src/vm/proftoeeinterfaceimpl.cpp
src/vm/proftoeeinterfaceimpl.h
src/vm/threadsuspend.h

index 994195f..d8b8f63 100644 (file)
     <Comment> An unknown error occurred in the Diagnpostics IPC Server. </Comment>
 </HRESULT>
 
+<HRESULT NumericValue="0x80131388">
+    <SymbolicName>CORPROF_E_SUSPENSION_IN_PROGRESS</SymbolicName>
+    <Comment> The runtime cannot be suspened since a suspension is already in progress. </Comment>
+</HRESULT>
+
 <HRESULT NumericValue="0x80131401">
        <SymbolicName>SECURITY_E_INCOMPATIBLE_SHARE</SymbolicName>
        <Message>"Loading this assembly would produce a different grant set from other instances."</Message>
index f5ad6b5..1d66137 100644 (file)
@@ -3974,6 +3974,12 @@ interface ICorProfilerInfo10 : ICorProfilerInfo9
                 [in]                       ULONG       cFunctions,
                 [in, size_is(cFunctions)]  ModuleID    moduleIds[],
                 [in, size_is(cFunctions)]  mdMethodDef methodIds[]);
+
+    // Suspend the runtime without performing a GC.
+    HRESULT SuspendRuntime();
+
+    // Restart the runtime from a previous suspension.
+    HRESULT ResumeRuntime();
 }
 
 /*
index 647457e..a5e2398 100644 (file)
@@ -117,6 +117,8 @@ struct ProfControlBlock
     BOOL fConcurrentGCDisabledForAttach;
 
     Volatile<BOOL> fProfControlBlockInitialized;
+
+    Volatile<BOOL> fProfilerRequestedRuntimeSuspend;
     
     void Init();
     void ResetPerSessionStatus();    
index 5a04bd3..5a0513f 100644 (file)
@@ -72,6 +72,8 @@ inline void ProfControlBlock::Init()
     ResetPerSessionStatus();
 
     fProfControlBlockInitialized = TRUE;
+
+    fProfilerRequestedRuntimeSuspend = FALSE;
 }
 
 // Reset those variables that is only for the current attach session
index 3d92d63..7bd0c10 100644 (file)
 #define CORDIAGIPC_E_UNKNOWN_COMMAND EMAKEHR(0x1385)
 #define CORDIAGIPC_E_UNKNOWN_MAGIC EMAKEHR(0x1386)
 #define CORDIAGIPC_E_UNKNOWN_ERROR EMAKEHR(0x1387)
+#define CORPROF_E_SUSPENSION_IN_PROGRESS EMAKEHR(0x1388)
 #define SECURITY_E_INCOMPATIBLE_SHARE EMAKEHR(0x1401)
 #define SECURITY_E_UNVERIFIABLE EMAKEHR(0x1402)
 #define SECURITY_E_INCOMPATIBLE_EVIDENCE EMAKEHR(0x1403)
index be4b810..2717067 100644 (file)
@@ -15208,6 +15208,10 @@ EXTERN_C const IID IID_ICorProfilerInfo10;
             /* [size_is][in] */ ModuleID moduleIds[  ],
             /* [size_is][in] */ mdMethodDef methodIds[  ]) = 0;
         
+        virtual HRESULT STDMETHODCALLTYPE SuspendRuntime( void) = 0;
+        
+        virtual HRESULT STDMETHODCALLTYPE ResumeRuntime( void) = 0;
+        
     };
     
     
@@ -15815,6 +15819,12 @@ EXTERN_C const IID IID_ICorProfilerInfo10;
             /* [size_is][in] */ ModuleID moduleIds[  ],
             /* [size_is][in] */ mdMethodDef methodIds[  ]);
         
+        HRESULT ( STDMETHODCALLTYPE *SuspendRuntime )( 
+            ICorProfilerInfo10 * This);
+        
+        HRESULT ( STDMETHODCALLTYPE *ResumeRuntime )( 
+            ICorProfilerInfo10 * This);
+        
         END_INTERFACE
     } ICorProfilerInfo10Vtbl;
 
@@ -16129,6 +16139,12 @@ EXTERN_C const IID IID_ICorProfilerInfo10;
 #define ICorProfilerInfo10_RequestReJITWithInliners(This,dwRejitFlags,cFunctions,moduleIds,methodIds)   \
     ( (This)->lpVtbl -> RequestReJITWithInliners(This,dwRejitFlags,cFunctions,moduleIds,methodIds) ) 
 
+#define ICorProfilerInfo10_SuspendRuntime(This) \
+    ( (This)->lpVtbl -> SuspendRuntime(This) ) 
+
+#define ICorProfilerInfo10_ResumeRuntime(This)  \
+    ( (This)->lpVtbl -> ResumeRuntime(This) ) 
+
 #endif /* COBJMACROS */
 
 
index b03638f..d583610 100644 (file)
@@ -523,7 +523,9 @@ HRESULT ProfilerThreadEnum::Init()
     }
     CONTRACTL_END;
 
-    ThreadStoreLockHolder tsLock;
+    // If a profiler has requested that the runtime suspend to do stack snapshots, it
+    // will be holding the ThreadStore lock already
+    ThreadStoreLockHolder tsLock(!g_profControlBlock.fProfilerRequestedRuntimeSuspend);
 
     Thread * pThread = NULL;
 
@@ -546,6 +548,7 @@ HRESULT ProfilerThreadEnum::Init()
         *m_elements.Append() = (ThreadID) pThread;
     }
     
+    _ASSERTE(ThreadStore::HoldingThreadStore() || g_profControlBlock.fProfilerRequestedRuntimeSuspend);
     return S_OK;
 }
 
index 4d9b733..13bf2a4 100644 (file)
@@ -6938,6 +6938,71 @@ HRESULT ProfToEEInterfaceImpl::GetLOHObjectSizeThreshold(DWORD *pThreshold)
     return S_OK;
 }
 
+HRESULT ProfToEEInterfaceImpl::SuspendRuntime()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_TRIGGERS;
+        MODE_ANY;
+        CAN_TAKE_LOCK;
+        EE_THREAD_NOT_REQUIRED;
+    }
+    CONTRACTL_END;
+
+    PROFILER_TO_CLR_ENTRYPOINT_SYNC_EX(
+        kP2EEAllowableAfterAttach | kP2EETriggers,
+        (LF_CORPROF,
+        LL_INFO1000,
+        "**PROF: SuspendRuntime\n"));
+
+    if (!g_fEEStarted)
+    {
+        return CORPROF_E_RUNTIME_UNINITIALIZED;
+    }
+
+    if (ThreadSuspend::SysIsSuspendInProgress() || (ThreadSuspend::GetSuspensionThread() != 0))
+    {
+        return CORPROF_E_SUSPENSION_IN_PROGRESS;
+    }
+    
+    g_profControlBlock.fProfilerRequestedRuntimeSuspend = TRUE;
+    ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_REASON::SUSPEND_FOR_PROFILER);
+    return S_OK;
+}
+
+HRESULT ProfToEEInterfaceImpl::ResumeRuntime()
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_TRIGGERS;
+        MODE_ANY;
+        CAN_TAKE_LOCK;
+    }
+    CONTRACTL_END;
+
+    PROFILER_TO_CLR_ENTRYPOINT_SYNC_EX(
+        kP2EEAllowableAfterAttach | kP2EETriggers,
+        (LF_CORPROF,
+        LL_INFO1000,
+        "**PROF: ResumeRuntime\n"));
+    
+    if (!g_fEEStarted)
+    {
+        return CORPROF_E_RUNTIME_UNINITIALIZED;
+    }
+
+    if (!g_profControlBlock.fProfilerRequestedRuntimeSuspend)
+    {
+        return CORPROF_E_UNSUPPORTED_CALL_SEQUENCE;
+    }
+
+    ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceeded */);
+    g_profControlBlock.fProfilerRequestedRuntimeSuspend = FALSE;
+    return S_OK;
+}
+
 /*
  * GetStringLayout
  *
@@ -7796,15 +7861,6 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
                                                BYTE * pbContext,
                                               ULONG32 contextSize)
 {
-
-#if !defined(FEATURE_HIJACK)
-
-    // DoStackSnapshot needs Thread::Suspend/ResumeThread functionality.
-    // On platforms w/o support for these APIs return E_NOTIMPL.
-    return E_NOTIMPL;
-
-#else // !defined(FEATURE_HIJACK)
-
     CONTRACTL
     {
         // Yay!  (Note: NOTHROW is vital.  The throw at minimum allocates
@@ -7968,7 +8024,7 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
     HostCallPreference hostCallPreference;
     
     // First, check "1) Target thread to walk == current thread OR Target thread is suspended"
-    if (pThreadToSnapshot != pCurrentThread)
+    if (pThreadToSnapshot != pCurrentThread && !g_profControlBlock.fProfilerRequestedRuntimeSuspend)
     {
 #ifndef PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
         hr = E_NOTIMPL;
@@ -8151,7 +8207,7 @@ HRESULT ProfToEEInterfaceImpl::DoStackSnapshot(ThreadID thread,
     // inlined P/Invoke.  In this case, the InlinedCallFrame will be used to help start off our
     // stackwalk at the top of the stack.
     //
-    if (pThreadToSnapshot != pCurrentThread)
+    if (pThreadToSnapshot != pCurrentThread && !g_profControlBlock.fProfilerRequestedRuntimeSuspend)
     {
 #ifndef PLATFORM_SUPPORTS_SAFE_THREADSUSPEND
         hr = E_NOTIMPL;
@@ -8253,8 +8309,6 @@ Cleanup:
     }
 
     return hr;
-
-#endif // !defined(FEATURE_HIJACK)
 }
 
 
index c6dc289..016b1f7 100644 (file)
@@ -615,6 +615,10 @@ public:
         ModuleID    moduleIds[],
         mdMethodDef methodIds[]);
 
+    COM_METHOD SuspendRuntime();
+
+    COM_METHOD ResumeRuntime();
+
     // end ICorProfilerInfo10    
 
 protected:
index 44ccce5..a5fb302 100644 (file)
@@ -183,7 +183,8 @@ public:
         SUSPEND_FOR_SHUTDOWN            = 4,
         SUSPEND_FOR_DEBUGGER            = 5,
         SUSPEND_FOR_GC_PREP             = 6,
-        SUSPEND_FOR_DEBUGGER_SWEEP      = 7     // This must only be used in Thread::SysSweepThreadsForDebug
+        SUSPEND_FOR_DEBUGGER_SWEEP      = 7,     // This must only be used in Thread::SysSweepThreadsForDebug
+        SUSPEND_FOR_PROFILER            = 8
     } SUSPEND_REASON;
 
 private: