Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / error.cpp
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 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7 XX                                                                           XX
8 XX                           error.cpp                                       XX
9 XX                                                                           XX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12 */
13
14 #include "jitpch.h"
15 #ifdef _MSC_VER
16 #pragma hdrstop
17 #endif
18 #include "compiler.h"
19
20 #if MEASURE_FATAL
21 unsigned fatal_badCode;
22 unsigned fatal_noWay;
23 unsigned fatal_NOMEM;
24 unsigned fatal_noWayAssertBody;
25 #ifdef DEBUG
26 unsigned fatal_noWayAssertBodyArgs;
27 #endif // DEBUG
28 unsigned fatal_NYI;
29 #endif // MEASURE_FATAL
30
31 /*****************************************************************************/
32 void DECLSPEC_NORETURN fatal(int errCode)
33 {
34 #ifdef DEBUG
35     if (errCode != CORJIT_SKIPPED) // Don't stop on NYI: use COMPlus_AltJitAssertOnNYI for that.
36     {
37         if (JitConfig.DebugBreakOnVerificationFailure())
38         {
39             DebugBreak();
40         }
41     }
42 #endif // DEBUG
43
44     ULONG_PTR exceptArg = errCode;
45     RaiseException(FATAL_JIT_EXCEPTION, EXCEPTION_NONCONTINUABLE, 1, &exceptArg);
46     UNREACHABLE();
47 }
48
49 /*****************************************************************************/
50 void DECLSPEC_NORETURN badCode()
51 {
52 #if MEASURE_FATAL
53     fatal_badCode += 1;
54 #endif // MEASURE_FATAL
55
56     fatal(CORJIT_BADCODE);
57 }
58
59 /*****************************************************************************/
60 void DECLSPEC_NORETURN noWay()
61 {
62 #if MEASURE_FATAL
63     fatal_noWay += 1;
64 #endif // MEASURE_FATAL
65
66     fatal(CORJIT_INTERNALERROR);
67 }
68
69 /*****************************************************************************/
70 void DECLSPEC_NORETURN NOMEM()
71 {
72 #if MEASURE_FATAL
73     fatal_NOMEM += 1;
74 #endif // MEASURE_FATAL
75
76     fatal(CORJIT_OUTOFMEM);
77 }
78
79 /*****************************************************************************/
80 void DECLSPEC_NORETURN noWayAssertBody()
81 {
82 #if MEASURE_FATAL
83     fatal_noWayAssertBody += 1;
84 #endif // MEASURE_FATAL
85
86 #ifndef DEBUG
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())
93     {
94         DebugBreak();
95     }
96 #endif // !DEBUG
97
98     fatal(CORJIT_RECOVERABLEERROR);
99 }
100
101 inline static bool ShouldThrowOnNoway(
102 #ifdef FEATURE_TRACELOGGING
103     const char* filename, unsigned line
104 #endif
105     )
106 {
107     return JitTls::GetCompiler() == nullptr ||
108            JitTls::GetCompiler()->compShouldThrowOnNoway(
109 #ifdef FEATURE_TRACELOGGING
110                filename, line
111 #endif
112                );
113 }
114
115 /*****************************************************************************/
116 void noWayAssertBodyConditional(
117 #ifdef FEATURE_TRACELOGGING
118     const char* filename, unsigned line
119 #endif
120     )
121 {
122 #ifdef FEATURE_TRACELOGGING
123     if (ShouldThrowOnNoway(filename, line))
124 #else
125     if (ShouldThrowOnNoway())
126 #endif // FEATURE_TRACELOGGING
127     {
128         noWayAssertBody();
129     }
130 }
131
132 #if defined(ALT_JIT) && (!defined(_TARGET_X86_) || !defined(LEGACY_BACKEND))
133
134 /*****************************************************************************/
135 void notYetImplemented(const char* msg, const char* filename, unsigned line)
136 {
137 #if FUNC_INFO_LOGGING
138 #ifdef DEBUG
139     LogEnv* env = JitTls::GetLogEnv();
140     if (env != nullptr)
141     {
142         const Compiler* const pCompiler = env->compiler;
143         if (pCompiler->verbose)
144         {
145             printf("\n\n%s - NYI (%s:%d - %s)\n", pCompiler->info.compFullName, filename, line, msg);
146         }
147     }
148     if (Compiler::compJitFuncInfoFile != nullptr)
149     {
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);
153     }
154 #else  // !DEBUG
155     if (Compiler::compJitFuncInfoFile != nullptr)
156     {
157         fprintf(Compiler::compJitFuncInfoFile, "NYI (%s:%d - %s)\n", filename, line, msg);
158         fflush(Compiler::compJitFuncInfoFile);
159     }
160 #endif // !DEBUG
161 #endif // FUNC_INFO_LOGGING
162
163 #ifdef DEBUG
164     Compiler* pCompiler = JitTls::GetCompiler();
165     if (pCompiler != nullptr)
166     {
167         // Assume we're within a compFunctionTrace boundary, which might not be true.
168         pCompiler->compFunctionTraceEnd(nullptr, 0, true);
169     }
170 #endif // DEBUG
171
172     DWORD value = JitConfig.AltJitAssertOnNYI();
173
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)
179     if (value & 1)
180     {
181 #ifdef DEBUG
182         assertAbort(msg, filename, line);
183 #endif
184     }
185
186     if ((value & 2) == 0)
187     {
188 #if MEASURE_FATAL
189         fatal_NYI += 1;
190 #endif // MEASURE_FATAL
191
192         fatal(CORJIT_SKIPPED);
193     }
194 }
195
196 #endif // #if defined(ALT_JIT) && (!defined(_TARGET_X86_) || !defined(LEGACY_BACKEND))
197
198 /*****************************************************************************/
199 LONG __JITfilter(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
200 {
201     DWORD exceptCode = pExceptionPointers->ExceptionRecord->ExceptionCode;
202
203     if (exceptCode == FATAL_JIT_EXCEPTION)
204     {
205         ErrorTrapParam* pParam = (ErrorTrapParam*)lpvParam;
206
207         assert(pExceptionPointers->ExceptionRecord->NumberParameters == 1);
208         pParam->errc = (int)pExceptionPointers->ExceptionRecord->ExceptionInformation[0];
209
210         ICorJitInfo* jitInfo = pParam->jitInfo;
211
212         if (jitInfo != nullptr)
213         {
214             jitInfo->reportFatalError((CorJitResult)pParam->errc);
215         }
216
217         return EXCEPTION_EXECUTE_HANDLER;
218     }
219
220     return EXCEPTION_CONTINUE_SEARCH;
221 }
222
223 /*****************************************************************************/
224 #ifdef DEBUG
225
226 DWORD getBreakOnBadCode()
227 {
228     return JitConfig.JitBreakOnBadCode();
229 }
230
231 /*****************************************************************************/
232 void debugError(const char* msg, const char* file, unsigned line)
233 {
234     const char* tail = strrchr(file, '\\');
235     if (tail)
236     {
237         file = tail + 1;
238     }
239
240     LogEnv* env = JitTls::GetLogEnv();
241
242     logf(LL_ERROR, "COMPILATION FAILED: file: %s:%d compiling method %s reason %s\n", file, line,
243          env->compiler->info.compFullName, msg);
244
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())
248     {
249         // Don't assert if verification is done.
250         if (!env->compiler->tiVerificationNeeded || getBreakOnBadCode())
251         {
252             assertAbort(msg, "NO-FILE", 0);
253         }
254     }
255
256     BreakIfDebuggerPresent();
257 }
258
259 /*****************************************************************************/
260 LogEnv::LogEnv(ICorJitInfo* aCompHnd) : compHnd(aCompHnd), compiler(nullptr)
261 {
262 }
263
264 /*****************************************************************************/
265 extern "C" void __cdecl assertAbort(const char* why, const char* file, unsigned line)
266 {
267     const char* msg       = why;
268     LogEnv*     env       = JitTls::GetLogEnv();
269     const int   BUFF_SIZE = 8192;
270     char*       buff      = (char*)alloca(BUFF_SIZE);
271     if (env->compiler)
272     {
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);
275         msg = buff;
276     }
277     printf(""); // null string means flush
278
279 #if FUNC_INFO_LOGGING
280     if (Compiler::compJitFuncInfoFile != nullptr)
281     {
282         fprintf(Compiler::compJitFuncInfoFile, "%s - Assertion failed (%s:%d - %s)\n",
283                 (env == nullptr) ? "UNKNOWN" : env->compiler->info.compFullName, file, line, why);
284     }
285 #endif // FUNC_INFO_LOGGING
286
287     if (env->compHnd->doAssert(file, line, msg))
288     {
289         DebugBreak();
290     }
291
292 #ifdef ALT_JIT
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)
302     {
303         fatal(CORJIT_SKIPPED);
304     }
305 #elif defined(_TARGET_ARM64_)
306     // TODO-ARM64-NYI: remove this after the JIT no longer asserts during startup
307     //
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.
310     //
311     bool interpreterFallback = (JitConfig.InterpreterFallback() != 0);
312
313     if (interpreterFallback)
314     {
315         fatal(CORJIT_SKIPPED);
316     }
317 #endif
318 }
319
320 /*********************************************************************/
321 BOOL vlogf(unsigned level, const char* fmt, va_list args)
322 {
323     return JitTls::GetLogEnv()->compHnd->logMsg(level, fmt, args);
324 }
325
326 int vflogf(FILE* file, const char* fmt, va_list args)
327 {
328     // 0-length string means flush
329     if (fmt[0] == '\0')
330     {
331         fflush(file);
332         return 0;
333     }
334
335     const int BUFF_SIZE = 8192;
336     char      buffer[BUFF_SIZE];
337     int       written = _vsnprintf_s(&buffer[0], BUFF_SIZE, _TRUNCATE, fmt, args);
338
339     if (JitConfig.JitDumpToDebugger())
340     {
341         OutputDebugStringA(buffer);
342     }
343
344     // We use fputs here so that this executes as fast a possible
345     fputs(&buffer[0], file);
346     return written;
347 }
348
349 int flogf(FILE* file, const char* fmt, ...)
350 {
351     va_list args;
352     va_start(args, fmt);
353     int written = vflogf(file, fmt, args);
354     va_end(args);
355     return written;
356 }
357
358 /*********************************************************************/
359 int logf(const char* fmt, ...)
360 {
361     va_list     args;
362     static bool logToEEfailed = false;
363     int         written       = 0;
364     //
365     // We remember when the EE failed to log, because vlogf()
366     // is very slow in a checked build.
367     //
368     // If it fails to log an LL_INFO1000 message once
369     // it will always fail when logging an LL_INFO1000 message.
370     //
371     if (!logToEEfailed)
372     {
373         va_start(args, fmt);
374         if (!vlogf(LL_INFO1000, fmt, args))
375         {
376             logToEEfailed = true;
377         }
378         va_end(args);
379     }
380
381     if (logToEEfailed)
382     {
383         // if the EE refuses to log it, we try to send it to stdout
384         va_start(args, fmt);
385         written = vflogf(jitstdout, fmt, args);
386         va_end(args);
387     }
388 #if 0  // Enable this only when you need it
389     else
390     {
391         //
392         // The EE just successfully logged our message
393         //
394         static ConfigDWORD fJitBreakOnDumpToken;
395         DWORD breakOnDumpToken = fJitBreakOnDumpToken.val(CLRConfig::INTERNAL_BreakOnDumpToken);
396         static DWORD forbidEntry = 0;
397         
398         if ((breakOnDumpToken != 0xffffffff) && (forbidEntry == 0)) 
399         {
400             forbidEntry = 1;
401             
402             // Use value of 0 to get the dump
403             static DWORD currentLine = 1;
404             
405             if (currentLine == breakOnDumpToken) 
406             {
407                 assert(!"Dump token reached");
408             }
409             
410             printf("(Token=0x%x) ", currentLine++);
411             forbidEntry = 0;
412         }
413     }
414 #endif // 0
415     va_end(args);
416
417     return written;
418 }
419
420 /*********************************************************************/
421 void gcDump_logf(const char* fmt, ...)
422 {
423     va_list     args;
424     static bool logToEEfailed = false;
425     //
426     // We remember when the EE failed to log, because vlogf()
427     // is very slow in a checked build.
428     //
429     // If it fails to log an LL_INFO1000 message once
430     // it will always fail when logging an LL_INFO1000 message.
431     //
432     if (!logToEEfailed)
433     {
434         va_start(args, fmt);
435         if (!vlogf(LL_INFO1000, fmt, args))
436         {
437             logToEEfailed = true;
438         }
439         va_end(args);
440     }
441
442     if (logToEEfailed)
443     {
444         // if the EE refuses to log it, we try to send it to stdout
445         va_start(args, fmt);
446         vflogf(jitstdout, fmt, args);
447         va_end(args);
448     }
449 #if 0  // Enable this only when you need it
450     else
451     {
452         //
453         // The EE just successfully logged our message
454         //
455         static ConfigDWORD fJitBreakOnDumpToken;
456         DWORD breakOnDumpToken = fJitBreakOnDumpToken.val(CLRConfig::INTERNAL_BreakOnDumpToken);
457         static DWORD forbidEntry = 0;
458         
459         if ((breakOnDumpToken != 0xffffffff) && (forbidEntry == 0)) 
460         {
461             forbidEntry = 1;
462             
463             // Use value of 0 to get the dump
464             static DWORD currentLine = 1;
465             
466             if (currentLine == breakOnDumpToken) 
467             {
468                 assert(!"Dump token reached");
469             }
470             
471             printf("(Token=0x%x) ", currentLine++);
472             forbidEntry = 0;
473         }
474     }
475 #endif // 0
476     va_end(args);
477 }
478
479 /*********************************************************************/
480 void logf(unsigned level, const char* fmt, ...)
481 {
482     va_list args;
483     va_start(args, fmt);
484     vlogf(level, fmt, args);
485     va_end(args);
486 }
487
488 void DECLSPEC_NORETURN badCode3(const char* msg, const char* msg2, int arg, __in_z const char* file, unsigned line)
489 {
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);
495
496     debugError(buf2, file, line);
497     badCode();
498 }
499
500 void noWayAssertAbortHelper(const char* cond, const char* file, unsigned line)
501 {
502     // Show the assert UI.
503     if (JitConfig.JitEnableNoWayAssert())
504     {
505         assertAbort(cond, file, line);
506     }
507 }
508
509 void noWayAssertBodyConditional(const char* cond, const char* file, unsigned line)
510 {
511 #ifdef FEATURE_TRACELOGGING
512     if (ShouldThrowOnNoway(file, line))
513 #else
514     if (ShouldThrowOnNoway())
515 #endif
516     {
517         noWayAssertBody(cond, file, line);
518     }
519     // In CHK we want the assert UI to show up in min-opts.
520     else
521     {
522         noWayAssertAbortHelper(cond, file, line);
523     }
524 }
525
526 void DECLSPEC_NORETURN noWayAssertBody(const char* cond, const char* file, unsigned line)
527 {
528 #if MEASURE_FATAL
529     fatal_noWayAssertBodyArgs += 1;
530 #endif // MEASURE_FATAL
531
532     noWayAssertAbortHelper(cond, file, line);
533     noWayAssertBody();
534 }
535
536 #endif // DEBUG