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 | // DebugMacros.h |
6 | // |
7 | // Wrappers for Debugging purposes. |
8 | // |
9 | //***************************************************************************** |
10 | |
11 | #ifndef __DebugMacros_h__ |
12 | #define __DebugMacros_h__ |
13 | |
14 | #include "stacktrace.h" |
15 | #include "debugmacrosext.h" |
16 | |
17 | #undef _ASSERTE |
18 | #undef VERIFY |
19 | |
20 | #ifdef __cplusplus |
21 | extern "C" { |
22 | #endif // __cplusplus |
23 | |
24 | #if defined(_DEBUG) |
25 | |
26 | class SString; |
27 | bool GetStackTraceAtContext(SString & s, struct _CONTEXT * pContext); |
28 | |
29 | void _cdecl DbgWriteEx(LPCTSTR szFmt, ...); |
30 | bool _DbgBreakCheck(LPCSTR szFile, int iLine, LPCSTR szExpr, BOOL fConstrained = FALSE); |
31 | |
32 | extern VOID DbgAssertDialog(const char *szFile, int iLine, const char *szExpr); |
33 | |
34 | #define TRACE_BUFF_SIZE (cchMaxAssertStackLevelStringLen * cfrMaxAssertStackLevels + cchMaxAssertExprLen + 1) |
35 | extern char g_szExprWithStack[TRACE_BUFF_SIZE]; |
36 | |
37 | extern int _DbgBreakCount; |
38 | |
39 | #define PRE_ASSERTE /* if you need to change modes before doing asserts override */ |
40 | #define POST_ASSERTE /* put it back */ |
41 | |
42 | #if !defined(_ASSERTE_MSG) |
43 | #define _ASSERTE_MSG(expr, msg) \ |
44 | do { \ |
45 | if (!(expr)) { \ |
46 | PRE_ASSERTE \ |
47 | DbgAssertDialog(__FILE__, __LINE__, msg); \ |
48 | POST_ASSERTE \ |
49 | } \ |
50 | } while (0) |
51 | #endif // _ASSERTE_MSG |
52 | |
53 | #if !defined(_ASSERTE) |
54 | #define _ASSERTE(expr) _ASSERTE_MSG(expr, #expr) |
55 | #endif // !_ASSERTE |
56 | |
57 | |
58 | #define VERIFY(stmt) _ASSERTE((stmt)) |
59 | |
60 | #define _ASSERTE_ALL_BUILDS(file, expr) _ASSERTE((expr)) |
61 | |
62 | #define FreeBuildDebugBreak() DebugBreak() |
63 | |
64 | #else // !_DEBUG |
65 | |
66 | #define _DbgBreakCount 0 |
67 | |
68 | #define _ASSERTE(expr) ((void)0) |
69 | #define _ASSERTE_MSG(expr, msg) ((void)0) |
70 | #define VERIFY(stmt) (void)(stmt) |
71 | |
72 | void __FreeBuildDebugBreak(); |
73 | void DECLSPEC_NORETURN __FreeBuildAssertFail(const char *szFile, int iLine, const char *szExpr); |
74 | |
75 | #define FreeBuildDebugBreak() __FreeBuildDebugBreak() |
76 | |
77 | // At this point, EEPOLICY_HANDLE_FATAL_ERROR may or may not be defined. It will be defined |
78 | // if we are building the VM folder, but outside VM, its not necessarily defined. |
79 | // |
80 | // Thus, if EEPOLICY_HANDLE_FATAL_ERROR is not defined, we will call into __FreeBuildAssertFail, |
81 | // but if it is defined, we will use it. |
82 | // |
83 | // Failing here implies an error in the runtime - hence we use COR_E_EXECUTIONENGINE. |
84 | |
85 | #ifdef EEPOLICY_HANDLE_FATAL_ERROR |
86 | #define _ASSERTE_ALL_BUILDS(file, expr) if (!(expr)) EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); |
87 | #else // !EEPOLICY_HANDLE_FATAL_ERROR |
88 | #define _ASSERTE_ALL_BUILDS(file, expr) if (!(expr)) __FreeBuildAssertFail(file, __LINE__, #expr); |
89 | #endif // EEPOLICY_HANDLE_FATAL_ERROR |
90 | |
91 | #endif |
92 | |
93 | |
94 | #define ASSERT_AND_CHECK(x) { \ |
95 | BOOL bResult = x; \ |
96 | if (!bResult) \ |
97 | { \ |
98 | _ASSERTE(x); \ |
99 | return FALSE; \ |
100 | } \ |
101 | } |
102 | |
103 | |
104 | #ifdef _DEBUG_IMPL |
105 | |
106 | // A macro to execute a statement only in _DEBUG_IMPL. |
107 | #define DEBUG_IMPL_STMT(stmt) stmt |
108 | |
109 | #define _ASSERTE_IMPL(expr) _ASSERTE((expr)) |
110 | |
111 | #if defined(_M_IX86) |
112 | #if defined(_MSC_VER) |
113 | #define _DbgBreak() __asm { int 3 } |
114 | #elif defined(__GNUC__) |
115 | #define _DbgBreak() __asm__ ("int $3"); |
116 | #else |
117 | #error Unknown compiler |
118 | #endif |
119 | #else |
120 | #define _DbgBreak() DebugBreak() |
121 | #endif |
122 | |
123 | extern VOID DebBreak(); |
124 | extern VOID DebBreakHr(HRESULT hr); |
125 | |
126 | #ifndef IfFailGoto |
127 | #define IfFailGoto(EXPR, LABEL) \ |
128 | do { hr = (EXPR); if(FAILED(hr)) { DebBreakHr(hr); goto LABEL; } } while (0) |
129 | #endif |
130 | |
131 | #ifndef IfFailRet |
132 | #define IfFailRet(EXPR) \ |
133 | do { hr = (EXPR); if(FAILED(hr)) { DebBreakHr(hr); return (hr); } } while (0) |
134 | #endif |
135 | |
136 | #ifndef IfFailWin32Ret |
137 | #define IfFailWin32Ret(EXPR) \ |
138 | do { hr = (EXPR); if(hr != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(hr); DebBreakHr(hr); return hr;} } while (0) |
139 | #endif |
140 | |
141 | #ifndef IfFailWin32Goto |
142 | #define IfFailWin32Goto(EXPR, LABEL) \ |
143 | do { hr = (EXPR); if(hr != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(hr); DebBreakHr(hr); goto LABEL; } } while (0) |
144 | #endif |
145 | |
146 | #ifndef IfFailGo |
147 | #define IfFailGo(EXPR) IfFailGoto(EXPR, ErrExit) |
148 | #endif |
149 | |
150 | #ifndef IfFailWin32Go |
151 | #define IfFailWin32Go(EXPR) IfFailWin32Goto(EXPR, ErrExit) |
152 | #endif |
153 | |
154 | #else // _DEBUG_IMPL |
155 | |
156 | #define _DbgBreak() {} |
157 | |
158 | #define DEBUG_IMPL_STMT(stmt) |
159 | |
160 | #define _ASSERTE_IMPL(expr) |
161 | |
162 | #define IfFailGoto(EXPR, LABEL) \ |
163 | do { hr = (EXPR); if(FAILED(hr)) { goto LABEL; } } while (0) |
164 | |
165 | #define IfFailRet(EXPR) \ |
166 | do { hr = (EXPR); if(FAILED(hr)) { return (hr); } } while (0) |
167 | |
168 | #define IfFailWin32Ret(EXPR) \ |
169 | do { hr = (EXPR); if(hr != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(hr); return hr;} } while (0) |
170 | |
171 | #define IfFailWin32Goto(EXPR, LABEL) \ |
172 | do { hr = (EXPR); if(hr != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(hr); goto LABEL; } } while (0) |
173 | |
174 | #define IfFailGo(EXPR) IfFailGoto(EXPR, ErrExit) |
175 | |
176 | #define IfFailWin32Go(EXPR) IfFailWin32Goto(EXPR, ErrExit) |
177 | |
178 | #endif // _DEBUG_IMPL |
179 | |
180 | |
181 | #define IfNullGoto(EXPR, LABEL) \ |
182 | do { if ((EXPR) == NULL) { OutOfMemory(); IfFailGoto(E_OUTOFMEMORY, LABEL); } } while (false) |
183 | |
184 | #ifndef IfNullRet |
185 | #define IfNullRet(EXPR) \ |
186 | do { if ((EXPR) == NULL) { OutOfMemory(); return E_OUTOFMEMORY; } } while (false) |
187 | #endif //!IfNullRet |
188 | |
189 | #define IfNullGo(EXPR) IfNullGoto(EXPR, ErrExit) |
190 | |
191 | #ifdef __cplusplus |
192 | } |
193 | |
194 | #endif // __cplusplus |
195 | |
196 | |
197 | #undef assert |
198 | #define assert _ASSERTE |
199 | #undef _ASSERT |
200 | #define _ASSERT _ASSERTE |
201 | |
202 | |
203 | #if defined(_DEBUG) && !defined(FEATURE_PAL) |
204 | |
205 | // This function returns the EXE time stamp (effectively a random number) |
206 | // Under retail it always returns 0. This is meant to be used in the |
207 | // RandomOnExe macro |
208 | unsigned DbgGetEXETimeStamp(); |
209 | |
210 | // returns true 'fractionOn' amount of the time using the EXE timestamp |
211 | // as the random number seed. For example DbgRandomOnExe(.1) returns true 1/10 |
212 | // of the time. We use the line number so that different uses of DbgRandomOnExe |
213 | // will not be coorelated with each other (9973 is prime). Returns false on a retail build |
214 | #define DbgRandomOnHashAndExe(hash, fractionOn) \ |
215 | (((DbgGetEXETimeStamp() * __LINE__ * ((hash) ? (hash) : 1)) % 9973) < \ |
216 | unsigned(fractionOn * 9973)) |
217 | #define DbgRandomOnExe(fractionOn) DbgRandomOnHashAndExe(0, fractionOn) |
218 | #define DbgRandomOnStringAndExe(string, fractionOn) DbgRandomOnHashAndExe(HashStringA(string), fractionOn) |
219 | |
220 | #else |
221 | |
222 | #define DbgGetEXETimeStamp() 0 |
223 | #define DbgRandomOnHashAndExe(hash, fractionOn) 0 |
224 | #define DbgRandomOnExe(fractionOn) 0 |
225 | #define DbgRandomOnStringAndExe(fractionOn) 0 |
226 | |
227 | #endif // _DEBUG && !FEATUREPAL |
228 | |
229 | #ifdef _DEBUG |
230 | namespace clr |
231 | { |
232 | namespace dbg |
233 | { |
234 | // In debug builds, this can be used to write known bad values into |
235 | // memory. One example is in ComUtil::IUnknownCommon::~IUnknownCommon, |
236 | // which overwrites its instance memory with a known bad value after |
237 | // completing its destructor. |
238 | template < typename T > |
239 | void PoisonMem(T &val) |
240 | { |
241 | ZeroMemory((void*)&val, sizeof(T)); |
242 | } |
243 | |
244 | template < typename T > |
245 | void PoisonMem(T* ptr, size_t len) |
246 | { |
247 | ZeroMemory((void*)ptr, sizeof(T)* len); |
248 | } |
249 | } |
250 | } |
251 | #else |
252 | |
253 | // Empty versions of the functions in retail that will be inlined |
254 | // and completely elided. |
255 | namespace clr |
256 | { |
257 | namespace dbg |
258 | { |
259 | template < typename T > |
260 | inline void PoisonMem(T &) {} |
261 | |
262 | template < typename T > |
263 | void PoisonMem(T* ptr, size_t len){} |
264 | } |
265 | } |
266 | #endif |
267 | |
268 | #endif |
269 | |