diff --git a/crash_printer/crash_printer/win.hpp b/crash_printer/crash_printer/win.hpp index b7126972..18e3ad01 100644 --- a/crash_printer/crash_printer/win.hpp +++ b/crash_printer/crash_printer/win.hpp @@ -12,4 +12,4 @@ void deinit(); } -#endif // _CRASH_PRINTER_WIN \ No newline at end of file +#endif // _CRASH_PRINTER_WIN diff --git a/crash_printer/linux.cpp b/crash_printer/linux.cpp index df3a4d12..dc38cd1e 100644 --- a/crash_printer/linux.cpp +++ b/crash_printer/linux.cpp @@ -1,165 +1,165 @@ -// https://stackoverflow.com/a/1925461 - -#include "common_helpers/common_helpers.hpp" -#include "crash_printer/linux.hpp" - -#include -#include -#include - -#include -#include // SIGBUS + SA_SIGINFO ... -#include // strsignal -#include // backtrace + backtrace_symbols - -constexpr static const int max_stack_frames = 50; - -static bool old_SIGILL = false; -static struct sigaction oldact_SIGILL{}; - -static bool old_SIGSEGV = false; -static struct sigaction oldact_SIGSEGV{}; - -static bool old_SIGBUS = false; -static struct sigaction oldact_SIGBUS{}; - -static std::string logs_filepath{}; - -static void restore_handlers() -{ - if (old_SIGILL) { - old_SIGILL = false; - sigaction(SIGILL, &oldact_SIGILL, nullptr); - } - - if (old_SIGSEGV) { - old_SIGSEGV = false; - sigaction(SIGSEGV, &oldact_SIGSEGV, nullptr); - } - - if (old_SIGBUS) { - old_SIGBUS = false; - sigaction(SIGBUS, &oldact_SIGBUS, nullptr); - } - -} - -static void exception_handler(int signal, siginfo_t *info, void *context, struct sigaction *oldact) -{ - if (!common_helpers::create_dir(logs_filepath)) { - return; - } - - std::ofstream file(logs_filepath, std::ios::app); - - std::string time(common_helpers::get_utc_time()); - common_helpers::write(file, "[" + time + "]"); - { - std::stringstream ss{}; - ss << "Unhandled exception:" << std::endl - << " code: " << std::dec << signal << " (" << strsignal(signal) << ")" << std::endl; - common_helpers::write(file, ss.str()); - } - void* stack_frames[max_stack_frames]; - int stack_size = backtrace(stack_frames, max_stack_frames); - char** stack_symbols = backtrace_symbols(stack_frames, stack_size); - - if (stack_symbols != nullptr) { - // fprintf(stderr, "Stack trace:\n"); - common_helpers::write(file, "*********** Stack trace ***********"); - for (int i = 1; i < stack_size; ++i) { - char *symbol = stack_symbols[i]; - std::stringstream ss{}; - ss << "[frame " << std::dec << (stack_size - i - 1) << "]: " - << std::hex << stack_frames[i] << " | " - << symbol; - common_helpers::write(file, ss.str()); - } - - free(stack_symbols); - } - - common_helpers::write(file, "**********************************\n"); - - file.close(); -} - -// Register the signal handler for illegal instruction (SIGILL) -static void exception_handler_SIGILL(int signal, siginfo_t *info, void *context) { - exception_handler(signal, info, context, &oldact_SIGILL); - sigaction(SIGILL, &oldact_SIGILL, nullptr); -} - -static void exception_handler_SIGSEGV(int signal, siginfo_t *info, void *context) { - exception_handler(signal, info, context, &oldact_SIGSEGV); - sigaction(SIGSEGV, &oldact_SIGSEGV, nullptr); -} - -static void exception_handler_SIGBUS(int signal, siginfo_t *info, void *context) { - exception_handler(signal, info, context, &oldact_SIGBUS); - sigaction(SIGBUS, &oldact_SIGBUS, nullptr); -} - - -bool crash_printer::init(const std::string &log_file) -{ - logs_filepath = log_file; - - // save old handlers - // https://linux.die.net/man/2/sigaction - if ( - sigaction(SIGILL, nullptr, &oldact_SIGILL) != 0 || - sigaction(SIGSEGV, nullptr, &oldact_SIGSEGV) != 0 || - sigaction(SIGBUS, nullptr, &oldact_SIGBUS) != 0) { - return false; - } - - constexpr int sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK | SA_RESETHAND | SA_NOCLDSTOP; - - // https://linux.die.net/man/2/sigaction - // register handler for illegal instruction (SIGILL) - struct sigaction sa_SIGILL{}; - sa_SIGILL.sa_sigaction = exception_handler_SIGILL; - sa_SIGILL.sa_flags = sa_flags; - sigemptyset(&sa_SIGILL.sa_mask); // all signals unblocked - if (sigaction(SIGILL, &sa_SIGILL, nullptr) != 0) { - restore_handlers(); - return false; - } - old_SIGILL = true; - - // register handler for segmentation fault (SIGSEGV) - struct sigaction sa_SIGSEGV{}; - sa_SIGSEGV.sa_sigaction = exception_handler_SIGSEGV; - sa_SIGSEGV.sa_flags = sa_flags; - sigemptyset(&sa_SIGSEGV.sa_mask); // all signals unblocked - if (sigaction(SIGSEGV, &sa_SIGSEGV, nullptr) != 0) { - restore_handlers(); - return false; - } - old_SIGSEGV = true; - - // register handler for bus error (SIGBUS) - struct sigaction sa_SIGBUS{}; - sa_SIGBUS.sa_sigaction = exception_handler_SIGBUS; - sa_SIGBUS.sa_flags = sa_flags; - sigemptyset(&sa_SIGBUS.sa_mask); // all signals unblocked - if (sigaction(SIGBUS, &sa_SIGBUS, nullptr) != 0) { - restore_handlers(); - return false; - } - old_SIGBUS = true; - - // // register handler for floating-point exception (SIGFPE) - // if (sigaction(SIGFPE, &sa, nullptr) != 0) { - // perror("Error setting up signal handler"); - // return EXIT_FAILURE; - // } - - return true; -} - -void crash_printer::deinit() -{ - restore_handlers(); -} +// https://stackoverflow.com/a/1925461 + +#include "common_helpers/common_helpers.hpp" +#include "crash_printer/linux.hpp" + +#include +#include +#include + +#include +#include // SIGBUS + SA_SIGINFO ... +#include // strsignal +#include // backtrace + backtrace_symbols + +constexpr static const int max_stack_frames = 50; + +static bool old_SIGILL = false; +static struct sigaction oldact_SIGILL{}; + +static bool old_SIGSEGV = false; +static struct sigaction oldact_SIGSEGV{}; + +static bool old_SIGBUS = false; +static struct sigaction oldact_SIGBUS{}; + +static std::string logs_filepath{}; + +static void restore_handlers() +{ + if (old_SIGILL) { + old_SIGILL = false; + sigaction(SIGILL, &oldact_SIGILL, nullptr); + } + + if (old_SIGSEGV) { + old_SIGSEGV = false; + sigaction(SIGSEGV, &oldact_SIGSEGV, nullptr); + } + + if (old_SIGBUS) { + old_SIGBUS = false; + sigaction(SIGBUS, &oldact_SIGBUS, nullptr); + } + +} + +static void exception_handler(int signal, siginfo_t *info, void *context, struct sigaction *oldact) +{ + if (!common_helpers::create_dir(logs_filepath)) { + return; + } + + std::ofstream file(logs_filepath, std::ios::app); + + std::string time(common_helpers::get_utc_time()); + common_helpers::write(file, "[" + time + "]"); + { + std::stringstream ss{}; + ss << "Unhandled exception:" << std::endl + << " code: " << std::dec << signal << " (" << strsignal(signal) << ")" << std::endl; + common_helpers::write(file, ss.str()); + } + void* stack_frames[max_stack_frames]; + int stack_size = backtrace(stack_frames, max_stack_frames); + char** stack_symbols = backtrace_symbols(stack_frames, stack_size); + + if (stack_symbols != nullptr) { + // fprintf(stderr, "Stack trace:\n"); + common_helpers::write(file, "*********** Stack trace ***********"); + for (int i = 1; i < stack_size; ++i) { + char *symbol = stack_symbols[i]; + std::stringstream ss{}; + ss << "[frame " << std::dec << (stack_size - i - 1) << "]: " + << std::hex << stack_frames[i] << " | " + << symbol; + common_helpers::write(file, ss.str()); + } + + free(stack_symbols); + } + + common_helpers::write(file, "**********************************\n"); + + file.close(); +} + +// Register the signal handler for illegal instruction (SIGILL) +static void exception_handler_SIGILL(int signal, siginfo_t *info, void *context) { + exception_handler(signal, info, context, &oldact_SIGILL); + sigaction(SIGILL, &oldact_SIGILL, nullptr); +} + +static void exception_handler_SIGSEGV(int signal, siginfo_t *info, void *context) { + exception_handler(signal, info, context, &oldact_SIGSEGV); + sigaction(SIGSEGV, &oldact_SIGSEGV, nullptr); +} + +static void exception_handler_SIGBUS(int signal, siginfo_t *info, void *context) { + exception_handler(signal, info, context, &oldact_SIGBUS); + sigaction(SIGBUS, &oldact_SIGBUS, nullptr); +} + + +bool crash_printer::init(const std::string &log_file) +{ + logs_filepath = log_file; + + // save old handlers + // https://linux.die.net/man/2/sigaction + if ( + sigaction(SIGILL, nullptr, &oldact_SIGILL) != 0 || + sigaction(SIGSEGV, nullptr, &oldact_SIGSEGV) != 0 || + sigaction(SIGBUS, nullptr, &oldact_SIGBUS) != 0) { + return false; + } + + constexpr int sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK | SA_RESETHAND | SA_NOCLDSTOP; + + // https://linux.die.net/man/2/sigaction + // register handler for illegal instruction (SIGILL) + struct sigaction sa_SIGILL{}; + sa_SIGILL.sa_sigaction = exception_handler_SIGILL; + sa_SIGILL.sa_flags = sa_flags; + sigemptyset(&sa_SIGILL.sa_mask); // all signals unblocked + if (sigaction(SIGILL, &sa_SIGILL, nullptr) != 0) { + restore_handlers(); + return false; + } + old_SIGILL = true; + + // register handler for segmentation fault (SIGSEGV) + struct sigaction sa_SIGSEGV{}; + sa_SIGSEGV.sa_sigaction = exception_handler_SIGSEGV; + sa_SIGSEGV.sa_flags = sa_flags; + sigemptyset(&sa_SIGSEGV.sa_mask); // all signals unblocked + if (sigaction(SIGSEGV, &sa_SIGSEGV, nullptr) != 0) { + restore_handlers(); + return false; + } + old_SIGSEGV = true; + + // register handler for bus error (SIGBUS) + struct sigaction sa_SIGBUS{}; + sa_SIGBUS.sa_sigaction = exception_handler_SIGBUS; + sa_SIGBUS.sa_flags = sa_flags; + sigemptyset(&sa_SIGBUS.sa_mask); // all signals unblocked + if (sigaction(SIGBUS, &sa_SIGBUS, nullptr) != 0) { + restore_handlers(); + return false; + } + old_SIGBUS = true; + + // // register handler for floating-point exception (SIGFPE) + // if (sigaction(SIGFPE, &sa, nullptr) != 0) { + // perror("Error setting up signal handler"); + // return EXIT_FAILURE; + // } + + return true; +} + +void crash_printer::deinit() +{ + restore_handlers(); +} diff --git a/crash_printer/tests/run_tests_linux.sh b/crash_printer/tests/run_tests_linux.sh deleted file mode 100644 index 6a83e160..00000000 --- a/crash_printer/tests/run_tests_linux.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -my_dir="$(cd "$(dirname "$0")" && pwd)" - -pushd "$my_dir" > /dev/null - -clang++ -x c++ -rdynamic -std=c++17 -fvisibility=hidden -fexceptions -fno-jump-tables -Og -g3 -fPIE -I../ ../linux.cpp ../common.cpp test_linux_sa_handler.cpp -otest_linux_sa_handler && { - ./test_linux_sa_handler ; - echo "exit code = $?" ; - rm -f ./test_linux_sa_handler ; -} - -clang++ -x c++ -rdynamic -std=c++17 -fvisibility=hidden -fexceptions -fno-jump-tables -Og -g3 -fPIE -I../ ../linux.cpp ../common.cpp test_linux_sa_sigaction.cpp -otest_linux_sa_sigaction && { - ./test_linux_sa_sigaction ; - echo "exit code = $?" ; - rm -f ./test_linux_sa_sigaction ; -} - -clang++ -m32 -x c++ -rdynamic -std=c++17 -fvisibility=hidden -fexceptions -fno-jump-tables -Og -g3 -fPIE -I../ ../linux.cpp ../common.cpp test_linux_sa_handler.cpp -otest_linux_sa_handler && { - ./test_linux_sa_handler ; - echo "exit code = $?" ; - rm -f ./test_linux_sa_handler ; -} - -clang++ -m32 -x c++ -rdynamic -std=c++17 -fvisibility=hidden -fexceptions -fno-jump-tables -Og -g3 -fPIE -I../ ../linux.cpp ../common.cpp test_linux_sa_sigaction.cpp -otest_linux_sa_sigaction && { - ./test_linux_sa_sigaction ; - echo "exit code = $?" ; - rm -f ./test_linux_sa_sigaction ; -} - -rm -f -r ./crash_test - -popd > /dev/null diff --git a/crash_printer/tests/run_tests_win.bat b/crash_printer/tests/run_tests_win.bat deleted file mode 100644 index 1dec4398..00000000 --- a/crash_printer/tests/run_tests_win.bat +++ /dev/null @@ -1,47 +0,0 @@ -@echo off - -pushd "%~dp0" - -call :cleanup - -setlocal -call ..\..\build_win_set_env.bat 64 -cl.exe /DEBUG:FULL /Z7 /Od /std:c++17 /DYNAMICBASE /errorReport:none /nologo /utf-8 /EHsc /GF /GL- /GS /MT /I../ ../win.cpp ../common.cpp test_win.cpp kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib Iphlpapi.lib Wldap32.lib Winmm.lib Bcrypt.lib Dbghelp.lib /link /DYNAMICBASE /ERRORREPORT:NONE /NOLOGO /OUT:test_win.exe && ( - call test_win.exe - - setlocal enableDelayedExpansion - echo exit code = !errorlevel! - endlocal - - call :cleanup -) -endlocal - -setlocal -call ..\..\build_win_set_env.bat 32 -cl.exe /DEBUG:FULL /Z7 /Od /std:c++17 /DYNAMICBASE /errorReport:none /nologo /utf-8 /EHsc /GF /GL- /GS /MT /I../ ../win.cpp ../common.cpp test_win.cpp kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib Iphlpapi.lib Wldap32.lib Winmm.lib Bcrypt.lib Dbghelp.lib /link /DYNAMICBASE /ERRORREPORT:NONE /NOLOGO /OUT:test_win.exe && ( - call test_win.exe - - setlocal enableDelayedExpansion - echo exit code = !errorlevel! - endlocal - - call :cleanup -) -endlocal - -rmdir /s /q crash_test - -popd - -exit /b 0 - - -:cleanup - del /f /q test_win.exe >nul 2>&1 - del /f /q test_win.ilk >nul 2>&1 - del /f /q test_win.obj >nul 2>&1 - del /f /q test_win.pdb >nul 2>&1 - del /f /q win.obj >nul 2>&1 - del /f /q common.obj >nul 2>&1 -exit /b diff --git a/crash_printer/tests/test_helper.hpp b/crash_printer/tests/test_helper.hpp index cdedb9d5..ad0fb1c1 100644 --- a/crash_printer/tests/test_helper.hpp +++ b/crash_printer/tests/test_helper.hpp @@ -7,7 +7,7 @@ static inline bool remove_file(const std::string &file) { - const std::filesystem::u8path p_file(std::filesystem::u8path(file)); + const std::filesystem::path p_file(std::filesystem::u8path(file)); if (!std::filesystem::exists(p_file)) { return true; } diff --git a/crash_printer/tests/test_win.cpp b/crash_printer/tests/test_win.cpp index ab7c14ed..0790c001 100644 --- a/crash_printer/tests/test_win.cpp +++ b/crash_printer/tests/test_win.cpp @@ -1,53 +1,57 @@ - -#include "crash_printer/win.hpp" -#include "./test_helper.hpp" - -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include - -std::wstring logs_filepath = L"./crash_test/zxc.txt"; - - -static LONG WINAPI exception_handler(LPEXCEPTION_POINTERS ex_pointers) -{ - std::ifstream file(logs_filepath); - if (file.is_open()) { - std::string line{}; - std::getline(file, line); - file.close(); - - if (line.size()) { - std::cout << "Success!" << std::endl; - exit(0); - } - } - - std::cerr << "Failed!" << std::endl; - exit(1); -} - - -int main() -{ - // simulate the existence of previous handler - SetUnhandledExceptionFilter(exception_handler); - - if (!remove_file(logs_filepath)) { - std::cerr << "failed to remove log" << std::endl; - return 1; - } - - if (!crash_printer::init(logs_filepath)) { - std::cerr << "failed to init" << std::endl; - return 1; - } - - // simulate a crash - volatile int * volatile ptr = nullptr; - *ptr = 42; - - return 0; -} + +#include "crash_printer/win.hpp" +#include "./test_helper.hpp" + +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +std::wstring logs_filepath = L"./crash_test/zxc.txt"; + + +static LONG WINAPI exception_handler(LPEXCEPTION_POINTERS ex_pointers) +{ + std::ifstream file(logs_filepath); + if (file.is_open()) { + std::string line{}; + std::getline(file, line); + file.close(); + + if (line.size()) { + std::cout << "Success!" << std::endl; + exit(0); + } + } + + std::cerr << "Failed!" << std::endl; + exit(1); +} + +int* get_ptr() +{ + return nullptr; +} + +int main() +{ + // simulate the existence of previous handler + SetUnhandledExceptionFilter(exception_handler); + + if (!remove_file(logs_filepath)) { + std::cerr << "failed to remove log" << std::endl; + return 1; + } + + if (!crash_printer::init(logs_filepath)) { + std::cerr << "failed to init" << std::endl; + return 1; + } + + // simulate a crash + volatile int * volatile ptr = get_ptr(); + *ptr = 42; + + return 0; +} diff --git a/premake5.lua b/premake5.lua index 0712bdf9..402df093 100644 --- a/premake5.lua +++ b/premake5.lua @@ -1441,6 +1441,60 @@ project "tool_file_dos_stub_changer" } -- End tool_file_dos_stub_changer + +-- Project test_crash_printer +--------- +project "test_crash_printer" + kind "ConsoleApp" + location "%{wks.location}/%{prj.name}" + targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tests/crash_printer") + targetname "test_crash_printer_%{cfg.platform}" + + + -- include dir + --------- + -- common include dir + filter {} -- reset the filter and remove all active keywords + includedirs { + 'crash_printer', + 'helpers', + } + + + -- common source & header files + --------- + filter {} -- reset the filter and remove all active keywords + files { -- added to all filters, later defines will be appended + 'crash_printer/' .. os_iden .. '.cpp', 'crash_printer/crash_printer/' .. os_iden .. '.hpp', + 'crash_printer/tests/test_helper.hpp', + -- helpers + 'helpers/common_helpers.cpp', 'helpers/common_helpers/**', + -- test files + 'crash_printer/tests/test_win.cpp', + } + removefiles { + 'post_build/**', + 'build/deps/**', + } + + + -- libs to link + --------- + filter {} -- reset the filter and remove all active keywords + -- Windows libs + links { + 'Dbghelp', + } + + + -- post build + --------- + filter {} -- reset the filter and remove all active keywords + postbuildcommands { + '%[%{!cfg.buildtarget.abspath}]', + } +-- End test_crash_printer + end -- End WINDOWS ONLY TARGETS @@ -1518,6 +1572,97 @@ project "steamclient_regular" } -- End steamclient_regular + +-- Project test_crash_printer_sa_handler +--------- +project "test_crash_printer_sa_handler" + kind "ConsoleApp" + location "%{wks.location}/%{prj.name}" + targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tests/crash_printer") + targetname "test_crash_printer_sa_handler_%{cfg.platform}" + + + -- include dir + --------- + -- common include dir + filter {} -- reset the filter and remove all active keywords + includedirs { + 'crash_printer', + 'helpers', + } + + + -- common source & header files + --------- + filter {} -- reset the filter and remove all active keywords + files { -- added to all filters, later defines will be appended + 'crash_printer/' .. os_iden .. '.cpp', 'crash_printer/crash_printer/' .. os_iden .. '.hpp', + 'crash_printer/tests/test_helper.hpp', + -- helpers + 'helpers/common_helpers.cpp', 'helpers/common_helpers/**', + -- test files + 'crash_printer/tests/test_linux_sa_handler.cpp', + } + removefiles { + 'post_build/**', + 'build/deps/**', + } + + + -- post build + --------- + filter {} -- reset the filter and remove all active keywords + postbuildcommands { + '%[%{!cfg.buildtarget.abspath}]', + } + +-- End test_crash_printer_sa_handler + + +-- Project test_crash_printer_sa_sigaction +--------- +project "test_crash_printer_sa_sigaction" + kind "ConsoleApp" + location "%{wks.location}/%{prj.name}" + targetdir("build/" .. os_iden .. "/%{_ACTION}/%{cfg.buildcfg}/tests/crash_printer") + targetname "test_crash_printer_sa_sigaction_%{cfg.platform}" + + + -- include dir + --------- + -- common include dir + filter {} -- reset the filter and remove all active keywords + includedirs { + 'crash_printer', + 'helpers', + } + + + -- common source & header files + --------- + filter {} -- reset the filter and remove all active keywords + files { -- added to all filters, later defines will be appended + 'crash_printer/' .. os_iden .. '.cpp', 'crash_printer/crash_printer/' .. os_iden .. '.hpp', + 'crash_printer/tests/test_helper.hpp', + -- helpers + 'helpers/common_helpers.cpp', 'helpers/common_helpers/**', + -- test files + 'crash_printer/tests/test_linux_sa_sigaction.cpp', + } + removefiles { + 'post_build/**', + 'build/deps/**', + } + + + -- post build + --------- + filter {} -- reset the filter and remove all active keywords + postbuildcommands { + '%[%{!cfg.buildtarget.abspath}]', + } +-- End test_crash_printer_sa_sigaction + end -- End LINUX ONLY TARGETS