Revert "[CMAKE] Make unattended bootcd configurable via cmake"
[reactos.git] / sdk / lib / crt / misc / dbgrpt.cpp
1 /*
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)
6 */
7
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.
11 //#ifdef _DEBUG
12
13 #include <crtdbg.h>
14 #include <stdio.h>
15 #include <signal.h>
16 #include <windows.h>
17
18 #undef OutputDebugString
19
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"
24
25 // Keep track of active asserts
26 static long _CrtInAssert = -1;
27 // State per type
28 static int _CrtModeOutputFormat[_CRT_ERRCNT] =
29 {
30 _CRTDBG_MODE_DEBUG,
31 _CRTDBG_MODE_WNDW,
32 _CRTDBG_MODE_WNDW,
33 };
34 // Caption per type
35 static const wchar_t* _CrtModeMessages[_CRT_ERRCNT] =
36 {
37 L"Warning",
38 L"Error",
39 L"Assertion Failed"
40 };
41
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;
46
47 template <typename char_t>
48 struct dbgrpt_char_traits;
49
50 template<>
51 struct dbgrpt_char_traits<char>
52 {
53 typedef char char_t;
54
55 static const wchar_t* szAssertionMessage;
56 static const char_t* szEmptyString;
57 static const char_t* szUnknownFile;
58
59 static void OutputDebugString(const char_t* message);
60 };
61
62 template<>
63 struct dbgrpt_char_traits<wchar_t>
64 {
65 typedef wchar_t char_t;
66
67 static const wchar_t* szAssertionMessage;
68 static const char_t* szEmptyString;
69 static const char_t* szUnknownFile;
70
71 static void OutputDebugString(const char_t* message);
72 };
73
74 // Shortcut
75 typedef dbgrpt_char_traits<char> achar_traits;
76 typedef dbgrpt_char_traits<wchar_t> wchar_traits;
77
78
79 const wchar_t* achar_traits::szAssertionMessage =
80 L"Debug %s!\n"
81 L"%s%hs" /* module */
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 =
87 L"Debug %s!\n"
88 L"%s%ws" /* module */
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)";
93
94 const achar_traits::char_t* achar_traits::szEmptyString = "";
95 const wchar_traits::char_t* wchar_traits::szEmptyString = L"";
96
97 const achar_traits::char_t* achar_traits::szUnknownFile = "<unknown file>";
98 const wchar_traits::char_t* wchar_traits::szUnknownFile = L"<unknown file>";
99
100 void achar_traits::OutputDebugString(const char* message)
101 {
102 OutputDebugStringA(message);
103 }
104
105 void wchar_traits::OutputDebugString(const wchar_t* message)
106 {
107 OutputDebugStringW(message);
108 }
109
110
111 static
112 HMODULE _CrtGetUser32()
113 {
114 if (_CrtUser32Handle == NULL)
115 {
116 HMODULE mod = LoadLibraryExW(L"user32.dll", NULL, 0 /* NT6+: LOAD_LIBRARY_SEARCH_SYSTEM32 */);
117 if (mod == NULL)
118 mod = (HMODULE)INVALID_HANDLE_VALUE;
119
120 if (_InterlockedCompareExchangePointer((PVOID*)&_CrtUser32Handle, mod, NULL))
121 {
122 if (mod != INVALID_HANDLE_VALUE)
123 FreeLibrary(mod);
124 }
125 }
126
127 return _CrtUser32Handle != INVALID_HANDLE_VALUE ? _CrtUser32Handle : NULL;
128 }
129
130 static tMessageBoxW _CrtGetMessageBox()
131 {
132 HMODULE mod = _CrtGetUser32();
133
134 if (_CrtMessageBoxW == NULL && mod != INVALID_HANDLE_VALUE)
135 {
136 tMessageBoxW proc = (tMessageBoxW)GetProcAddress(mod, "MessageBoxW");
137 if (proc == NULL)
138 proc = (tMessageBoxW)INVALID_HANDLE_VALUE;
139
140 _InterlockedCompareExchangePointer((PVOID*)&_CrtMessageBoxW, (PVOID)proc, NULL);
141 }
142
143 return _CrtMessageBoxW != INVALID_HANDLE_VALUE ? _CrtMessageBoxW : NULL;
144 }
145
146
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)
149 {
150 typedef dbgrpt_char_traits<char_t> traits;
151
152 wchar_t szCompleteMessage[(DBGRPT_MAX_BUFFER_SIZE+1)*2] = {0};
153 wchar_t LineBuffer[20] = {0};
154
155 if (filename && !filename[0])
156 filename = NULL;
157 if (moduleName && !moduleName[0])
158 moduleName = NULL;
159 if (message && !message[0])
160 message = NULL;
161 if (linenumber)
162 _itow(linenumber, LineBuffer, 10);
163
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);
171
172 if (IsDebuggerPresent())
173 {
174 OutputDebugStringW(szCompleteMessage);
175 }
176
177 tMessageBoxW messageBox = _CrtGetMessageBox();
178 if (!messageBox)
179 return IsDebuggerPresent() ? IDRETRY : IDABORT;
180
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);
184 }
185
186 template <typename char_t>
187 static int _CrtEnterDbgReport(int reportType, const char_t *filename, int linenumber)
188 {
189 typedef dbgrpt_char_traits<char_t> traits;
190
191 if (reportType < 0 || reportType >= _CRT_ERRCNT)
192 return FALSE;
193
194 if (reportType == _CRT_ASSERT)
195 {
196 if (_InterlockedIncrement(&_CrtInAssert) > 0)
197 {
198 char LineBuffer[20] = {0};
199
200 _itoa(linenumber, LineBuffer, 10);
201
202 OutputDebugStringA("Nested Assert from File: ");
203 traits::OutputDebugString(filename ? filename : traits::szUnknownFile);
204 OutputDebugStringA(", Line: ");
205 OutputDebugStringA(LineBuffer);
206 OutputDebugStringA("\n");
207
208 _CrtDbgBreak();
209
210 _InterlockedDecrement(&_CrtInAssert);
211 return FALSE;
212 }
213 }
214 return TRUE;
215 }
216
217 static
218 void _CrtLeaveDbgReport(int reportType)
219 {
220 if (reportType == _CRT_ASSERT)
221 _InterlockedDecrement(&_CrtInAssert);
222 }
223
224
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)
228 {
229 typedef dbgrpt_char_traits<char_t> traits;
230
231 if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_FILE)
232 {
233 OutputDebugStringA("ERROR: Please implement _CrtSetReportFile first\n");
234 _CrtDbgBreak();
235 }
236
237 if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_DEBUG)
238 {
239 traits::OutputDebugString(szCompleteMessage);
240 }
241
242 if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_WNDW)
243 {
244 int nResult = _CrtDbgReportWindow(reportType, filename, linenumber, moduleName, szFormatted);
245 switch (nResult)
246 {
247 case IDRETRY:
248 return TRUE;
249 case IDIGNORE:
250 default:
251 return FALSE;
252 case IDABORT:
253 raise(SIGABRT);
254 _exit(3);
255 return FALSE; // Unreachable
256 }
257 }
258
259 return FALSE;
260 }
261
262
263 EXTERN_C
264 int __cdecl _CrtDbgReport(int reportType, const char *filename, int linenumber, const char *moduleName, const char *format, ...)
265 {
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
268
269 // Check for recursive _CrtDbgReport calls, and validate reportType
270 if (!_CrtEnterDbgReport(reportType, filename, linenumber))
271 return -1;
272
273 if (filename)
274 {
275 _snprintf(szCompleteMessage, DBGRPT_MAX_BUFFER_SIZE, "%s(%d) : ", filename, linenumber);
276 }
277
278 if (format)
279 {
280 va_list arglist;
281 va_start(arglist, format);
282 int len = _vsnprintf(szFormatted, DBGRPT_MAX_BUFFER_SIZE - 2 - sizeof(DBGRPT_ASSERT_PREFIX_MESSAGE), format, arglist);
283 va_end(arglist);
284
285 if (len < 0)
286 {
287 strcpy(szFormatted, DBGRPT_STRING_TOO_LONG);
288 }
289
290 if (reportType == _CRT_ASSERT)
291 strcat(szCompleteMessage, DBGRPT_ASSERT_PREFIX_MESSAGE);
292 strcat(szCompleteMessage, szFormatted);
293 }
294 else if (reportType == _CRT_ASSERT)
295 {
296 strcat(szCompleteMessage, DBGRPT_ASSERT_PREFIX_NOMESSAGE);
297 }
298
299 if (reportType == _CRT_ASSERT)
300 {
301 if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_FILE)
302 strcat(szCompleteMessage, "\r");
303 strcat(szCompleteMessage, "\n");
304 }
305
306 // FIXME: Handle user report hooks here
307
308 int nResult = _CrtHandleDbgReport(reportType, szCompleteMessage, szFormatted, filename, linenumber, moduleName);
309
310 _CrtLeaveDbgReport(reportType);
311
312 return nResult;
313 }
314
315 EXTERN_C
316 int __cdecl _CrtDbgReportW(int reportType, const wchar_t *filename, int linenumber, const wchar_t *moduleName, const wchar_t *format, ...)
317 {
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
320
321 // Check for recursive _CrtDbgReportW calls, and validate reportType
322 if (!_CrtEnterDbgReport(reportType, filename, linenumber))
323 return -1;
324
325 if (filename)
326 {
327 _snwprintf(szCompleteMessage, DBGRPT_MAX_BUFFER_SIZE, L"%s(%d) : ", filename, linenumber);
328 }
329
330 if (format)
331 {
332 va_list arglist;
333 va_start(arglist, format);
334 int len = _vsnwprintf(szFormatted, DBGRPT_MAX_BUFFER_SIZE - 2 - sizeof(DBGRPT_ASSERT_PREFIX_MESSAGE), format, arglist);
335 va_end(arglist);
336
337 if (len < 0)
338 {
339 wcscpy(szFormatted, _CRT_WIDE(DBGRPT_STRING_TOO_LONG));
340 }
341
342 if (reportType == _CRT_ASSERT)
343 wcscat(szCompleteMessage, _CRT_WIDE(DBGRPT_ASSERT_PREFIX_MESSAGE));
344 wcscat(szCompleteMessage, szFormatted);
345 }
346 else if (reportType == _CRT_ASSERT)
347 {
348 wcscat(szCompleteMessage, _CRT_WIDE(DBGRPT_ASSERT_PREFIX_NOMESSAGE));
349 }
350
351 if (reportType == _CRT_ASSERT)
352 {
353 if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_FILE)
354 wcscat(szCompleteMessage, L"\r");
355 wcscat(szCompleteMessage, L"\n");
356 }
357
358 // FIXME: Handle user report hooks here
359
360 int nResult = _CrtHandleDbgReport(reportType, szCompleteMessage, szFormatted, filename, linenumber, moduleName);
361
362 _CrtLeaveDbgReport(reportType);
363
364 return nResult;
365 }
366
367
368 //#endif // _DEBUG