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.
5 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
21 unsigned fatal_badCode;
24 unsigned fatal_noWayAssertBody;
26 unsigned fatal_noWayAssertBodyArgs;
29 #endif // MEASURE_FATAL
31 /*****************************************************************************/
32 void DECLSPEC_NORETURN fatal(int errCode)
35 if (errCode != CORJIT_SKIPPED) // Don't stop on NYI: use COMPlus_AltJitAssertOnNYI for that.
37 if (JitConfig.DebugBreakOnVerificationFailure())
44 ULONG_PTR exceptArg = errCode;
45 RaiseException(FATAL_JIT_EXCEPTION, EXCEPTION_NONCONTINUABLE, 1, &exceptArg);
49 /*****************************************************************************/
50 void DECLSPEC_NORETURN badCode()
54 #endif // MEASURE_FATAL
56 fatal(CORJIT_BADCODE);
59 /*****************************************************************************/
60 void DECLSPEC_NORETURN noWay()
64 #endif // MEASURE_FATAL
66 fatal(CORJIT_INTERNALERROR);
69 /*****************************************************************************/
70 void DECLSPEC_NORETURN NOMEM()
74 #endif // MEASURE_FATAL
76 fatal(CORJIT_OUTOFMEM);
79 /*****************************************************************************/
80 void DECLSPEC_NORETURN noWayAssertBody()
83 fatal_noWayAssertBody += 1;
84 #endif // MEASURE_FATAL
87 // Even in retail, if we hit a noway, and we have this variable set, we don't want to fall back
88 // to MinOpts, which might hide a regression. Instead, hit a breakpoint (and crash). We don't
89 // have the assert code to fall back on here.
90 // The debug path goes through this function also, to do the call to 'fatal'.
91 // This kind of noway is hit for unreached().
92 if (JitConfig.JitEnableNoWayAssert())
98 fatal(CORJIT_RECOVERABLEERROR);
101 inline static bool ShouldThrowOnNoway(
102 #ifdef FEATURE_TRACELOGGING
103 const char* filename, unsigned line
107 return JitTls::GetCompiler() == nullptr ||
108 JitTls::GetCompiler()->compShouldThrowOnNoway(
109 #ifdef FEATURE_TRACELOGGING
115 /*****************************************************************************/
116 void noWayAssertBodyConditional(
117 #ifdef FEATURE_TRACELOGGING
118 const char* filename, unsigned line
122 #ifdef FEATURE_TRACELOGGING
123 if (ShouldThrowOnNoway(filename, line))
125 if (ShouldThrowOnNoway())
126 #endif // FEATURE_TRACELOGGING
132 #if defined(ALT_JIT) && (!defined(_TARGET_X86_) || !defined(LEGACY_BACKEND))
134 /*****************************************************************************/
135 void notYetImplemented(const char* msg, const char* filename, unsigned line)
137 #if FUNC_INFO_LOGGING
139 LogEnv* env = JitTls::GetLogEnv();
142 const Compiler* const pCompiler = env->compiler;
143 if (pCompiler->verbose)
145 printf("\n\n%s - NYI (%s:%d - %s)\n", pCompiler->info.compFullName, filename, line, msg);
148 if (Compiler::compJitFuncInfoFile != nullptr)
150 fprintf(Compiler::compJitFuncInfoFile, "%s - NYI (%s:%d - %s)\n",
151 (env == nullptr) ? "UNKNOWN" : env->compiler->info.compFullName, filename, line, msg);
152 fflush(Compiler::compJitFuncInfoFile);
155 if (Compiler::compJitFuncInfoFile != nullptr)
157 fprintf(Compiler::compJitFuncInfoFile, "NYI (%s:%d - %s)\n", filename, line, msg);
158 fflush(Compiler::compJitFuncInfoFile);
161 #endif // FUNC_INFO_LOGGING
164 Compiler* pCompiler = JitTls::GetCompiler();
165 if (pCompiler != nullptr)
167 // Assume we're within a compFunctionTrace boundary, which might not be true.
168 pCompiler->compFunctionTraceEnd(nullptr, 0, true);
172 DWORD value = JitConfig.AltJitAssertOnNYI();
174 // 0 means just silently skip
175 // If we are in retail builds, assume ignore
176 // 1 means popup the assert (abort=abort, retry=debugger, ignore=skip)
177 // 2 means silently don't skip (same as 3 for retail)
178 // 3 means popup the assert (abort=abort, retry=debugger, ignore=don't skip)
182 assertAbort(msg, filename, line);
186 if ((value & 2) == 0)
190 #endif // MEASURE_FATAL
192 fatal(CORJIT_SKIPPED);
196 #endif // #if defined(ALT_JIT) && (!defined(_TARGET_X86_) || !defined(LEGACY_BACKEND))
198 /*****************************************************************************/
199 LONG __JITfilter(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
201 DWORD exceptCode = pExceptionPointers->ExceptionRecord->ExceptionCode;
203 if (exceptCode == FATAL_JIT_EXCEPTION)
205 ErrorTrapParam* pParam = (ErrorTrapParam*)lpvParam;
207 assert(pExceptionPointers->ExceptionRecord->NumberParameters == 1);
208 pParam->errc = (int)pExceptionPointers->ExceptionRecord->ExceptionInformation[0];
210 ICorJitInfo* jitInfo = pParam->jitInfo;
212 if (jitInfo != nullptr)
214 jitInfo->reportFatalError((CorJitResult)pParam->errc);
217 return EXCEPTION_EXECUTE_HANDLER;
220 return EXCEPTION_CONTINUE_SEARCH;
223 /*****************************************************************************/
226 DWORD getBreakOnBadCode()
228 return JitConfig.JitBreakOnBadCode();
231 /*****************************************************************************/
232 void debugError(const char* msg, const char* file, unsigned line)
234 const char* tail = strrchr(file, '\\');
240 LogEnv* env = JitTls::GetLogEnv();
242 logf(LL_ERROR, "COMPILATION FAILED: file: %s:%d compiling method %s reason %s\n", file, line,
243 env->compiler->info.compFullName, msg);
245 // We now only assert when user explicitly set ComPlus_JitRequired=1
246 // If ComPlus_JitRequired is 0 or is not set, we will not assert.
247 if (JitConfig.JitRequired() == 1 || getBreakOnBadCode())
249 // Don't assert if verification is done.
250 if (!env->compiler->tiVerificationNeeded || getBreakOnBadCode())
252 assertAbort(msg, "NO-FILE", 0);
256 BreakIfDebuggerPresent();
259 /*****************************************************************************/
260 LogEnv::LogEnv(ICorJitInfo* aCompHnd) : compHnd(aCompHnd), compiler(nullptr)
264 /*****************************************************************************/
265 extern "C" void __cdecl assertAbort(const char* why, const char* file, unsigned line)
267 const char* msg = why;
268 LogEnv* env = JitTls::GetLogEnv();
269 const int BUFF_SIZE = 8192;
270 char* buff = (char*)alloca(BUFF_SIZE);
273 _snprintf_s(buff, BUFF_SIZE, _TRUNCATE, "Assertion failed '%s' in '%s' (IL size %d)\n", why,
274 env->compiler->info.compFullName, env->compiler->info.compILCodeSize);
277 printf(""); // null string means flush
279 #if FUNC_INFO_LOGGING
280 if (Compiler::compJitFuncInfoFile != nullptr)
282 fprintf(Compiler::compJitFuncInfoFile, "%s - Assertion failed (%s:%d - %s)\n",
283 (env == nullptr) ? "UNKNOWN" : env->compiler->info.compFullName, file, line, why);
285 #endif // FUNC_INFO_LOGGING
287 if (env->compHnd->doAssert(file, line, msg))
293 // If we hit an assert, and we got here, it's either because the user hit "ignore" on the
294 // dialog pop-up, or they set COMPlus_ContinueOnAssert=1 to not emit a pop-up, but just continue.
295 // If we're an altjit, we have two options: (1) silently continue, as a normal JIT would, probably
296 // leading to additional asserts, or (2) tell the VM that the AltJit wants to skip this function,
297 // thus falling back to the fallback JIT. Setting COMPlus_AltJitSkipOnAssert=1 chooses this "skip"
298 // to the fallback JIT behavior. This is useful when doing ASM diffs, where we only want to see
299 // the first assert for any function, but we don't want to kill the whole ngen process on the
300 // first assert (which would happen if you used COMPlus_NoGuiOnAssert=1 for example).
301 if (JitConfig.AltJitSkipOnAssert() != 0)
303 fatal(CORJIT_SKIPPED);
305 #elif defined(_TARGET_ARM64_)
306 // TODO-ARM64-NYI: remove this after the JIT no longer asserts during startup
308 // When we are bringing up the new Arm64 JIT we set COMPlus_ContinueOnAssert=1
309 // We only want to hit one assert then we will fall back to the interpreter.
311 bool interpreterFallback = (JitConfig.InterpreterFallback() != 0);
313 if (interpreterFallback)
315 fatal(CORJIT_SKIPPED);
320 /*********************************************************************/
321 BOOL vlogf(unsigned level, const char* fmt, va_list args)
323 return JitTls::GetLogEnv()->compHnd->logMsg(level, fmt, args);
326 int vflogf(FILE* file, const char* fmt, va_list args)
328 // 0-length string means flush
335 const int BUFF_SIZE = 8192;
336 char buffer[BUFF_SIZE];
337 int written = _vsnprintf_s(&buffer[0], BUFF_SIZE, _TRUNCATE, fmt, args);
339 if (JitConfig.JitDumpToDebugger())
341 OutputDebugStringA(buffer);
344 // We use fputs here so that this executes as fast a possible
345 fputs(&buffer[0], file);
349 int flogf(FILE* file, const char* fmt, ...)
353 int written = vflogf(file, fmt, args);
358 /*********************************************************************/
359 int logf(const char* fmt, ...)
362 static bool logToEEfailed = false;
365 // We remember when the EE failed to log, because vlogf()
366 // is very slow in a checked build.
368 // If it fails to log an LL_INFO1000 message once
369 // it will always fail when logging an LL_INFO1000 message.
374 if (!vlogf(LL_INFO1000, fmt, args))
376 logToEEfailed = true;
383 // if the EE refuses to log it, we try to send it to stdout
385 written = vflogf(jitstdout, fmt, args);
388 #if 0 // Enable this only when you need it
392 // The EE just successfully logged our message
394 static ConfigDWORD fJitBreakOnDumpToken;
395 DWORD breakOnDumpToken = fJitBreakOnDumpToken.val(CLRConfig::INTERNAL_BreakOnDumpToken);
396 static DWORD forbidEntry = 0;
398 if ((breakOnDumpToken != 0xffffffff) && (forbidEntry == 0))
402 // Use value of 0 to get the dump
403 static DWORD currentLine = 1;
405 if (currentLine == breakOnDumpToken)
407 assert(!"Dump token reached");
410 printf("(Token=0x%x) ", currentLine++);
420 /*********************************************************************/
421 void gcDump_logf(const char* fmt, ...)
424 static bool logToEEfailed = false;
426 // We remember when the EE failed to log, because vlogf()
427 // is very slow in a checked build.
429 // If it fails to log an LL_INFO1000 message once
430 // it will always fail when logging an LL_INFO1000 message.
435 if (!vlogf(LL_INFO1000, fmt, args))
437 logToEEfailed = true;
444 // if the EE refuses to log it, we try to send it to stdout
446 vflogf(jitstdout, fmt, args);
449 #if 0 // Enable this only when you need it
453 // The EE just successfully logged our message
455 static ConfigDWORD fJitBreakOnDumpToken;
456 DWORD breakOnDumpToken = fJitBreakOnDumpToken.val(CLRConfig::INTERNAL_BreakOnDumpToken);
457 static DWORD forbidEntry = 0;
459 if ((breakOnDumpToken != 0xffffffff) && (forbidEntry == 0))
463 // Use value of 0 to get the dump
464 static DWORD currentLine = 1;
466 if (currentLine == breakOnDumpToken)
468 assert(!"Dump token reached");
471 printf("(Token=0x%x) ", currentLine++);
479 /*********************************************************************/
480 void logf(unsigned level, const char* fmt, ...)
484 vlogf(level, fmt, args);
488 void DECLSPEC_NORETURN badCode3(const char* msg, const char* msg2, int arg, __in_z const char* file, unsigned line)
490 const int BUFF_SIZE = 512;
491 char buf1[BUFF_SIZE];
492 char buf2[BUFF_SIZE];
493 sprintf_s(buf1, BUFF_SIZE, "%s%s", msg, msg2);
494 sprintf_s(buf2, BUFF_SIZE, buf1, arg);
496 debugError(buf2, file, line);
500 void noWayAssertAbortHelper(const char* cond, const char* file, unsigned line)
502 // Show the assert UI.
503 if (JitConfig.JitEnableNoWayAssert())
505 assertAbort(cond, file, line);
509 void noWayAssertBodyConditional(const char* cond, const char* file, unsigned line)
511 #ifdef FEATURE_TRACELOGGING
512 if (ShouldThrowOnNoway(file, line))
514 if (ShouldThrowOnNoway())
517 noWayAssertBody(cond, file, line);
519 // In CHK we want the assert UI to show up in min-opts.
522 noWayAssertAbortHelper(cond, file, line);
526 void DECLSPEC_NORETURN noWayAssertBody(const char* cond, const char* file, unsigned line)
529 fatal_noWayAssertBodyArgs += 1;
530 #endif // MEASURE_FATAL
532 noWayAssertAbortHelper(cond, file, line);