From d0a8733379bc7b8c07add6b4ae9b536547972d72 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sat, 15 Jun 2024 16:44:11 +0800 Subject: [PATCH] refactor: change 2 functions declarations in WinFctHelper - update GetTempPath and GetModuleName function to let them more reliable. --- src/ExceptionHelper.cpp | 119 ++++++++++++++++++++++++++++++---------- src/WinFctHelper.cpp | 17 +++--- src/WinFctHelper.hpp | 12 ++-- testbench/main.cpp | 10 +++- 4 files changed, 113 insertions(+), 45 deletions(-) diff --git a/src/ExceptionHelper.cpp b/src/ExceptionHelper.cpp index d83f23b..51deb72 100644 --- a/src/ExceptionHelper.cpp +++ b/src/ExceptionHelper.cpp @@ -2,6 +2,10 @@ #if YYCC_OS == YYCC_OS_WINDOWS #include "WinFctHelper.hpp" +#include "ConsoleHelper.hpp" +#include "StringHelper.hpp" +#include "IOHelper.hpp" +#include "EncodingHelper.hpp" #include #include #include @@ -13,7 +17,7 @@ #include "WinImportSuffix.hpp" namespace YYCC::ExceptionHelper { - + /** * @brief True if the exception handler already registered, otherwise false. * @details @@ -27,7 +31,7 @@ namespace YYCC::ExceptionHelper { static bool g_IsRegistered = false; /** * @brief True if a exception handler is running, otherwise false. - * @details + * @details * This variable is served for blocking possible infinity recursive exception handling. * \n * When entering unhandled exception handler, we must check whether this variable is true. @@ -36,7 +40,7 @@ namespace YYCC::ExceptionHelper { * Otherwise, this variable should be set to true indicating we are processing unhandled exception. * After processing exception, at the end of unhandled exception handler, * we should restore this value to false. - * + * */ static bool g_IsProcessing = false; /** @@ -49,6 +53,11 @@ namespace YYCC::ExceptionHelper { #pragma region Exception Handler Implementation + /** + * @brief Get human-readable exception string from given exception code. + * @param[in] code Exception code + * @return The const string pointer to corresponding exception explanation string. + */ static const char* UExceptionGetCodeName(DWORD code) { switch (code) { case EXCEPTION_ACCESS_VIOLATION: @@ -96,24 +105,50 @@ namespace YYCC::ExceptionHelper { } } + /** + * @brief 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 UExceptionBacktraceFormatLine(std::FILE* fs, const char* fmt, ...) { // write to file - va_list arg1; - va_start(arg1, fmt); - std::vfprintf(fs, fmt, arg1); - va_end(arg1); - // write to stdout + if (fs != nullptr) { + va_list arg1; + va_start(arg1, fmt); + std::vfprintf(fs, fmt, arg1); + std::fputs("\n", fs); + va_end(arg1); + } + // write to stderr va_list arg2; va_start(arg2, fmt); - std::vfprintf(stdout, fmt, arg2); + ConsoleHelper::ErrWriteLine(YYCC::StringHelper::VPrintf(fmt, arg2).c_str()); va_end(arg2); } + /** + * @brief Backtrace used output function + * @details + * This function will write given string 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] strl The string to be written. + */ static void UExceptionBacktraceWriteLine(std::FILE* fs, const char* strl) { // write to file - std::fputs(strl, fs); - // write to stdout - std::fputs(strl, stdout); + if (fs != nullptr) { + std::fputs(strl, fs); + std::fputs("\n", fs); + } + // write to stderr + ConsoleHelper::ErrWriteLine(strl); } static void UExceptionBacktrace(FILE* fs, LPCONTEXT context, int maxdepth) { @@ -126,8 +161,8 @@ namespace YYCC::ExceptionHelper { // init symbol if (!SymInitialize(process, 0, TRUE)) { - // fail to load. return - UExceptionBacktraceWriteLine(fs, "Lost symbol file!\n"); + // fail to init. return + UExceptionBacktraceWriteLine(fs, "Fail to initialize symbol handle for process!"); return; } @@ -172,9 +207,6 @@ namespace YYCC::ExceptionHelper { frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat; - // other variables - char module_name_raw[MAX_PATH]; - // stack walker while (StackWalk64(machine_type, process, thread, &frame, context, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) { @@ -187,14 +219,17 @@ namespace YYCC::ExceptionHelper { } // get module name - DWORD64 module_base = SymGetModuleBase64(process, frame.AddrPC.Offset); - const char* module_name = "[unknown module]"; - if (module_base && GetModuleFileNameA((HINSTANCE)module_base, module_name_raw, MAX_PATH)) { - module_name = module_name_raw; + const char* module_name = ""; + std::string module_name_raw; + DWORD64 module_base; + if (module_base = SymGetModuleBase64(process, frame.AddrPC.Offset)) { + if (WinFctHelper::GetModuleName((HINSTANCE)module_base, module_name_raw)) { + module_name = module_name_raw.c_str(); + } } // get source file and line - const char* source_file = "[unknow_source_file]"; + const char* source_file = ""; DWORD64 source_file_line = 0; DWORD dwDisplacement; IMAGEHLP_LINE64 winline; @@ -205,9 +240,10 @@ namespace YYCC::ExceptionHelper { } // write to file - UExceptionBacktraceFormatLine(fs, "0x%016llx(rel: 0x%016llx)[%s]\t%s#%llu\n", - frame.AddrPC.Offset, frame.AddrPC.Offset - module_base, module_name, - source_file, source_file_line + UExceptionBacktraceFormatLine(fs, "0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "[%s+0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "]\t%s#L%" PRIu64 "\n", + frame.AddrPC.Offset, // memory adress + module_name, frame.AddrPC.Offset - module_base, // module name + relative address + source_file, source_file_line // source file + source line ); } @@ -218,13 +254,20 @@ namespace YYCC::ExceptionHelper { SymCleanup(process); } - static void UExceptionErrorLog(const std::wstring& filename, LPEXCEPTION_POINTERS info) { + static void UExceptionErrorLog(const std::string& u8_filename, LPEXCEPTION_POINTERS info) { } - static void UExceptionCoreDump(LPCWSTR filename, LPEXCEPTION_POINTERS info) { + static void UExceptionCoreDump(const std::string& u8_filename, LPEXCEPTION_POINTERS info) { + // convert file encoding + std::wstring filename; + if (u8_filename.empty()) + return; // if no given file name, return + if (!YYCC::EncodingHelper::UTF8ToWchar(u8_filename.c_str(), filename)) + return; // if convertion failed, return + // open file and write - HANDLE hFile = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFile = CreateFileW(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { MINIDUMP_EXCEPTION_INFORMATION exception_info; exception_info.ThreadId = GetCurrentThreadId(); @@ -241,6 +284,24 @@ namespace YYCC::ExceptionHelper { } static void UExceptionFetchRecordPath(std::wstring& log_path, std::wstring& coredump_path) { + // get self name first + std::string self_name; + + std::filesystem::path ironpad_path; + WCHAR module_path[MAX_PATH]; + std::memset(module_path, 0, sizeof(module_path)); + if (GetModuleFileNameW(WinFctHelper::GetCurrentModule(), module_path, MAX_PATH) == 0) { + //goto failed; + } + ironpad_path = module_path; + ironpad_path = ironpad_path.parent_path(); + + // create 2 filename + auto logfilename = ironpad_path / "IronPad.log"; + auto dmpfilename = ironpad_path / "IronPad.dmp"; + ConsoleHelper::ErrWriteLine(""); + ConsoleHelper::ErrFormatLine("Exception Log: %s\n", logfilename.string().c_str()); + ConsoleHelper::ErrFormatLine("Exception Coredump: %s\n", dmpfilename.string().c_str()); } @@ -302,7 +363,7 @@ namespace YYCC::ExceptionHelper { // output minidump { - UExceptionCoreDump(dmpfilename.wstring().c_str(), info); + //UExceptionCoreDump(dmpfilename.wstring().c_str(), info); } } diff --git a/src/WinFctHelper.cpp b/src/WinFctHelper.cpp index 08edfcc..cc7e9eb 100644 --- a/src/WinFctHelper.cpp +++ b/src/WinFctHelper.cpp @@ -16,7 +16,7 @@ namespace YYCC::WinFctHelper { return hModule; } - std::string GetTempDirectory() { + bool GetTempDirectory(std::string& ret) { // create wchar buffer for receiving the temp path. std::wstring wpath(MAX_PATH + 1u, L'\0'); DWORD expected_size; @@ -25,9 +25,7 @@ namespace YYCC::WinFctHelper { while (true) { if ((expected_size = GetTempPathW(static_cast(wpath.size()), wpath.data())) == 0) { // failed, set to empty - expected_size = 0; - // and break while - break; + return false; } if (expected_size > static_cast(wpath.size())) { @@ -42,19 +40,18 @@ namespace YYCC::WinFctHelper { // resize result wpath.resize(expected_size); // convert to utf8 and return - return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str()); + return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str(), ret); } - std::string GetModuleName(HINSTANCE hModule) { + bool GetModuleName(HINSTANCE hModule, std::string& ret) { // create wchar buffer for receiving the temp path. std::wstring wpath(MAX_PATH + 1u, L'\0'); DWORD copied_size; while (true) { if ((copied_size = GetModuleFileNameW(hModule, wpath.data(), static_cast(wpath.size()))) == 0) { - // failed, return empty string - copied_size = 0; - break; + // failed, return + return false; } // check insufficient buffer @@ -70,7 +67,7 @@ namespace YYCC::WinFctHelper { // resize result wpath.resize(copied_size); // convert to utf8 and return - return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str()); + return YYCC::EncodingHelper::WcharToUTF8(wpath.c_str(), ret); } } diff --git a/src/WinFctHelper.hpp b/src/WinFctHelper.hpp index c26c834..05263ec 100644 --- a/src/WinFctHelper.hpp +++ b/src/WinFctHelper.hpp @@ -33,18 +33,22 @@ namespace YYCC::WinFctHelper { /** * @brief Get path to Windows temp folder. - * @return UTF8 encoded path to Windows temp folder. Empty string if failed. + * @param[out] ret + * The variable receiving UTF8 encoded path to Windows temp folder. + * @return True if success, otherwise false. */ - std::string GetTempDirectory(); + bool GetTempDirectory(std::string& ret); /** * @brief Get the file name of given module HANDLE * @param[in] hModule * The HANDLE to the module where we want get file name. * It is same as the HANDLE parameter of \c GetModuleFileName. - * @return UTF8 encoded file name of given module. Empty string if failed. + * @param[out] ret + * The variable receiving UTF8 encoded file name of given module. + * @return True if success, otherwise false. */ - std::string GetModuleName(HINSTANCE hModule); + bool GetModuleName(HINSTANCE hModule, std::string& ret); } #endif diff --git a/testbench/main.cpp b/testbench/main.cpp index c8aa2cb..8b80947 100644 --- a/testbench/main.cpp +++ b/testbench/main.cpp @@ -217,8 +217,14 @@ namespace YYCCTestbench { static void WinFctTestbench() { Console::FormatLine("Current Module HANDLE: 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR, YYCC::WinFctHelper::GetCurrentModule()); - Console::FormatLine("Temp Directory: %s", YYCC::WinFctHelper::GetTempDirectory().c_str()); - Console::FormatLine("Current Module Name: %s", YYCC::WinFctHelper::GetModuleName(YYCC::WinFctHelper::GetCurrentModule()).c_str()); + + std::string test_temp; + Assert(YYCC::WinFctHelper::GetTempDirectory(test_temp), "YYCC::WinFctHelper::GetTempDirectory"); + Console::FormatLine("Temp Directory: %s", test_temp.c_str()); + + std::string test_module_name; + Assert(YYCC::WinFctHelper::GetModuleName(YYCC::WinFctHelper::GetCurrentModule(), test_module_name), "YYCC::WinFctHelper::GetModuleName"); + Console::FormatLine("Current Module Name: %s", test_module_name.c_str()); } }