https://git.reactos.org/?p=reactos.git;a=commitdiff;h=62c1bb6448bc698dc0c89…
commit 62c1bb6448bc698dc0c8909875414acc6ffa1ab0
Author: Mark Jansen <mark.jansen(a)reactos.org>
AuthorDate: Sun Mar 15 22:17:30 2020 +0100
Commit: Mark Jansen <mark.jansen(a)reactos.org>
CommitDate: Sat Apr 4 19:55:24 2020 +0200
[CRT] Implement _CrtDbgReport and _CrtDbgReportW
Most functionality is working, except output to file
CORE-11835
---
sdk/lib/crt/misc/dbgrpt.cpp | 368 ++++++++++++++++++++++++++++++++++++++++++++
sdk/lib/crt/msvcrtex.cmake | 1 +
2 files changed, 369 insertions(+)
diff --git a/sdk/lib/crt/misc/dbgrpt.cpp b/sdk/lib/crt/misc/dbgrpt.cpp
new file mode 100644
index 00000000000..9259ff8edab
--- /dev/null
+++ b/sdk/lib/crt/misc/dbgrpt.cpp
@@ -0,0 +1,368 @@
+/*
+ * PROJECT: ReactOS CRT library
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Debug CRT reporting functions
+ * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen(a)reactos.org)
+ */
+
+// This file should not be included in release builds,
+// but since we do not have a good mechanism for this at the moment,
+// just rely on the compiler to optimize it away instead of omitting the code.
+//#ifdef _DEBUG
+
+#include <crtdbg.h>
+#include <stdio.h>
+#include <signal.h>
+#include <windows.h>
+
+#undef OutputDebugString
+
+#define DBGRPT_MAX_BUFFER_SIZE 4096
+#define DBGRPT_ASSERT_PREFIX_MESSAGE "Assertion failed: "
+#define DBGRPT_ASSERT_PREFIX_NOMESSAGE "Assertion failed!"
+#define DBGRPT_STRING_TOO_LONG "_CrtDbgReport: String too long"
+
+// Keep track of active asserts
+static long _CrtInAssert = -1;
+// State per type
+static int _CrtModeOutputFormat[_CRT_ERRCNT] =
+{
+ _CRTDBG_MODE_DEBUG,
+ _CRTDBG_MODE_WNDW,
+ _CRTDBG_MODE_WNDW,
+};
+// Caption per type
+static const wchar_t* _CrtModeMessages[_CRT_ERRCNT] =
+{
+ L"Warning",
+ L"Error",
+ L"Assertion Failed"
+};
+
+// Manually delay-load as to not have a dependency on user32
+typedef int (WINAPI *tMessageBoxW)(_In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_
LPCWSTR lpCaption, _In_ UINT uType);
+static HMODULE _CrtUser32Handle = NULL;
+static tMessageBoxW _CrtMessageBoxW = NULL;
+
+template <typename char_t>
+struct dbgrpt_char_traits;
+
+template<>
+struct dbgrpt_char_traits<char>
+{
+ typedef char char_t;
+
+ static const wchar_t* szAssertionMessage;
+ static const char_t* szEmptyString;
+ static const char_t* szUnknownFile;
+
+ static void OutputDebugString(const char_t* message);
+};
+
+template<>
+struct dbgrpt_char_traits<wchar_t>
+{
+ typedef wchar_t char_t;
+
+ static const wchar_t* szAssertionMessage;
+ static const char_t* szEmptyString;
+ static const char_t* szUnknownFile;
+
+ static void OutputDebugString(const char_t* message);
+};
+
+// Shortcut
+typedef dbgrpt_char_traits<char> achar_traits;
+typedef dbgrpt_char_traits<wchar_t> wchar_traits;
+
+
+const wchar_t* achar_traits::szAssertionMessage =
+ L"Debug %s!\n"
+ L"%s%hs" /* module */
+ L"%s%hs" /* filename */
+ L"%s%s" /* linenumber */
+ L"%s%hs" /* message */
+ L"\n\n(Press Retry to debug the application)";
+const wchar_t* wchar_traits::szAssertionMessage =
+ L"Debug %s!\n"
+ L"%s%ws" /* module */
+ L"%s%ws" /* filename */
+ L"%s%s" /* linenumber */
+ L"%s%ws" /* message */
+ L"\n\n(Press Retry to debug the application)";
+
+const achar_traits::char_t* achar_traits::szEmptyString = "";
+const wchar_traits::char_t* wchar_traits::szEmptyString = L"";
+
+const achar_traits::char_t* achar_traits::szUnknownFile = "<unknown
file>";
+const wchar_traits::char_t* wchar_traits::szUnknownFile = L"<unknown
file>";
+
+void achar_traits::OutputDebugString(const char* message)
+{
+ OutputDebugStringA(message);
+}
+
+void wchar_traits::OutputDebugString(const wchar_t* message)
+{
+ OutputDebugStringW(message);
+}
+
+
+static
+HMODULE _CrtGetUser32()
+{
+ if (_CrtUser32Handle == NULL)
+ {
+ HMODULE mod = LoadLibraryExW(L"user32.dll", NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (mod == NULL)
+ mod = (HMODULE)INVALID_HANDLE_VALUE;
+
+ if (_InterlockedCompareExchangePointer((PVOID*)&_CrtUser32Handle, mod,
NULL))
+ {
+ if (mod != INVALID_HANDLE_VALUE)
+ FreeLibrary(mod);
+ }
+ }
+
+ return _CrtUser32Handle != INVALID_HANDLE_VALUE ? _CrtUser32Handle : NULL;
+}
+
+static tMessageBoxW _CrtGetMessageBox()
+{
+ HMODULE mod = _CrtGetUser32();
+
+ if (_CrtMessageBoxW == NULL && mod != INVALID_HANDLE_VALUE)
+ {
+ tMessageBoxW proc = (tMessageBoxW)GetProcAddress(mod, "MessageBoxW");
+ if (proc == NULL)
+ proc = (tMessageBoxW)INVALID_HANDLE_VALUE;
+
+ _InterlockedCompareExchangePointer((PVOID*)&_CrtMessageBoxW, (PVOID)proc,
NULL);
+ }
+
+ return _CrtMessageBoxW != INVALID_HANDLE_VALUE ? _CrtMessageBoxW : NULL;
+}
+
+
+template <typename char_t>
+static int _CrtDbgReportWindow(int reportType, const char_t *filename, int linenumber,
const char_t *moduleName, const char_t* message)
+{
+ typedef dbgrpt_char_traits<char_t> traits;
+
+ wchar_t szCompleteMessage[(DBGRPT_MAX_BUFFER_SIZE+1)*2] = {0};
+ wchar_t LineBuffer[20] = {0};
+
+ if (filename && !filename[0])
+ filename = NULL;
+ if (moduleName && !moduleName[0])
+ moduleName = NULL;
+ if (message && !message[0])
+ message = NULL;
+ if (linenumber)
+ _itow(linenumber, LineBuffer, 10);
+
+ _snwprintf(szCompleteMessage, DBGRPT_MAX_BUFFER_SIZE * 2,
+ traits::szAssertionMessage,
+ _CrtModeMessages[reportType],
+ moduleName ? L"\nModule: " : L"", moduleName ?
moduleName : traits::szEmptyString,
+ filename ? L"\nFile: " : L"", filename ? filename :
traits::szEmptyString,
+ LineBuffer[0] ? L"\nLine: " : L"", LineBuffer[0] ?
LineBuffer : L"",
+ message ? L"\n\n" : L"", message ? message :
traits::szEmptyString);
+
+ if (IsDebuggerPresent())
+ {
+ OutputDebugStringW(szCompleteMessage);
+ }
+
+ tMessageBoxW messageBox = _CrtGetMessageBox();
+ if (!messageBox)
+ return IsDebuggerPresent() ? IDRETRY : IDABORT;
+
+ // TODO: If we are not interacive, add MB_SERVICE_NOTIFICATION
+ return messageBox(NULL, szCompleteMessage, L"ReactOS C++ Runtime Library",
+ MB_ABORTRETRYIGNORE | MB_ICONHAND | MB_SETFOREGROUND |
MB_TASKMODAL);
+}
+
+template <typename char_t>
+static int _CrtEnterDbgReport(int reportType, const char_t *filename, int linenumber)
+{
+ typedef dbgrpt_char_traits<char_t> traits;
+
+ if (reportType < 0 || reportType >= _CRT_ERRCNT)
+ return FALSE;
+
+ if (reportType == _CRT_ASSERT)
+ {
+ if (_InterlockedIncrement(&_CrtInAssert) > 0)
+ {
+ char LineBuffer[20] = {0};
+
+ _itoa(linenumber, LineBuffer, 10);
+
+ OutputDebugStringA("Nested Assert from File: ");
+ traits::OutputDebugString(filename ? filename : traits::szUnknownFile);
+ OutputDebugStringA(", Line: ");
+ OutputDebugStringA(LineBuffer);
+ OutputDebugStringA("\n");
+
+ _CrtDbgBreak();
+
+ _InterlockedDecrement(&_CrtInAssert);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static
+void _CrtLeaveDbgReport(int reportType)
+{
+ if (reportType == _CRT_ASSERT)
+ _InterlockedDecrement(&_CrtInAssert);
+}
+
+
+template <typename char_t>
+static int _CrtHandleDbgReport(int reportType, const char_t* szCompleteMessage, const
char_t* szFormatted,
+ const char_t *filename, int linenumber, const char_t
*moduleName)
+{
+ typedef dbgrpt_char_traits<char_t> traits;
+
+ if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_FILE)
+ {
+ OutputDebugStringA("ERROR: Please implement _CrtSetReportFile
first\n");
+ _CrtDbgBreak();
+ }
+
+ if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_DEBUG)
+ {
+ traits::OutputDebugString(szCompleteMessage);
+ }
+
+ if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_WNDW)
+ {
+ int nResult = _CrtDbgReportWindow(reportType, filename, linenumber, moduleName,
szFormatted);
+ switch (nResult)
+ {
+ case IDRETRY:
+ return TRUE;
+ case IDIGNORE:
+ default:
+ return FALSE;
+ case IDABORT:
+ raise(SIGABRT);
+ _exit(3);
+ return FALSE; // Unreachable
+ }
+ }
+
+ return FALSE;
+}
+
+
+EXTERN_C
+int __cdecl _CrtDbgReport(int reportType, const char *filename, int linenumber, const
char *moduleName, const char *format, ...)
+{
+ char szFormatted[DBGRPT_MAX_BUFFER_SIZE+1] = {0}; // The user provided
message
+ char szCompleteMessage[(DBGRPT_MAX_BUFFER_SIZE+1)*2] = {0}; // The output for debug /
file
+
+ // Check for recursive _CrtDbgReport calls, and validate reportType
+ if (!_CrtEnterDbgReport(reportType, filename, linenumber))
+ return -1;
+
+ if (filename)
+ {
+ _snprintf(szCompleteMessage, DBGRPT_MAX_BUFFER_SIZE, "%s(%d) : ",
filename, linenumber);
+ }
+
+ if (format)
+ {
+ va_list arglist;
+ va_start(arglist, format);
+ int len = _vsnprintf(szFormatted, DBGRPT_MAX_BUFFER_SIZE - 2 -
sizeof(DBGRPT_ASSERT_PREFIX_MESSAGE), format, arglist);
+ va_end(arglist);
+
+ if (len < 0)
+ {
+ strcpy(szFormatted, DBGRPT_STRING_TOO_LONG);
+ }
+
+ if (reportType == _CRT_ASSERT)
+ strcat(szCompleteMessage, DBGRPT_ASSERT_PREFIX_MESSAGE);
+ strcat(szCompleteMessage, szFormatted);
+ }
+ else if (reportType == _CRT_ASSERT)
+ {
+ strcat(szCompleteMessage, DBGRPT_ASSERT_PREFIX_NOMESSAGE);
+ }
+
+ if (reportType == _CRT_ASSERT)
+ {
+ if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_FILE)
+ strcat(szCompleteMessage, "\r");
+ strcat(szCompleteMessage, "\n");
+ }
+
+ // FIXME: Handle user report hooks here
+
+ int nResult = _CrtHandleDbgReport(reportType, szCompleteMessage, szFormatted,
filename, linenumber, moduleName);
+
+ _CrtLeaveDbgReport(reportType);
+
+ return nResult;
+}
+
+EXTERN_C
+int __cdecl _CrtDbgReportW(int reportType, const wchar_t *filename, int linenumber, const
wchar_t *moduleName, const wchar_t *format, ...)
+{
+ wchar_t szFormatted[DBGRPT_MAX_BUFFER_SIZE+1] = {0}; // The user provided
message
+ wchar_t szCompleteMessage[(DBGRPT_MAX_BUFFER_SIZE+1)*2] = {0}; // The output for
debug / file
+
+ // Check for recursive _CrtDbgReportW calls, and validate reportType
+ if (!_CrtEnterDbgReport(reportType, filename, linenumber))
+ return -1;
+
+ if (filename)
+ {
+ _snwprintf(szCompleteMessage, DBGRPT_MAX_BUFFER_SIZE, L"%s(%d) : ",
filename, linenumber);
+ }
+
+ if (format)
+ {
+ va_list arglist;
+ va_start(arglist, format);
+ int len = _vsnwprintf(szFormatted, DBGRPT_MAX_BUFFER_SIZE - 2 -
sizeof(DBGRPT_ASSERT_PREFIX_MESSAGE), format, arglist);
+ va_end(arglist);
+
+ if (len < 0)
+ {
+ wcscpy(szFormatted, _CRT_WIDE(DBGRPT_STRING_TOO_LONG));
+ }
+
+ if (reportType == _CRT_ASSERT)
+ wcscat(szCompleteMessage, _CRT_WIDE(DBGRPT_ASSERT_PREFIX_MESSAGE));
+ wcscat(szCompleteMessage, szFormatted);
+ }
+ else if (reportType == _CRT_ASSERT)
+ {
+ wcscat(szCompleteMessage, _CRT_WIDE(DBGRPT_ASSERT_PREFIX_NOMESSAGE));
+ }
+
+ if (reportType == _CRT_ASSERT)
+ {
+ if (_CrtModeOutputFormat[reportType] & _CRTDBG_MODE_FILE)
+ wcscat(szCompleteMessage, L"\r");
+ wcscat(szCompleteMessage, L"\n");
+ }
+
+ // FIXME: Handle user report hooks here
+
+ int nResult = _CrtHandleDbgReport(reportType, szCompleteMessage, szFormatted,
filename, linenumber, moduleName);
+
+ _CrtLeaveDbgReport(reportType);
+
+ return nResult;
+}
+
+
+//#endif // _DEBUG
diff --git a/sdk/lib/crt/msvcrtex.cmake b/sdk/lib/crt/msvcrtex.cmake
index 5be20560a53..3da70e6b10b 100644
--- a/sdk/lib/crt/msvcrtex.cmake
+++ b/sdk/lib/crt/msvcrtex.cmake
@@ -36,6 +36,7 @@ list(APPEND MSVCRTEX_SOURCE
startup/crt0_w.c
startup/dllentry.c
startup/reactos.c
+ misc/dbgrpt.cpp
misc/fltused.c
misc/isblank.c
misc/iswblank.c