diff --git a/CMakeLists.txt b/CMakeLists.txt index 41d09f6aa..682b4d9b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,8 @@ option(DISCORD_DYNAMIC "Enable discovering Discord rich presence libraries at ru option(PREFER_BUNDLED_LIBS "Prefer bundled libraries over system libraries" ${AUTO_DEPENDENCIES_DEFAULT}) option(DEV "Don't generate stuff necessary for packaging" OFF) +option(EXCEPTION_HANDLING "Enable exception handling (only works with Windows as of now)" OFF) + if(TEST_MYSQL) set(MYSQL ON) endif() @@ -217,6 +219,14 @@ if(NOT MSVC AND NOT HAIKU) add_c_compiler_flag_if_supported(OUR_FLAGS -stdlib=libc++) endif() + if(EXCEPTION_HANDLING) + add_c_compiler_flag_if_supported(OUR_FLAGS -DCONF_EXCEPTION_HANDLING) + # use the frame pointer (frame pointer usage is disabled by default in + # some architectures like x86_64 and for some optimization levels; and it + # may be impossible to walk the call stack without it) + add_c_compiler_flag_if_supported(OUR_FLAGS -fno-omit-frame-pointer) + endif() + add_c_compiler_flag_if_supported(OUR_FLAGS_OWN -Wall) if(CMAKE_VERSION VERSION_GREATER 3.3 OR CMAKE_VERSION VERSION_EQUAL 3.3) add_c_compiler_flag_if_supported(OUR_FLAGS_OWN @@ -239,6 +249,12 @@ if(NOT MSVC AND NOT HAIKU) #add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN "-Wuseless-cast") endif() +if(MSVC) + if(EXCEPTION_HANDLING) + add_c_compiler_flag_if_supported(OUR_FLAGS /DCONF_EXCEPTION_HANDLING) + endif() +endif() + if(NOT MSVC AND NOT HAIKU) check_c_compiler_flag("-O2;-Wp,-Werror;-D_FORTIFY_SOURCE=2" DEFINE_FORTIFY_SOURCE) # Some distributions define _FORTIFY_SOURCE by themselves. endif() @@ -440,6 +456,10 @@ else() set(WEBSOCKETS_INCLUDE_DIRS) endif() +if(EXCEPTION_HANDLING) + find_package(ExceptionHandling) +endif() + if(TARGET_OS AND TARGET_OS STREQUAL "mac") find_program(CMAKE_OTOOL otool) find_program(DMGBUILD dmgbuild) @@ -1399,6 +1419,7 @@ set(COPY_FILES ${FFMPEG_COPY_FILES} ${WEBSOCKETS_COPY_FILES} ${DISCORDSDK_COPY_FILES} + ${EXCEPTION_HANDLING_COPY_FILES} ) file(COPY ${COPY_FILES} DESTINATION .) diff --git a/cmake/FindExceptionHandling.cmake b/cmake/FindExceptionHandling.cmake new file mode 100644 index 000000000..f6d12632e --- /dev/null +++ b/cmake/FindExceptionHandling.cmake @@ -0,0 +1,21 @@ +if(TARGET_OS STREQUAL "windows") + set_extra_dirs_lib(EXCEPTION_HANDLING drmingw) + find_file(EXCEPTION_HANDLING_LIBRARY + NAMES exchndl.dll + HINTS ${HINTS_EXCEPTION_HANDLING_LIBDIR} + PATHS ${PATHS_EXCEPTION_HANDLING_LIBDIR} + ${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH} + ) + + is_bundled(EXCEPTION_HANDLING_BUNDLED "${EXCEPTION_HANDLING_LIBRARY}") + if(NOT EXCEPTION_HANDLING_BUNDLED) + message(FATAL_ERROR "could not find exception handling paths") + endif() + set(EXCEPTION_HANDLING_COPY_FILES + "${EXTRA_EXCEPTION_HANDLING_LIBDIR}/exchndl.dll" + "${EXTRA_EXCEPTION_HANDLING_LIBDIR}/dbgcore.dll" + "${EXTRA_EXCEPTION_HANDLING_LIBDIR}/dbghelp.dll" + "${EXTRA_EXCEPTION_HANDLING_LIBDIR}/mgwhelp.dll" + "${EXTRA_EXCEPTION_HANDLING_LIBDIR}/symsrv.dll" + ) +endif() diff --git a/ddnet-libs b/ddnet-libs index 1cdf759f4..635aa7afb 160000 --- a/ddnet-libs +++ b/ddnet-libs @@ -1 +1 @@ -Subproject commit 1cdf759f4e46edbe174250f523b9cc737c4ad8ea +Subproject commit 635aa7afb698ed68feeb733aaea2cf861c2a1899 diff --git a/src/base/system.cpp b/src/base/system.cpp index f351b35bd..b861256c5 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -4060,4 +4060,45 @@ int os_version_str(char *version, int length) return 0; #endif } + +#if defined(CONF_EXCEPTION_HANDLING) +#if defined(CONF_FAMILY_WINDOWS) +static HMODULE gs_ExceptionHandlingModule = nullptr; +#endif + +void init_exception_handler() +{ +#if defined(CONF_FAMILY_WINDOWS) + gs_ExceptionHandlingModule = LoadLibraryA("exchndl.dll"); + if(gs_ExceptionHandlingModule != nullptr) + { + auto pfnExcHndlInit = (void APIENTRY (*)())GetProcAddress(gs_ExceptionHandlingModule, "ExcHndlInit"); + pfnExcHndlInit(); + } +#else +#error exception handling not implemented +#endif +} + +void set_exception_handler_log_file(const char *pLogFilePath) +{ +#if defined(CONF_FAMILY_WINDOWS) + if(gs_ExceptionHandlingModule != nullptr) + { + // Intentional +#ifdef __MINGW32__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type" +#endif + auto pExceptionLogFilePathFunc = (BOOL APIENTRY(*)(const char *))(GetProcAddress(gs_ExceptionHandlingModule, "ExcHndlSetLogFileNameA")); +#ifdef __MINGW32__ +#pragma clang diagnostic pop +#endif + pExceptionLogFilePathFunc(pLogFilePath); + } +#else +#error exception handling not implemented +#endif +} +#endif } diff --git a/src/base/system.h b/src/base/system.h index 1dc8f68e2..7398b5b39 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -2368,6 +2368,11 @@ void set_console_msg_color(const void *rgbvoid); */ int os_version_str(char *version, int length); +#if defined(CONF_EXCEPTION_HANDLING) +void init_exception_handler(); +void set_exception_handler_log_file(const char *pLogFilePath); +#endif + #if defined(__cplusplus) } #endif diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index d6e3c4959..9cb5628ae 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -4268,6 +4268,10 @@ int main(int argc, const char **argv) InitAndroid(); #endif +#if defined(CONF_EXCEPTION_HANDLING) + init_exception_handler(); +#endif + if(secure_random_init() != 0) { RandInitFailed = true; @@ -4292,6 +4296,16 @@ int main(int argc, const char **argv) IDiscord *pDiscord = CreateDiscord(); ISteam *pSteam = CreateSteam(); +#if defined(CONF_EXCEPTION_HANDLING) + char aBuf[IO_MAX_PATH_LENGTH]; + char aBufName[IO_MAX_PATH_LENGTH]; + char aDate[64]; + str_timestamp(aDate, sizeof(aDate)); + str_format(aBufName, sizeof(aBufName), "dumps/" GAME_NAME "_crash_log_%d_%s.RTP", pid(), aDate); + pStorage->GetCompletePath(IStorage::TYPE_SAVE, aBufName, aBuf, sizeof(aBuf)); + set_exception_handler_log_file(aBuf); +#endif + if(RandInitFailed) { dbg_msg("secure", "could not initialize secure RNG"); diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 693161bed..89055b502 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -3645,6 +3645,10 @@ int main(int argc, const char **argv) signal(SIGINT, HandleSigIntTerm); signal(SIGTERM, HandleSigIntTerm); +#if defined(CONF_EXCEPTION_HANDLING) + init_exception_handler(); +#endif + CServer *pServer = CreateServer(); IKernel *pKernel = IKernel::Create(); @@ -3658,6 +3662,16 @@ int main(int argc, const char **argv) IConfigManager *pConfigManager = CreateConfigManager(); IEngineAntibot *pEngineAntibot = CreateEngineAntibot(); +#if defined(CONF_EXCEPTION_HANDLING) + char aBuf[IO_MAX_PATH_LENGTH]; + char aBufName[IO_MAX_PATH_LENGTH]; + char aDate[64]; + str_timestamp(aDate, sizeof(aDate)); + str_format(aBufName, sizeof(aBufName), "dumps/" GAME_NAME "-Server_crash_log_%d_%s.RTP", pid(), aDate); + pStorage->GetCompletePath(IStorage::TYPE_SAVE, aBufName, aBuf, sizeof(aBuf)); + set_exception_handler_log_file(aBuf); +#endif + pServer->InitRegister(&pServer->m_NetServer, pEngineMasterServer, pConfigManager->Values(), pConsole); {