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() :
|
ExceptionRegister() :
|
||||||
m_CoreMutex(),
|
m_CoreMutex(),
|
||||||
m_IsRegistered(false), m_IsProcessing(false), m_PrevProcHandler(nullptr),
|
m_IsRegistered(false), m_IsProcessing(false), m_PrevProcHandler(nullptr),
|
||||||
|
m_UserCallback(nullptr),
|
||||||
m_SingletonMutex(NULL) {}
|
m_SingletonMutex(NULL) {}
|
||||||
~ExceptionRegister() {
|
~ExceptionRegister() {
|
||||||
Unregister();
|
Unregister();
|
||||||
|
@ -35,7 +36,7 @@ namespace YYCC::ExceptionHelper {
|
||||||
/**
|
/**
|
||||||
* @brief Try to register unhandled exception handler.
|
* @brief Try to register unhandled exception handler.
|
||||||
*/
|
*/
|
||||||
void Register() {
|
void Register(ExceptionCallback callback) {
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
||||||
// if we have registered, return
|
// if we have registered, return
|
||||||
if (m_IsRegistered) return;
|
if (m_IsRegistered) return;
|
||||||
|
@ -63,6 +64,8 @@ namespace YYCC::ExceptionHelper {
|
||||||
// okey, we can register it.
|
// okey, we can register it.
|
||||||
// backup old handler
|
// backup old handler
|
||||||
m_PrevProcHandler = SetUnhandledExceptionFilter(UExceptionImpl);
|
m_PrevProcHandler = SetUnhandledExceptionFilter(UExceptionImpl);
|
||||||
|
// set user callback
|
||||||
|
m_UserCallback = callback;
|
||||||
// mark registered
|
// mark registered
|
||||||
m_IsRegistered = true;
|
m_IsRegistered = true;
|
||||||
}
|
}
|
||||||
|
@ -115,6 +118,14 @@ namespace YYCC::ExceptionHelper {
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
||||||
return m_PrevProcHandler;
|
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.
|
* @brief Try to start process unhandled exception.
|
||||||
|
@ -154,6 +165,12 @@ namespace YYCC::ExceptionHelper {
|
||||||
* True if it is, otherwise false.
|
* True if it is, otherwise false.
|
||||||
*/
|
*/
|
||||||
bool m_IsProcessing;
|
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.
|
* @brief The backup of old unhandled exception handler.
|
||||||
*/
|
*/
|
||||||
|
@ -166,6 +183,7 @@ namespace YYCC::ExceptionHelper {
|
||||||
HANDLE m_SingletonMutex;
|
HANDLE m_SingletonMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Core register singleton.
|
||||||
static ExceptionRegister g_ExceptionRegister;
|
static ExceptionRegister g_ExceptionRegister;
|
||||||
|
|
||||||
#pragma region Exception Handler Implementation
|
#pragma region Exception Handler Implementation
|
||||||
|
@ -505,10 +523,14 @@ namespace YYCC::ExceptionHelper {
|
||||||
// write crash coredump
|
// write crash coredump
|
||||||
UExceptionCoreDump(coredump_path, info);
|
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
|
// stop process
|
||||||
g_ExceptionRegister.StartProcessing();
|
g_ExceptionRegister.StopProcessing();
|
||||||
|
|
||||||
end_proc:
|
end_proc:
|
||||||
// if backup proc can be run, run it
|
// if backup proc can be run, run it
|
||||||
|
@ -523,8 +545,8 @@ namespace YYCC::ExceptionHelper {
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
void Register() {
|
void Register(ExceptionCallback callback) {
|
||||||
g_ExceptionRegister.Register();
|
g_ExceptionRegister.Register(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unregister() {
|
void Unregister() {
|
||||||
|
|
|
@ -11,6 +11,24 @@
|
||||||
*/
|
*/
|
||||||
namespace YYCC::ExceptionHelper {
|
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
|
* @brief Register unhandled exception handler
|
||||||
* @details
|
* @details
|
||||||
|
@ -23,7 +41,7 @@ namespace YYCC::ExceptionHelper {
|
||||||
*
|
*
|
||||||
* This function usually is called at the start of program.
|
* This function usually is called at the start of program.
|
||||||
*/
|
*/
|
||||||
void Register();
|
void Register(ExceptionCallback callback = nullptr);
|
||||||
/**
|
/**
|
||||||
* @brief Unregister unhandled exception handler
|
* @brief Unregister unhandled exception handler
|
||||||
* @details
|
* @details
|
||||||
|
|
|
@ -338,7 +338,15 @@ namespace YYCCTestbench {
|
||||||
static void ExceptionTestbench() {
|
static void ExceptionTestbench() {
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
#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.
|
// Perform a div zero exception.
|
||||||
int i = 1, j = 0;
|
int i = 1, j = 0;
|
||||||
|
@ -376,7 +384,7 @@ namespace YYCCTestbench {
|
||||||
std::filesystem::path test_path;
|
std::filesystem::path test_path;
|
||||||
for (const auto& strl : c_UTF8TestStrTable) {
|
for (const auto& strl : c_UTF8TestStrTable) {
|
||||||
test_path /= YYCC::FsPathPatch::FromUTF8Path(strl.c_str());
|
test_path /= YYCC::FsPathPatch::FromUTF8Path(strl.c_str());
|
||||||
}
|
}
|
||||||
YYCC::yycc_u8string test_slashed_path(YYCC::FsPathPatch::ToUTF8Path(test_path));
|
YYCC::yycc_u8string test_slashed_path(YYCC::FsPathPatch::ToUTF8Path(test_path));
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
@ -389,7 +397,7 @@ namespace YYCCTestbench {
|
||||||
|
|
||||||
Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::FsPathPatch"));
|
Assert(test_slashed_path == test_joined_path, YYCC_U8("YYCC::FsPathPatch"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TestEnum : int8_t {
|
enum class TestEnum : int8_t {
|
||||||
Test1, Test2, Test3
|
Test1, Test2, Test3
|
||||||
|
|
Loading…
Reference in New Issue
Block a user