ReactOS  0.4.15-dev-1206-g731eddf
dbgrpt.cpp
Go to the documentation of this file.
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
29 {
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);
46 
47 template <typename char_t>
49 
50 template<>
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<>
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
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 
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 
101 {
103 }
104 
106 {
108 }
109 
110 
111 static
113 {
114  if (_CrtUser32Handle == NULL)
115  {
116  HMODULE mod = LoadLibraryExW(L"user32.dll", NULL, 0 /* NT6+: LOAD_LIBRARY_SEARCH_SYSTEM32 */);
117  if (mod == NULL)
119 
121  {
122  if (mod != INVALID_HANDLE_VALUE)
123  FreeLibrary(mod);
124  }
125  }
126 
128 }
129 
131 {
133 
135  {
136  tMessageBoxW proc = (tMessageBoxW)GetProcAddress(mod, "MessageBoxW");
137  if (proc == NULL)
139 
141  }
142 
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",
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  {
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 
211  return FALSE;
212  }
213  }
214  return TRUE;
215 }
216 
217 static
218 void _CrtLeaveDbgReport(int reportType)
219 {
220  if (reportType == _CRT_ASSERT)
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  {
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  {
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
BOOL WINAPI IsDebuggerPresent(VOID)
Definition: debugger.c:615
#define MB_ICONHAND
Definition: winuser.h:782
dbgrpt_char_traits< char > achar_traits
Definition: dbgrpt.cpp:75
Definition: tftpd.h:59
#define SIGABRT
Definition: signal.h:28
#define DBGRPT_ASSERT_PREFIX_NOMESSAGE
Definition: dbgrpt.cpp:22
static HMODULE _CrtUser32Handle
Definition: dbgrpt.cpp:44
static HMODULE _CrtGetUser32()
Definition: dbgrpt.cpp:112
#define IDABORT
Definition: winuser.h:826
char * strcat(char *DstString, const char *SrcString)
Definition: utclib.c:568
#define __cdecl
Definition: accygwin.h:79
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185
EXTERN_C int __cdecl _CrtDbgReportW(int reportType, const wchar_t *filename, int linenumber, const wchar_t *moduleName, const wchar_t *format,...)
Definition: dbgrpt.cpp:316
#define TRUE
Definition: types.h:120
static const wchar_t * szAssertionMessage
Definition: dbgrpt.cpp:55
EXTERN_C int __cdecl _CrtDbgReport(int reportType, const char *filename, int linenumber, const char *moduleName, const char *format,...)
Definition: dbgrpt.cpp:264
HINSTANCE WINAPI DECLSPEC_HOTPATCH LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
Definition: loader.c:288
HWND hWnd
Definition: settings.c:17
#define INVALID_HANDLE_VALUE
Definition: compat.h:590
static HANDLE proc()
Definition: pdb.c:32
_CRTIMP wchar_t *__cdecl _itow(_In_ int _Value, _Pre_notnull_ _Post_z_ wchar_t *_Dest, _In_ int _Radix)
static tMessageBoxW _CrtGetMessageBox()
Definition: dbgrpt.cpp:130
#define OutputDebugString
Definition: winbase.h:3730
const char * filename
Definition: ioapi.h:135
static const char_t * szEmptyString
Definition: dbgrpt.cpp:56
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl.h:1546
#define _In_opt_
Definition: no_sal2.h:212
#define DBGRPT_STRING_TOO_LONG
Definition: dbgrpt.cpp:23
static int _CrtHandleDbgReport(int reportType, const char_t *szCompleteMessage, const char_t *szFormatted, const char_t *filename, int linenumber, const char_t *moduleName)
Definition: dbgrpt.cpp:226
static const char_t * szEmptyString
Definition: dbgrpt.cpp:68
void WINAPI SHIM_OBJ_NAME() OutputDebugStringW(LPCWSTR lpOutputString)
Definition: ignoredbgout.c:23
#define _snprintf
Definition: xmlstorage.h:200
long __cdecl _InterlockedIncrement(_Interlocked_operand_ long volatile *_Addend)
#define va_end(ap)
Definition: acmsvcex.h:90
#define FALSE
Definition: types.h:117
#define MB_TASKMODAL
Definition: winuser.h:810
#define _CRT_ERRCNT
Definition: crtdbg.h:30
long __cdecl _InterlockedDecrement(_Interlocked_operand_ long volatile *_Addend)
int _snwprintf(wchar_t *buffer, size_t count, const wchar_t *format,...)
smooth NULL
Definition: ftsmooth.c:416
unsigned char
Definition: typeof.h:29
char * va_list
Definition: acmsvcex.h:78
void _exit(int exitcode)
Definition: _exit.c:25
#define _CrtDbgBreak()
Definition: crtdbg.h:113
unsigned short wchar_t
Definition: crtdefs.h:324
_CRTIMP char *__cdecl _itoa(_In_ int _Value, _Pre_notnull_ _Post_z_ char *_Dest, _In_ int _Radix)
#define FreeLibrary(x)
Definition: compat.h:607
#define WINAPI
Definition: msvc.h:6
dbgrpt_char_traits< wchar_t > wchar_traits
Definition: dbgrpt.cpp:76
static const wchar_t * _CrtModeMessages[_CRT_ERRCNT]
Definition: dbgrpt.cpp:35
static const char_t * szUnknownFile
Definition: dbgrpt.cpp:69
#define IDRETRY
Definition: winuser.h:827
_CRTIMP wchar_t *__cdecl wcscpy(_Out_writes_z_(_String_length_(_Source)+1) wchar_t *_Dest, _In_z_ const wchar_t *_Source)
static void _CrtLeaveDbgReport(int reportType)
Definition: dbgrpt.cpp:218
static const WCHAR L[]
Definition: oid.c:1250
#define _CRTDBG_MODE_FILE
Definition: crtdbg.h:32
GLenum GLsizei len
Definition: glext.h:6722
static const wchar_t * szAssertionMessage
Definition: dbgrpt.cpp:67
#define _In_
Definition: no_sal2.h:158
HANDLE HMODULE
Definition: typedefs.h:77
void * _InterlockedCompareExchangePointer(_Interlocked_operand_ void *volatile *_Destination, void *_Exchange, void *_Comparand)
static void OutputDebugString(const char_t *message)
Definition: dbgrpt.cpp:105
#define MB_ABORTRETRYIGNORE
Definition: winuser.h:785
static int _CrtDbgReportWindow(int reportType, const char_t *filename, int linenumber, const char_t *moduleName, const char_t *message)
Definition: dbgrpt.cpp:148
_CRTIMP int __cdecl _vsnwprintf(wchar_t *_Dest, size_t _Count, const wchar_t *_Format, va_list _Args)
#define DBGRPT_ASSERT_PREFIX_MESSAGE
Definition: dbgrpt.cpp:21
#define DBGRPT_MAX_BUFFER_SIZE
Definition: dbgrpt.cpp:20
static void OutputDebugString(const char_t *message)
Definition: dbgrpt.cpp:100
_CRTIMP wchar_t *__cdecl wcscat(_Inout_updates_z_(_String_length_(_Dest)+_String_length_(_Source)+1) wchar_t *_Dest, _In_z_ const wchar_t *_Source)
#define va_start(ap, A)
Definition: acmsvcex.h:91
unsigned int UINT
Definition: ndis.h:50
#define _CRT_ASSERT
Definition: crtdbg.h:29
#define _vsnprintf
Definition: xmlstorage.h:202
#define _CRTDBG_MODE_WNDW
Definition: crtdbg.h:34
static int _CrtModeOutputFormat[_CRT_ERRCNT]
Definition: dbgrpt.cpp:28
char * strcpy(char *DstString, const char *SrcString)
Definition: utclib.c:388
static const char_t * szUnknownFile
Definition: dbgrpt.cpp:57
int(WINAPI * tMessageBoxW)(_In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_ LPCWSTR lpCaption, _In_ UINT uType)
Definition: dbgrpt.cpp:43
static int _CrtEnterDbgReport(int reportType, const char_t *filename, int linenumber)
Definition: dbgrpt.cpp:187
#define GetProcAddress(x, y)
Definition: compat.h:612
#define _CRT_WIDE(_String)
Definition: crtdefs.h:43
#define MB_SETFOREGROUND
Definition: winuser.h:808
#define _CRTDBG_MODE_DEBUG
Definition: crtdbg.h:33
#define EXTERN_C
Definition: basetyps.h:12
static tMessageBoxW _CrtMessageBoxW
Definition: dbgrpt.cpp:45
static long _CrtInAssert
Definition: dbgrpt.cpp:26
void WINAPI SHIM_OBJ_NAME() OutputDebugStringA(LPCSTR lpOutputString)
Definition: ignoredbgout.c:18
#define IDIGNORE
Definition: winuser.h:828
static int mod
Definition: i386-dis.c:1273
unsigned int(__cdecl typeof(jpeg_read_scanlines))(struct jpeg_decompress_struct *
Definition: typeof.h:31
va_lists_t arglist[FMT_ARGMAX+1]
Definition: format.c:284