feat: add callback for unhandled exception handler.
- add callback for unhandled exception handler to give programmer a chance to fetch log and coredump path, especially for GUI application because its stderr is invisible. - fix fatal anto-recursive calling bug in unhandled exception handler.
This commit is contained in:
parent
e8a0299fbc
commit
650fcd12ec
|
@ -26,6 +26,7 @@ namespace YYCC::ExceptionHelper {
|
|||
ExceptionRegister() :
|
||||
m_CoreMutex(),
|
||||
m_IsRegistered(false), m_IsProcessing(false), m_PrevProcHandler(nullptr),
|
||||
m_UserCallback(nullptr),
|
||||
m_SingletonMutex(NULL) {}
|
||||
~ExceptionRegister() {
|
||||
Unregister();
|
||||
|
@ -35,7 +36,7 @@ namespace YYCC::ExceptionHelper {
|
|||
/**
|
||||
* @brief Try to register unhandled exception handler.
|
||||
*/
|
||||
void Register() {
|
||||
void Register(ExceptionCallback callback) {
|
||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
||||
// if we have registered, return
|
||||
if (m_IsRegistered) return;
|
||||
|
@ -63,6 +64,8 @@ namespace YYCC::ExceptionHelper {
|
|||
// okey, we can register it.
|
||||
// backup old handler
|
||||
m_PrevProcHandler = SetUnhandledExceptionFilter(UExceptionImpl);
|
||||
// set user callback
|
||||
m_UserCallback = callback;
|
||||
// mark registered
|
||||
m_IsRegistered = true;
|
||||
}
|
||||
|
@ -115,6 +118,14 @@ namespace YYCC::ExceptionHelper {
|
|||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
||||
return m_PrevProcHandler;
|
||||
}
|
||||
/**
|
||||
* @brief Get user specified callback.
|
||||
* @return The function pointer to user callback. nullptr if no associated callback.
|
||||
*/
|
||||
ExceptionCallback GetUserCallback() const {
|
||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
||||
return m_UserCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to start process unhandled exception.
|
||||
|
@ -154,6 +165,12 @@ namespace YYCC::ExceptionHelper {
|
|||
* True if it is, otherwise false.
|
||||
*/
|
||||
bool m_IsProcessing;
|
||||
/**
|
||||
* @brief User defined callback.
|
||||
* @details It will be called at the tail of unhandled exception handler, because it may raise exception.
|
||||
* We must make sure all log and coredump have been done before calling it.
|
||||
*/
|
||||
ExceptionCallback m_UserCallback;
|
||||
/**
|
||||
* @brief The backup of old unhandled exception handler.
|
||||
*/
|
||||
|
@ -166,6 +183,7 @@ namespace YYCC::ExceptionHelper {
|
|||
HANDLE m_SingletonMutex;
|
||||
};
|
||||
|
||||
/// @brief Core register singleton.
|
||||
static ExceptionRegister g_ExceptionRegister;
|
||||
|
||||
#pragma region Exception Handler Implementation
|
||||
|
@ -505,10 +523,14 @@ namespace YYCC::ExceptionHelper {
|
|||
// write crash coredump
|
||||
UExceptionCoreDump(coredump_path, info);
|
||||
|
||||
// call user callback
|
||||
ExceptionCallback user_callback = g_ExceptionRegister.GetUserCallback();
|
||||
if (user_callback != nullptr)
|
||||
user_callback(log_path, coredump_path);
|
||||
}
|
||||
|
||||
// stop process
|
||||
g_ExceptionRegister.StartProcessing();
|
||||
g_ExceptionRegister.StopProcessing();
|
||||
|
||||
end_proc:
|
||||
// if backup proc can be run, run it
|
||||
|
@ -523,8 +545,8 @@ namespace YYCC::ExceptionHelper {
|
|||
|
||||
#pragma endregion
|
||||
|
||||
void Register() {
|
||||
g_ExceptionRegister.Register();
|
||||
void Register(ExceptionCallback callback) {
|
||||
g_ExceptionRegister.Register(callback);
|
||||
}
|
||||
|
||||
void Unregister() {
|
||||
|
|
|
@ -11,6 +11,24 @@
|
|||
*/
|
||||
namespace YYCC::ExceptionHelper {
|
||||
|
||||
/**
|
||||
* @brief The callback function prototype which will be called when unhandled exception happened after registering.
|
||||
* @details
|
||||
* During registering unhandled exception handler,
|
||||
* caller can optionally provide a function pointer matching this prorotype to register.
|
||||
* Then it will be called if unhandled exception hanppened.
|
||||
*
|
||||
* This callback will provide 2 readonly arguments.
|
||||
* First is the path to error log file.
|
||||
* Second is the path to core dump file.
|
||||
* These pathes may be empty if internal handler fail to create them.
|
||||
*
|
||||
* This callback is convenient for programmer using an explicit way to tell user an exception happened.
|
||||
* Because in default, handler will only write error log to \c stderr and file.
|
||||
* It will be totally invisible on a GUI application.
|
||||
*/
|
||||
using ExceptionCallback = void(*)(const yycc_u8string& log_path, const yycc_u8string& coredump_path);
|
||||
|
||||
/**
|
||||
* @brief Register unhandled exception handler
|
||||
* @details
|
||||
|
@ -23,7 +41,7 @@ namespace YYCC::ExceptionHelper {
|
|||
*
|
||||
* This function usually is called at the start of program.
|
||||
*/
|
||||
void Register();
|
||||
void Register(ExceptionCallback callback = nullptr);
|
||||
/**
|
||||
* @brief Unregister unhandled exception handler
|
||||
* @details
|
||||
|
|
|
@ -338,7 +338,15 @@ namespace YYCCTestbench {
|
|||
static void ExceptionTestbench() {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
YYCC::ExceptionHelper::Register();
|
||||
YYCC::ExceptionHelper::Register([](const YYCC::yycc_u8string& log_path, const YYCC::yycc_u8string& coredump_path) -> void {
|
||||
MessageBoxW(
|
||||
NULL,
|
||||
YYCC::EncodingHelper::UTF8ToWchar(
|
||||
YYCC::StringHelper::Printf(YYCC_U8("Log generated:\nLog path: %s\nCore dump path: %s"), log_path.c_str(), coredump_path.c_str())
|
||||
).c_str(),
|
||||
L"Fatal Error", MB_OK + MB_ICONERROR
|
||||
);
|
||||
});
|
||||
|
||||
// Perform a div zero exception.
|
||||
int i = 1, j = 0;
|
||||
|
@ -389,7 +397,7 @@ namespace YYCCTestbench {
|
|||
|
||||
Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::FsPathPatch"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
enum class TestEnum : int8_t {
|
||||
Test1, Test2, Test3
|
||||
|
|
Loading…
Reference in New Issue
Block a user