1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | // --------------------------------------------------------------------------- |
6 | // EEContract.cpp |
7 | // |
8 | |
9 | // ! I am the owner for issues in the contract *infrastructure*, not for every |
10 | // ! CONTRACT_VIOLATION dialog that comes up. If you interrupt my work for a routine |
11 | // ! CONTRACT_VIOLATION, you will become the new owner of this file. |
12 | // --------------------------------------------------------------------------- |
13 | |
14 | |
15 | #include "common.h" |
16 | #include "dbginterface.h" |
17 | |
18 | |
19 | #ifdef ENABLE_CONTRACTS |
20 | |
21 | void EEContract::Disable() |
22 | { |
23 | BaseContract::Disable(); |
24 | } |
25 | |
26 | void EEContract::DoChecks(UINT testmask, __in_z const char *szFunction, __in_z const char *szFile, int lineNum) |
27 | { |
28 | SCAN_IGNORE_THROW; // Tell the static contract analyzer to ignore contract violations |
29 | SCAN_IGNORE_FAULT; // due to the contract checking logic itself. |
30 | SCAN_IGNORE_TRIGGER; |
31 | SCAN_IGNORE_LOCK; |
32 | SCAN_IGNORE_SO; |
33 | |
34 | // Many of the checks below result in calls to GetThread() |
35 | // that work just fine if GetThread() returns NULL, so temporarily |
36 | // allow such calls. |
37 | BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; |
38 | m_pThread = GetThread(); |
39 | if (m_pThread != NULL) |
40 | { |
41 | m_pClrDebugState = m_pThread->GetClrDebugState(); |
42 | } |
43 | |
44 | // Call our base DoChecks. |
45 | BaseContract::DoChecks(testmask, szFunction, szFile, lineNum); |
46 | |
47 | m_testmask = testmask; |
48 | m_contractStackRecord.m_testmask = testmask; |
49 | |
50 | // GC mode check |
51 | switch (testmask & MODE_Mask) |
52 | { |
53 | case MODE_Coop: |
54 | if (m_pThread == NULL || !m_pThread->PreemptiveGCDisabled()) |
55 | { |
56 | // |
57 | // Check if this is the debugger helper thread and has the runtime |
58 | // stoppped. If both of these things are true, then we do not care |
59 | // whether we are in COOP mode or not. |
60 | // |
61 | if ((g_pDebugInterface != NULL) && |
62 | g_pDebugInterface->ThisIsHelperThread() && |
63 | g_pDebugInterface->IsStopped()) |
64 | { |
65 | break; |
66 | } |
67 | |
68 | // Pretend that the threads doing GC are in cooperative mode so that code with |
69 | // MODE_COOPERATIVE contract works fine on them. |
70 | if (IsGCThread()) |
71 | { |
72 | break; |
73 | } |
74 | |
75 | if (!( (ModeViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) |
76 | { |
77 | if (m_pThread == NULL) |
78 | { |
79 | CONTRACT_ASSERT("You must have called SetupThread in order to be in GC Cooperative mode." , |
80 | Contract::MODE_Preempt, |
81 | Contract::MODE_Mask, |
82 | m_contractStackRecord.m_szFunction, |
83 | m_contractStackRecord.m_szFile, |
84 | m_contractStackRecord.m_lineNum |
85 | ); |
86 | } |
87 | else |
88 | { |
89 | CONTRACT_ASSERT("MODE_COOPERATIVE encountered while thread is in preemptive state." , |
90 | Contract::MODE_Preempt, |
91 | Contract::MODE_Mask, |
92 | m_contractStackRecord.m_szFunction, |
93 | m_contractStackRecord.m_szFile, |
94 | m_contractStackRecord.m_lineNum |
95 | ); |
96 | } |
97 | } |
98 | } |
99 | break; |
100 | |
101 | case MODE_Preempt: |
102 | // Unmanaged threads are considered permanently preemptive so a NULL thread amounts to a passing case here. |
103 | if (m_pThread != NULL && m_pThread->PreemptiveGCDisabled()) |
104 | { |
105 | if (!( (ModeViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) |
106 | { |
107 | CONTRACT_ASSERT("MODE_PREEMPTIVE encountered while thread is in cooperative state." , |
108 | Contract::MODE_Coop, |
109 | Contract::MODE_Mask, |
110 | m_contractStackRecord.m_szFunction, |
111 | m_contractStackRecord.m_szFile, |
112 | m_contractStackRecord.m_lineNum |
113 | ); |
114 | } |
115 | } |
116 | break; |
117 | |
118 | case MODE_Disabled: |
119 | // Nothing |
120 | break; |
121 | |
122 | default: |
123 | UNREACHABLE(); |
124 | } |
125 | |
126 | // GC Trigger check |
127 | switch (testmask & GC_Mask) |
128 | { |
129 | case GC_Triggers: |
130 | // We don't want to do a full TRIGGERSGC here as this could corrupt |
131 | // OBJECTREF-typed arguments to the function. |
132 | { |
133 | if (m_pClrDebugState->GetGCNoTriggerCount()) |
134 | { |
135 | if (!( (GCViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) |
136 | { |
137 | CONTRACT_ASSERT("GC_TRIGGERS encountered in a GC_NOTRIGGER scope" , |
138 | Contract::GC_NoTrigger, |
139 | Contract::GC_Mask, |
140 | m_contractStackRecord.m_szFunction, |
141 | m_contractStackRecord.m_szFile, |
142 | m_contractStackRecord.m_lineNum |
143 | ); |
144 | } |
145 | } |
146 | } |
147 | break; |
148 | |
149 | case GC_NoTrigger: |
150 | m_pClrDebugState->ViolationMaskReset( GCViolation ); |
151 | |
152 | // Inlined BeginNoTriggerGC |
153 | m_pClrDebugState->IncrementGCNoTriggerCount(); |
154 | if (m_pThread && m_pThread->m_fPreemptiveGCDisabled) |
155 | { |
156 | m_pClrDebugState->IncrementGCForbidCount(); |
157 | } |
158 | |
159 | break; |
160 | |
161 | case GC_Disabled: |
162 | // Nothing |
163 | break; |
164 | |
165 | default: |
166 | UNREACHABLE(); |
167 | } |
168 | |
169 | // Host Triggers check |
170 | switch (testmask & HOST_Mask) |
171 | { |
172 | case HOST_Calls: |
173 | { |
174 | if (!m_pClrDebugState->IsHostCaller()) |
175 | { |
176 | if (!( (HostViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) |
177 | { |
178 | // Avoid infinite recursion by temporarily allowing HOST_CALLS |
179 | // violations so that we don't get contract asserts in anything |
180 | // called downstream of CONTRACT_ASSERT. If we unwind out of |
181 | // here, our dtor will reset our state to what it was on entry. |
182 | CONTRACT_VIOLATION(HostViolation); |
183 | CONTRACT_ASSERT("HOST_CALLS encountered in a HOST_NOCALLS scope" , |
184 | Contract::HOST_NoCalls, |
185 | Contract::HOST_Mask, |
186 | m_contractStackRecord.m_szFunction, |
187 | m_contractStackRecord.m_szFile, |
188 | m_contractStackRecord.m_lineNum |
189 | ); |
190 | } |
191 | } |
192 | } |
193 | break; |
194 | |
195 | case HOST_NoCalls: |
196 | // m_pClrDebugState->ViolationMaskReset( HostViolation ); |
197 | m_pClrDebugState->ResetHostCaller(); |
198 | break; |
199 | |
200 | case HOST_Disabled: |
201 | // Nothing |
202 | break; |
203 | |
204 | default: |
205 | UNREACHABLE(); |
206 | } |
207 | END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; |
208 | |
209 | // EE Thread-required check |
210 | // NOTE: The following must NOT be inside BEGIN/END_GETTHREAD_ALLOWED, |
211 | // as the change to m_pClrDebugState->m_allowGetThread below would be |
212 | // overwritten by END_GETTHREAD_ALLOWED. |
213 | switch (testmask & EE_THREAD_Mask) |
214 | { |
215 | case EE_THREAD_Required: |
216 | if (!((EEThreadViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) |
217 | { |
218 | if (m_pThread == NULL) |
219 | { |
220 | CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered with no current EE Thread object in TLS." , |
221 | Contract::EE_THREAD_Required, |
222 | Contract::EE_THREAD_Mask, |
223 | m_contractStackRecord.m_szFunction, |
224 | m_contractStackRecord.m_szFile, |
225 | m_contractStackRecord.m_lineNum |
226 | ); |
227 | } |
228 | else if (!m_pClrDebugState->IsGetThreadAllowed()) |
229 | { |
230 | // In general, it's unsafe for an EE_THREAD_NOT_REQUIRED function to |
231 | // call an EE_THREAD_REQUIRED function. In cases where it is safe, |
232 | // you may wrap the call to the EE_THREAD_REQUIRED function inside a |
233 | // BEGIN/END_GETTHREAD_ALLOWED block, but you may only do so if the |
234 | // case where GetThread() == NULL is clearly handled in a way that |
235 | // prevents entry into the BEGIN/END_GETTHREAD_ALLOWED block. |
236 | CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered in an EE_THREAD_NOT_REQUIRED scope, without an intervening BEGIN/END_GETTHREAD_ALLOWED block." , |
237 | Contract::EE_THREAD_Required, |
238 | Contract::EE_THREAD_Mask, |
239 | m_contractStackRecord.m_szFunction, |
240 | m_contractStackRecord.m_szFile, |
241 | m_contractStackRecord.m_lineNum |
242 | ); |
243 | } |
244 | } |
245 | m_pClrDebugState->SetGetThreadAllowed(); |
246 | break; |
247 | |
248 | case EE_THREAD_Not_Required: |
249 | m_pClrDebugState->ResetGetThreadAllowed(); |
250 | break; |
251 | |
252 | case EE_THREAD_Disabled: |
253 | break; |
254 | |
255 | default: |
256 | UNREACHABLE(); |
257 | } |
258 | } |
259 | #endif // ENABLE_CONTRACTS |
260 | |
261 | |
262 | BYTE* __stdcall GetAddrOfContractShutoffFlag() |
263 | { |
264 | LIMITED_METHOD_CONTRACT; |
265 | |
266 | // Exposed entrypoint where we cannot probe or do anything TLS |
267 | // related |
268 | static BYTE gContractShutoffFlag = 0; |
269 | |
270 | return &gContractShutoffFlag; |
271 | } |
272 | |
273 | |