summaryrefslogtreecommitdiff
path: root/src/vm/proftoeeinterfaceimpl.inl
blob: a2334a2a95218c24b5b74931de9fd1aaefb10878 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
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__