Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501/* * 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#undef _CrtSetReportMode#undef _CrtSetReportFile
#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 assertsstatic long _CrtInAssert = -1;// State per typestatic int _CrtModeOutputFormat[_CRT_ERRCNT] ={ _CRTDBG_MODE_DEBUG, _CRTDBG_MODE_WNDW, _CRTDBG_MODE_WNDW,};// Caption per typestatic const wchar_t* _CrtModeMessages[_CRT_ERRCNT] ={ L"Warning", L"Error", L"Assertion Failed"};// Report filesstatic _HFILE _CrtReportFiles[_CRT_ERRCNT] ={ _CRTDBG_INVALID_HFILE, _CRTDBG_INVALID_HFILE, _CRTDBG_INVALID_HFILE};
// Manually delay-load as to not have a dependency on user32typedef 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); static size_t StringLength(const char_t* str) { return strlen(str); }};
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); static size_t StringLength(const char_t* str) { return wcslen(str); };};
// Shortcuttypedef 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>";
inline void achar_traits::OutputDebugString(const char* message){ OutputDebugStringA(message);}
inline void wchar_traits::OutputDebugString(const wchar_t* message){ OutputDebugStringW(message);}
staticHMODULE _CrtGetUser32(){ if (_CrtUser32Handle == NULL) { HMODULE mod = LoadLibraryExW(L"user32.dll", NULL, 0 /* NT6+: 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] = {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, _countof(szCompleteMessage) - 1, 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;}
staticvoid _CrtLeaveDbgReport(int reportType){ if (reportType == _CRT_ASSERT) _InterlockedDecrement(&_CrtInAssert);}
EXTERN_Cint __cdecl _CrtSetReportMode(int reportType, int reportMode){ if (reportType >= _CRT_ERRCNT || reportType < 0) return 0;
int oldReportMode = _CrtModeOutputFormat[reportType]; if (reportMode != _CRTDBG_REPORT_MODE) _CrtModeOutputFormat[reportType] = reportMode; return oldReportMode;}
EXTERN_C_HFILE __cdecl _CrtSetReportFile(int reportType, _HFILE reportFile){ if (reportType >= _CRT_ERRCNT || reportType < 0) return NULL;
_HFILE oldReportFile = _CrtReportFiles[reportType]; if (reportFile != _CRTDBG_REPORT_FILE) _CrtReportFiles[reportType] = reportFile; return oldReportFile;}
template <typename char_t>static inline BOOL _CrtDbgReportToFile(HANDLE hFile, const char_t* szMsg){ typedef dbgrpt_char_traits<char_t> traits;
if (hFile == _CRTDBG_INVALID_HFILE || hFile == NULL) return FALSE;
if (hFile == _CRTDBG_FILE_STDOUT) hFile = ::GetStdHandle(STD_OUTPUT_HANDLE); else if (hFile == _CRTDBG_FILE_STDERR) hFile = ::GetStdHandle(STD_ERROR_HANDLE);
DWORD cbMsg = (DWORD)(traits::StringLength(szMsg) * sizeof(char_t)); return ::WriteFile(hFile, szMsg, cbMsg, &cbMsg, NULL);}
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) { _CrtDbgReportToFile<char_t>(_CrtReportFiles[reportType], szCompleteMessage); }
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_Cint __cdecl_VCrtDbgReportA( int reportType, const char *filename, int linenumber, const char *moduleName, const char *format, va_list arglist){ char szFormatted[DBGRPT_MAX_BUFFER_SIZE] = {0}; // The user provided message char szCompleteMessage[DBGRPT_MAX_BUFFER_SIZE] = {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, _countof(szCompleteMessage) - 1, "%s(%d) : ", filename, linenumber); }
if (format) { int len = _vsnprintf(szFormatted, _countof(szFormatted) - 2 - _countof(DBGRPT_ASSERT_PREFIX_MESSAGE), format, 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_Cint __cdecl_VCrtDbgReportW( int reportType, const wchar_t *filename, int linenumber, const wchar_t *moduleName, const wchar_t *format, va_list arglist){ wchar_t szFormatted[DBGRPT_MAX_BUFFER_SIZE] = {0}; // The user provided message wchar_t szCompleteMessage[DBGRPT_MAX_BUFFER_SIZE] = {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, _countof(szCompleteMessage) - 1, L"%s(%d) : ", filename, linenumber); }
if (format) { int len = _vsnwprintf(szFormatted, _countof(szFormatted) - 2 - _countof(DBGRPT_ASSERT_PREFIX_MESSAGE), format, 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;}
EXTERN_Cint __cdecl_CrtDbgReportV( int reportType, const char *filename, int linenumber, const char *moduleName, const char *format, va_list arglist){ return _VCrtDbgReportA(reportType, filename, linenumber, moduleName, format, arglist);}
EXTERN_Cint __cdecl_CrtDbgReportWV( int reportType, const wchar_t *filename, int linenumber, const wchar_t *moduleName, const wchar_t *format, va_list arglist){ return _VCrtDbgReportW(reportType, filename, linenumber, moduleName, format, arglist);}
EXTERN_Cint __cdecl_CrtDbgReport( int reportType, const char *filename, int linenumber, const char *moduleName, const char *format, ...){ va_list arglist; int result;
va_start(arglist, format); result = _VCrtDbgReportA(reportType, filename, linenumber, moduleName, format, arglist); va_end(arglist); return result;}
EXTERN_Cint __cdecl_CrtDbgReportW( int reportType, const wchar_t *filename, int linenumber, const wchar_t *moduleName, const wchar_t *format, ...){ va_list arglist; int result;
va_start(arglist, format); result = _VCrtDbgReportW(reportType, filename, linenumber, moduleName, format, arglist); va_end(arglist); return result;}
//#endif // _DEBUG