mirror of
https://github.com/reactos/reactos.git
synced 2025-02-22 08:25:03 +00:00
[CRT] Implement _CrtDbgReport and _CrtDbgReportW
Most functionality is working, except output to file CORE-11835
This commit is contained in:
parent
68b6639ade
commit
62c1bb6448
2 changed files with 369 additions and 0 deletions
368
sdk/lib/crt/misc/dbgrpt.cpp
Normal file
368
sdk/lib/crt/misc/dbgrpt.cpp
Normal file
|
@ -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@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
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue