https://git.reactos.org/?p=reactos.git;a=commitdiff;h=62c1bb6448bc698dc0c8909875414acc6ffa1ab0

commit 62c1bb6448bc698dc0c8909875414acc6ffa1ab0
Author:     Mark Jansen <[email protected]>
AuthorDate: Sun Mar 15 22:17:30 2020 +0100
Commit:     Mark Jansen <[email protected]>
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 ([email protected])
+ */
+
+// 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

Reply via email to