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 | |
6 | // |
7 | // --------------------------------------------------------------------------- |
8 | // EEPolicy.cpp |
9 | // --------------------------------------------------------------------------- |
10 | |
11 | |
12 | #include "common.h" |
13 | #include "eepolicy.h" |
14 | #include "corhost.h" |
15 | #include "dbginterface.h" |
16 | #include "eemessagebox.h" |
17 | |
18 | #include "eventreporter.h" |
19 | #include "finalizerthread.h" |
20 | #include "threadsuspend.h" |
21 | |
22 | #include "typestring.h" |
23 | |
24 | #ifndef FEATURE_PAL |
25 | #include "dwreport.h" |
26 | #endif // !FEATURE_PAL |
27 | |
28 | #include "eventtrace.h" |
29 | #undef ExitProcess |
30 | |
31 | BYTE g_EEPolicyInstance[sizeof(EEPolicy)]; |
32 | |
33 | void InitEEPolicy() |
34 | { |
35 | WRAPPER_NO_CONTRACT; |
36 | new (g_EEPolicyInstance) EEPolicy(); |
37 | } |
38 | |
39 | EEPolicy::EEPolicy () |
40 | { |
41 | CONTRACTL |
42 | { |
43 | GC_NOTRIGGER; |
44 | NOTHROW; |
45 | } |
46 | CONTRACTL_END; |
47 | |
48 | int n; |
49 | for (n = 0; n < MaxClrOperation; n++) { |
50 | m_Timeout[n] = INFINITE; |
51 | m_ActionOnTimeout[n] = eNoAction; |
52 | m_DefaultAction[n] = eNoAction; |
53 | } |
54 | m_Timeout[OPR_ProcessExit] = 40000; |
55 | m_ActionOnTimeout[OPR_ProcessExit] = eRudeExitProcess; |
56 | m_ActionOnTimeout[OPR_ThreadAbort] = eAbortThread; |
57 | m_ActionOnTimeout[OPR_ThreadRudeAbortInNonCriticalRegion] = eRudeAbortThread; |
58 | m_ActionOnTimeout[OPR_ThreadRudeAbortInCriticalRegion] = eRudeAbortThread; |
59 | |
60 | m_DefaultAction[OPR_ThreadAbort] = eAbortThread; |
61 | m_DefaultAction[OPR_ThreadRudeAbortInNonCriticalRegion] = eRudeAbortThread; |
62 | m_DefaultAction[OPR_ThreadRudeAbortInCriticalRegion] = eRudeAbortThread; |
63 | m_DefaultAction[OPR_AppDomainUnload] = eUnloadAppDomain; |
64 | m_DefaultAction[OPR_AppDomainRudeUnload] = eRudeUnloadAppDomain; |
65 | m_DefaultAction[OPR_ProcessExit] = eExitProcess; |
66 | m_DefaultAction[OPR_FinalizerRun] = eNoAction; |
67 | |
68 | for (n = 0; n < MaxClrFailure; n++) { |
69 | m_ActionOnFailure[n] = eNoAction; |
70 | } |
71 | m_ActionOnFailure[FAIL_CriticalResource] = eThrowException; |
72 | m_ActionOnFailure[FAIL_NonCriticalResource] = eThrowException; |
73 | m_ActionOnFailure[FAIL_OrphanedLock] = eNoAction; |
74 | m_ActionOnFailure[FAIL_FatalRuntime] = eRudeExitProcess; |
75 | // For CoreCLR, initialize the default action for AV processing to all |
76 | // all kind of code to catch AV exception. If the host wants, they can |
77 | // specify a different action for this. |
78 | m_ActionOnFailure[FAIL_AccessViolation] = eNoAction; |
79 | m_ActionOnFailure[FAIL_StackOverflow] = eRudeExitProcess; |
80 | m_ActionOnFailure[FAIL_CodeContract] = eThrowException; |
81 | m_unhandledExceptionPolicy = eRuntimeDeterminedPolicy; |
82 | } |
83 | |
84 | BOOL EEPolicy::IsValidActionForOperation(EClrOperation operation, EPolicyAction action) |
85 | { |
86 | CONTRACTL |
87 | { |
88 | GC_NOTRIGGER; |
89 | NOTHROW; |
90 | } |
91 | CONTRACTL_END; |
92 | |
93 | switch (operation) { |
94 | case OPR_ThreadAbort: |
95 | return action >= eAbortThread && |
96 | action < MaxPolicyAction; |
97 | case OPR_ThreadRudeAbortInNonCriticalRegion: |
98 | case OPR_ThreadRudeAbortInCriticalRegion: |
99 | return action >= eRudeAbortThread && action != eUnloadAppDomain && |
100 | action < MaxPolicyAction; |
101 | case OPR_AppDomainUnload: |
102 | return action >= eUnloadAppDomain && |
103 | action < MaxPolicyAction; |
104 | case OPR_AppDomainRudeUnload: |
105 | return action >= eRudeUnloadAppDomain && |
106 | action < MaxPolicyAction; |
107 | case OPR_ProcessExit: |
108 | return action >= eExitProcess && |
109 | action < MaxPolicyAction; |
110 | case OPR_FinalizerRun: |
111 | return action == eNoAction || |
112 | (action >= eAbortThread && |
113 | action < MaxPolicyAction); |
114 | default: |
115 | _ASSERT (!"Do not know valid action for this operation" ); |
116 | break; |
117 | } |
118 | return FALSE; |
119 | } |
120 | |
121 | BOOL EEPolicy::IsValidActionForTimeout(EClrOperation operation, EPolicyAction action) |
122 | { |
123 | CONTRACTL |
124 | { |
125 | GC_NOTRIGGER; |
126 | NOTHROW; |
127 | } |
128 | CONTRACTL_END; |
129 | |
130 | switch (operation) { |
131 | case OPR_ThreadAbort: |
132 | return action > eAbortThread && |
133 | action < MaxPolicyAction; |
134 | case OPR_ThreadRudeAbortInNonCriticalRegion: |
135 | case OPR_ThreadRudeAbortInCriticalRegion: |
136 | return action > eRudeUnloadAppDomain && |
137 | action < MaxPolicyAction; |
138 | case OPR_AppDomainUnload: |
139 | return action > eUnloadAppDomain && |
140 | action < MaxPolicyAction; |
141 | case OPR_AppDomainRudeUnload: |
142 | return action > eRudeUnloadAppDomain && |
143 | action < MaxPolicyAction; |
144 | case OPR_ProcessExit: |
145 | return action > eExitProcess && |
146 | action < MaxPolicyAction; |
147 | case OPR_FinalizerRun: |
148 | return action == eNoAction || |
149 | (action >= eAbortThread && |
150 | action < MaxPolicyAction); |
151 | default: |
152 | _ASSERT (!"Do not know valid action for this operation" ); |
153 | break; |
154 | } |
155 | return FALSE; |
156 | } |
157 | |
158 | BOOL EEPolicy::IsValidActionForFailure(EClrFailure failure, EPolicyAction action) |
159 | { |
160 | CONTRACTL |
161 | { |
162 | GC_NOTRIGGER; |
163 | NOTHROW; |
164 | } |
165 | CONTRACTL_END; |
166 | |
167 | switch (failure) { |
168 | case FAIL_NonCriticalResource: |
169 | return action >= eThrowException && |
170 | action < MaxPolicyAction; |
171 | case FAIL_CriticalResource: |
172 | return action >= eThrowException && |
173 | action < MaxPolicyAction; |
174 | case FAIL_FatalRuntime: |
175 | return action >= eRudeExitProcess && |
176 | action < MaxPolicyAction; |
177 | case FAIL_OrphanedLock: |
178 | return action >= eUnloadAppDomain && |
179 | action < MaxPolicyAction; |
180 | case FAIL_AccessViolation: |
181 | // Allowed actions on failure are: |
182 | // |
183 | // eNoAction or eRudeExitProcess. |
184 | return ((action == eNoAction) || (action == eRudeExitProcess)); |
185 | case FAIL_StackOverflow: |
186 | return action >= eRudeUnloadAppDomain && |
187 | action < MaxPolicyAction; |
188 | case FAIL_CodeContract: |
189 | return action >= eThrowException && |
190 | action <= eExitProcess; |
191 | default: |
192 | _ASSERTE (!"Do not know valid action for this failure" ); |
193 | break; |
194 | } |
195 | |
196 | return FALSE; |
197 | } |
198 | |
199 | HRESULT EEPolicy::SetTimeout(EClrOperation operation, DWORD timeout) |
200 | { |
201 | CONTRACTL |
202 | { |
203 | MODE_ANY; |
204 | GC_NOTRIGGER; |
205 | NOTHROW; |
206 | } |
207 | CONTRACTL_END; |
208 | |
209 | if (static_cast<UINT>(operation) < MaxClrOperation) |
210 | { |
211 | m_Timeout[operation] = timeout; |
212 | if (operation == OPR_FinalizerRun && |
213 | g_fEEStarted) |
214 | { |
215 | FastInterlockOr((DWORD*)&g_FinalizerWaiterStatus, FWS_WaitInterrupt); |
216 | FinalizerThread::SignalFinalizationDone(FALSE); |
217 | } |
218 | return S_OK; |
219 | } |
220 | else |
221 | { |
222 | return E_INVALIDARG; |
223 | } |
224 | } |
225 | |
226 | HRESULT EEPolicy::SetActionOnTimeout(EClrOperation operation, EPolicyAction action) |
227 | { |
228 | CONTRACTL |
229 | { |
230 | GC_NOTRIGGER; |
231 | NOTHROW; |
232 | } |
233 | CONTRACTL_END; |
234 | |
235 | if (static_cast<UINT>(operation) < MaxClrOperation && |
236 | IsValidActionForTimeout(operation, action)) |
237 | { |
238 | m_ActionOnTimeout[operation] = action; |
239 | return S_OK; |
240 | } |
241 | else |
242 | { |
243 | return E_INVALIDARG; |
244 | } |
245 | } |
246 | |
247 | EPolicyAction EEPolicy::GetFinalAction(EPolicyAction action, Thread *pThread) |
248 | { |
249 | LIMITED_METHOD_CONTRACT; |
250 | _ASSERTE(static_cast<UINT>(action) < MaxPolicyAction); |
251 | |
252 | if (action < eAbortThread || action > eFastExitProcess) |
253 | { |
254 | return action; |
255 | } |
256 | |
257 | while(TRUE) |
258 | { |
259 | // Look at default action. If the default action is more severe, |
260 | // use the default action instead. |
261 | EPolicyAction defaultAction = action; |
262 | switch (action) |
263 | { |
264 | case eAbortThread: |
265 | defaultAction = m_DefaultAction[OPR_ThreadAbort]; |
266 | break; |
267 | case eRudeAbortThread: |
268 | if (pThread && !pThread->HasLockInCurrentDomain()) |
269 | { |
270 | defaultAction = m_DefaultAction[OPR_ThreadRudeAbortInNonCriticalRegion]; |
271 | } |
272 | else |
273 | { |
274 | defaultAction = m_DefaultAction[OPR_ThreadRudeAbortInCriticalRegion]; |
275 | } |
276 | break; |
277 | case eUnloadAppDomain: |
278 | defaultAction = m_DefaultAction[OPR_AppDomainUnload]; |
279 | break; |
280 | case eRudeUnloadAppDomain: |
281 | defaultAction = m_DefaultAction[OPR_AppDomainRudeUnload]; |
282 | break; |
283 | case eExitProcess: |
284 | case eFastExitProcess: |
285 | defaultAction = m_DefaultAction[OPR_ProcessExit]; |
286 | if (defaultAction < action) |
287 | { |
288 | defaultAction = action; |
289 | } |
290 | break; |
291 | default: |
292 | break; |
293 | } |
294 | _ASSERTE(static_cast<UINT>(defaultAction) < MaxPolicyAction); |
295 | |
296 | if (defaultAction == action) |
297 | { |
298 | return action; |
299 | } |
300 | |
301 | _ASSERTE(defaultAction > action); |
302 | action = defaultAction; |
303 | } |
304 | } |
305 | |
306 | // Allow setting timeout and action in one call. |
307 | // If we decide to have atomical operation on Policy, we can use lock here |
308 | // while SetTimeout and SetActionOnTimeout can not. |
309 | HRESULT EEPolicy::SetTimeoutAndAction(EClrOperation operation, DWORD timeout, EPolicyAction action) |
310 | { |
311 | CONTRACTL |
312 | { |
313 | GC_NOTRIGGER; |
314 | NOTHROW; |
315 | } |
316 | CONTRACTL_END; |
317 | |
318 | if (static_cast<UINT>(operation) < MaxClrOperation && |
319 | IsValidActionForTimeout(operation, action)) |
320 | { |
321 | m_ActionOnTimeout[operation] = action; |
322 | m_Timeout[operation] = timeout; |
323 | if (operation == OPR_FinalizerRun && |
324 | g_fEEStarted) |
325 | { |
326 | FastInterlockOr((DWORD*)&g_FinalizerWaiterStatus, FWS_WaitInterrupt); |
327 | FinalizerThread::SignalFinalizationDone(FALSE); |
328 | } |
329 | return S_OK; |
330 | } |
331 | else |
332 | { |
333 | return E_INVALIDARG; |
334 | } |
335 | } |
336 | |
337 | HRESULT EEPolicy::SetDefaultAction(EClrOperation operation, EPolicyAction action) |
338 | { |
339 | CONTRACTL |
340 | { |
341 | GC_NOTRIGGER; |
342 | NOTHROW; |
343 | } |
344 | CONTRACTL_END; |
345 | |
346 | if (static_cast<UINT>(operation) < MaxClrOperation && |
347 | IsValidActionForOperation(operation, action)) |
348 | { |
349 | m_DefaultAction[operation] = action; |
350 | return S_OK; |
351 | } |
352 | else |
353 | { |
354 | return E_INVALIDARG; |
355 | } |
356 | } |
357 | |
358 | HRESULT EEPolicy::SetActionOnFailure(EClrFailure failure, EPolicyAction action) |
359 | { |
360 | CONTRACTL |
361 | { |
362 | GC_NOTRIGGER; |
363 | NOTHROW; |
364 | } |
365 | CONTRACTL_END; |
366 | |
367 | if (static_cast<UINT>(failure) < MaxClrFailure && |
368 | IsValidActionForFailure(failure, action)) |
369 | { |
370 | m_ActionOnFailure[failure] = action; |
371 | return S_OK; |
372 | } |
373 | else |
374 | { |
375 | return E_INVALIDARG; |
376 | } |
377 | } |
378 | |
379 | EPolicyAction EEPolicy::GetActionOnFailureNoHostNotification(EClrFailure failure) |
380 | { |
381 | CONTRACTL |
382 | { |
383 | SO_TOLERANT; |
384 | MODE_ANY; |
385 | GC_NOTRIGGER; |
386 | NOTHROW; |
387 | }CONTRACTL_END; |
388 | |
389 | _ASSERTE (failure < MaxClrFailure); |
390 | if (failure == FAIL_StackOverflow) |
391 | { |
392 | return m_ActionOnFailure[failure]; |
393 | } |
394 | |
395 | return GetFinalAction(m_ActionOnFailure[failure], GetThread()); |
396 | } |
397 | |
398 | EPolicyAction EEPolicy::GetActionOnFailure(EClrFailure failure) |
399 | { |
400 | CONTRACTL |
401 | { |
402 | SO_TOLERANT; |
403 | MODE_ANY; |
404 | GC_NOTRIGGER; |
405 | NOTHROW; |
406 | }CONTRACTL_END; |
407 | |
408 | _ASSERTE(static_cast<UINT>(failure) < MaxClrFailure); |
409 | if (failure == FAIL_StackOverflow) |
410 | { |
411 | return m_ActionOnFailure[failure]; |
412 | } |
413 | |
414 | EPolicyAction finalAction = GetActionOnFailureNoHostNotification(failure); |
415 | return finalAction; |
416 | } |
417 | |
418 | |
419 | void EEPolicy::NotifyHostOnTimeout(EClrOperation operation, EPolicyAction action) |
420 | { |
421 | CONTRACTL |
422 | { |
423 | THROWS; |
424 | GC_NOTRIGGER; |
425 | MODE_ANY; |
426 | SO_TOLERANT; |
427 | } |
428 | CONTRACTL_END; |
429 | |
430 | } |
431 | |
432 | |
433 | void EEPolicy::NotifyHostOnDefaultAction(EClrOperation operation, EPolicyAction action) |
434 | { |
435 | CONTRACTL |
436 | { |
437 | NOTHROW; |
438 | GC_NOTRIGGER; |
439 | MODE_ANY; |
440 | SO_TOLERANT; |
441 | } |
442 | CONTRACTL_END; |
443 | |
444 | } |
445 | |
446 | void SafeExitProcess(UINT exitCode, BOOL fAbort = FALSE, ShutdownCompleteAction sca = SCA_ExitProcessWhenShutdownComplete) |
447 | { |
448 | // The process is shutting down. No need to check SO contract. |
449 | SO_NOT_MAINLINE_FUNCTION; |
450 | STRESS_LOG2(LF_SYNC, LL_INFO10, "SafeExitProcess: exitCode = %d, fAbort = %d\n" , exitCode, fAbort); |
451 | CONTRACTL |
452 | { |
453 | DISABLED(GC_TRIGGERS); |
454 | NOTHROW; |
455 | } |
456 | CONTRACTL_END; |
457 | |
458 | // The runtime must be in the appropriate thread mode when we exit, so that we |
459 | // aren't surprised by the thread mode when our DLL_PROCESS_DETACH occurs, or when |
460 | // other DLLs call Release() on us in their detach [dangerous!], etc. |
461 | GCX_PREEMP_NO_DTOR(); |
462 | |
463 | FastInterlockExchange((LONG*)&g_fForbidEnterEE, TRUE); |
464 | |
465 | ProcessEventForHost(Event_ClrDisabled, NULL); |
466 | |
467 | // Note that for free and retail builds StressLog must also be enabled |
468 | if (g_pConfig && g_pConfig->StressLog()) |
469 | { |
470 | if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnBadExit)) |
471 | { |
472 | // Workaround for aspnet |
473 | PathString wszFilename; |
474 | bool bShouldAssert = true; |
475 | if (WszGetModuleFileName(NULL, wszFilename)) |
476 | { |
477 | wszFilename.LowerCase(); |
478 | |
479 | if (wcsstr(wszFilename, W("aspnet_compiler" ))) |
480 | { |
481 | bShouldAssert = false; |
482 | } |
483 | } |
484 | |
485 | unsigned goodExit = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_SuccessExit); |
486 | if (bShouldAssert && exitCode != goodExit) |
487 | { |
488 | _ASSERTE(!"Bad Exit value" ); |
489 | FAULT_NOT_FATAL(); // if we OOM we can simply give up |
490 | SetErrorMode(0); // Insure that we actually cause the messsage box to pop. |
491 | EEMessageBoxCatastrophic(IDS_EE_ERRORMESSAGETEMPLATE, IDS_EE_ERRORTITLE, exitCode, W("BreakOnBadExit: returning bad exit code" )); |
492 | } |
493 | } |
494 | } |
495 | |
496 | // If we call ExitProcess, other threads will be torn down |
497 | // so we don't get to debug their state. Stop this! |
498 | #ifdef _DEBUG |
499 | if (_DbgBreakCount) |
500 | _ASSERTE(!"In SafeExitProcess: An assert was hit on some other thread" ); |
501 | #endif |
502 | |
503 | // Turn off exception processing, because if some other random DLL has a |
504 | // fault in DLL_PROCESS_DETACH, we could get called for exception handling. |
505 | // Since we've turned off part of the runtime, we can't, for instance, |
506 | // properly execute the GC that handling an exception might trigger. |
507 | g_fNoExceptions = true; |
508 | LOG((LF_EH, LL_INFO10, "SafeExitProcess: turning off exceptions\n" )); |
509 | |
510 | if (sca == SCA_ExitProcessWhenShutdownComplete) |
511 | { |
512 | // disabled because if we fault in this code path we will trigger our |
513 | // Watson code via EntryPointFilter which is THROWS (see Dev11 317016) |
514 | CONTRACT_VIOLATION(ThrowsViolation); |
515 | |
516 | #ifdef FEATURE_PAL |
517 | if (fAbort) |
518 | { |
519 | TerminateProcess(GetCurrentProcess(), exitCode); |
520 | } |
521 | #endif |
522 | |
523 | EEPolicy::ExitProcessViaShim(exitCode); |
524 | } |
525 | } |
526 | |
527 | // This is a helper to exit the process after coordinating with the shim. It is used by |
528 | // SafeExitProcess above, as well as from CorHost2::ExitProcess when we know that we must |
529 | // exit the process without doing further work to shutdown this runtime. This first attempts |
530 | // to call back to the Shim to shutdown any other runtimes within the process. |
531 | // |
532 | // IMPORTANT NOTE: exercise extreme caution when adding new calls to this method. It is highly |
533 | // likely that you want to call SafeExitProcess, or EEPolicy::HandleExitProcess instead of this. |
534 | // This function only exists to factor some common code out of the methods mentioned above. |
535 | |
536 | //static |
537 | void EEPolicy::ExitProcessViaShim(UINT exitCode) |
538 | { |
539 | LIMITED_METHOD_CONTRACT; |
540 | |
541 | // We must call back to the Shim in order to exit the process, as this may be just one |
542 | // runtime in a process with many. We need to give the other runtimes a chance to exit |
543 | // cleanly. If we can't make the call, or if the call fails for some reason, then we |
544 | // simply exit the process here, which is rude to the others, but the best we can do. |
545 | |
546 | ExitProcess(exitCode); |
547 | } |
548 | |
549 | |
550 | //--------------------------------------------------------------------------------------- |
551 | // DisableRuntime disables this runtime, suspending all managed execution and preventing |
552 | // threads from entering the runtime. This will cause the caller to block forever as well |
553 | // unless sca is SCA_ReturnWhenShutdownComplete. |
554 | //--------------------------------------------------------------------------------------- |
555 | void DisableRuntime(ShutdownCompleteAction sca) |
556 | { |
557 | CONTRACTL |
558 | { |
559 | DISABLED(GC_TRIGGERS); |
560 | NOTHROW; |
561 | } |
562 | CONTRACTL_END; |
563 | |
564 | FastInterlockExchange((LONG*)&g_fForbidEnterEE, TRUE); |
565 | |
566 | if (!g_fSuspendOnShutdown) |
567 | { |
568 | if (!IsGCThread()) |
569 | { |
570 | if (ThreadStore::HoldingThreadStore(GetThread())) |
571 | { |
572 | ThreadSuspend::UnlockThreadStore(); |
573 | } |
574 | ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_SHUTDOWN); |
575 | } |
576 | |
577 | if (!g_fSuspendOnShutdown) |
578 | { |
579 | ThreadStore::TrapReturningThreads(TRUE); |
580 | g_fSuspendOnShutdown = TRUE; |
581 | ClrFlsSetThreadType(ThreadType_Shutdown); |
582 | } |
583 | |
584 | // Don't restart runtime. CLR is disabled. |
585 | } |
586 | |
587 | GCX_PREEMP_NO_DTOR(); |
588 | |
589 | ProcessEventForHost(Event_ClrDisabled, NULL); |
590 | ClrFlsClearThreadType(ThreadType_Shutdown); |
591 | |
592 | if (g_pDebugInterface != NULL) |
593 | { |
594 | g_pDebugInterface->DisableDebugger(); |
595 | } |
596 | |
597 | if (sca == SCA_ExitProcessWhenShutdownComplete) |
598 | { |
599 | __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING); |
600 | _ASSERTE (!"Should not reach here" ); |
601 | SafeExitProcess(0); |
602 | } |
603 | } |
604 | |
605 | //--------------------------------------------------------------------------------------- |
606 | // HandleExitProcessHelper is used to shutdown the runtime as specified by the given |
607 | // action, then to exit the process. Note, however, that the process will not exit if |
608 | // sca is SCA_ReturnWhenShutdownComplete. In that case, this method will simply return after |
609 | // performing the shutdown actions. |
610 | //--------------------------------------------------------------------------------------- |
611 | |
612 | // If g_fFastExitProcess is 0, normal shutdown |
613 | // If g_fFastExitProcess is 1, fast shutdown. Only doing log. |
614 | // If g_fFastExitProcess is 2, do not run EEShutDown. |
615 | DWORD g_fFastExitProcess = 0; |
616 | |
617 | extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading); |
618 | |
619 | static void HandleExitProcessHelper(EPolicyAction action, UINT exitCode, ShutdownCompleteAction sca) |
620 | { |
621 | WRAPPER_NO_CONTRACT; |
622 | |
623 | switch (action) { |
624 | case eFastExitProcess: |
625 | g_fFastExitProcess = 1; |
626 | case eExitProcess: |
627 | if (g_fEEStarted) |
628 | { |
629 | EEShutDown(FALSE); |
630 | } |
631 | if (exitCode == 0) |
632 | { |
633 | exitCode = GetLatchedExitCode(); |
634 | } |
635 | SafeExitProcess(exitCode, FALSE, sca); |
636 | break; |
637 | case eRudeExitProcess: |
638 | g_fFastExitProcess = 2; |
639 | SafeExitProcess(exitCode, TRUE, sca); |
640 | break; |
641 | case eDisableRuntime: |
642 | DisableRuntime(sca); |
643 | break; |
644 | default: |
645 | _ASSERTE (!"Invalid policy" ); |
646 | break; |
647 | } |
648 | } |
649 | |
650 | |
651 | EPolicyAction EEPolicy::DetermineResourceConstraintAction(Thread *pThread) |
652 | { |
653 | CONTRACTL |
654 | { |
655 | NOTHROW; |
656 | GC_NOTRIGGER; |
657 | SO_TOLERANT; |
658 | MODE_ANY; |
659 | } |
660 | CONTRACTL_END; |
661 | |
662 | EPolicyAction action; |
663 | if (pThread->HasLockInCurrentDomain()) { |
664 | action = GetEEPolicy()->GetActionOnFailure(FAIL_CriticalResource); |
665 | } |
666 | else |
667 | action = GetEEPolicy()->GetActionOnFailure(FAIL_NonCriticalResource); |
668 | |
669 | AppDomain *pDomain = GetAppDomain(); |
670 | // If it is default domain, we can not unload the appdomain |
671 | if (pDomain == SystemDomain::System()->DefaultDomain() && |
672 | (action == eUnloadAppDomain || action == eRudeUnloadAppDomain)) |
673 | { |
674 | action = eThrowException; |
675 | } |
676 | // If the current thread is AD unload helper thread, it should not block itself. |
677 | else if (pThread->HasThreadStateNC(Thread::TSNC_ADUnloadHelper) && |
678 | action < eExitProcess) |
679 | { |
680 | action = eThrowException; |
681 | } |
682 | return action; |
683 | } |
684 | |
685 | void EEPolicy::PerformResourceConstraintAction(Thread *pThread, EPolicyAction action, UINT exitCode, BOOL haveStack) |
686 | { |
687 | WRAPPER_NO_CONTRACT; |
688 | |
689 | _ASSERTE(GetAppDomain() != NULL); |
690 | |
691 | switch (action) { |
692 | case eThrowException: |
693 | // Caller is going to rethrow. |
694 | return; |
695 | break; |
696 | case eAbortThread: |
697 | pThread->UserAbort(Thread::TAR_Thread, TA_Safe, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal); |
698 | break; |
699 | case eRudeAbortThread: |
700 | pThread->UserAbort(Thread::TAR_Thread, TA_Rude, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal); |
701 | break; |
702 | case eExitProcess: |
703 | case eFastExitProcess: |
704 | case eRudeExitProcess: |
705 | case eDisableRuntime: |
706 | HandleExitProcessFromEscalation(action, exitCode); |
707 | break; |
708 | default: |
709 | _ASSERTE (!"Invalid policy" ); |
710 | break; |
711 | } |
712 | } |
713 | |
714 | void EEPolicy::HandleOutOfMemory() |
715 | { |
716 | WRAPPER_NO_CONTRACT; |
717 | |
718 | _ASSERTE (g_pOutOfMemoryExceptionClass); |
719 | |
720 | Thread *pThread = GetThread(); |
721 | _ASSERTE (pThread); |
722 | |
723 | EPolicyAction action = DetermineResourceConstraintAction(pThread); |
724 | |
725 | // Check if we are executing in the context of a Constrained Execution Region. |
726 | if (action != eThrowException && Thread::IsExecutingWithinCer()) |
727 | { |
728 | // Hitting OOM in a CER region should throw the OOM without regard to the escalation policy |
729 | // since the CER author has declared they are hardened against such failures. That's |
730 | // the whole point of CERs, to denote regions where code knows exactly how to deal with |
731 | // failures in an attempt to minimize the need for rollback or recycling. |
732 | return; |
733 | } |
734 | |
735 | PerformResourceConstraintAction(pThread, action, HOST_E_EXITPROCESS_OUTOFMEMORY, TRUE); |
736 | } |
737 | |
738 | #ifdef FEATURE_STACK_PROBE |
739 | //--------------------------------------------------------------------------------------- |
740 | // |
741 | // IsSOTolerant - Is the current thread in SO Tolerant region? |
742 | // |
743 | // Arguments: |
744 | // pLimitFrame: the limit of search for frames |
745 | // |
746 | // Return Value: |
747 | // TRUE if in SO tolerant region. |
748 | // FALSE if in SO intolerant region. |
749 | // |
750 | // Note: |
751 | // We walk our frame chain to decide. If HelperMethodFrame is seen first, we are in tolerant |
752 | // region. If EnterSOIntolerantCodeFrame is seen first, we are in intolerant region. |
753 | // |
754 | BOOL Thread::IsSOTolerant(void * pLimitFrame) |
755 | { |
756 | LIMITED_METHOD_CONTRACT; |
757 | |
758 | Frame *pFrame = GetFrame(); |
759 | void* pSOIntolerantMarker = ClrFlsGetValue(TlsIdx_SOIntolerantTransitionHandler); |
760 | if (pSOIntolerantMarker == FRAME_TOP) |
761 | { |
762 | // We have not set a marker for intolerant transition yet. |
763 | return TRUE; |
764 | } |
765 | while (pFrame != FRAME_TOP && pFrame < pLimitFrame) |
766 | { |
767 | Frame::ETransitionType type = pFrame->GetTransitionType(); |
768 | if (pFrame > pSOIntolerantMarker) |
769 | { |
770 | return FALSE; |
771 | } |
772 | else if (type == Frame::TT_M2U || type == Frame::TT_InternalCall || |
773 | // We can not call HelperMethodFrame::GetFunction on SO since the call |
774 | // may need to call into host. This is why we check for TT_InternalCall first. |
775 | pFrame->GetFunction() != NULL) |
776 | { |
777 | return TRUE; |
778 | } |
779 | pFrame = pFrame->Next(); |
780 | } |
781 | |
782 | if (pFrame == FRAME_TOP) |
783 | // We walked to the end of chain, but the thread has one IntolerantMarker on stack decided from |
784 | // the check above while loop. |
785 | return FALSE; |
786 | else |
787 | return TRUE; |
788 | } |
789 | |
790 | #endif |
791 | |
792 | //--------------------------------------------------------------------------------------- |
793 | // |
794 | // EEPolicy::HandleStackOverflow - Handle stack overflow according to policy |
795 | // |
796 | // Arguments: |
797 | // detector: |
798 | // pLimitFrame: the limit of search for frames in order to decide if in SO tolerant |
799 | // |
800 | // Return Value: |
801 | // None. |
802 | // |
803 | // How is stack overflow handled? |
804 | // If stack overflows in non-hosted case, we terminate the process. |
805 | // For hosted case with escalation policy |
806 | // 1. If stack overflows in managed code, or in VM before switching to SO intolerant region, and the GC mode is Cooperative |
807 | // the domain is rudely unloaded, or the process is terminated if the current domain is default domain. |
808 | // a. This action is done through BEGIN_SO_TOLERANT_CODE if there is one. |
809 | // b. If there is not this macro on the stack, we mark the domain being unload requested, and when the thread |
810 | // dies or is recycled, we finish the AD unload. |
811 | // 2. If stack overflows in SO tolerant region, but the GC mode is Preemptive, the process is killed in vector handler, or our |
812 | // managed exception handler (COMPlusFrameHandler or ProcessCLRException). |
813 | // 3. If stack overflows in SO intolerant region, the process is killed as soon as the exception is seen by our vector handler, or |
814 | // our managed exception handler. |
815 | // |
816 | // If SO Probing code is disabled (by FEATURE_STACK_PROBE not defined) then the process |
817 | // is terminated if there is StackOverflow as all clr code will be considered SO Intolerant. |
818 | void EEPolicy::HandleStackOverflow(StackOverflowDetector detector, void * pLimitFrame) |
819 | { |
820 | WRAPPER_NO_CONTRACT; |
821 | |
822 | STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::HandleStackOverflow\n" ); |
823 | |
824 | Thread *pThread = GetThread(); |
825 | |
826 | if (pThread == NULL) |
827 | { |
828 | //_ASSERTE (detector != SOD_ManagedFrameHandler); |
829 | // ProcessSOEventForHost(NULL, FALSE); |
830 | |
831 | // For security reason, it is not safe to continue execution if stack overflow happens |
832 | // unless a host tells us to do something different. |
833 | // EEPolicy::HandleFatalStackOverflow(NULL); |
834 | return; |
835 | } |
836 | |
837 | #ifdef FEATURE_STACK_PROBE |
838 | |
839 | // We only process SO once at |
840 | // 1. VectoredExceptionHandler if SO in mscorwks |
841 | // 2. managed exception handler |
842 | // 3. SO_Tolerant transition handler |
843 | if (pThread->HasThreadStateNC(Thread::TSNC_SOWorkNeeded) && |
844 | detector != SOD_UnmanagedFrameHandler) |
845 | { |
846 | return; |
847 | } |
848 | #endif |
849 | |
850 | #ifdef FEATURE_STACK_PROBE |
851 | BOOL fInSoTolerant = pThread->IsSOTolerant(pLimitFrame); |
852 | #else |
853 | BOOL fInSoTolerant = false; |
854 | #endif |
855 | |
856 | EXCEPTION_POINTERS exceptionInfo; |
857 | GetCurrentExceptionPointers(&exceptionInfo); |
858 | |
859 | _ASSERTE(exceptionInfo.ExceptionRecord); |
860 | |
861 | #ifdef FEATURE_STACK_PROBE |
862 | DWORD exceptionCode = exceptionInfo.ExceptionRecord->ExceptionCode; |
863 | |
864 | AppDomain *pCurrentDomain = ::GetAppDomain(); |
865 | BOOL fInDefaultDomain = (pCurrentDomain == SystemDomain::System()->DefaultDomain()); |
866 | BOOL fInCLR = IsIPInModule(g_pMSCorEE, (PCODE)GetIP(exceptionInfo.ContextRecord)); |
867 | |
868 | if (exceptionCode == EXCEPTION_SOFTSO) |
869 | { |
870 | // Our probe detects a thread does not have enough stack. But we have not trashed the process |
871 | // state yet. |
872 | fInSoTolerant = TRUE; |
873 | } |
874 | else |
875 | { |
876 | _ASSERTE (exceptionCode == STATUS_STACK_OVERFLOW); |
877 | |
878 | switch (detector) |
879 | { |
880 | case SOD_ManagedFrameHandler: |
881 | if (!pThread->PreemptiveGCDisabled() && !fInCLR && fInSoTolerant) |
882 | { |
883 | // Managed exception handler detects SO, but the thread is in preemptive GC mode, |
884 | // and the IP is outside CLR. This means we are inside a PINVOKE call. |
885 | fInSoTolerant = FALSE; |
886 | } |
887 | break; |
888 | |
889 | case SOD_UnmanagedFrameHandler: |
890 | break; |
891 | |
892 | case SOD_SOIntolerantTransitor: |
893 | fInSoTolerant = FALSE; |
894 | break; |
895 | |
896 | case SOD_SOTolerantTransitor: |
897 | if (!fInCLR) |
898 | { |
899 | // If SO happens outside of CLR, and it is not detected by managed frame handler, |
900 | // it is fatal |
901 | fInSoTolerant = FALSE; |
902 | } |
903 | break; |
904 | |
905 | default: |
906 | _ASSERTE(!"should not get here" ); |
907 | } |
908 | |
909 | if (fInDefaultDomain) |
910 | { |
911 | // StackOverflow in default domain is fatal |
912 | fInSoTolerant = FALSE; |
913 | } |
914 | } |
915 | |
916 | #endif // FEATURE_STACK_PROBE |
917 | |
918 | ProcessSOEventForHost(&exceptionInfo, fInSoTolerant); |
919 | |
920 | #ifdef FEATURE_STACK_PROBE |
921 | if (!CLRHosted() || GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) != eRudeUnloadAppDomain) |
922 | { |
923 | // For security reason, it is not safe to continue execution if stack overflow happens |
924 | // unless a host tells us to do something different. |
925 | EEPolicy::HandleFatalStackOverflow(&exceptionInfo); |
926 | } |
927 | #endif |
928 | |
929 | if (!fInSoTolerant) |
930 | { |
931 | EEPolicy::HandleFatalStackOverflow(&exceptionInfo); |
932 | } |
933 | #ifdef FEATURE_STACK_PROBE |
934 | else |
935 | { |
936 | // EnableADUnloadWorker is SO_Intolerant. |
937 | // But here we know that if we have only one page, we will only update states of the Domain. |
938 | CONTRACT_VIOLATION(SOToleranceViolation); |
939 | |
940 | pThread->PrepareThreadForSOWork(); |
941 | |
942 | pThread->MarkThreadForAbort( |
943 | (Thread::ThreadAbortRequester)(Thread::TAR_Thread|Thread::TAR_StackOverflow), |
944 | EEPolicy::TA_Rude); |
945 | |
946 | pThread->SetSOWorkNeeded(); |
947 | } |
948 | #endif |
949 | } |
950 | |
951 | |
952 | // We provide WatsonLastChance with a SO exception record. The ExceptionAddress is set to 0 |
953 | // here. This ExceptionPointers struct is handed off to the debugger as is. A copy of this struct |
954 | // is made before invoking Watson and the ExceptionAddress is set by inspecting the stack. Note |
955 | // that the ExceptionContext member is unused and so it's ok to set it to NULL. |
956 | static EXCEPTION_RECORD g_SOExceptionRecord = { |
957 | STATUS_STACK_OVERFLOW, // ExceptionCode |
958 | 0, // ExceptionFlags |
959 | NULL, // ExceptionRecord |
960 | 0, // ExceptionAddress |
961 | 0, // NumberOfParameters |
962 | {} }; // ExceptionInformation |
963 | |
964 | EXCEPTION_POINTERS g_SOExceptionPointers = {&g_SOExceptionRecord, NULL}; |
965 | |
966 | #ifdef FEATURE_STACK_PROBE |
967 | // This function may be called on a thread before debugger is notified of the thread, like in |
968 | // ManagedThreadBase_DispatchMiddle. Currently we can not notify managed debugger, because |
969 | // RS requires that notification is sent first. |
970 | void EEPolicy::HandleSoftStackOverflow(BOOL fSkipDebugger) |
971 | { |
972 | WRAPPER_NO_CONTRACT; |
973 | |
974 | // If we trigger a SO while handling the soft stack overflow, |
975 | // we'll rip the process |
976 | BEGIN_SO_INTOLERANT_CODE_NOPROBE; |
977 | |
978 | AppDomain *pCurrentDomain = ::GetAppDomain(); |
979 | |
980 | if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) != eRudeUnloadAppDomain || |
981 | pCurrentDomain == SystemDomain::System()->DefaultDomain()) |
982 | { |
983 | // We may not be able to build a context on stack |
984 | ProcessSOEventForHost(NULL, FALSE); |
985 | |
986 | |
987 | EEPolicy::HandleFatalStackOverflow(&g_SOExceptionPointers, fSkipDebugger); |
988 | } |
989 | //else if (pCurrentDomain == SystemDomain::System()->DefaultDomain()) |
990 | //{ |
991 | // We hit soft SO in Default domain, but default domain can not be unloaded. |
992 | // Soft SO can happen in default domain, eg. GetResourceString, or EnsureGrantSetSerialized. |
993 | // So the caller is going to throw a managed exception. |
994 | // RaiseException(EXCEPTION_SOFTSO, 0, 0, NULL); |
995 | //} |
996 | else |
997 | { |
998 | Thread* pThread = GetThread(); |
999 | |
1000 | // We are leaving VM boundary, either entering managed code, or entering |
1001 | // non-VM unmanaged code. |
1002 | // We should not throw internal C++ exception. Instead we throw an exception |
1003 | // with EXCEPTION_SOFTSO code. |
1004 | RaiseException(EXCEPTION_SOFTSO, 0, 0, NULL); |
1005 | } |
1006 | |
1007 | END_SO_INTOLERANT_CODE_NOPROBE; |
1008 | |
1009 | } |
1010 | |
1011 | void EEPolicy::HandleStackOverflowAfterCatch() |
1012 | { |
1013 | CONTRACTL |
1014 | { |
1015 | NOTHROW; |
1016 | GC_NOTRIGGER; |
1017 | SO_TOLERANT; |
1018 | MODE_ANY; |
1019 | } |
1020 | CONTRACTL_END; |
1021 | |
1022 | #ifdef STACK_GUARDS_DEBUG |
1023 | BaseStackGuard::RestoreCurrentGuard(FALSE); |
1024 | #endif |
1025 | Thread *pThread = GetThread(); |
1026 | pThread->RestoreGuardPage(); |
1027 | pThread->FinishSOWork(); |
1028 | } |
1029 | #endif |
1030 | |
1031 | |
1032 | //--------------------------------------------------------------------------------------- |
1033 | // HandleExitProcess is used to shutdown the runtime, based on policy previously set, |
1034 | // then to exit the process. Note, however, that the process will not exit if |
1035 | // sca is SCA_ReturnWhenShutdownComplete. In that case, this method will simply return after |
1036 | // performing the shutdown actions. |
1037 | //--------------------------------------------------------------------------------------- |
1038 | void EEPolicy::HandleExitProcess(ShutdownCompleteAction sca) |
1039 | { |
1040 | WRAPPER_NO_CONTRACT; |
1041 | |
1042 | STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::HandleExitProcess\n" ); |
1043 | |
1044 | EPolicyAction action = GetEEPolicy()->GetDefaultAction(OPR_ProcessExit, NULL); |
1045 | GetEEPolicy()->NotifyHostOnDefaultAction(OPR_ProcessExit,action); |
1046 | HandleExitProcessHelper(action, 0, sca); |
1047 | } |
1048 | |
1049 | StackWalkAction LogCallstackForLogCallback( |
1050 | CrawlFrame *pCF, // |
1051 | VOID* pData // Caller's private data |
1052 | ) |
1053 | { |
1054 | CONTRACTL |
1055 | { |
1056 | THROWS; |
1057 | GC_TRIGGERS; |
1058 | SO_INTOLERANT; |
1059 | MODE_ANY; |
1060 | } |
1061 | CONTRACTL_END; |
1062 | |
1063 | SmallStackSString *pWordAt = ((SmallStackSString*)pData); |
1064 | |
1065 | MethodDesc *pMD = pCF->GetFunction(); |
1066 | _ASSERTE(pMD != NULL); |
1067 | |
1068 | StackSString str; |
1069 | str = *pWordAt; |
1070 | |
1071 | TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst|TypeString::FormatSignature); |
1072 | PrintToStdErrW(str.GetUnicode()); |
1073 | PrintToStdErrA("\n" ); |
1074 | |
1075 | return SWA_CONTINUE; |
1076 | } |
1077 | |
1078 | //--------------------------------------------------------------------------------------- |
1079 | // |
1080 | // A worker to save managed stack trace. |
1081 | // |
1082 | // Arguments: |
1083 | // None |
1084 | // |
1085 | // Return Value: |
1086 | // None |
1087 | // |
1088 | inline void LogCallstackForLogWorker() |
1089 | { |
1090 | Thread* pThread = GetThread(); |
1091 | _ASSERTE (pThread); |
1092 | |
1093 | SmallStackSString WordAt; |
1094 | |
1095 | if (!WordAt.LoadResource(CCompRC::Optional, IDS_ER_WORDAT)) |
1096 | { |
1097 | WordAt.Set(W(" at" )); |
1098 | } |
1099 | else |
1100 | { |
1101 | WordAt.Insert(WordAt.Begin(), W(" " )); |
1102 | } |
1103 | WordAt += W(" " ); |
1104 | |
1105 | pThread->StackWalkFrames(&LogCallstackForLogCallback, &WordAt, QUICKUNWIND | FUNCTIONSONLY); |
1106 | } |
1107 | |
1108 | //--------------------------------------------------------------------------------------- |
1109 | // |
1110 | // Generate an EventLog entry for unhandled exception. |
1111 | // |
1112 | // Arguments: |
1113 | // pExceptionInfo - Exception information |
1114 | // |
1115 | // Return Value: |
1116 | // None |
1117 | // |
1118 | inline void (LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource, LPCWSTR argExceptionString) |
1119 | { |
1120 | WRAPPER_NO_CONTRACT; |
1121 | |
1122 | Thread *pThread = GetThread(); |
1123 | EX_TRY |
1124 | { |
1125 | if (errorSource == NULL) |
1126 | { |
1127 | PrintToStdErrA("FailFast:" ); |
1128 | } |
1129 | else |
1130 | { |
1131 | PrintToStdErrW((WCHAR*)errorSource); |
1132 | } |
1133 | |
1134 | PrintToStdErrA("\n" ); |
1135 | PrintToStdErrW((WCHAR*)pszMessage); |
1136 | PrintToStdErrA("\n" ); |
1137 | |
1138 | if (pThread && errorSource == NULL) |
1139 | { |
1140 | PrintToStdErrA("\n" ); |
1141 | LogCallstackForLogWorker(); |
1142 | |
1143 | if (argExceptionString != NULL) { |
1144 | PrintToStdErrA("\n" ); |
1145 | PrintToStdErrA("Exception details:" ); |
1146 | PrintToStdErrA("\n" ); |
1147 | PrintToStdErrW((WCHAR*)argExceptionString); |
1148 | PrintToStdErrA("\n" ); |
1149 | } |
1150 | } |
1151 | } |
1152 | EX_CATCH |
1153 | { |
1154 | } |
1155 | EX_END_CATCH(SwallowAllExceptions) |
1156 | } |
1157 | |
1158 | //This starts FALSE and then converts to true if HandleFatalError has ever been called by a GC thread |
1159 | BOOL g_fFatalErrorOccuredOnGCThread = FALSE; |
1160 | // |
1161 | // Log an error to the event log if possible, then throw up a dialog box. |
1162 | // |
1163 | |
1164 | void EEPolicy::(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource, LPCWSTR argExceptionString) |
1165 | { |
1166 | STATIC_CONTRACT_NOTHROW; |
1167 | STATIC_CONTRACT_GC_TRIGGERS; |
1168 | STATIC_CONTRACT_MODE_ANY; |
1169 | |
1170 | _ASSERTE(pExceptionInfo != NULL); |
1171 | |
1172 | // Log FailFast exception to StdErr |
1173 | if (exitCode == (UINT)COR_E_FAILFAST) |
1174 | { |
1175 | DoLogForFailFastException(pszMessage, pExceptionInfo, errorSource, argExceptionString); |
1176 | } |
1177 | |
1178 | if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast)) |
1179 | { |
1180 | // Fire an ETW FailFast event |
1181 | FireEtwFailFast(pszMessage, |
1182 | (const PVOID)address, |
1183 | ((pExceptionInfo && pExceptionInfo->ExceptionRecord) ? pExceptionInfo->ExceptionRecord->ExceptionCode : 0), |
1184 | exitCode, |
1185 | GetClrInstanceId()); |
1186 | } |
1187 | |
1188 | #ifndef FEATURE_PAL |
1189 | // Write an event log entry. We do allocate some resources here (spread between the stack and maybe the heap for longer |
1190 | // messages), so it's possible for the event write to fail. If needs be we can use a more elaborate scheme here in the future |
1191 | // (maybe trying multiple approaches and backing off on failure, falling back on a limited size static buffer as a last |
1192 | // resort). In all likelihood the Win32 event reporting mechanism requires resources though, so it's not clear how much |
1193 | // effort we should put into this without knowing the benefit we'd receive. |
1194 | EX_TRY |
1195 | { |
1196 | if (ShouldLogInEventLog()) |
1197 | { |
1198 | // If the exit code is COR_E_FAILFAST then the fatal error was raised by managed code and the address argument points to a |
1199 | // unicode message buffer rather than a faulting EIP. |
1200 | EventReporter::EventReporterType failureType = EventReporter::ERT_UnmanagedFailFast; |
1201 | if (exitCode == (UINT)COR_E_FAILFAST) |
1202 | failureType = EventReporter::ERT_ManagedFailFast; |
1203 | else if (exitCode == (UINT)COR_E_CODECONTRACTFAILED) |
1204 | failureType = EventReporter::ERT_CodeContractFailed; |
1205 | EventReporter reporter(failureType); |
1206 | StackSString s(argExceptionString); |
1207 | |
1208 | if ((exitCode == (UINT)COR_E_FAILFAST) || (exitCode == (UINT)COR_E_CODECONTRACTFAILED) || (exitCode == (UINT)CLR_E_GC_OOM)) |
1209 | { |
1210 | if (pszMessage) |
1211 | { |
1212 | reporter.AddDescription((WCHAR*)pszMessage); |
1213 | } |
1214 | |
1215 | if (argExceptionString) |
1216 | { |
1217 | reporter.AddFailFastStackTrace(s); |
1218 | } |
1219 | |
1220 | if (exitCode != (UINT)CLR_E_GC_OOM) |
1221 | LogCallstackForEventReporter(reporter); |
1222 | } |
1223 | else |
1224 | { |
1225 | // Fetch the localized Fatal Execution Engine Error text or fall back on a hardcoded variant if things get dire. |
1226 | InlineSString<80> ssMessage; |
1227 | InlineSString<80> ssErrorFormat; |
1228 | if(!ssErrorFormat.LoadResource(CCompRC::Optional, IDS_ER_UNMANAGEDFAILFASTMSG )) |
1229 | ssErrorFormat.Set(W("at IP %1 (%2) with exit code %3." )); |
1230 | SmallStackSString addressString; |
1231 | addressString.Printf(W("%p" ), pExceptionInfo? (UINT_PTR)pExceptionInfo->ExceptionRecord->ExceptionAddress : address); |
1232 | |
1233 | // We should always have the reference to the runtime's instance |
1234 | _ASSERTE(g_pMSCorEE != NULL); |
1235 | |
1236 | // Setup the string to contain the runtime's base address. Thus, when customers report FEEE with just |
1237 | // the event log entry containing this string, we can use the absolute and base addresses to determine |
1238 | // where the fault happened inside the runtime. |
1239 | SmallStackSString runtimeBaseAddressString; |
1240 | runtimeBaseAddressString.Printf(W("%p" ), g_pMSCorEE); |
1241 | |
1242 | SmallStackSString exitCodeString; |
1243 | exitCodeString.Printf(W("%x" ), exitCode); |
1244 | |
1245 | // Format the string |
1246 | ssMessage.FormatMessage(FORMAT_MESSAGE_FROM_STRING, (LPCWSTR)ssErrorFormat, 0, 0, addressString, runtimeBaseAddressString, |
1247 | exitCodeString); |
1248 | reporter.AddDescription(ssMessage); |
1249 | } |
1250 | |
1251 | reporter.Report(); |
1252 | } |
1253 | } |
1254 | EX_CATCH |
1255 | { |
1256 | } |
1257 | EX_END_CATCH(SwallowAllExceptions) |
1258 | #endif // !FEATURE_PAL |
1259 | |
1260 | #ifdef _DEBUG |
1261 | // If we're native-only (Win32) debugging this process, we'd love to break now. |
1262 | // However, we should not do this because a managed debugger attached to a |
1263 | // SxS runtime also appears to be a native debugger. Unfortunately, the managed |
1264 | // debugger won't handle any native event from another runtime, which means this |
1265 | // breakpoint would go unhandled and terminate the process. Instead, we will let |
1266 | // the process continue so at least the fatal error is logged rather than abrupt |
1267 | // termination. |
1268 | // |
1269 | // This behavior can still be overridden if the right config value is set. |
1270 | if (IsDebuggerPresent()) |
1271 | { |
1272 | bool fBreak = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgOOBinFEEE) != 0); |
1273 | |
1274 | if (fBreak) |
1275 | { |
1276 | DebugBreak(); |
1277 | } |
1278 | } |
1279 | #endif // _DEBUG |
1280 | |
1281 | // We're here logging a fatal error. If the policy is to then do anything other than |
1282 | // disable the runtime (ie, if the policy is to terminate the runtime), we should give |
1283 | // Watson an opportunity to capture an error report. |
1284 | // Presumably, hosts that are sophisticated enough to disable the runtime are also cognizant |
1285 | // of how they want to handle fatal errors in the runtime, including whether they want |
1286 | // to capture Watson information (for which they are responsible). |
1287 | if (GetEEPolicy()->GetActionOnFailureNoHostNotification(FAIL_FatalRuntime) != eDisableRuntime) |
1288 | { |
1289 | #ifdef DEBUGGING_SUPPORTED |
1290 | //Give a managed debugger a chance if this fatal error is on a managed thread. |
1291 | Thread *pThread = GetThread(); |
1292 | |
1293 | if (pThread && !g_fFatalErrorOccuredOnGCThread) |
1294 | { |
1295 | GCX_COOP(); |
1296 | |
1297 | OBJECTHANDLE ohException = NULL; |
1298 | |
1299 | if (exitCode == (UINT)COR_E_STACKOVERFLOW) |
1300 | { |
1301 | // If we're going down because of stack overflow, go ahead and use the preallocated SO exception. |
1302 | ohException = CLRException::GetPreallocatedStackOverflowExceptionHandle(); |
1303 | } |
1304 | else |
1305 | { |
1306 | // Though we would like to remove the usage of ExecutionEngineException in any manner, |
1307 | // we cannot. Its okay to use it in the case below since the process is terminating |
1308 | // and this will serve as an exception object for debugger. |
1309 | ohException = CLRException::GetPreallocatedExecutionEngineExceptionHandle(); |
1310 | } |
1311 | |
1312 | // Preallocated exception handles can be null if FailFast is invoked before LoadBaseSystemClasses |
1313 | // (in SystemDomain::Init) finished. See Dev10 Bug 677432 for the detail. |
1314 | if (ohException != NULL) |
1315 | { |
1316 | // for fail-fast, if there's a LTO available then use that as the inner exception object |
1317 | // for the FEEE we'll be reporting. this can help the Watson back-end to generate better |
1318 | // buckets for apps that call Environment.FailFast() and supply an exception object. |
1319 | OBJECTREF lto = pThread->LastThrownObject(); |
1320 | |
1321 | if (exitCode == static_cast<UINT>(COR_E_FAILFAST) && lto != NULL) |
1322 | { |
1323 | EXCEPTIONREF curEx = (EXCEPTIONREF)ObjectFromHandle(ohException); |
1324 | curEx->SetInnerException(lto); |
1325 | } |
1326 | pThread->SetLastThrownObject(ObjectFromHandle(ohException), TRUE); |
1327 | } |
1328 | |
1329 | // If a managed debugger is already attached, and if that debugger is thinking it might be inclined to |
1330 | // try to intercept this excepiton, then tell it that's not possible. |
1331 | if (pThread->IsExceptionInProgress()) |
1332 | { |
1333 | pThread->GetExceptionState()->GetFlags()->SetDebuggerInterceptNotPossible(); |
1334 | } |
1335 | } |
1336 | |
1337 | if (EXCEPTION_CONTINUE_EXECUTION == WatsonLastChance(pThread, pExceptionInfo, TypeOfReportedError::FatalError)) |
1338 | { |
1339 | LOG((LF_EH, LL_INFO100, "EEPolicy::LogFatalError: debugger ==> EXCEPTION_CONTINUE_EXECUTION\n" )); |
1340 | _ASSERTE(!"Debugger should not have returned ContinueExecution" ); |
1341 | } |
1342 | #endif // DEBUGGING_SUPPORTED |
1343 | } |
1344 | } |
1345 | |
1346 | void DisplayStackOverflowException() |
1347 | { |
1348 | LIMITED_METHOD_CONTRACT; |
1349 | PrintToStdErrA("\n" ); |
1350 | |
1351 | PrintToStdErrA("Process is terminating due to StackOverflowException.\n" ); |
1352 | } |
1353 | |
1354 | void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pExceptionInfo, BOOL fSkipDebugger) |
1355 | { |
1356 | // This is fatal error. We do not care about SO mode any more. |
1357 | // All of the code from here on out is robust to any failures in any API's that are called. |
1358 | CONTRACT_VIOLATION(GCViolation | ModeViolation | SOToleranceViolation | FaultNotFatal | TakesLockViolation); |
1359 | |
1360 | WRAPPER_NO_CONTRACT; |
1361 | |
1362 | STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::HandleFatalStackOverflow\n" ); |
1363 | |
1364 | DisplayStackOverflowException(); |
1365 | |
1366 | if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast)) |
1367 | { |
1368 | // Fire an ETW FailFast event |
1369 | FireEtwFailFast(W("StackOverflowException" ), |
1370 | (const PVOID)((pExceptionInfo && pExceptionInfo->ContextRecord) ? GetIP(pExceptionInfo->ContextRecord) : 0), |
1371 | ((pExceptionInfo && pExceptionInfo->ExceptionRecord) ? pExceptionInfo->ExceptionRecord->ExceptionCode : 0), |
1372 | COR_E_STACKOVERFLOW, |
1373 | GetClrInstanceId()); |
1374 | } |
1375 | |
1376 | if (!fSkipDebugger) |
1377 | { |
1378 | Thread *pThread = GetThread(); |
1379 | BOOL fTreatAsNativeUnhandledException = FALSE; |
1380 | if (pThread) |
1381 | { |
1382 | GCX_COOP(); |
1383 | // If we had a SO before preallocated exception objects are initialized, we will AV here. This can happen |
1384 | // during the initialization of SystemDomain during EEStartup. Thus, setup the SO throwable only if its not |
1385 | // NULL. |
1386 | // |
1387 | // When WatsonLastChance (WLC) is invoked below, it treats this case as UnhandledException. If there is no |
1388 | // managed exception object available, we should treat this case as NativeUnhandledException. This aligns |
1389 | // well with the fact that there cannot be a managed debugger attached at this point that will require |
1390 | // LastChanceManagedException notification to be delivered. Also, this is the same as how |
1391 | // we treat an unhandled exception as NativeUnhandled when throwable is not available. |
1392 | OBJECTHANDLE ohSO = CLRException::GetPreallocatedStackOverflowExceptionHandle(); |
1393 | if (ohSO != NULL) |
1394 | { |
1395 | pThread->SafeSetThrowables(ObjectFromHandle(ohSO) |
1396 | DEBUG_ARG(ThreadExceptionState::STEC_CurrentTrackerEqualNullOkHackForFatalStackOverflow), |
1397 | TRUE); |
1398 | } |
1399 | else |
1400 | { |
1401 | // We dont have a throwable - treat this as native unhandled exception |
1402 | fTreatAsNativeUnhandledException = TRUE; |
1403 | } |
1404 | } |
1405 | FrameWithCookie<FaultingExceptionFrame> fef; |
1406 | #if defined(WIN64EXCEPTIONS) |
1407 | *((&fef)->GetGSCookiePtr()) = GetProcessGSCookie(); |
1408 | #endif // WIN64EXCEPTIONS |
1409 | if (pExceptionInfo && pExceptionInfo->ContextRecord) |
1410 | { |
1411 | GCX_COOP(); |
1412 | fef.InitAndLink(pExceptionInfo->ContextRecord); |
1413 | } |
1414 | |
1415 | #ifndef FEATURE_PAL |
1416 | if (IsWatsonEnabled() && (g_pDebugInterface != NULL)) |
1417 | { |
1418 | _ASSERTE(pExceptionInfo != NULL); |
1419 | |
1420 | ResetWatsonBucketsParams param; |
1421 | param.m_pThread = pThread; |
1422 | param.pExceptionRecord = pExceptionInfo->ExceptionRecord; |
1423 | g_pDebugInterface->RequestFavor(ResetWatsonBucketsFavorWorker, reinterpret_cast<void *>(¶m)); |
1424 | } |
1425 | #endif // !FEATURE_PAL |
1426 | |
1427 | WatsonLastChance(pThread, pExceptionInfo, |
1428 | (fTreatAsNativeUnhandledException == FALSE)? TypeOfReportedError::UnhandledException: TypeOfReportedError::NativeThreadUnhandledException); |
1429 | } |
1430 | |
1431 | TerminateProcess(GetCurrentProcess(), COR_E_STACKOVERFLOW); |
1432 | UNREACHABLE(); |
1433 | } |
1434 | |
1435 | |
1436 | |
1437 | |
1438 | void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage /* = NULL */, PEXCEPTION_POINTERS pExceptionInfo /* = NULL */, LPCWSTR errorSource /* = NULL */, LPCWSTR argExceptionString /* = NULL */) |
1439 | { |
1440 | WRAPPER_NO_CONTRACT; |
1441 | |
1442 | // All of the code from here on out is robust to any failures in any API's that are called. |
1443 | FAULT_NOT_FATAL(); |
1444 | |
1445 | EXCEPTION_RECORD exceptionRecord; |
1446 | EXCEPTION_POINTERS exceptionPointers; |
1447 | CONTEXT context; |
1448 | |
1449 | if (pExceptionInfo == NULL) |
1450 | { |
1451 | ZeroMemory(&exceptionPointers, sizeof(exceptionPointers)); |
1452 | ZeroMemory(&exceptionRecord, sizeof(exceptionRecord)); |
1453 | ZeroMemory(&context, sizeof(context)); |
1454 | |
1455 | context.ContextFlags = CONTEXT_CONTROL; |
1456 | ClrCaptureContext(&context); |
1457 | |
1458 | exceptionRecord.ExceptionCode = exitCode; |
1459 | exceptionRecord.ExceptionAddress = reinterpret_cast< PVOID >(address); |
1460 | |
1461 | exceptionPointers.ExceptionRecord = &exceptionRecord; |
1462 | exceptionPointers.ContextRecord = &context; |
1463 | pExceptionInfo = &exceptionPointers; |
1464 | } |
1465 | |
1466 | // All of the code from here on out is allowed to trigger a GC, even if we're in a no-trigger region. We're |
1467 | // ripping the process down due to a fatal error... our invariants are already gone. |
1468 | { |
1469 | // This is fatal error. We do not care about SO mode any more. |
1470 | // All of the code from here on out is robust to any failures in any API's that are called. |
1471 | CONTRACT_VIOLATION(GCViolation | ModeViolation | SOToleranceViolation | FaultNotFatal | TakesLockViolation); |
1472 | |
1473 | |
1474 | // Setting g_fFatalErrorOccuredOnGCThread allows code to avoid attempting to make GC mode transitions which could |
1475 | // block indefinately if the fatal error occured during the GC. |
1476 | if (IsGCSpecialThread() && GCHeapUtilities::IsGCInProgress()) |
1477 | { |
1478 | g_fFatalErrorOccuredOnGCThread = TRUE; |
1479 | } |
1480 | |
1481 | // ThreadStore lock needs to be released before continuing with the FatalError handling should |
1482 | // because debugger is going to take CrstDebuggerMutex, whose lock level is higher than that of |
1483 | // CrstThreadStore. It should be safe to release the lock since execution will not be resumed |
1484 | // after fatal errors. |
1485 | if (ThreadStore::HoldingThreadStore(GetThread())) |
1486 | { |
1487 | ThreadSuspend::UnlockThreadStore(); |
1488 | } |
1489 | |
1490 | g_fFastExitProcess = 2; |
1491 | |
1492 | STRESS_LOG0(LF_CORDB,LL_INFO100, "D::HFE: About to call LogFatalError\n" ); |
1493 | switch (GetEEPolicy()->GetActionOnFailure(FAIL_FatalRuntime)) |
1494 | { |
1495 | case eRudeExitProcess: |
1496 | LogFatalError(exitCode, address, pszMessage, pExceptionInfo, errorSource, argExceptionString); |
1497 | SafeExitProcess(exitCode, TRUE); |
1498 | break; |
1499 | case eDisableRuntime: |
1500 | LogFatalError(exitCode, address, pszMessage, pExceptionInfo, errorSource, argExceptionString); |
1501 | DisableRuntime(SCA_ExitProcessWhenShutdownComplete); |
1502 | break; |
1503 | default: |
1504 | _ASSERTE(!"Invalid action for FAIL_FatalRuntime" ); |
1505 | break; |
1506 | } |
1507 | } |
1508 | |
1509 | UNREACHABLE(); |
1510 | } |
1511 | |
1512 | void EEPolicy::HandleExitProcessFromEscalation(EPolicyAction action, UINT exitCode) |
1513 | { |
1514 | WRAPPER_NO_CONTRACT; |
1515 | CONTRACT_VIOLATION(GCViolation); |
1516 | |
1517 | _ASSERTE (action >= eExitProcess); |
1518 | // If policy for ExitProcess is not default action, i.e. ExitProcess, we will use it. |
1519 | // Otherwise overwrite it with passing arg action; |
1520 | EPolicyAction todo = GetEEPolicy()->GetDefaultAction(OPR_ProcessExit, NULL); |
1521 | if (todo == eExitProcess) |
1522 | { |
1523 | todo = action; |
1524 | } |
1525 | GetEEPolicy()->NotifyHostOnDefaultAction(OPR_ProcessExit,todo); |
1526 | |
1527 | HandleExitProcessHelper(todo, exitCode, SCA_ExitProcessWhenShutdownComplete); |
1528 | } |
1529 | |
1530 | void EEPolicy::HandleCodeContractFailure(LPCWSTR pMessage, LPCWSTR pCondition, LPCWSTR pInnerExceptionAsString) |
1531 | { |
1532 | WRAPPER_NO_CONTRACT; |
1533 | |
1534 | EEPolicy* pPolicy = GetEEPolicy(); |
1535 | // GetActionOnFailure will notify the host for us. |
1536 | EPolicyAction action = pPolicy->GetActionOnFailure(FAIL_CodeContract); |
1537 | Thread* pThread = GetThread(); |
1538 | AppDomain* pCurrentDomain = ::GetAppDomain(); |
1539 | |
1540 | switch(action) { |
1541 | case eThrowException: |
1542 | // Let managed code throw a ContractException (it's easier to pass the right parameters to the constructor). |
1543 | break; |
1544 | case eAbortThread: |
1545 | pThread->UserAbort(Thread::TAR_Thread, TA_Safe, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal); |
1546 | break; |
1547 | case eRudeAbortThread: |
1548 | pThread->UserAbort(Thread::TAR_Thread, TA_Rude, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal); |
1549 | break; |
1550 | case eExitProcess: // Merged w/ default case |
1551 | default: |
1552 | _ASSERTE(action == eExitProcess); |
1553 | // Since we have no exception object, make sure |
1554 | // UE tracker is clean so that RetrieveManagedBucketParameters |
1555 | // does not take any bucket details. |
1556 | #ifndef FEATURE_PAL |
1557 | pThread->GetExceptionState()->GetUEWatsonBucketTracker()->ClearWatsonBucketDetails(); |
1558 | #endif // !FEATURE_PAL |
1559 | pPolicy->HandleFatalError(COR_E_CODECONTRACTFAILED, NULL, pMessage); |
1560 | break; |
1561 | } |
1562 | } |
1563 | |
1564 | |