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
16size_t CHECK::s_cLeakedBytes = 0;
17size_t CHECK::s_cNumFailures = 0;
18#endif
19
20BOOL CHECK::s_neverEnforceAsserts = 0;
21
22
23// Currently used for scan SPECIAL_HOLDER_* trickery
24DEBUG_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...
31BOOL BaseContract::s_alwaysEnforceContracts = 1;
32
33
34void PAL_TryMarker::Enter()
35{
36 SCAN_SCOPE_BEGIN;
37 STATIC_CONTRACT_THROWS;
38};
39
40void PAL_TryMarker::Leave()
41{
42 SCAN_SCOPE_END;
43};
44
45
46#define SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask) \
47template<> 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) \
55template<> 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
68SPECIALIZED_VIOLATION(0);
69
70// Basic Specializations
71
72SPECIALIZED_VIOLATION(AllViolation);
73SPECIALIZED_VIOLATION(ThrowsViolation);
74SPECIALIZED_VIOLATION(GCViolation);
75SPECIALIZED_VIOLATION(ModeViolation);
76SPECIALIZED_VIOLATION(FaultViolation);
77SPECIALIZED_VIOLATION(FaultNotFatal);
78SPECIALIZED_VIOLATION(SOToleranceViolation);
79SPECIALIZED_VIOLATION(HostViolation);
80SPECIALIZED_VIOLATION(TakesLockViolation);
81SPECIALIZED_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
86SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation);
87SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation|TakesLockViolation);
88SPECIALIZED_VIOLATION(ThrowsViolation|SOToleranceViolation);
89SPECIALIZED_VIOLATION(ThrowsViolation|ModeViolation);
90SPECIALIZED_VIOLATION(ThrowsViolation|FaultNotFatal);
91SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation);
92SPECIALIZED_VIOLATION(ThrowsViolation|TakesLockViolation);
93SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|SOToleranceViolation);
94SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|TakesLockViolation);
95SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation);
96SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|TakesLockViolation|LoadsTypeViolation);
97SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation);
98SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal);
99SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal|TakesLockViolation);
100SPECIALIZED_VIOLATION(GCViolation|SOToleranceViolation);
101SPECIALIZED_VIOLATION(GCViolation|FaultViolation);
102SPECIALIZED_VIOLATION(GCViolation|FaultViolation|SOToleranceViolation);
103SPECIALIZED_VIOLATION(GCViolation|FaultViolation|ModeViolation|SOToleranceViolation);
104SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation);
105SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation|FaultNotFatal);
106SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation|FaultNotFatal|TakesLockViolation);
107SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|TakesLockViolation);
108SPECIALIZED_VIOLATION(FaultViolation|FaultNotFatal);
109SPECIALIZED_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
122void 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
169void 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
223LPCSTR 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
278LPCSTR 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
303void WINAPI ReleaseCheckTls(LPVOID pTlsData)
304{
305 CHECK::ReleaseTls(pTlsData);
306}
307
308#endif
309