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);
102 inline static bool ShouldThrowOnNoway(
103 #ifdef FEATURE_TRACELOGGING
104 const char* filename, unsigned line
108 return JitTls::GetCompiler() == NULL ||
109 JitTls::GetCompiler()->compShouldThrowOnNoway(
110 #ifdef FEATURE_TRACELOGGING
116 /*****************************************************************************/
117 void noWayAssertBodyConditional(
118 #ifdef FEATURE_TRACELOGGING
119 const char* filename, unsigned line
123 #ifdef FEATURE_TRACELOGGING
124 if (ShouldThrowOnNoway(filename, line))
126 if (ShouldThrowOnNoway())
127 #endif // FEATURE_TRACELOGGING
133 #if !defined(_TARGET_X86_) || !defined(LEGACY_BACKEND)
135 /*****************************************************************************/
136 void notYetImplemented(const char * msg, const char * filename, unsigned line)
138 #if FUNC_INFO_LOGGING
140 LogEnv* env = JitTls::GetLogEnv();
143 const Compiler* const pCompiler = env->compiler;
144 if (pCompiler->verbose)
146 printf("\n\n%s - NYI (%s:%d - %s)\n", pCompiler->info.compFullName,
152 if (Compiler::compJitFuncInfoFile != NULL)
154 fprintf(Compiler::compJitFuncInfoFile, "%s - NYI (%s:%d - %s)\n",
155 (env == NULL) ? "UNKNOWN" : env->compiler->info.compFullName,
159 fflush(Compiler::compJitFuncInfoFile);
162 if (Compiler::compJitFuncInfoFile != NULL)
164 fprintf(Compiler::compJitFuncInfoFile, "NYI (%s:%d - %s)\n",
168 fflush(Compiler::compJitFuncInfoFile);
171 #endif // FUNC_INFO_LOGGING
174 Compiler* pCompiler = JitTls::GetCompiler();
175 if (pCompiler != nullptr)
177 // Assume we're within a compFunctionTrace boundary, which might not be true.
178 pCompiler->compFunctionTraceEnd(nullptr, 0, true);
182 DWORD value = JitConfig.AltJitAssertOnNYI();
184 // 0 means just silently skip
185 // If we are in retail builds, assume ignore
186 // 1 means popup the assert (abort=abort, retry=debugger, ignore=skip)
187 // 2 means silently don't skip (same as 3 for retail)
188 // 3 means popup the assert (abort=abort, retry=debugger, ignore=don't skip)
192 assertAbort(msg, filename, line);
196 if ((value & 2) == 0)
200 #endif // MEASURE_FATAL
202 fatal(CORJIT_SKIPPED);
206 #endif // #if !defined(_TARGET_X86_) || !defined(LEGACY_BACKEND)
208 /*****************************************************************************/
209 LONG __JITfilter(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
211 DWORD exceptCode = pExceptionPointers->ExceptionRecord->ExceptionCode;
213 if (exceptCode == FATAL_JIT_EXCEPTION)
215 ErrorTrapParam * pParam = (ErrorTrapParam *)lpvParam;
217 assert(pExceptionPointers->ExceptionRecord->NumberParameters == 1);
218 pParam->errc = (int)pExceptionPointers->ExceptionRecord->ExceptionInformation[0];
220 ICorJitInfo * jitInfo = pParam->jitInfo;
223 jitInfo->reportFatalError((CorJitResult)pParam->errc);
225 return EXCEPTION_EXECUTE_HANDLER;
228 return EXCEPTION_CONTINUE_SEARCH;
231 /*****************************************************************************/
234 DWORD getBreakOnBadCode()
236 return JitConfig.JitBreakOnBadCode();
239 /*****************************************************************************/
240 void debugError(const char* msg, const char* file, unsigned line)
242 const char* tail = strrchr(file, '\\');
243 if (tail) file = tail+1;
245 LogEnv* env = JitTls::GetLogEnv();
247 logf(LL_ERROR, "COMPILATION FAILED: file: %s:%d compiling method %s reason %s\n", file, line, env->compiler->info.compFullName, msg);
249 // We now only assert when user explicitly set ComPlus_JitRequired=1
250 // If ComPlus_JitRequired is 0 or is not set, we will not assert.
251 if (JitConfig.JitRequired() == 1 || getBreakOnBadCode())
253 // Don't assert if verification is done.
254 if (!env->compiler->tiVerificationNeeded || getBreakOnBadCode())
255 assertAbort(msg, "NO-FILE", 0);
258 BreakIfDebuggerPresent();
262 /*****************************************************************************/
263 LogEnv::LogEnv(ICorJitInfo* aCompHnd)
269 /*****************************************************************************/
271 void __cdecl assertAbort(const char *why, const char *file, unsigned line)
273 const char* msg = why;
274 LogEnv* env = JitTls::GetLogEnv();
275 const int BUFF_SIZE = 8192;
276 char *buff = (char*)alloca(BUFF_SIZE);
278 _snprintf_s(buff, BUFF_SIZE, _TRUNCATE, "Assertion failed '%s' in '%s' (IL size %d)\n", why, env->compiler->info.compFullName, env->compiler->info.compILCodeSize);
281 printf(""); // null string means flush
283 #if FUNC_INFO_LOGGING
284 if (Compiler::compJitFuncInfoFile != NULL)
286 fprintf(Compiler::compJitFuncInfoFile, "%s - Assertion failed (%s:%d - %s)\n",
287 (env == NULL) ? "UNKNOWN" : env->compiler->info.compFullName,
292 #endif // FUNC_INFO_LOGGING
294 if (env->compHnd->doAssert(file, line, msg))
298 // If we hit an assert, and we got here, it's either because the user hit "ignore" on the
299 // dialog pop-up, or they set COMPlus_ContinueOnAssert=1 to not emit a pop-up, but just continue.
300 // If we're an altjit, we have two options: (1) silently continue, as a normal JIT would, probably
301 // leading to additional asserts, or (2) tell the VM that the AltJit wants to skip this function,
302 // thus falling back to the fallback JIT. Setting COMPlus_AltJitSkipOnAssert=1 chooses this "skip"
303 // to the fallback JIT behavior. This is useful when doing ASM diffs, where we only want to see
304 // the first assert for any function, but we don't want to kill the whole ngen process on the
305 // first assert (which would happen if you used COMPlus_NoGuiOnAssert=1 for example).
306 if (JitConfig.AltJitSkipOnAssert() != 0)
308 fatal(CORJIT_SKIPPED);
310 #elif defined(_TARGET_ARM64_)
311 // TODO-ARM64-NYI: remove this after the JIT no longer asserts during startup
313 // When we are bringing up the new Arm64 JIT we set COMPlus_ContinueOnAssert=1
314 // We only want to hit one assert then we will fall back to the interpreter.
316 bool interpreterFallback = (JitConfig.InterpreterFallback() != 0);
318 if (interpreterFallback)
320 fatal(CORJIT_SKIPPED);
325 /*********************************************************************/
326 BOOL vlogf(unsigned level, const char* fmt, va_list args)
328 return JitTls::GetLogEnv()->compHnd->logMsg(level, fmt, args);
331 int vflogf(FILE* file, const char* fmt, va_list args)
333 // 0-length string means flush
340 const int BUFF_SIZE = 8192;
341 char buffer[BUFF_SIZE];
342 int written = _vsnprintf_s(&buffer[0], BUFF_SIZE, _TRUNCATE, fmt, args);
344 if (JitConfig.JitDumpToDebugger())
346 OutputDebugStringA(buffer);
349 // We use fputs here so that this executes as fast a possible
350 fputs(&buffer[0], file);
354 int flogf(FILE* file, const char* fmt, ...)
358 int written = vflogf(file, fmt, args);
363 /*********************************************************************/
364 int logf(const char* fmt, ...)
367 static bool logToEEfailed = false;
370 // We remember when the EE failed to log, because vlogf()
371 // is very slow in a checked build.
373 // If it fails to log an LL_INFO1000 message once
374 // it will always fail when logging an LL_INFO1000 message.
379 if (!vlogf(LL_INFO1000, fmt, args))
380 logToEEfailed = true;
386 // if the EE refuses to log it, we try to send it to stdout
388 written = vflogf(jitstdout, fmt, args);
391 #if 0 // Enable this only when you need it
395 // The EE just successfully logged our message
397 static ConfigDWORD fJitBreakOnDumpToken;
398 DWORD breakOnDumpToken = fJitBreakOnDumpToken.val(CLRConfig::INTERNAL_BreakOnDumpToken);
399 static DWORD forbidEntry = 0;
401 if ((breakOnDumpToken != 0xffffffff) && (forbidEntry == 0))
405 // Use value of 0 to get the dump
406 static DWORD currentLine = 1;
408 if (currentLine == breakOnDumpToken)
410 assert(!"Dump token reached");
413 printf("(Token=0x%x) ", currentLine++);
423 /*********************************************************************/
424 void gcDump_logf(const char* fmt, ...)
427 static bool logToEEfailed = false;
429 // We remember when the EE failed to log, because vlogf()
430 // is very slow in a checked build.
432 // If it fails to log an LL_INFO1000 message once
433 // it will always fail when logging an LL_INFO1000 message.
438 if (!vlogf(LL_INFO1000, fmt, args))
439 logToEEfailed = true;
445 // if the EE refuses to log it, we try to send it to stdout
447 vflogf(jitstdout, fmt, args);
450 #if 0 // Enable this only when you need it
454 // The EE just successfully logged our message
456 static ConfigDWORD fJitBreakOnDumpToken;
457 DWORD breakOnDumpToken = fJitBreakOnDumpToken.val(CLRConfig::INTERNAL_BreakOnDumpToken);
458 static DWORD forbidEntry = 0;
460 if ((breakOnDumpToken != 0xffffffff) && (forbidEntry == 0))
464 // Use value of 0 to get the dump
465 static DWORD currentLine = 1;
467 if (currentLine == breakOnDumpToken)
469 assert(!"Dump token reached");
472 printf("(Token=0x%x) ", currentLine++);
480 /*********************************************************************/
481 void logf(unsigned level, const char* fmt, ...)
485 vlogf(level, fmt, args);
489 void DECLSPEC_NORETURN badCode3(const char* msg, const char* msg2, int arg,
490 __in_z const char* file, unsigned line)
492 const int BUFF_SIZE = 512;
493 char buf1[BUFF_SIZE];
494 char buf2[BUFF_SIZE];
495 sprintf_s(buf1, BUFF_SIZE, "%s%s", msg, msg2);
496 sprintf_s(buf2, BUFF_SIZE, buf1, arg);
498 debugError(buf2, file, line);
502 void noWayAssertAbortHelper(const char * cond, const char * file, unsigned line)
504 // Show the assert UI.
505 if (JitConfig.JitEnableNoWayAssert())
507 assertAbort(cond, file, line);
511 void noWayAssertBodyConditional(const char * cond, const char * file, unsigned line)
513 #ifdef FEATURE_TRACELOGGING
514 if (ShouldThrowOnNoway(file, line))
516 if (ShouldThrowOnNoway())
519 noWayAssertBody(cond, file, line);
521 // In CHK we want the assert UI to show up in min-opts.
524 noWayAssertAbortHelper(cond, file, line);
528 void DECLSPEC_NORETURN noWayAssertBody(const char * cond, const char * file, unsigned line)
531 fatal_noWayAssertBodyArgs += 1;
532 #endif // MEASURE_FATAL
534 noWayAssertAbortHelper(cond, file, line);