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 | // Assertion checking infrastructure |
7 | //================================================================================ |
8 | |
9 | #include "stdafx.h" |
10 | #include <check.h> |
11 | #include <sstring.h> |
12 | #include <ex.h> |
13 | #include <contract.h> |
14 | |
15 | #ifdef _DEBUG |
16 | size_t CHECK::s_cLeakedBytes = 0; |
17 | size_t CHECK::s_cNumFailures = 0; |
18 | #endif |
19 | |
20 | BOOL CHECK::s_neverEnforceAsserts = 0; |
21 | |
22 | |
23 | // Currently used for scan SPECIAL_HOLDER_* trickery |
24 | DEBUG_NOINLINE BOOL CHECK::EnforceAssert_StaticCheckOnly() |
25 | { |
26 | return s_neverEnforceAsserts; |
27 | } |
28 | |
29 | #ifdef ENABLE_CONTRACTS_IMPL |
30 | // Need a place to stick this, there is no contract.cpp... |
31 | BOOL BaseContract::s_alwaysEnforceContracts = 1; |
32 | |
33 | |
34 | void PAL_TryMarker::Enter() |
35 | { |
36 | SCAN_SCOPE_BEGIN; |
37 | STATIC_CONTRACT_THROWS; |
38 | }; |
39 | |
40 | void PAL_TryMarker::Leave() |
41 | { |
42 | SCAN_SCOPE_END; |
43 | }; |
44 | |
45 | |
46 | #define SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask) \ |
47 | template<> void ContractViolationHolder<mask>::Enter() \ |
48 | { \ |
49 | SCAN_SCOPE_BEGIN; \ |
50 | ANNOTATION_VIOLATION(mask); \ |
51 | EnterInternal(mask); \ |
52 | }; |
53 | |
54 | #define SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask) \ |
55 | template<> AutoCleanupContractViolationHolder<mask>::AutoCleanupContractViolationHolder(BOOL fEnterViolation) \ |
56 | { \ |
57 | SCAN_SCOPE_BEGIN; \ |
58 | ANNOTATION_VIOLATION(mask); \ |
59 | EnterInternal(fEnterViolation ? mask : 0); \ |
60 | }; |
61 | |
62 | #define SPECIALIZED_VIOLATION(mask) \ |
63 | SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask); \ |
64 | SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask) |
65 | |
66 | // There is a special case that requires 0... Why??? Who knows, let's fix that case. |
67 | |
68 | SPECIALIZED_VIOLATION(0); |
69 | |
70 | // Basic Specializations |
71 | |
72 | SPECIALIZED_VIOLATION(AllViolation); |
73 | SPECIALIZED_VIOLATION(ThrowsViolation); |
74 | SPECIALIZED_VIOLATION(GCViolation); |
75 | SPECIALIZED_VIOLATION(ModeViolation); |
76 | SPECIALIZED_VIOLATION(FaultViolation); |
77 | SPECIALIZED_VIOLATION(FaultNotFatal); |
78 | SPECIALIZED_VIOLATION(SOToleranceViolation); |
79 | SPECIALIZED_VIOLATION(HostViolation); |
80 | SPECIALIZED_VIOLATION(TakesLockViolation); |
81 | SPECIALIZED_VIOLATION(LoadsTypeViolation); |
82 | |
83 | // Other Specializations used by the RUNTIME, if you get a compile time error you need |
84 | // to add the specific specialization that you are using here. |
85 | |
86 | SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation); |
87 | SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation|TakesLockViolation); |
88 | SPECIALIZED_VIOLATION(ThrowsViolation|SOToleranceViolation); |
89 | SPECIALIZED_VIOLATION(ThrowsViolation|ModeViolation); |
90 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultNotFatal); |
91 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation); |
92 | SPECIALIZED_VIOLATION(ThrowsViolation|TakesLockViolation); |
93 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|SOToleranceViolation); |
94 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|TakesLockViolation); |
95 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation); |
96 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|TakesLockViolation|LoadsTypeViolation); |
97 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation); |
98 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal); |
99 | SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal|TakesLockViolation); |
100 | SPECIALIZED_VIOLATION(GCViolation|SOToleranceViolation); |
101 | SPECIALIZED_VIOLATION(GCViolation|FaultViolation); |
102 | SPECIALIZED_VIOLATION(GCViolation|FaultViolation|SOToleranceViolation); |
103 | SPECIALIZED_VIOLATION(GCViolation|FaultViolation|ModeViolation|SOToleranceViolation); |
104 | SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation); |
105 | SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation|FaultNotFatal); |
106 | SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation|FaultNotFatal|TakesLockViolation); |
107 | SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|TakesLockViolation); |
108 | SPECIALIZED_VIOLATION(FaultViolation|FaultNotFatal); |
109 | SPECIALIZED_VIOLATION(FaultNotFatal|TakesLockViolation); |
110 | |
111 | |
112 | |
113 | #undef SPECIALIZED_VIOLATION |
114 | #undef SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER |
115 | #undef SPECIALIZE_CONTRACT_VIOLATION_HOLDER |
116 | |
117 | #endif |
118 | |
119 | // Trigger triggers the actual check failure. The trigger may provide a reason |
120 | // to include in the failure message. |
121 | |
122 | void CHECK::Trigger(LPCSTR reason) |
123 | { |
124 | STATIC_CONTRACT_SO_NOT_MAINLINE; |
125 | STATIC_CONTRACT_NOTHROW; |
126 | STATIC_CONTRACT_GC_NOTRIGGER; |
127 | |
128 | const char *messageString = NULL; |
129 | NewHolder<StackScratchBuffer> pScratch(NULL); |
130 | NewHolder<StackSString> pMessage(NULL); |
131 | |
132 | EX_TRY |
133 | { |
134 | FAULT_NOT_FATAL(); |
135 | |
136 | pScratch = new StackScratchBuffer(); |
137 | pMessage = new StackSString(); |
138 | |
139 | pMessage->AppendASCII(reason); |
140 | pMessage->AppendASCII(": " ); |
141 | if (m_message != NULL) |
142 | pMessage->AppendASCII((m_message != (LPCSTR)1) ? m_message : "<runtime check failure>" ); |
143 | |
144 | #if _DEBUG |
145 | pMessage->AppendASCII("FAILED: " ); |
146 | pMessage->AppendASCII(m_condition); |
147 | #endif |
148 | |
149 | messageString = pMessage->GetANSI(*pScratch); |
150 | } |
151 | EX_CATCH |
152 | { |
153 | messageString = "<exception occurred while building failure description>" ; |
154 | } |
155 | EX_END_CATCH(SwallowAllExceptions); |
156 | |
157 | #if _DEBUG |
158 | DbgAssertDialog((char*)m_file, m_line, (char *)messageString); |
159 | #else |
160 | OutputDebugStringA(messageString); |
161 | DebugBreak(); |
162 | #endif |
163 | |
164 | } |
165 | |
166 | #ifdef _DEBUG |
167 | // Setup records context info after a failure. |
168 | |
169 | void CHECK::Setup(LPCSTR message, LPCSTR condition, LPCSTR file, INT line) |
170 | { |
171 | STATIC_CONTRACT_SO_NOT_MAINLINE; |
172 | STATIC_CONTRACT_NOTHROW; |
173 | STATIC_CONTRACT_GC_NOTRIGGER; |
174 | STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; |
175 | |
176 | // |
177 | // It might be nice to collect all of the message here. But for now, we will just |
178 | // retain the innermost one. |
179 | // |
180 | |
181 | if (m_message == NULL) |
182 | { |
183 | m_message = message; |
184 | m_condition = condition; |
185 | m_file = file; |
186 | m_line = line; |
187 | } |
188 | |
189 | #ifdef _DEBUG |
190 | else if (IsInAssert()) |
191 | { |
192 | EX_TRY |
193 | { |
194 | FAULT_NOT_FATAL(); |
195 | // Try to build a stack of condition failures |
196 | |
197 | StackSString context; |
198 | context.Printf("%s\n\t%s%s FAILED: %s\n\t\t%s, line: %d" , |
199 | m_condition, |
200 | message && *message ? message : "" , |
201 | message && *message ? ": " : "" , |
202 | condition, |
203 | file, line); |
204 | |
205 | m_condition = AllocateDynamicMessage(context); |
206 | } |
207 | EX_CATCH |
208 | { |
209 | // If anything goes wrong, we don't push extra context |
210 | } |
211 | EX_END_CATCH(SwallowAllExceptions) |
212 | } |
213 | #endif |
214 | |
215 | #if defined(_DEBUG_IMPL) |
216 | if (IsInAssert() && IsDebuggerPresent()) |
217 | { |
218 | DebugBreak(); |
219 | } |
220 | #endif |
221 | } |
222 | |
223 | LPCSTR CHECK::FormatMessage(LPCSTR messageFormat, ...) |
224 | { |
225 | STATIC_CONTRACT_SO_NOT_MAINLINE; |
226 | STATIC_CONTRACT_NOTHROW; |
227 | STATIC_CONTRACT_GC_NOTRIGGER; |
228 | |
229 | LPCSTR result = NULL; |
230 | |
231 | // We never delete this allocated string. A dtor would conflict with places |
232 | // we use this around SEH stuff. We could have some fancy stack-based allocator, |
233 | // but that's too much work for now. In fact we believe that leaking is a reasonable |
234 | // policy, since allocations will only happen on a failed assert, and a failed assert |
235 | // will generally be fatal to the process. |
236 | |
237 | // The most common place for format strings will be in an assert; in |
238 | // which case we don't care about leaking. |
239 | // But to be safe, if we're not-inside an assert, then we'll just use |
240 | // the format string literal to avoid allocated (and leaking) any memory. |
241 | |
242 | CHECK chk; |
243 | if (!chk.IsInAssert()) |
244 | result = messageFormat; |
245 | else |
246 | { |
247 | // This path is only run in debug. TakesLockViolation suppresses |
248 | // problems with SString below. |
249 | CONTRACT_VIOLATION(FaultNotFatal|TakesLockViolation); |
250 | |
251 | EX_TRY |
252 | { |
253 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
254 | |
255 | // Format it. |
256 | va_list args; |
257 | va_start( args, messageFormat); |
258 | |
259 | SString s; |
260 | s.VPrintf(messageFormat, args); |
261 | |
262 | va_end(args); |
263 | |
264 | result = AllocateDynamicMessage(s); |
265 | |
266 | } |
267 | EX_CATCH |
268 | { |
269 | // If anything goes wrong, just use the format string. |
270 | result = messageFormat; |
271 | } |
272 | EX_END_CATCH(SwallowAllExceptions) |
273 | } |
274 | |
275 | return result; |
276 | } |
277 | |
278 | LPCSTR CHECK::AllocateDynamicMessage(const SString &s) |
279 | { |
280 | STATIC_CONTRACT_NOTHROW; |
281 | STATIC_CONTRACT_GC_NOTRIGGER; |
282 | STATIC_CONTRACT_SO_NOT_MAINLINE; |
283 | |
284 | // Make a copy of it. |
285 | StackScratchBuffer buffer; |
286 | const char * pMsg = s.GetANSI(buffer); |
287 | |
288 | // Must copy that into our own field. |
289 | size_t len = strlen(pMsg) + 1; |
290 | char * p = new char[len]; |
291 | strcpy(p, pMsg); |
292 | |
293 | // But we'll keep counters of how much we're leaking for diagnostic purposes. |
294 | s_cLeakedBytes += len; |
295 | s_cNumFailures ++; |
296 | |
297 | // This should never fire. |
298 | // Note use an ASSERTE (not a check) to avoid a recursive deadlock. |
299 | _ASSERTE(s_cLeakedBytes < 10000 || !"Warning - check misuse - leaked over 10,000B due to unexpected usage pattern" ); |
300 | return p; |
301 | } |
302 | |
303 | void WINAPI ReleaseCheckTls(LPVOID pTlsData) |
304 | { |
305 | CHECK::ReleaseTls(pTlsData); |
306 | } |
307 | |
308 | #endif |
309 | |