diff options
Diffstat (limited to 'src/vm/proftoeeinterfaceimpl.inl')
-rw-r--r-- | src/vm/proftoeeinterfaceimpl.inl | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/src/vm/proftoeeinterfaceimpl.inl b/src/vm/proftoeeinterfaceimpl.inl new file mode 100644 index 0000000000..a2334a2a95 --- /dev/null +++ b/src/vm/proftoeeinterfaceimpl.inl @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// FILE: ProfToEEInterfaceImpl.inl +// +// Inline implementation of portions of the code used to help implement the +// ICorProfilerInfo* interfaces, which allow the Profiler to communicate with the EE. +// + +// +// ====================================================================================== + +#ifndef __PROFTOEEINTERFACEIMPL_INL__ +#define __PROFTOEEINTERFACEIMPL_INL__ + +#ifdef PROFILING_SUPPORTED + +//--------------------------------------------------------------------------------------- +// Helpers + + +//--------------------------------------------------------------------------------------- +// +// "Callback flags" are typically set on the current EE Thread object just before we +// call into a profiler (see SetCallbackStateFlagsHolder). This helps us remember that +// we deliberately called into the profiler, as opposed to the profiler gaining control +// by hijacking a thread. This helper function is used in PROFILER_TO_CLR_ENTRYPOINT_SYNC +// to test the flags in order to authorize a profiler's call into us. The macro is +// placed at the top of any call that's supposed to be synchronous-only. If no flags are +// set, that implies the profiler hijacked the thread, so we reject the call. In +// contrast, PROFILER_TO_CLR_ENTRYPOINT_ASYNC does NOT call this helper function, and +// thus deliberately allows the hijacked thread to continue calling back into the runtime. +// +// Arguments: +// dwFlags - Flags to test +// +// Return Value: +// If no EE Thread object: nonzero +// If EE Thread object AND any of the specified flags are set on it: nonzero +// Else zero (FALSE) +// + +inline BOOL AreCallbackStateFlagsSet(DWORD dwFlags) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + CANNOT_TAKE_LOCK; + EE_THREAD_NOT_REQUIRED; + SO_NOT_MAINLINE; + } + CONTRACTL_END; + + Thread * pThread = GetThreadNULLOk(); + if (pThread == NULL) + { + // Not a managed thread; profiler can do whatever it wants + return TRUE; + } + + BOOL fRet; + BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; + DWORD dwProfilerCallbackFullStateFlags = pThread->GetProfilerCallbackFullState(); + if ((dwProfilerCallbackFullStateFlags & COR_PRF_CALLBACKSTATE_FORCEGC_WAS_CALLED) != 0) + { + // Threads on which ForceGC() was successfully called should be treated just + // like native threads. Profiler can do whatever it wants + return TRUE; + } + + fRet = ((dwProfilerCallbackFullStateFlags & dwFlags) == dwFlags); + END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; + return fRet; +} + + +//--------------------------------------------------------------------------------------- +// +// Simple helper that returns nonzero iff the currently-executing function was called +// asynchronously (i.e., from outside of a callback, as hijacking profilers do) +// + +inline BOOL IsCalledAsynchronously() +{ + LIMITED_METHOD_CONTRACT; + return !(AreCallbackStateFlagsSet(COR_PRF_CALLBACKSTATE_INCALLBACK)); +} + + +//--------------------------------------------------------------------------------------- +// +// Simple helper that decides whether we should avoid calling into the host. Generally, +// host calls should be avoided if the current Info method was called asynchronously +// (i.e., from an F1-style hijack), for fear of re-entering the host (mainly SQL). +// +// Server GC threads are native (non-EE) threads, which therefore do not track enough +// state for us to determine if a call is made asynhronously on those threads. So we +// pessimistically assume that the current call on a server GC thread is from a hijack +// for the purposes of determining whether we may enter the host. Reasoning for this: +// * SQL enables server-mode GC +// * server GC threads are responsible for performing runtime suspension, and thus +// call Thread::SuspendThread() which yields/sleeps and thus enters the host. So +// server GC threads are examples of non-EE Threads that actually do spend time +// in the host (this otherwise almost never happens for other non-EE threads). +// * In spite of this pessimism, the effect on the profiler should be minimal. The +// host calls we're avoiding are from the code manager's lock, which: +// * a) Is only used when doing stack walks or translating IPs to functions +// * b) Is only affected if it tries to yield/sleep when the code manager +// writer lock is taken, and that happens for incredibly tiny windows of +// time. +// + +inline BOOL ShouldAvoidHostCalls() +{ + LIMITED_METHOD_CONTRACT; + + return + ( + IsCalledAsynchronously() || + ( + (GetThreadNULLOk() == NULL) && IsGCSpecialThread() + ) + ); +} + + +//--------------------------------------------------------------------------------------- +// +// Simple helper that returns nonzero iff the current thread is a non-EE thread in the +// process of doing a GC +// + +inline BOOL NativeThreadInGC() +{ + LIMITED_METHOD_CONTRACT; + return ((g_profControlBlock.fGCInProgress) && (GetThreadNULLOk() == NULL)); +} + +//--------------------------------------------------------------------------------------- +// +// ProfToEE functions can use these overloads to determine whether a Thread should be +// visible to a profiler and thus be suitable for querying information about, by a +// profiler. If the Thread is non-NULL and is NOT a GCSpecial thread, then it's +// considered "managed", and is thus visible to the profiler. +// +// Arguments: +// pThread or threadId - Thread to check +// +// Return Value: +// nonzero iff the thread can run managed code + +// Notes: +// See code:Thread::m_fGCSpecial for more information +// + +inline BOOL IsManagedThread(Thread * pThread) +{ + LIMITED_METHOD_CONTRACT; + return ((pThread != NULL) && (!pThread->IsGCSpecial())); +} + +inline BOOL IsManagedThread(ThreadID threadId) +{ + LIMITED_METHOD_CONTRACT; + return IsManagedThread(reinterpret_cast<Thread *>(threadId)); +} + +//--------------------------------------------------------------------------------------- +// +// ProfToEEInterfaceImpl ctor. +// + +inline ProfToEEInterfaceImpl::ProfToEEInterfaceImpl() +{ + LIMITED_METHOD_CONTRACT; +}; + + +inline BOOL IsClassOfMethodTableInited(MethodTable * pMethodTable, AppDomain * pAppDomain) +{ + LIMITED_METHOD_CONTRACT; + + return (pMethodTable->IsRestored() && + (pMethodTable->GetModuleForStatics() != NULL) && + (pMethodTable->GetDomainLocalModule(pAppDomain) != NULL) && + pMethodTable->IsClassInited(pAppDomain)); +} + + +#endif // PROFILING_SUPPORTED + +#endif // __PROFTOEEINTERFACEIMPL_INL__ |