| 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 | |