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// Debug.cpp
6//
7// Helper code for debugging.
8//*****************************************************************************
9//
10
11
12#include "stdafx.h"
13#include "utilcode.h"
14#include "ex.h"
15#include "corexcep.h"
16
17#ifdef _DEBUG
18#define LOGGING
19#endif
20
21
22#include "log.h"
23
24extern "C" _CRTIMP int __cdecl _flushall(void);
25
26// Global state counter to implement SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE.
27Volatile<LONG> g_DbgSuppressAllocationAsserts = 0;
28
29
30#ifdef _DEBUG
31
32int LowResourceMessageBoxHelperAnsi(
33 LPCSTR szText, // Text message
34 LPCSTR szTitle, // Title
35 UINT uType); // Style of MessageBox
36
37//*****************************************************************************
38// This struct tracks the asserts we want to ignore in the rest of this
39// run of the application.
40//*****************************************************************************
41struct _DBGIGNOREDATA
42{
43 char rcFile[_MAX_PATH];
44 int iLine;
45 bool bIgnore;
46};
47
48typedef CDynArray<_DBGIGNOREDATA> DBGIGNORE;
49static BYTE grIgnoreMemory[sizeof(DBGIGNORE)];
50inline DBGIGNORE* GetDBGIGNORE()
51{
52 STATIC_CONTRACT_NOTHROW;
53 STATIC_CONTRACT_GC_NOTRIGGER;
54
55 static bool fInit; // = false;
56 if (!fInit)
57 {
58 SCAN_IGNORE_THROW; // Doesn't really throw here.
59 new (grIgnoreMemory) CDynArray<_DBGIGNOREDATA>();
60 fInit = true;
61 }
62
63 return (DBGIGNORE*)grIgnoreMemory;
64}
65
66// Continue the app on an assert. Still output the assert, but
67// Don't throw up a GUI. This is useful for testing fatal error
68// paths (like FEEE) where the runtime asserts.
69BOOL ContinueOnAssert()
70{
71 STATIC_CONTRACT_NOTHROW;
72 STATIC_CONTRACT_GC_NOTRIGGER;
73 STATIC_CONTRACT_DEBUG_ONLY;
74
75 static ConfigDWORD fNoGui;
76 return fNoGui.val(CLRConfig::INTERNAL_ContinueOnAssert);
77}
78
79void DoRaiseExceptionOnAssert(DWORD chance)
80{
81 STATIC_CONTRACT_NOTHROW;
82 STATIC_CONTRACT_GC_NOTRIGGER;
83 STATIC_CONTRACT_DEBUG_ONLY;
84 STATIC_CONTRACT_FORBID_FAULT;
85 STATIC_CONTRACT_SUPPORTS_DAC;
86
87#if !defined(DACCESS_COMPILE)
88 if (chance)
89 {
90#ifndef FEATURE_PAL
91 PAL_TRY_NAKED
92 {
93 RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL);
94 }
95 PAL_EXCEPT_NAKED((chance == 1) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
96 {
97 }
98 PAL_ENDTRY_NAKED
99#else // FEATURE_PAL
100 // For PAL always raise the exception.
101 RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL);
102#endif // FEATURE_PAL
103 }
104#endif // !DACCESS_COMPILE
105}
106
107enum RaiseOnAssertOptions { rTestAndRaise, rTestOnly };
108
109BOOL RaiseExceptionOnAssert(RaiseOnAssertOptions option = rTestAndRaise)
110{
111 STATIC_CONTRACT_NOTHROW;
112 STATIC_CONTRACT_GC_NOTRIGGER;
113 STATIC_CONTRACT_DEBUG_ONLY;
114 STATIC_CONTRACT_FORBID_FAULT;
115 STATIC_CONTRACT_SUPPORTS_DAC;
116
117 // ok for debug-only code to take locks
118 CONTRACT_VIOLATION(TakesLockViolation);
119
120 DWORD fRet = 0;
121
122#if !defined(DACCESS_COMPILE)
123 static ConfigDWORD fRaiseExceptionOnAssert;
124 //
125 // we don't want this config key to affect mscordacwks as well!
126 //
127 EX_TRY
128 {
129 fRet = fRaiseExceptionOnAssert.val(CLRConfig::INTERNAL_RaiseExceptionOnAssert);
130 }
131 EX_CATCH
132 {
133 }
134 EX_END_CATCH(SwallowAllExceptions);
135
136 if (option == rTestAndRaise && fRet != 0)
137 {
138 DoRaiseExceptionOnAssert(fRet);
139 }
140#endif // !DACCESS_COMPILE
141
142 return fRet != 0;
143}
144
145BOOL DebugBreakOnAssert()
146{
147 STATIC_CONTRACT_NOTHROW;
148 STATIC_CONTRACT_GC_NOTRIGGER;
149 STATIC_CONTRACT_DEBUG_ONLY;
150 STATIC_CONTRACT_FORBID_FAULT;
151 STATIC_CONTRACT_SUPPORTS_DAC;
152
153 // ok for debug-only code to take locks
154 CONTRACT_VIOLATION(TakesLockViolation);
155
156 BOOL fRet = FALSE;
157
158#ifndef DACCESS_COMPILE
159 static ConfigDWORD fDebugBreak;
160 //
161 // we don't want this config key to affect mscordacwks as well!
162 //
163 EX_TRY
164 {
165 fRet = fDebugBreak.val(CLRConfig::INTERNAL_DebugBreakOnAssert);
166 }
167 EX_CATCH
168 {
169 }
170 EX_END_CATCH(SwallowAllExceptions);
171#endif // DACCESS_COMPILE
172
173 return fRet;
174}
175
176VOID TerminateOnAssert()
177{
178 STATIC_CONTRACT_NOTHROW;
179 STATIC_CONTRACT_GC_NOTRIGGER;
180 STATIC_CONTRACT_DEBUG_ONLY;
181
182 ShutdownLogging();
183 RaiseFailFastException(NULL, NULL, 0);
184}
185
186// Whether this thread is already displaying an assert dialog.
187BOOL IsDisplayingAssertDlg()
188{
189 STATIC_CONTRACT_NOTHROW;
190 STATIC_CONTRACT_GC_NOTRIGGER;
191 STATIC_CONTRACT_DEBUG_ONLY;
192
193 size_t flag = 0;
194 if (ClrFlsCheckValue(TlsIdx_AssertDlgStatus, (LPVOID *)&flag))
195 {
196 return (flag != 0);
197 }
198 return FALSE;
199}
200
201void SetDisplayingAssertDlg(BOOL value)
202{
203 STATIC_CONTRACT_NOTHROW;
204 STATIC_CONTRACT_GC_NOTRIGGER;
205 STATIC_CONTRACT_DEBUG_ONLY;
206
207 ClrFlsSetValue(TlsIdx_AssertDlgStatus, (LPVOID)(size_t)value);
208}
209
210VOID LogAssert(
211 LPCSTR szFile,
212 int iLine,
213 LPCSTR szExpr
214)
215{
216 STATIC_CONTRACT_NOTHROW;
217 STATIC_CONTRACT_GC_NOTRIGGER;
218 STATIC_CONTRACT_SO_TOLERANT;
219 STATIC_CONTRACT_DEBUG_ONLY;
220
221 // Log asserts to the stress log. Note that we can't include the szExpr b/c that
222 // may not be a string literal (particularly for formatt-able asserts).
223 STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine);
224
225 SYSTEMTIME st;
226#ifndef FEATURE_PAL
227 GetLocalTime(&st);
228#else
229 GetSystemTime(&st);
230#endif
231
232 PathString exename;
233 WszGetModuleFileName(NULL, exename);
234
235 LOG((LF_ASSERT,
236 LL_FATALERROR,
237 "FAILED ASSERT(PID %d [0x%08x], Thread: %d [0x%x]) (%lu/%lu/%lu: %02lu:%02lu:%02lu %s): File: %s, Line %d : %s\n",
238 GetCurrentProcessId(),
239 GetCurrentProcessId(),
240 GetCurrentThreadId(),
241 GetCurrentThreadId(),
242 (ULONG)st.wMonth,
243 (ULONG)st.wDay,
244 (ULONG)st.wYear,
245 1 + (( (ULONG)st.wHour + 11 ) % 12),
246 (ULONG)st.wMinute,
247 (ULONG)st.wSecond,
248 (st.wHour < 12) ? "am" : "pm",
249 szFile,
250 iLine,
251 szExpr));
252 LOG((LF_ASSERT, LL_FATALERROR, "RUNNING EXE: %ws\n", exename.GetUnicode()));
253}
254
255//*****************************************************************************
256
257BOOL LaunchJITDebugger()
258{
259 STATIC_CONTRACT_NOTHROW;
260 STATIC_CONTRACT_GC_NOTRIGGER;
261 STATIC_CONTRACT_DEBUG_ONLY;
262
263 BOOL fSuccess = FALSE;
264#ifndef FEATURE_PAL
265 EX_TRY
266 {
267 SString debugger;
268 GetDebuggerSettingInfo(debugger, NULL);
269
270 SECURITY_ATTRIBUTES sa;
271 sa.nLength = sizeof(sa);
272 sa.lpSecurityDescriptor = NULL;
273 sa.bInheritHandle = TRUE;
274
275 // We can leave this event as it is since it is inherited by a child process.
276 // We will block one scheduler, but the process is asking a user if they want to attach debugger.
277 HandleHolder eventHandle = WszCreateEvent(&sa, TRUE, FALSE, NULL);
278 if (eventHandle == NULL)
279 ThrowOutOfMemory();
280
281 SString cmdLine;
282 cmdLine.Printf(debugger, GetCurrentProcessId(), eventHandle.GetValue());
283
284 STARTUPINFO StartupInfo;
285 memset(&StartupInfo, 0, sizeof(StartupInfo));
286 StartupInfo.cb = sizeof(StartupInfo);
287 StartupInfo.lpDesktop = const_cast<LPWSTR>(W("Winsta0\\Default"));
288
289 PROCESS_INFORMATION ProcessInformation;
290 if (WszCreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation))
291 {
292 WaitForSingleObject(eventHandle.GetValue(), INFINITE);
293 }
294
295 fSuccess = TRUE;
296 }
297 EX_CATCH
298 {
299 }
300 EX_END_CATCH(SwallowAllExceptions);
301#endif // !FEATURE_PAL
302 return fSuccess;
303}
304
305
306//*****************************************************************************
307// This function is called in order to ultimately return an out of memory
308// failed hresult. But this guy will check what environment you are running
309// in and give an assert for running in a debug build environment. Usually
310// out of memory on a dev machine is a bogus allocation, and this allows you
311// to catch such errors. But when run in a stress envrionment where you are
312// trying to get out of memory, assert behavior stops the tests.
313//*****************************************************************************
314HRESULT _OutOfMemory(LPCSTR szFile, int iLine)
315{
316 STATIC_CONTRACT_NOTHROW;
317 STATIC_CONTRACT_GC_NOTRIGGER;
318 STATIC_CONTRACT_DEBUG_ONLY;
319
320 DbgWriteEx(W("WARNING: Out of memory condition being issued from: %hs, line %d\n"),
321 szFile, iLine);
322 return (E_OUTOFMEMORY);
323}
324
325int _DbgBreakCount = 0;
326static const char * szLowMemoryAssertMessage = "Assert failure (unable to format)";
327
328//*****************************************************************************
329// This function will handle ignore codes and tell the user what is happening.
330//*****************************************************************************
331bool _DbgBreakCheck(
332 LPCSTR szFile,
333 int iLine,
334 LPCSTR szExpr,
335 BOOL fConstrained)
336{
337 STATIC_CONTRACT_THROWS;
338 STATIC_CONTRACT_GC_NOTRIGGER;
339 STATIC_CONTRACT_FORBID_FAULT;
340 STATIC_CONTRACT_DEBUG_ONLY;
341
342 DBGIGNORE* pDBGIFNORE = GetDBGIGNORE();
343 _DBGIGNOREDATA *psData;
344 int i;
345
346 // Check for ignore all.
347 for (i = 0, psData = pDBGIFNORE->Ptr(); i < pDBGIFNORE->Count(); i++, psData++)
348 {
349 if (psData->iLine == iLine && SString::_stricmp(psData->rcFile, szFile) == 0 && psData->bIgnore == true)
350 {
351 return false;
352 }
353 }
354
355 CONTRACT_VIOLATION(FaultNotFatal | GCViolation | TakesLockViolation);
356
357 SString debugOutput;
358 SString dialogOutput;
359 SString modulePath;
360 SString dialogTitle;
361 SString dialogIgnoreMessage;
362 BOOL formattedMessages = FALSE;
363
364 // If we are low on memory we cannot even format a message. If this happens we want to
365 // contain the exception here but display as much information as we can about the exception.
366 if (!fConstrained)
367 {
368 EX_TRY
369 {
370 ClrGetModuleFileName(0, modulePath);
371 debugOutput.Printf(
372 W("\nAssert failure(PID %d [0x%08x], Thread: %d [0x%04x]): %hs\n")
373 W(" File: %hs Line: %d\n")
374 W(" Image: "),
375 GetCurrentProcessId(), GetCurrentProcessId(),
376 GetCurrentThreadId(), GetCurrentThreadId(),
377 szExpr, szFile, iLine);
378 debugOutput.Append(modulePath);
379 debugOutput.Append(W("\n\n"));
380
381 // Change format for message box. The extra spaces in the title
382 // are there to get around format truncation.
383 dialogOutput.Printf(
384 W("%hs\n\n%hs, Line: %d\n\nAbort - Kill program\nRetry - Debug\nIgnore - Keep running\n")
385 W("\n\nImage:\n"), szExpr, szFile, iLine);
386 dialogOutput.Append(modulePath);
387 dialogOutput.Append(W("\n"));
388 dialogTitle.Printf(W("Assert Failure (PID %d, Thread %d/0x%04x)"),
389 GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId());
390
391 dialogIgnoreMessage.Printf(W("Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n\n%hs\nLine: %d\n"),
392 szFile, iLine);
393
394 formattedMessages = TRUE;
395 }
396 EX_CATCH
397 {
398 }
399 EX_END_CATCH(SwallowAllExceptions);
400 }
401
402 // Emit assert in debug output and console for easy access.
403 if (formattedMessages)
404 {
405 WszOutputDebugString(debugOutput);
406 fwprintf(stderr, W("%s"), (const WCHAR*)debugOutput);
407 }
408 else
409 {
410 // Note: we cannot convert to unicode or concatenate in this situation.
411 OutputDebugStringA(szLowMemoryAssertMessage);
412 OutputDebugStringA("\n");
413 OutputDebugStringA(szFile);
414 OutputDebugStringA("\n");
415 OutputDebugStringA(szExpr);
416 OutputDebugStringA("\n");
417 printf(szLowMemoryAssertMessage);
418 printf("\n");
419 printf(szFile);
420 printf("\n");
421 printf("%s", szExpr);
422 printf("\n");
423 }
424
425 LogAssert(szFile, iLine, szExpr);
426 FlushLogging(); // make certain we get the last part of the log
427 _flushall();
428
429 if (ContinueOnAssert())
430 {
431 return false; // don't stop debugger. No gui.
432 }
433
434 if (IsDebuggerPresent() || DebugBreakOnAssert())
435 {
436 return true; // like a retry
437 }
438
439 if (NoGuiOnAssert())
440 {
441 TerminateOnAssert();
442 }
443
444 if (IsDisplayingAssertDlg())
445 {
446 // We are already displaying an assert dialog box on this thread. The reason why we came here is
447 // the message loop run by the API we call to display the UI. A message was dispatched and execution
448 // ended up in the runtime where it fired another assertion. If this happens before the dialog had
449 // a chance to fully initialize the original assert may not be visible which is misleading for the
450 // user. So we just continue.
451 return false;
452 }
453
454 SetDisplayingAssertDlg(TRUE);
455
456 // Tell user there was an error.
457 _DbgBreakCount++;
458 int ret;
459 if (formattedMessages)
460 {
461 ret = UtilMessageBoxCatastrophicNonLocalized(
462 W("%s"), dialogTitle, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION, TRUE, (const WCHAR*)dialogOutput);
463 }
464 else
465 {
466 ret = LowResourceMessageBoxHelperAnsi(
467 szExpr, szLowMemoryAssertMessage, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION);
468 }
469 --_DbgBreakCount;
470
471 SetDisplayingAssertDlg(FALSE);
472
473 switch(ret)
474 {
475 case 0:
476#if 0
477 // The message box was not displayed. Tell caller to break.
478 return true;
479#endif
480 // For abort, just quit the app.
481 case IDABORT:
482 TerminateProcess(GetCurrentProcess(), 1);
483 break;
484
485 // Tell caller to break at the correct loction.
486 case IDRETRY:
487 if (IsDebuggerPresent())
488 {
489 SetErrorMode(0);
490 }
491 else
492 {
493 LaunchJITDebugger();
494 }
495 return true;
496
497 // If we want to ignore the assert, find out if this is forever.
498 case IDIGNORE:
499 if (formattedMessages)
500 {
501 if (UtilMessageBoxCatastrophicNonLocalized(
502 dialogIgnoreMessage,
503 W("Ignore Assert Forever?"),
504 MB_ICONQUESTION | MB_YESNO,
505 TRUE) != IDYES)
506 {
507 break;
508 }
509 }
510 else
511 {
512 if (LowResourceMessageBoxHelperAnsi(
513 "Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n",
514 "Ignore Assert Forever?",
515 MB_ICONQUESTION | MB_YESNO) != IDYES)
516 {
517 break;
518 }
519 }
520 if ((psData = pDBGIFNORE->Append()) == 0)
521 {
522 return false;
523 }
524 psData->bIgnore = true;
525 psData->iLine = iLine;
526 strcpy(psData->rcFile, szFile);
527 break;
528 }
529
530 return false;
531}
532
533bool _DbgBreakCheckNoThrow(
534 LPCSTR szFile,
535 int iLine,
536 LPCSTR szExpr,
537 BOOL fConstrained)
538{
539 STATIC_CONTRACT_NOTHROW;
540 STATIC_CONTRACT_GC_NOTRIGGER;
541 STATIC_CONTRACT_FORBID_FAULT;
542 STATIC_CONTRACT_DEBUG_ONLY;
543
544 bool failed = false;
545 bool result = false;
546 EX_TRY
547 {
548 result = _DbgBreakCheck(szFile, iLine, szExpr, fConstrained);
549 }
550 EX_CATCH
551 {
552 failed = true;
553 }
554 EX_END_CATCH(SwallowAllExceptions);
555
556 if (failed)
557 {
558 return true;
559 }
560 return result;
561}
562
563#ifndef FEATURE_PAL
564// Get the timestamp from the PE file header. This is useful
565unsigned DbgGetEXETimeStamp()
566{
567 STATIC_CONTRACT_NOTHROW;
568 STATIC_CONTRACT_GC_NOTRIGGER;
569 STATIC_CONTRACT_DEBUG_ONLY;
570
571 static ULONG cache = 0;
572 if (cache == 0) {
573 // Use GetModuleHandleA to avoid contracts - this results in a recursive loop initializing the
574 // debug allocator.
575 BYTE* imageBase = (BYTE*) GetModuleHandleA(NULL);
576 if (imageBase == 0)
577 return(0);
578 IMAGE_DOS_HEADER *pDOS = (IMAGE_DOS_HEADER*) imageBase;
579 if ((pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE)) || (pDOS->e_lfanew == 0))
580 return(0);
581
582 IMAGE_NT_HEADERS *pNT = (IMAGE_NT_HEADERS*) (VAL32(pDOS->e_lfanew) + imageBase);
583 cache = VAL32(pNT->FileHeader.TimeDateStamp);
584 }
585
586 return cache;
587}
588#endif // FEATURE_PAL
589
590// Called from within the IfFail...() macros. Set a breakpoint here to break on
591// errors.
592VOID DebBreak()
593{
594 STATIC_CONTRACT_LEAF;
595 static int i = 0; // add some code here so that we'll be able to set a BP
596 i++;
597}
598
599VOID DebBreakHr(HRESULT hr)
600{
601 STATIC_CONTRACT_LEAF;
602 static int i = 0; // add some code here so that we'll be able to set a BP
603 _ASSERTE(hr != (HRESULT) 0xcccccccc);
604 i++;
605
606 // @CONSIDER: We maybe want this on retail builds.
607 #ifdef _DEBUG
608 static DWORD dwBreakHR = 99;
609
610 if (dwBreakHR == 99)
611 dwBreakHR = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnHR);
612 if (dwBreakHR == (DWORD)hr)
613 {
614 _DbgBreak();
615 }
616 #endif
617}
618
619#ifndef FEATURE_PAL
620CHAR g_szExprWithStack2[10480];
621#endif
622void *dbgForceToMemory; // dummy pointer that pessimises enregistration
623
624#ifdef MDA_SUPPORTED
625#ifdef _DEBUG
626BOOL g_bMdaDisableAsserts = FALSE;
627#endif
628#endif
629
630int g_BufferLock = -1;
631
632VOID DbgAssertDialog(const char *szFile, int iLine, const char *szExpr)
633{
634 STATIC_CONTRACT_NOTHROW;
635 STATIC_CONTRACT_GC_NOTRIGGER;
636 STATIC_CONTRACT_FORBID_FAULT;
637 STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
638
639 DEBUG_ONLY_FUNCTION;
640
641#ifdef MDA_SUPPORTED
642#ifdef _DEBUG
643 if (g_bMdaDisableAsserts)
644 return;
645#endif
646#endif
647
648#ifdef DACCESS_COMPILE
649 // In the DAC case, asserts can mean one of two things.
650 // Either there is a bug in the DAC infrastructure itself (a real assert), or just
651 // that the target is corrupt or being accessed at an inconsistent state (a "target
652 // consistency failure"). For target consistency failures, we need a mechanism to disable them
653 // (without affecting other asserts) so that we can test corrupt / inconsistent targets.
654
655 // @dbgtodo DAC: For now we're treating all asserts as if they are target consistency checks.
656 // In the future we should differentiate the two so that real asserts continue to fire, even when
657 // we expect the target to be inconsistent. See DevDiv Bugs 31674.
658 if( !DacTargetConsistencyAssertsEnabled() )
659 {
660 return;
661 }
662#endif // #ifndef DACCESS_COMPILE
663
664 // We increment this every time we use the SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE
665 // macro below. If it is a big number it means either a lot of threads are asserting
666 // or we have a recursion in the Assert logic (usually the latter). At least with this
667 // code in place, we don't get stack overflow (and the process torn down).
668 // the correct fix is to avoid calling asserting when allocating memory with an assert.
669 if (g_DbgSuppressAllocationAsserts > 16)
670 DebugBreak();
671
672 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
673
674 // Raising the assert dialog can cause us to re-enter the host when allocating
675 // memory for the string. Since this is debug-only code, we can safely skip
676 // violation asserts here, particularly since they can also cause infinite
677 // recursion.
678 PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonDebugOnly);
679
680 dbgForceToMemory = &szFile; //make certain these args are available in the debugger
681 dbgForceToMemory = &iLine;
682 dbgForceToMemory = &szExpr;
683
684 RaiseExceptionOnAssert(rTestAndRaise);
685
686 BOOL fConstrained = FALSE;
687
688 DWORD dwAssertStacktrace = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertStacktrace);
689
690#if !defined(DACCESS_COMPILE) && defined(FEATURE_STACK_PROBE)
691 //global g_fpCheckNStackPagesAvailable is not present when SO infrastructure code is not present
692 // Trying to get a stack trace if there is little stack available can cause a silent process
693 // teardown, so only try to do this there is plenty of stack.
694 if ((dwAssertStacktrace) != 0 && (g_fpCheckNStackPagesAvailable != NULL))
695 {
696 if (!g_fpCheckNStackPagesAvailable(12))
697 {
698 fConstrained = TRUE;
699 }
700 }
701#endif
702
703 LONG lAlreadyOwned = InterlockedExchange((LPLONG)&g_BufferLock, 1);
704 if (fConstrained || dwAssertStacktrace == 0 || lAlreadyOwned == 1)
705 {
706 if (_DbgBreakCheckNoThrow(szFile, iLine, szExpr, fConstrained))
707 {
708 _DbgBreak();
709 }
710 }
711 else
712 {
713 char *szExprToDisplay = (char*)szExpr;
714#ifdef FEATURE_PAL
715 BOOL fGotStackTrace = TRUE;
716#else
717 BOOL fGotStackTrace = FALSE;
718#ifndef DACCESS_COMPILE
719 EX_TRY
720 {
721 FAULT_NOT_FATAL();
722 szExprToDisplay = &g_szExprWithStack2[0];
723 strcpy(szExprToDisplay, szExpr);
724 strcat_s(szExprToDisplay, _countof(g_szExprWithStack2), "\n\n");
725 GetStringFromStackLevels(1, 10, szExprToDisplay + strlen(szExprToDisplay));
726 fGotStackTrace = TRUE;
727 }
728 EX_CATCH
729 {
730 }
731 EX_END_CATCH(SwallowAllExceptions);
732#endif // DACCESS_COMPILE
733#endif // FEATURE_PAL
734
735 if (_DbgBreakCheckNoThrow(szFile, iLine, szExprToDisplay, !fGotStackTrace))
736 {
737 _DbgBreak();
738 }
739
740 g_BufferLock = 0;
741 }
742} // DbgAssertDialog
743
744#if !defined(DACCESS_COMPILE)
745//-----------------------------------------------------------------------------
746// Returns an a stacktrace for a given context.
747// Very useful inside exception filters.
748// Returns true if successful, false on failure (such as OOM).
749// This never throws.
750//-----------------------------------------------------------------------------
751bool GetStackTraceAtContext(SString & s, CONTEXT * pContext)
752{
753 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
754 STATIC_CONTRACT_DEBUG_ONLY;
755
756 // NULL means use the current context.
757 bool fSuccess = false;
758
759 FAULT_NOT_FATAL();
760
761#ifndef FEATURE_PAL
762 EX_TRY
763 {
764 const int cTotal = cfrMaxAssertStackLevels - 1;
765 // If we have a supplied context, then don't skip any frames. Else we'll
766 // be using the current context, so skip this frame.
767 const int cSkip = (pContext == NULL) ? 1 : 0;
768 char * szString = s.OpenANSIBuffer(cchMaxAssertStackLevelStringLen * cTotal);
769 GetStringFromStackLevels(cSkip, cTotal, szString, pContext);
770 s.CloseBuffer((COUNT_T) strlen(szString));
771
772 // If we made it this far w/o throwing, we succeeded.
773 fSuccess = true;
774 }
775 EX_CATCH
776 {
777 // Nothing to do here.
778 }
779 EX_END_CATCH(SwallowAllExceptions);
780#endif // FEATURE_PAL
781
782 return fSuccess;
783} // GetStackTraceAtContext
784#endif // !defined(DACCESS_COMPILE)
785#endif // _DEBUG
786
787BOOL NoGuiOnAssert()
788{
789 STATIC_CONTRACT_NOTHROW;
790 STATIC_CONTRACT_GC_NOTRIGGER;
791 STATIC_CONTRACT_DEBUG_ONLY;
792
793 static ConfigDWORD fNoGui;
794 return fNoGui.val(CLRConfig::INTERNAL_NoGuiOnAssert);
795}
796
797// This helper will throw up a message box without allocating or using stack if possible, and is
798// appropriate for either low memory or low stack situations.
799int LowResourceMessageBoxHelperAnsi(
800 LPCSTR szText, // Text message
801 LPCSTR szTitle, // Title
802 UINT uType) // Style of MessageBox
803{
804 CONTRACTL
805 {
806 NOTHROW;
807 INJECT_FAULT(return IDCANCEL;);
808 }
809 CONTRACTL_END;
810
811 // In low memory or stack constrained code we cannot format or convert strings, so use the
812 // ANSI version.
813 int result = MessageBoxA(NULL, szText, szTitle, uType);
814 return result;
815}
816
817
818/****************************************************************************
819 The following two functions are defined to allow Free builds to call
820 DebugBreak or to Assert with a stack trace for unexpected fatal errors.
821 Typically these paths are enabled via a registry key in a Free Build
822*****************************************************************************/
823
824VOID __FreeBuildDebugBreak()
825{
826 WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency!
827
828 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnRetailAssert))
829 {
830 DebugBreak();
831 }
832}
833
834void *freForceToMemory; // dummy pointer that pessimises enregistration
835
836void DECLSPEC_NORETURN __FreeBuildAssertFail(const char *szFile, int iLine, const char *szExpr)
837{
838 WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency!
839
840 freForceToMemory = &szFile; //make certain these args are available in the debugger
841 freForceToMemory = &iLine;
842 freForceToMemory = &szExpr;
843
844 __FreeBuildDebugBreak();
845
846 SString buffer;
847 SString modulePath;
848
849 // Give assert in output for easy access.
850 ClrGetModuleFileName(0, modulePath);
851#ifndef FEATURE_PAL
852 buffer.Printf(W("CLR: Assert failure(PID %d [0x%08x], Thread: %d [0x%x]): %hs\n")
853 W(" File: %hs, Line: %d Image:\n"),
854 GetCurrentProcessId(), GetCurrentProcessId(),
855 GetCurrentThreadId(), GetCurrentThreadId(),
856 szExpr, szFile, iLine);
857 buffer.Append(modulePath);
858 buffer.Append(W("\n"));
859 WszOutputDebugString(buffer);
860 // Write out the error to the console
861 _putws(buffer);
862#else // FEATURE_PAL
863 // UNIXTODO: Do this for Unix.
864#endif // FEATURE_PAL
865 // Log to the stress log. Note that we can't include the szExpr b/c that
866 // may not be a string literal (particularly for formatt-able asserts).
867 STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine);
868
869 FlushLogging(); // make certain we get the last part of the log
870
871 _flushall();
872
873 ShutdownLogging();
874
875 RaiseFailFastException(NULL, NULL, 0);
876
877 UNREACHABLE();
878}
879