fix: fix fatal error of ExceptionHelper in x86 environemnt.
- fix a wrong placeholder of printf in ExceptionHelper which cause crash in unhandled exception handler. - improve format function in ExceptionHelper. - add a new debugging option and macro in CMake script and code for the convenience of debugging unhandled exception handler. - add docuementation about previous term.
This commit is contained in:
parent
1cfbcb3b18
commit
0ac6b477f9
|
@ -7,6 +7,7 @@ project(YYCC
|
||||||
# Provide options
|
# Provide options
|
||||||
option(YYCC_BUILD_TESTBENCH "Build testbench of YYCCommonplace." OFF)
|
option(YYCC_BUILD_TESTBENCH "Build testbench of YYCCommonplace." OFF)
|
||||||
option(YYCC_BUILD_DOC "Build document of YYCCommonplace." OFF)
|
option(YYCC_BUILD_DOC "Build document of YYCCommonplace." OFF)
|
||||||
|
option(YYCC_DEBUG_UE_FILTER "YYCC developer used switch for testing Windows unhandled exception filter. Should not set to ON!!!" OFF)
|
||||||
|
|
||||||
# Setup install path from CMake provided install path for convenient use.
|
# Setup install path from CMake provided install path for convenient use.
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
|
@ -159,4 +159,11 @@ that MSVC distribution places all static library under one director \c lib.
|
||||||
Thus in MSVC project user can simply spcify the install path of YYCC,
|
Thus in MSVC project user can simply spcify the install path of YYCC,
|
||||||
and use MSVC macros in path to choose correct static library for linking
|
and use MSVC macros in path to choose correct static library for linking
|
||||||
|
|
||||||
|
\section intro__debug Debug Tips
|
||||||
|
|
||||||
|
YYCC CMake build script contains a special option called \c YYCC_DEBUG_UE_FILTER.
|
||||||
|
If you set it to true, it will add a public macro \c YYCC_DEBUG_UE_FILTER to YYCC project.
|
||||||
|
This macro will enable special code path for the convenience of debugging \ref exception_helper related features.
|
||||||
|
So in common use, user should not enable this option.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -62,8 +62,11 @@ PROPERTIES
|
||||||
CXX_STANDARD_REQUIRED 17
|
CXX_STANDARD_REQUIRED 17
|
||||||
CXX_EXTENSION OFF
|
CXX_EXTENSION OFF
|
||||||
)
|
)
|
||||||
# Order Unicode charset for private using
|
|
||||||
target_compile_definitions(YYCCommonplace
|
target_compile_definitions(YYCCommonplace
|
||||||
|
# Debug macro should populate to child projects
|
||||||
|
PUBLIC
|
||||||
|
$<$<BOOL:${YYCC_DEBUG_UE_FILTER}>:YYCC_DEBUG_UE_FILTER>
|
||||||
|
# Unicode charset for private using
|
||||||
PRIVATE
|
PRIVATE
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:UNICODE>
|
$<$<CXX_COMPILER_ID:MSVC>:UNICODE>
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:_UNICODE>
|
$<$<CXX_COMPILER_ID:MSVC>:_UNICODE>
|
||||||
|
|
|
@ -242,33 +242,6 @@ namespace YYCC::ExceptionHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Error log (including backtrace) used output function with format feature
|
|
||||||
* @details
|
|
||||||
* This function will format message first.
|
|
||||||
* And write them into given file stream and stderr.
|
|
||||||
* @param[in] fs
|
|
||||||
* The file stream where we write.
|
|
||||||
* If it is nullptr, function will skip writing for file stream.
|
|
||||||
* @param[in] fmt The format string.
|
|
||||||
* @param[in] ... The argument to be formatted.
|
|
||||||
*/
|
|
||||||
static void UExceptionErrLogFormatLine(std::FILE* fs, const yycc_char8_t* fmt, ...) {
|
|
||||||
// write to file
|
|
||||||
if (fs != nullptr) {
|
|
||||||
va_list arg1;
|
|
||||||
va_start(arg1, fmt);
|
|
||||||
std::vfprintf(fs, EncodingHelper::ToOrdinary(fmt), arg1);
|
|
||||||
std::fputs("\n", fs);
|
|
||||||
va_end(arg1);
|
|
||||||
}
|
|
||||||
// write to stderr
|
|
||||||
va_list arg2;
|
|
||||||
va_start(arg2, fmt);
|
|
||||||
ConsoleHelper::ErrWriteLine(YYCC::StringHelper::VPrintf(fmt, arg2).c_str());
|
|
||||||
va_end(arg2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Error log (including backtrace) used output function
|
* @brief Error log (including backtrace) used output function
|
||||||
* @details
|
* @details
|
||||||
|
@ -288,6 +261,27 @@ namespace YYCC::ExceptionHelper {
|
||||||
ConsoleHelper::ErrWriteLine(strl);
|
ConsoleHelper::ErrWriteLine(strl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Error log (including backtrace) used output function with format feature
|
||||||
|
* @details
|
||||||
|
* This function will format message first.
|
||||||
|
* And write them into given file stream and stderr.
|
||||||
|
* @param[in] fs
|
||||||
|
* The file stream where we write.
|
||||||
|
* If it is nullptr, function will skip writing for file stream.
|
||||||
|
* @param[in] fmt The format string.
|
||||||
|
* @param[in] ... The argument to be formatted.
|
||||||
|
*/
|
||||||
|
static void UExceptionErrLogFormatLine(std::FILE* fs, const yycc_char8_t* fmt, ...) {
|
||||||
|
// do format first
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, fmt);
|
||||||
|
auto fmt_result = YYCC::StringHelper::VPrintf(fmt, arg);
|
||||||
|
va_end(arg);
|
||||||
|
// write to file and console
|
||||||
|
UExceptionErrLogWriteLine(fs, fmt_result.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
static void UExceptionBacktrace(FILE* fs, LPCONTEXT context, int maxdepth) {
|
static void UExceptionBacktrace(FILE* fs, LPCONTEXT context, int maxdepth) {
|
||||||
// setup loading symbol options
|
// setup loading symbol options
|
||||||
SymSetOptions(SymGetOptions() | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); // lazy load symbol, and load line number.
|
SymSetOptions(SymGetOptions() | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); // lazy load symbol, and load line number.
|
||||||
|
@ -356,12 +350,12 @@ namespace YYCC::ExceptionHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get module name
|
// get module name
|
||||||
const yycc_char8_t* module_name = YYCC_U8("<unknown module>");
|
const yycc_char8_t* no_module_name = YYCC_U8("<unknown module>");
|
||||||
yycc_u8string module_name_raw;
|
yycc_u8string module_name(no_module_name);
|
||||||
DWORD64 module_base;
|
DWORD64 module_base;
|
||||||
if (module_base = SymGetModuleBase64(process, frame.AddrPC.Offset)) {
|
if (module_base = SymGetModuleBase64(process, frame.AddrPC.Offset)) {
|
||||||
if (WinFctHelper::GetModuleFileName((HINSTANCE)module_base, module_name_raw)) {
|
if (!WinFctHelper::GetModuleFileName((HINSTANCE)module_base, module_name)) {
|
||||||
module_name = module_name_raw.c_str();
|
module_name = no_module_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,9 +371,12 @@ namespace YYCC::ExceptionHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// write to file
|
// write to file
|
||||||
UExceptionErrLogFormatLine(fs, YYCC_U8("0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "[%s+0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "]\t%s#L%" PRIu64),
|
// MARK: should not use PRIXPTR to print adddress.
|
||||||
|
// because Windows always use DWORD64 as the type of address.
|
||||||
|
// use PRIX64 instead.
|
||||||
|
UExceptionErrLogFormatLine(fs, YYCC_U8("0x%" PRI_XPTR_LEFT_PADDING PRIX64 "[%s+0x%" PRI_XPTR_LEFT_PADDING PRIX64 "]\t%s#L%" PRIu64),
|
||||||
frame.AddrPC.Offset, // memory adress
|
frame.AddrPC.Offset, // memory adress
|
||||||
module_name, frame.AddrPC.Offset - module_base, // module name + relative address
|
module_name.c_str(), frame.AddrPC.Offset - module_base, // module name + relative address
|
||||||
source_file, source_file_line // source file + source line
|
source_file, source_file_line // source file + source line
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -555,6 +552,12 @@ namespace YYCC::ExceptionHelper {
|
||||||
g_ExceptionRegister.Unregister();
|
g_ExceptionRegister.Unregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(YYCC_DEBUG_UE_FILTER)
|
||||||
|
long __stdcall DebugCallUExceptionImpl(void* data) {
|
||||||
|
return UExceptionImpl(static_cast<LPEXCEPTION_POINTERS>(data));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -55,6 +55,10 @@ namespace YYCC::ExceptionHelper {
|
||||||
*/
|
*/
|
||||||
void Unregister();
|
void Unregister();
|
||||||
|
|
||||||
|
#if defined(YYCC_DEBUG_UE_FILTER)
|
||||||
|
long __stdcall DebugCallUExceptionImpl(void*);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -349,8 +349,20 @@ namespace YYCCTestbench {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Perform a div zero exception.
|
// Perform a div zero exception.
|
||||||
|
#if defined (YYCC_DEBUG_UE_FILTER)
|
||||||
|
// Reference: https://stackoverflow.com/questions/20981982/is-it-possible-to-debug-unhandledexceptionfilters-with-a-debugger
|
||||||
|
__try {
|
||||||
|
// all of code normally inside of main or WinMain here...
|
||||||
|
int i = 1, j = 0;
|
||||||
|
int k = i / j;
|
||||||
|
}
|
||||||
|
__except (YYCC::ExceptionHelper::DebugCallUExceptionImpl(GetExceptionInformation())) {
|
||||||
|
OutputDebugStringW(L"executed filter function\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
int i = 1, j = 0;
|
int i = 1, j = 0;
|
||||||
int k = i / j;
|
int k = i / j;
|
||||||
|
#endif
|
||||||
|
|
||||||
YYCC::ExceptionHelper::Unregister();
|
YYCC::ExceptionHelper::Unregister();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user