2 * PROJECT: ReactOS CRT library
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Debug CRT reporting functions
5 * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
8 // This file should not be included in release builds,
9 // but since we do not have a good mechanism for this at the moment,
10 // just rely on the compiler to optimize it away instead of omitting the code.
18 #undef OutputDebugString
20 #define DBGRPT_MAX_BUFFER_SIZE 4096
21 #define DBGRPT_ASSERT_PREFIX_MESSAGE "Assertion failed: "
22 #define DBGRPT_ASSERT_PREFIX_NOMESSAGE "Assertion failed!"
23 #define DBGRPT_STRING_TOO_LONG "_CrtDbgReport: String too long"
25 // Keep track of active asserts
26 static long _CrtInAssert
= -1;
28 static int _CrtModeOutputFormat
[_CRT_ERRCNT
] =
35 static const wchar_t* _CrtModeMessages
[_CRT_ERRCNT
] =
42 // Manually delay-load as to not have a dependency on user32
43 typedef int (WINAPI
*tMessageBoxW
)(_In_opt_ HWND hWnd
, _In_opt_ LPCWSTR lpText
, _In_opt_ LPCWSTR lpCaption
, _In_ UINT uType
);
44 static HMODULE _CrtUser32Handle
= NULL
;
45 static tMessageBoxW _CrtMessageBoxW
= NULL
;
47 template <typename char_t
>
48 struct dbgrpt_char_traits
;
51 struct dbgrpt_char_traits
<char>
55 static const wchar_t* szAssertionMessage
;
56 static const char_t
* szEmptyString
;
57 static const char_t
* szUnknownFile
;
59 static void OutputDebugString(const char_t
* message
);
63 struct dbgrpt_char_traits
<wchar_t>
65 typedef wchar_t char_t
;
67 static const wchar_t* szAssertionMessage
;
68 static const char_t
* szEmptyString
;
69 static const char_t
* szUnknownFile
;
71 static void OutputDebugString(const char_t
* message
);
75 typedef dbgrpt_char_traits
<char> achar_traits
;
76 typedef dbgrpt_char_traits
<wchar_t> wchar_traits
;
79 const wchar_t* achar_traits::szAssertionMessage
=
82 L
"%s%hs" /* filename */
83 L
"%s%s" /* linenumber */
84 L
"%s%hs" /* message */
85 L
"\n\n(Press Retry to debug the application)";
86 const wchar_t* wchar_traits::szAssertionMessage
=
89 L
"%s%ws" /* filename */
90 L
"%s%s" /* linenumber */
91 L
"%s%ws" /* message */
92 L
"\n\n(Press Retry to debug the application)";
94 const achar_traits::char_t
* achar_traits::szEmptyString
= "";
95 const wchar_traits::char_t
* wchar_traits::szEmptyString
= L
"";
97 const achar_traits::char_t
* achar_traits::szUnknownFile
= "<unknown file>";
98 const wchar_traits::char_t
* wchar_traits::szUnknownFile
= L
"<unknown file>";
100 void achar_traits::OutputDebugString(const char* message
)
102 OutputDebugStringA(message
);
105 void wchar_traits::OutputDebugString(const wchar_t* message
)
107 OutputDebugStringW(message
);
112 HMODULE
_CrtGetUser32()
114 if (_CrtUser32Handle
== NULL
)
116 HMODULE mod
= LoadLibraryExW(L
"user32.dll", NULL
, 0 /* NT6+: LOAD_LIBRARY_SEARCH_SYSTEM32 */);
118 mod
= (HMODULE
)INVALID_HANDLE_VALUE
;
120 if (_InterlockedCompareExchangePointer((PVOID
*)&_CrtUser32Handle
, mod
, NULL
))
122 if (mod
!= INVALID_HANDLE_VALUE
)
127 return _CrtUser32Handle
!= INVALID_HANDLE_VALUE
? _CrtUser32Handle
: NULL
;
130 static tMessageBoxW
_CrtGetMessageBox()
132 HMODULE mod
= _CrtGetUser32();
134 if (_CrtMessageBoxW
== NULL
&& mod
!= INVALID_HANDLE_VALUE
)
136 tMessageBoxW proc
= (tMessageBoxW
)GetProcAddress(mod
, "MessageBoxW");
138 proc
= (tMessageBoxW
)INVALID_HANDLE_VALUE
;
140 _InterlockedCompareExchangePointer((PVOID
*)&_CrtMessageBoxW
, (PVOID
)proc
, NULL
);
143 return _CrtMessageBoxW
!= INVALID_HANDLE_VALUE
? _CrtMessageBoxW
: NULL
;
147 template <typename char_t
>
148 static int _CrtDbgReportWindow(int reportType
, const char_t
*filename
, int linenumber
, const char_t
*moduleName
, const char_t
* message
)
150 typedef dbgrpt_char_traits
<char_t
> traits
;
152 wchar_t szCompleteMessage
[(DBGRPT_MAX_BUFFER_SIZE
+1)*2] = {0};
153 wchar_t LineBuffer
[20] = {0};
155 if (filename
&& !filename
[0])
157 if (moduleName
&& !moduleName
[0])
159 if (message
&& !message
[0])
162 _itow(linenumber
, LineBuffer
, 10);
164 _snwprintf(szCompleteMessage
, DBGRPT_MAX_BUFFER_SIZE
* 2,
165 traits::szAssertionMessage
,
166 _CrtModeMessages
[reportType
],
167 moduleName
? L
"\nModule: " : L
"", moduleName
? moduleName
: traits::szEmptyString
,
168 filename
? L
"\nFile: " : L
"", filename
? filename
: traits::szEmptyString
,
169 LineBuffer
[0] ? L
"\nLine: " : L
"", LineBuffer
[0] ? LineBuffer
: L
"",
170 message
? L
"\n\n" : L
"", message
? message
: traits::szEmptyString
);
172 if (IsDebuggerPresent())
174 OutputDebugStringW(szCompleteMessage
);
177 tMessageBoxW messageBox
= _CrtGetMessageBox();
179 return IsDebuggerPresent() ? IDRETRY
: IDABORT
;
181 // TODO: If we are not interacive, add MB_SERVICE_NOTIFICATION
182 return messageBox(NULL
, szCompleteMessage
, L
"ReactOS C++ Runtime Library",
183 MB_ABORTRETRYIGNORE
| MB_ICONHAND
| MB_SETFOREGROUND
| MB_TASKMODAL
);
186 template <typename char_t
>
187 static int _CrtEnterDbgReport(int reportType
, const char_t
*filename
, int linenumber
)
189 typedef dbgrpt_char_traits
<char_t
> traits
;
191 if (reportType
< 0 || reportType
>= _CRT_ERRCNT
)
194 if (reportType
== _CRT_ASSERT
)
196 if (_InterlockedIncrement(&_CrtInAssert
) > 0)
198 char LineBuffer
[20] = {0};
200 _itoa(linenumber
, LineBuffer
, 10);
202 OutputDebugStringA("Nested Assert from File: ");
203 traits::OutputDebugString(filename
? filename
: traits::szUnknownFile
);
204 OutputDebugStringA(", Line: ");
205 OutputDebugStringA(LineBuffer
);
206 OutputDebugStringA("\n");
210 _InterlockedDecrement(&_CrtInAssert
);
218 void _CrtLeaveDbgReport(int reportType
)
220 if (reportType
== _CRT_ASSERT
)
221 _InterlockedDecrement(&_CrtInAssert
);
225 template <typename char_t
>
226 static int _CrtHandleDbgReport(int reportType
, const char_t
* szCompleteMessage
, const char_t
* szFormatted
,
227 const char_t
*filename
, int linenumber
, const char_t
*moduleName
)
229 typedef dbgrpt_char_traits
<char_t
> traits
;
231 if (_CrtModeOutputFormat
[reportType
] & _CRTDBG_MODE_FILE
)
233 OutputDebugStringA("ERROR: Please implement _CrtSetReportFile first\n");
237 if (_CrtModeOutputFormat
[reportType
] & _CRTDBG_MODE_DEBUG
)
239 traits::OutputDebugString(szCompleteMessage
);
242 if (_CrtModeOutputFormat
[reportType
] & _CRTDBG_MODE_WNDW
)
244 int nResult
= _CrtDbgReportWindow(reportType
, filename
, linenumber
, moduleName
, szFormatted
);
255 return FALSE
; // Unreachable
264 int __cdecl
_CrtDbgReport(int reportType
, const char *filename
, int linenumber
, const char *moduleName
, const char *format
, ...)
266 char szFormatted
[DBGRPT_MAX_BUFFER_SIZE
+1] = {0}; // The user provided message
267 char szCompleteMessage
[(DBGRPT_MAX_BUFFER_SIZE
+1)*2] = {0}; // The output for debug / file
269 // Check for recursive _CrtDbgReport calls, and validate reportType
270 if (!_CrtEnterDbgReport(reportType
, filename
, linenumber
))
275 _snprintf(szCompleteMessage
, DBGRPT_MAX_BUFFER_SIZE
, "%s(%d) : ", filename
, linenumber
);
281 va_start(arglist
, format
);
282 int len
= _vsnprintf(szFormatted
, DBGRPT_MAX_BUFFER_SIZE
- 2 - sizeof(DBGRPT_ASSERT_PREFIX_MESSAGE
), format
, arglist
);
287 strcpy(szFormatted
, DBGRPT_STRING_TOO_LONG
);
290 if (reportType
== _CRT_ASSERT
)
291 strcat(szCompleteMessage
, DBGRPT_ASSERT_PREFIX_MESSAGE
);
292 strcat(szCompleteMessage
, szFormatted
);
294 else if (reportType
== _CRT_ASSERT
)
296 strcat(szCompleteMessage
, DBGRPT_ASSERT_PREFIX_NOMESSAGE
);
299 if (reportType
== _CRT_ASSERT
)
301 if (_CrtModeOutputFormat
[reportType
] & _CRTDBG_MODE_FILE
)
302 strcat(szCompleteMessage
, "\r");
303 strcat(szCompleteMessage
, "\n");
306 // FIXME: Handle user report hooks here
308 int nResult
= _CrtHandleDbgReport(reportType
, szCompleteMessage
, szFormatted
, filename
, linenumber
, moduleName
);
310 _CrtLeaveDbgReport(reportType
);
316 int __cdecl
_CrtDbgReportW(int reportType
, const wchar_t *filename
, int linenumber
, const wchar_t *moduleName
, const wchar_t *format
, ...)
318 wchar_t szFormatted
[DBGRPT_MAX_BUFFER_SIZE
+1] = {0}; // The user provided message
319 wchar_t szCompleteMessage
[(DBGRPT_MAX_BUFFER_SIZE
+1)*2] = {0}; // The output for debug / file
321 // Check for recursive _CrtDbgReportW calls, and validate reportType
322 if (!_CrtEnterDbgReport(reportType
, filename
, linenumber
))
327 _snwprintf(szCompleteMessage
, DBGRPT_MAX_BUFFER_SIZE
, L
"%s(%d) : ", filename
, linenumber
);
333 va_start(arglist
, format
);
334 int len
= _vsnwprintf(szFormatted
, DBGRPT_MAX_BUFFER_SIZE
- 2 - sizeof(DBGRPT_ASSERT_PREFIX_MESSAGE
), format
, arglist
);
339 wcscpy(szFormatted
, _CRT_WIDE(DBGRPT_STRING_TOO_LONG
));
342 if (reportType
== _CRT_ASSERT
)
343 wcscat(szCompleteMessage
, _CRT_WIDE(DBGRPT_ASSERT_PREFIX_MESSAGE
));
344 wcscat(szCompleteMessage
, szFormatted
);
346 else if (reportType
== _CRT_ASSERT
)
348 wcscat(szCompleteMessage
, _CRT_WIDE(DBGRPT_ASSERT_PREFIX_NOMESSAGE
));
351 if (reportType
== _CRT_ASSERT
)
353 if (_CrtModeOutputFormat
[reportType
] & _CRTDBG_MODE_FILE
)
354 wcscat(szCompleteMessage
, L
"\r");
355 wcscat(szCompleteMessage
, L
"\n");
358 // FIXME: Handle user report hooks here
360 int nResult
= _CrtHandleDbgReport(reportType
, szCompleteMessage
, szFormatted
, filename
, linenumber
, moduleName
);
362 _CrtLeaveDbgReport(reportType
);