From 17bc485fb51eb443679ecb6745cec456832672f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Thu, 7 Apr 2016 00:10:12 +0200 Subject: [PATCH] Breakpad for windows This enables breakpad Windows support **only**. The scope of this was limited to allow for inclusion into 0.0.4 release. Breakpad for now is a Windows-only functionality, as this platform has largest audience and is the only one that has no system-provided stack tracing utility. Upon crash, breakpad will try to create a dump and save current game, present user with some vital information, then open explorer on the created files for user convenience, asking him/her to create a new issue with OpenRCT2 on Github. --- openrct2.vcxproj | 10 ++- openrct2.vcxproj.filters | 4 +- scripts/ps/install.ps1 | 2 +- src/openrct2.c | 14 ++-- src/platform/breakpad.cpp | 19 ----- src/platform/crash.cpp | 105 +++++++++++++++++++++++++++ src/platform/{breakpad.h => crash.h} | 8 +- src/platform/shared.c | 2 +- src/platform/windows.c | 2 +- 9 files changed, 128 insertions(+), 38 deletions(-) delete mode 100644 src/platform/breakpad.cpp create mode 100644 src/platform/crash.cpp rename src/platform/{breakpad.h => crash.h} (53%) diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 3b5ae319ce..db5e3b08b4 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -87,6 +87,7 @@ + @@ -261,6 +262,7 @@ + @@ -329,20 +331,21 @@ - $(SolutionDir)lib\include;$(SolutionDir)lib\include\libspeex;$(SolutionDir)lib\include\sdl;$(SolutionDir)lib\include\jansson;$(SolutionDir)lib\include\sdl_ttf;$(SolutionDir)lib\include\libpng;$(SolutionDir)lib\include\zlib;$(IncludePath) + $(SolutionDir)lib\include;$(SolutionDir)lib\include\breakpad;$(SolutionDir)lib\include\libspeex;$(SolutionDir)lib\include\sdl;$(SolutionDir)lib\include\jansson;$(SolutionDir)lib\include\sdl_ttf;$(SolutionDir)lib\include\libpng;$(SolutionDir)lib\include\zlib;$(IncludePath) $(SolutionDir)lib;$(LibraryPath) $(SolutionDir)bin\ $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ - $(SolutionDir)lib\include;$(SolutionDir)lib\include\libspeex;$(SolutionDir)lib\include\sdl;$(SolutionDir)lib\include\jansson;$(SolutionDir)lib\include\sdl_ttf;$(SolutionDir)lib\include\libpng;$(SolutionDir)lib\include\zlib;$(IncludePath) + $(SolutionDir)lib\include;$(SolutionDir)lib\include\breakpad;$(SolutionDir)lib\include\libspeex;$(SolutionDir)lib\include\sdl;$(SolutionDir)lib\include\jansson;$(SolutionDir)lib\include\sdl_ttf;$(SolutionDir)lib\include\libpng;$(SolutionDir)lib\include\zlib;$(IncludePath) $(SolutionDir)lib;$(LibraryPath) $(SolutionDir)bin\ $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + 4091;%(DisableSpecificWarnings) Level3 Disabled true @@ -363,6 +366,7 @@ + 4091;%(DisableSpecificWarnings) Level3 Full true @@ -388,4 +392,4 @@ - \ No newline at end of file + diff --git a/openrct2.vcxproj.filters b/openrct2.vcxproj.filters index 333e9eccff..07a3682284 100644 --- a/openrct2.vcxproj.filters +++ b/openrct2.vcxproj.filters @@ -587,6 +587,7 @@ Source\Core + @@ -896,5 +897,6 @@ Source\Core + - \ No newline at end of file + diff --git a/scripts/ps/install.ps1 b/scripts/ps/install.ps1 index 7b87a2176a..016e181e65 100644 --- a/scripts/ps/install.ps1 +++ b/scripts/ps/install.ps1 @@ -14,7 +14,7 @@ Import-Module "$scriptsPath\common.psm1" -DisableNameChecking # Constants $libsUrl = "https://openrct2.website/files/openrct2-libs-vs2015.zip" -$libsVersion = 6 +$libsVersion = 7 # Get paths $rootPath = Get-RootPath diff --git a/src/openrct2.c b/src/openrct2.c index b5c6648374..5d9bf0973e 100644 --- a/src/openrct2.c +++ b/src/openrct2.c @@ -39,7 +39,7 @@ #include "util/sawyercoding.h" #include "util/util.h" #include "world/mapgen.h" -#include "platform/breakpad.h" +#include "platform/crash.h" #if defined(__unix__) #include @@ -185,6 +185,12 @@ bool openrct2_initialise() return false; } + // Exception handling - breakpad + // Uses user data directory for storing dumps + CExceptionHandler eh; + // never free + eh = newCExceptionHandlerSimple(); + if (!openrct2_setup_rct2_segment()) { log_fatal("Unable to load RCT2 data sector"); return false; @@ -264,12 +270,6 @@ bool openrct2_initialise() */ void openrct2_launch() { -#ifdef USE_BREAKPAD - CExceptionHandler eh; - // never free - eh = newCExceptionHandlerSimple(); -#endif // USE_BREAKPAD - if (openrct2_initialise()) { RCT2_GLOBAL(RCT2_ADDRESS_RUN_INTRO_TICK_PART, uint8) = 0; if((gOpenRCT2StartupAction == STARTUP_ACTION_TITLE) && gConfigGeneral.play_intro) diff --git a/src/platform/breakpad.cpp b/src/platform/breakpad.cpp deleted file mode 100644 index a4241019cf..0000000000 --- a/src/platform/breakpad.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "breakpad.h" - -#ifdef USE_BREAKPAD -#include "client/linux/handler/exception_handler.h" -#include - -static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) { - printf("Dump path: %s\n", descriptor.path()); - return succeeded; -} - -extern "C" CExceptionHandler newCExceptionHandlerSimple(void) -{ - printf("init Simple breakpad\n"); - google_breakpad::MinidumpDescriptor descriptor("/tmp"); - //google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1); - return reinterpret_cast(new google_breakpad::ExceptionHandler(descriptor, NULL, dumpCallback, NULL, true, -1)); -} -#endif // USE_BREAKPAD diff --git a/src/platform/crash.cpp b/src/platform/crash.cpp new file mode 100644 index 0000000000..9f114b0080 --- /dev/null +++ b/src/platform/crash.cpp @@ -0,0 +1,105 @@ +extern "C" { +#include "platform.h" +#include "../localisation/language.h" +#include "../scenario.h" +} + +#include "crash.h" +#include + +#ifdef USE_BREAKPAD +#include + +#ifdef __WINDOWS__ +#include +#include +#include +#define BREAKPAD_PATH "." +#else +#error Breakpad support not implemented yet for this platform +#endif __WINDOWS__ + +#ifdef __LINUX__ +#include +#define BREAKPAD_PATH "/tmp" +#endif __LINUX__ + + +static bool dumpCallback(const wchar_t *dump_path, const wchar_t *minidump_id, void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion, bool succeeded) { + if (!succeeded) { + const char *msg = "Failed to create the dump. Nothing left to do. Please file an issue with OpenRCT2 on Github and provide latest save."; + log_fatal(msg); + MessageBoxA(NULL, msg, "OpenRCT2", MB_OK); + return succeeded; + } + char *buffer_path = widechar_to_utf8(dump_path); + char *buffer_minidump = widechar_to_utf8(minidump_id); + char dump_file_path[MAX_PATH]; + char save_file_path[MAX_PATH]; + sprintf(dump_file_path, "%s%s.dmp", buffer_path, buffer_minidump); + sprintf(save_file_path, "%s%s.sv6", buffer_path, buffer_minidump); + log_fatal("Dump path: %s", buffer_path); + log_fatal("minidump id: %s", buffer_minidump); + log_fatal("Version: %s", OPENRCT2_VERSION); + log_fatal("Commit: %s", OPENRCT2_COMMIT_SHA1_SHORT); + SDL_RWops* rw = SDL_RWFromFile(save_file_path, "wb+"); + bool saved = false; + if (rw != NULL) { + scenario_save(rw, 0x80000000); + saved = true; + SDL_RWclose(rw); + } + char message[MAX_PATH * 2]; + sprintf(message, "A crash has occurred and dump was created at %s. Please create an issue with OpenRCT2 on Github and provide the dump and %s. Version: %s, Commit: %s", + dump_file_path, saved ? save_file_path : "latest save", OPENRCT2_VERSION, OPENRCT2_COMMIT_SHA1_SHORT); + // Cannot use platform_show_messagebox here, it tries to set parent window already dead. + MessageBoxA(NULL, message, "OpenRCT2", MB_OK); + HRESULT coinit_result = CoInitialize(NULL); + if (coinit_result == S_OK) { + //ShellExecute(NULL, "explore", message, NULL, NULL, 5); + ITEMIDLIST *pidl = ILCreateFromPath(buffer_path); + ITEMIDLIST *files[2]; + files[0] = ILCreateFromPath(dump_file_path); + int files_count = 1; + if (saved) { + files[1] = ILCreateFromPath(save_file_path); + files_count = 2; + } + if (pidl) { + HRESULT res = SHOpenFolderAndSelectItems(pidl, files_count, (LPCITEMIDLIST *)files, 0); + ILFree(pidl); + ILFree(files[0]); + if (saved) { + ILFree(files[1]); + } + } + CoUninitialize(); + } + free(buffer_path); + free(buffer_minidump); + return succeeded; +} +#endif // USE_BREAKPAD + +extern "C" CExceptionHandler newCExceptionHandlerSimple(void) +{ +#ifdef USE_BREAKPAD + char path[MAX_PATH]; + platform_get_user_directory(path, NULL); + wchar_t *wpath_buffer = utf8_to_widechar(path); + std::wstring wpath(wpath_buffer); + free(wpath_buffer); + // Path must exist and be RW! + return reinterpret_cast(new google_breakpad::ExceptionHandler( + wpath, + 0, + dumpCallback, + 0, + google_breakpad::ExceptionHandler::HANDLER_ALL, + MiniDumpNormal, + L"openrct2-bpad", // using non-null pipe name here lets breakpad try setting OOP crash handling + 0 )); +#else // USE_BREAKPAD + return nullptr; +#endif // USE_BREAKPAD +} diff --git a/src/platform/breakpad.h b/src/platform/crash.h similarity index 53% rename from src/platform/breakpad.h rename to src/platform/crash.h index 31e358a4a5..0b9938a285 100644 --- a/src/platform/breakpad.h +++ b/src/platform/crash.h @@ -1,7 +1,6 @@ -#ifndef _OPENRCT2_BREAKPAD_ -#define _OPENRCT2_BREAKPAD_ +#ifndef _OPENRCT2_CRASH_ +#define _OPENRCT2_CRASH_ -#ifdef USE_BREAKPAD typedef void* CExceptionHandler; #ifdef __cplusplus @@ -12,6 +11,5 @@ CExceptionHandler newCExceptionHandlerSimple(void); #ifdef __cplusplus } #endif -#endif // USE_BREAKPAD -#endif /* _OPENRCT2_BREAKPAD_ */ +#endif /* _OPENRCT2_CRASH_ */ diff --git a/src/platform/shared.c b/src/platform/shared.c index 1a8bfc935f..8749755954 100644 --- a/src/platform/shared.c +++ b/src/platform/shared.c @@ -811,7 +811,7 @@ void platform_free() SDL_Quit(); } -void platform_start_text_input(char* buffer, int max_length) +void platform_start_text_input(utf8* buffer, int max_length) { // TODO This doesn't work, and position could be improved to where text entry is SDL_Rect rect = { 10, 10, 100, 100 }; diff --git a/src/platform/windows.c b/src/platform/windows.c index 2f57d3b2c7..deb82e5859 100644 --- a/src/platform/windows.c +++ b/src/platform/windows.c @@ -573,7 +573,7 @@ void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory) } } -void platform_show_messagebox(char *message) +void platform_show_messagebox(utf8 *message) { MessageBoxA(windows_get_window_handle(), message, "OpenRCT2", MB_OK); }