mirror of
https://github.com/reactos/reactos.git
synced 2025-06-12 09:38:29 +00:00
467 lines
14 KiB
C++
467 lines
14 KiB
C++
/***
|
|
*dbgrpt.c - Debug CRT Reporting Functions
|
|
*
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
*
|
|
*******************************************************************************/
|
|
#ifndef _DEBUG
|
|
#error This file is supported only in debug builds
|
|
#endif
|
|
|
|
#include <corecrt_internal.h>
|
|
#include <corecrt_internal_traits.h>
|
|
#include <intrin.h>
|
|
#include <malloc.h>
|
|
#include <minmax.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
*
|
|
* Debug Reporting
|
|
*
|
|
--------------------------------------------------------------------------*/
|
|
|
|
extern "C" _CRT_REPORT_HOOK _pfnReportHook;
|
|
extern "C" __crt_report_hook_node<char>* _pReportHookList;
|
|
extern "C" __crt_report_hook_node<wchar_t>* _pReportHookListW;
|
|
|
|
static __crt_report_hook_node<char>*& __cdecl get_report_hook_list(char) throw() { return _pReportHookList; }
|
|
static __crt_report_hook_node<wchar_t>*& __cdecl get_report_hook_list(wchar_t) throw() { return _pReportHookListW; }
|
|
|
|
static wchar_t const* const report_type_messages[_CRT_ERRCNT] =
|
|
{
|
|
L"Warning",
|
|
L"Error",
|
|
L"Assertion Failed"
|
|
};
|
|
|
|
// Enclaves only support MODE_DEBUG for error output
|
|
#ifndef _UCRT_ENCLAVE_BUILD
|
|
|
|
static wchar_t const* __cdecl get_output_message_format(char) throw()
|
|
{
|
|
return L"Debug %ls!\n\nProgram: %hs%ls%ls%hs%ls%hs%ls%hs%ls%ls%hs%ls\n\n(Press Retry to debug the application)\n";
|
|
}
|
|
|
|
static wchar_t const* __cdecl get_output_message_format(wchar_t) throw()
|
|
{
|
|
return L"Debug %ls!\n\nProgram: %ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls\n\n(Press Retry to debug the application)\n";
|
|
}
|
|
|
|
static wchar_t const* const more_info_string =
|
|
L"\n\nFor information on how your program can cause an assertion"
|
|
L"\nfailure, see the Visual C++ documentation on asserts.";
|
|
|
|
_GENERATE_TCHAR_STRING_FUNCTIONS(program_name_unknown_text, "<program name unknown>")
|
|
|
|
#endif /* _UCRT_ENCLAVE_BUILD */
|
|
|
|
/***
|
|
*_CRT_REPORT_HOOK _CrtSetReportHook2() - configure client report hook in list
|
|
*
|
|
*Purpose:
|
|
* Install or remove a client report hook from the report list. Exists
|
|
* separately from _CrtSetReportHook because the older function doesn't
|
|
* work well in an environment where DLLs that are loaded and unloaded
|
|
* dynamically out of LIFO order want to install report hooks.
|
|
* This function exists in 2 forms - ANSI & UNICODE
|
|
*
|
|
*Entry:
|
|
* int mode - _CRT_RPTHOOK_INSTALL or _CRT_RPTHOOK_REMOVE
|
|
* _CRT_REPORT_HOOK pfnNewHook - report hook to install/remove/query
|
|
*
|
|
*Exit:
|
|
* Returns -1 if an error was encountered, with EINVAL or ENOMEM set,
|
|
* else returns the reference count of pfnNewHook after the call.
|
|
*
|
|
*Exceptions:
|
|
* Input parameters are validated. Refer to the validation section of the function.
|
|
*
|
|
*******************************************************************************/
|
|
template <typename Character, typename Hook>
|
|
static int __cdecl common_set_report_hook(
|
|
int const mode,
|
|
Hook const new_hook
|
|
) throw()
|
|
{
|
|
using node_type = __crt_report_hook_node<Character>;
|
|
|
|
_VALIDATE_RETURN(mode == _CRT_RPTHOOK_INSTALL || mode == _CRT_RPTHOOK_REMOVE, EINVAL, -1);
|
|
_VALIDATE_RETURN(new_hook != nullptr, EINVAL, -1);
|
|
|
|
return __acrt_lock_and_call(__acrt_debug_lock, [&]
|
|
{
|
|
node_type*& hook_list = get_report_hook_list(Character());
|
|
|
|
node_type* p;
|
|
int ret = 0;
|
|
|
|
// Search for new hook function to see if it's already installed
|
|
for (p = hook_list; p != nullptr; p = p->next)
|
|
if (p->hook == new_hook)
|
|
break;
|
|
|
|
if (mode == _CRT_RPTHOOK_REMOVE)
|
|
{
|
|
// Remove request - free list node if refcount goes to zero
|
|
if (p != nullptr)
|
|
{
|
|
if ((ret = --p->refcount) == 0)
|
|
{
|
|
if (p->next)
|
|
{
|
|
p->next->prev = p->prev;
|
|
}
|
|
if (p->prev)
|
|
{
|
|
p->prev->next = p->next;
|
|
}
|
|
else
|
|
{
|
|
hook_list = p->next;
|
|
}
|
|
|
|
_free_crt(p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_ASSERTE(("The hook function is not in the list!", 0));
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Insert request
|
|
if (p != nullptr)
|
|
{
|
|
// Hook function already registered, move to head of list
|
|
ret = ++p->refcount;
|
|
if (p != hook_list)
|
|
{
|
|
if (p->next)
|
|
p->next->prev = p->prev;
|
|
p->prev->next = p->next;
|
|
p->prev = nullptr;
|
|
p->next = hook_list;
|
|
hook_list->prev = p;
|
|
hook_list = p;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Hook function not already registered, insert new node
|
|
__crt_unique_heap_ptr<node_type> new_node(_calloc_crt_t(node_type, 1));
|
|
if (!new_node)
|
|
{
|
|
ret = -1;
|
|
errno = ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
new_node.get()->prev = nullptr;
|
|
new_node.get()->next = hook_list;
|
|
|
|
if (hook_list)
|
|
{
|
|
hook_list->prev = new_node.get();
|
|
}
|
|
|
|
ret = new_node.get()->refcount = 1;
|
|
new_node.get()->hook = new_hook;
|
|
hook_list = new_node.detach();
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
});
|
|
}
|
|
|
|
extern "C" int __cdecl _CrtSetReportHook2(
|
|
int const mode,
|
|
_CRT_REPORT_HOOK const new_hook
|
|
)
|
|
{
|
|
return common_set_report_hook<char>(mode, new_hook);
|
|
}
|
|
|
|
extern "C" int __cdecl _CrtSetReportHookW2(
|
|
int const mode,
|
|
_CRT_REPORT_HOOKW const new_hook
|
|
)
|
|
{
|
|
return common_set_report_hook<wchar_t>(mode, new_hook);
|
|
}
|
|
|
|
#define MAXLINELEN 64
|
|
|
|
/***
|
|
*int _CrtDbgReport() - primary reporting function
|
|
*
|
|
*Purpose:
|
|
* Display a message window with the following format.
|
|
*
|
|
* ================= Microsft Visual C++ Debug Library ================
|
|
*
|
|
* {Warning! | Error! | Assertion Failed!}
|
|
*
|
|
* Program: c:\test\mytest\foo.exe
|
|
* [Module: c:\test\mytest\bar.dll]
|
|
* [File: c:\test\mytest\bar.c]
|
|
* [Line: 69]
|
|
*
|
|
* {<warning or error message> | Expression: <expression>}
|
|
*
|
|
* [For information on how your program can cause an assertion
|
|
* failure, see the Visual C++ documentation on asserts]
|
|
*
|
|
* (Press Retry to debug the application)
|
|
*
|
|
* ===================================================================
|
|
*
|
|
*Entry:
|
|
* int nRptType - report type
|
|
* void * returnAddress - return address of caller
|
|
* const TCHAR * szFile - file name
|
|
* int nLine - line number
|
|
* const TCHAR * szModule - module name
|
|
* const TCHAR * szFormat - format string
|
|
* ... - var args
|
|
*
|
|
*Exit:
|
|
* if (MessageBox)
|
|
* {
|
|
* Abort -> aborts
|
|
* Retry -> return TRUE
|
|
* Ignore-> return FALSE
|
|
* }
|
|
* else
|
|
* return FALSE
|
|
*
|
|
*Exceptions:
|
|
* If something goes wrong, we do not assert, but we return -1.
|
|
*
|
|
*******************************************************************************/
|
|
extern "C" int __cdecl _CrtDbgReport(
|
|
int const report_type,
|
|
char const* const file_name,
|
|
int const line_number,
|
|
char const* const module_name,
|
|
char const* const format,
|
|
...)
|
|
{
|
|
va_list arglist;
|
|
va_start(arglist, format);
|
|
int const result = _VCrtDbgReportA(report_type, _ReturnAddress(), file_name, line_number, module_name, format, arglist);
|
|
va_end(arglist);
|
|
return result;
|
|
}
|
|
|
|
extern "C" int __cdecl _CrtDbgReportW(
|
|
int const report_type,
|
|
wchar_t const* const file_name,
|
|
int const line_number,
|
|
wchar_t const* const module_name,
|
|
wchar_t const* const format,
|
|
...)
|
|
{
|
|
va_list arglist;
|
|
va_start(arglist, format);
|
|
int const result = _VCrtDbgReportW(report_type, _ReturnAddress(), file_name, line_number, module_name, format, arglist);
|
|
va_end(arglist);
|
|
return result;
|
|
}
|
|
|
|
// Enclaves only support MODE_DEBUG for error output
|
|
#if !defined _UCRT_ENCLAVE_BUILD
|
|
|
|
/***
|
|
*int __crtMessageWindow() - report to a message window
|
|
*
|
|
*Purpose:
|
|
* put report into message window, allow user to choose action to take
|
|
*
|
|
*Entry:
|
|
* int nRptType - report type
|
|
* const TCHAR * szFile - file name
|
|
* const TCHAR * szLine - line number
|
|
* const TCHAR * szModule - module name
|
|
* const TCHAR * szUserMessage - user message
|
|
*
|
|
*Exit:
|
|
* if (MessageBox)
|
|
* {
|
|
* Abort -> aborts
|
|
* Retry -> return TRUE
|
|
* Ignore-> return FALSE
|
|
* }
|
|
* else
|
|
* return FALSE
|
|
*
|
|
*Exceptions:
|
|
* If something goes wrong, we do not assert, but we simply return -1,
|
|
* which will trigger the debugger automatically (the same as the user
|
|
* pressing the Retry button).
|
|
*
|
|
*******************************************************************************/
|
|
template <typename Character>
|
|
static int __cdecl common_message_window(
|
|
int const report_type,
|
|
void* const return_address,
|
|
Character const* const file_name,
|
|
Character const* const line_number,
|
|
Character const* const module_name,
|
|
Character const* const user_message
|
|
) throw()
|
|
{
|
|
using traits = __crt_char_traits<Character>;
|
|
|
|
if (user_message == nullptr)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
HMODULE module = nullptr;
|
|
if (!GetModuleHandleExW(
|
|
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
|
static_cast<LPCWSTR>(return_address),
|
|
&module))
|
|
{
|
|
module = nullptr;
|
|
}
|
|
#ifdef CRTDLL
|
|
else if (module == reinterpret_cast<HMODULE>(&__ImageBase))
|
|
{
|
|
// If the message was reported from within the CRT DLL, report it as
|
|
// having come from the EXE instead:
|
|
module = nullptr;
|
|
}
|
|
#endif
|
|
|
|
Character program_name[MAX_PATH + 1]{};
|
|
if (!traits::get_module_file_name(module, program_name, static_cast<DWORD>(_countof(program_name))))
|
|
{
|
|
_ERRCHECK(traits::tcscpy_s(program_name, _countof(program_name), get_program_name_unknown_text(Character())));
|
|
}
|
|
|
|
// Shorten the program name:
|
|
size_t const program_name_length = traits::tcslen(program_name);
|
|
Character* short_program_name = program_name;
|
|
if (program_name_length > MAXLINELEN)
|
|
{
|
|
short_program_name += program_name_length - MAXLINELEN;
|
|
static_assert(MAXLINELEN > 3, "");
|
|
#pragma warning(push)
|
|
#pragma warning(disable:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY) // 26015
|
|
short_program_name[0] = '.';
|
|
short_program_name[1] = '.';
|
|
short_program_name[2] = '.';
|
|
#pragma warning(pop)
|
|
}
|
|
|
|
// Shorten the module name:
|
|
size_t const module_name_length = module_name ? traits::tcslen(module_name) : 0;
|
|
Character const* short_module_name = nullptr;
|
|
if (module_name && module_name_length > MAXLINELEN)
|
|
{
|
|
short_module_name = module_name + module_name_length - MAXLINELEN + 3;
|
|
}
|
|
|
|
static Character const empty_string[] = { '\0' };
|
|
|
|
wchar_t message_buffer[DBGRPT_MAX_MSG];
|
|
int const sprintf_result = _snwprintf_s(
|
|
message_buffer,
|
|
_countof(message_buffer),
|
|
_countof(message_buffer) - 1,
|
|
|
|
get_output_message_format(Character()),
|
|
|
|
report_type_messages[report_type],
|
|
short_program_name,
|
|
module_name ? L"\nModule: " : L"",
|
|
short_module_name ? L"..." : L"",
|
|
short_module_name ? short_module_name : (module_name ? module_name : empty_string),
|
|
file_name ? L"\nFile: " : L"",
|
|
file_name ? file_name : empty_string,
|
|
line_number ? L"\nLine: " : L"",
|
|
line_number ? line_number : empty_string,
|
|
user_message[0] ? L"\n\n" : L"",
|
|
user_message[0] && _CRT_ASSERT == report_type ? L"Expression: " : L"",
|
|
user_message[0] ? user_message : empty_string,
|
|
_CRT_ASSERT == report_type ? more_info_string : L"");
|
|
|
|
_ERRCHECK_SPRINTF(sprintf_result);
|
|
|
|
if (sprintf_result < 0)
|
|
{
|
|
_ERRCHECK(wcscpy_s(message_buffer, DBGRPT_MAX_MSG, _CRT_WIDE(DBGRPT_TOOLONGMSG)));
|
|
}
|
|
|
|
int const message_box_result = __acrt_show_wide_message_box(
|
|
message_buffer,
|
|
L"Microsoft Visual C++ Runtime Library",
|
|
MB_TASKMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE | MB_SETFOREGROUND);
|
|
|
|
switch (message_box_result)
|
|
{
|
|
case IDABORT: // Abort: Terminate execution
|
|
{
|
|
// Note that even though we are "aborting," we do not call abort()
|
|
// because we do not want to invoke Watson (the user has already had an
|
|
// opportunity to debug the error and chose not to).
|
|
__crt_signal_handler_t const sigabrt_action = __acrt_get_sigabrt_handler();
|
|
if (sigabrt_action != SIG_DFL)
|
|
{
|
|
raise(SIGABRT);
|
|
}
|
|
|
|
TerminateProcess(GetCurrentProcess(), 3);
|
|
}
|
|
|
|
case IDRETRY:
|
|
{
|
|
return 1; // Retry: Debug break
|
|
}
|
|
|
|
case IDIGNORE:
|
|
default:
|
|
{
|
|
return 0; // Ignore: Continue execution
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" int __cdecl __acrt_MessageWindowA(
|
|
int const report_type,
|
|
void* const return_address,
|
|
char const* const file_name,
|
|
char const* const line_number,
|
|
char const* const module_name,
|
|
char const* const user_message
|
|
)
|
|
{
|
|
return common_message_window(report_type, return_address, file_name, line_number, module_name, user_message);
|
|
}
|
|
|
|
extern "C" int __cdecl __acrt_MessageWindowW(
|
|
int const report_type,
|
|
void* const return_address,
|
|
wchar_t const* const file_name,
|
|
wchar_t const* const line_number,
|
|
wchar_t const* const module_name,
|
|
wchar_t const* const user_message
|
|
)
|
|
{
|
|
return common_message_window(report_type, return_address, file_name, line_number, module_name, user_message);
|
|
}
|
|
|
|
#endif
|