From e28de49a34837823617e58a566b3a4b06706ca0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sun, 3 Apr 2016 22:41:24 +0200 Subject: [PATCH 1/9] Breakpad integration Provide your own breakpad and point cmake at it. git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git mkdir breakpad && cd breakpad ../depot_tools/fetch breakpad # edit the script to use python2 cd src LDFLAGS="-m32" CXXFLAGS="-m32" CFLAGS="-m32" CPPFLAGS="-m32" ./configure LDFLAGS="-m32" CXXFLAGS="-m32" CFLAGS="-m32" CPPFLAGS="-m32" make -j 8 Run cmake on openrct2: cd openrct2 vim CMakeLists.txt # provide your path to breakpad mkdir build && cd build cmake ../ -DWITH_BREAKPAD=ON make -j 8 ./openrct2 # open a game, select "about openrct2", crash --- CMakeLists.txt | 16 +++++++++++++--- src/openrct2.c | 7 +++++++ src/platform/breakpad.cpp | 19 +++++++++++++++++++ src/platform/breakpad.h | 17 +++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/platform/breakpad.cpp create mode 100644 src/platform/breakpad.h diff --git a/CMakeLists.txt b/CMakeLists.txt index caed4dda44..c087e91808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,6 +143,16 @@ if (STATIC) endif (WIN32) endif () +option(WITH_BREAKPAD "Enable breakpad") +if (WITH_BREAKPAD) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_BREAKPAD") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_BREAKPAD") + set(BREAKPAD_DIR "/home/janisozaur/workspace/breakpad/src") + set(BREAKPAD_INCLUDE_DIR "${BREAKPAD_DIR}/src") + set(BREAKPAD_LIBRARY_DIR "${BREAKPAD_DIR}/src/client/linux") + set(BREAKPAD_LIBS breakpad_client pthread) +endif (WITH_BREAKPAD) + # find and include SDL2 PKG_CHECK_MODULES(SDL2 REQUIRED sdl2 SDL2_ttf) if (STATIC) @@ -180,9 +190,9 @@ if (UNIX) set(DLLIB dl) endif (UNIX) -INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS} ${LIBCURL_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} ${SPEEX_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS} ${LIBCURL_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} ${SPEEX_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIR}) -LINK_DIRECTORIES(${SDL2_LIBRARY_DIRS} ${JANSSON_LIBRARY_DIRS} ${LIBCURL_LIBRARY_DIRS} ${PNG_LIBRARY_DIRS} ${ZLIB_LIBRARY_DIRS}) +LINK_DIRECTORIES(${SDL2_LIBRARY_DIRS} ${JANSSON_LIBRARY_DIRS} ${LIBCURL_LIBRARY_DIRS} ${PNG_LIBRARY_DIRS} ${ZLIB_LIBRARY_DIRS} ${BREAKPAD_LIBRARY_DIR}) if (WIN32) # build as library for now, replace with add_executable @@ -215,7 +225,7 @@ endif (UNIX AND NOT APPLE) # libopenrct2.dll -> openrct2.dll set_target_properties(${PROJECT} PROPERTIES PREFIX "") -TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2LIBS} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES} ${DLLIB} ${REQUIREDLIBS}) +TARGET_LINK_LIBRARIES(${PROJECT} ${SDL2LIBS} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES} ${DLLIB} ${REQUIREDLIBS} ${BREAKPAD_LIBS}) if (APPLE OR STATIC) FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c) diff --git a/src/openrct2.c b/src/openrct2.c index 87ee82cc9b..b5c6648374 100644 --- a/src/openrct2.c +++ b/src/openrct2.c @@ -39,6 +39,7 @@ #include "util/sawyercoding.h" #include "util/util.h" #include "world/mapgen.h" +#include "platform/breakpad.h" #if defined(__unix__) #include @@ -263,6 +264,12 @@ 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 new file mode 100644 index 0000000000..a4241019cf --- /dev/null +++ b/src/platform/breakpad.cpp @@ -0,0 +1,19 @@ +#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/breakpad.h b/src/platform/breakpad.h new file mode 100644 index 0000000000..31e358a4a5 --- /dev/null +++ b/src/platform/breakpad.h @@ -0,0 +1,17 @@ +#ifndef _OPENRCT2_BREAKPAD_ +#define _OPENRCT2_BREAKPAD_ + +#ifdef USE_BREAKPAD +typedef void* CExceptionHandler; + +#ifdef __cplusplus +extern "C" +{ +#endif +CExceptionHandler newCExceptionHandlerSimple(void); +#ifdef __cplusplus +} +#endif +#endif // USE_BREAKPAD + +#endif /* _OPENRCT2_BREAKPAD_ */ 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 2/9] 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); } From d59a010b00095b8e9fb908fd653bb981f279cb82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 9 Apr 2016 18:55:59 +0200 Subject: [PATCH 3/9] Add new files to xcodeproj --- OpenRCT2.xcodeproj/project.pbxproj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 72ec93e595..fa60aa53af 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 426da7e58564472ba1b7991f /* crash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = a9793fe06a4244938f5d4b61 /* crash.cpp */; }; 001085F01C90FD030075A2AD /* textinputbuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 001085EE1C90FD030075A2AD /* textinputbuffer.c */; }; C62A08D51C787C2A00F3AA76 /* drawing_fast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C62A08D41C787C2A00F3AA76 /* drawing_fast.cpp */; }; D41B73EF1C2101890080A7B9 /* libcurl.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B73EE1C2101890080A7B9 /* libcurl.tbd */; }; @@ -222,6 +223,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + a9793fe06a4244938f5d4b61 /* crash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash.cpp; path = src/platform/crash.cpp; sourceTree = ""; }; 001085EE1C90FD030075A2AD /* textinputbuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = textinputbuffer.c; sourceTree = ""; }; 001085EF1C90FD030075A2AD /* textinputbuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = textinputbuffer.h; sourceTree = ""; }; C62A08D41C787C2A00F3AA76 /* drawing_fast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drawing_fast.cpp; sourceTree = ""; }; @@ -655,7 +657,8 @@ D4EC47711C26342F0024B507 /* title.c */, D4EC47721C26342F0024B507 /* title.h */, D4163F671C2A044D00B83136 /* version.h */, - ); + a9793fe06a4244938f5d4b61 /* crash.cpp */, +); name = Sources; sourceTree = ""; }; @@ -1505,7 +1508,8 @@ D4EC48721C26342F0024B507 /* balloon.c in Sources */, D4EC48571C26342F0024B507 /* save_prompt.c in Sources */, D4EC48701C26342F0024B507 /* viewport.c in Sources */, - ); + 426da7e58564472ba1b7991f /* crash.cpp in Sources */, +); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ From 46fb82fd91095037e78fc9b5b4e4e322614ca480 Mon Sep 17 00:00:00 2001 From: Hielke Morsink <123mannetje@gmail.com> Date: Sat, 9 Apr 2016 19:21:40 +0200 Subject: [PATCH 4/9] Fix interface redifinition warning. --- src/platform/crash.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/platform/crash.cpp b/src/platform/crash.cpp index 9f114b0080..5cfd0e3ff4 100644 --- a/src/platform/crash.cpp +++ b/src/platform/crash.cpp @@ -1,9 +1,3 @@ -extern "C" { -#include "platform.h" -#include "../localisation/language.h" -#include "../scenario.h" -} - #include "crash.h" #include @@ -24,6 +18,12 @@ extern "C" { #define BREAKPAD_PATH "/tmp" #endif __LINUX__ +extern "C" { +#include "platform.h" +#include "../localisation/language.h" +#include "../scenario.h" +} + static bool dumpCallback(const wchar_t *dump_path, const wchar_t *minidump_id, void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion, bool succeeded) { if (!succeeded) { From 736fdc2f3356778c4a959717bfc3b2c5c3ede09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 9 Apr 2016 19:59:45 +0200 Subject: [PATCH 5/9] Enable breakpad on Appveyor --- openrct2.vcxproj | 5 +++++ scripts/ps/build.ps1 | 7 +++++-- scripts/ps/publish.ps1 | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/openrct2.vcxproj b/openrct2.vcxproj index db5e3b08b4..b616449867 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -343,6 +343,11 @@ $(SolutionDir)bin\ $(SolutionDir)obj\$(ProjectName)\$(Configuration)\ + + + USE_BREAKPAD;%(PreprocessorDefinitions) + + 4091;%(DisableSpecificWarnings) diff --git a/scripts/ps/build.ps1 b/scripts/ps/build.ps1 index 027985eb0b..99a188f97a 100644 --- a/scripts/ps/build.ps1 +++ b/scripts/ps/build.ps1 @@ -10,7 +10,10 @@ param ( [string]$Configuration = "Release", [Parameter(Mandatory = $false)] - [switch]$Rebuild = $false + [switch]$Rebuild = $false, + + [Parameter(Mandatory = $false)] + [switch]$Breakpad = $false ) # Setup @@ -40,7 +43,7 @@ function Build-OpenRCT2() { $target = "/t:rebuild" } - msbuild $rootPath\openrct2.sln /p:Configuration=$Configuration /p:Platform=Win32 $target /v:minimal | Write-Host + msbuild $rootPath\openrct2.sln /p:Breakpad=$Breakpad /p:Configuration=$Configuration /p:Platform=Win32 $target /v:minimal | Write-Host return $LASTEXITCODE } diff --git a/scripts/ps/publish.ps1 b/scripts/ps/publish.ps1 index a0e2a3532c..5fa9f27952 100644 --- a/scripts/ps/publish.ps1 +++ b/scripts/ps/publish.ps1 @@ -77,7 +77,7 @@ function Do-PrepareSource() function Do-Build() { Write-Host "Building OpenRCT2..." -ForegroundColor Cyan - & "$scriptsPath\build.ps1" all -Rebuild + & "$scriptsPath\build.ps1" all -Rebuild -Breakpad if ($LASTEXITCODE -ne 0) { Write-Host "Failed to build OpenRCT2" -ForegroundColor Red From 9e2610fc08782cd59037542a3bf5e1660c16418b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 9 Apr 2016 20:47:31 +0200 Subject: [PATCH 6/9] Switch dump format to MiniDumpWithDataSegs --- src/platform/crash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/crash.cpp b/src/platform/crash.cpp index 5cfd0e3ff4..188abc64b0 100644 --- a/src/platform/crash.cpp +++ b/src/platform/crash.cpp @@ -96,7 +96,7 @@ extern "C" CExceptionHandler newCExceptionHandlerSimple(void) dumpCallback, 0, google_breakpad::ExceptionHandler::HANDLER_ALL, - MiniDumpNormal, + MiniDumpWithDataSegs, L"openrct2-bpad", // using non-null pipe name here lets breakpad try setting OOP crash handling 0 )); #else // USE_BREAKPAD From a9983b5cd8de952dbf89f352f9c3b1e00de87a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 9 Apr 2016 21:37:27 +0200 Subject: [PATCH 7/9] Make Appveyor publish zipped symbols --- appveyor.yml | 2 ++ scripts/ps/appveyor_run.ps1 | 24 +++++++++++++++++++++++ scripts/ps/publish.ps1 | 39 ++++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 2a883cc798..f756ad5db5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,3 +28,5 @@ artifacts: name: OpenRCT2-portable - path: .\artifacts\*.exe name: OpenRCT2-installer +- path: .\artifacts\openrct2-symbols.zip + name: OpenRCT2 debug symbols diff --git a/scripts/ps/appveyor_run.ps1 b/scripts/ps/appveyor_run.ps1 index baab8d99bb..a063aedda2 100644 --- a/scripts/ps/appveyor_run.ps1 +++ b/scripts/ps/appveyor_run.ps1 @@ -40,10 +40,12 @@ if (${env:CODE-SIGN-KEY-OPENRCT2.ORG.PFX.PASSWORD} -ne $null) # Enable pushing builds to OpenRCT2.org if token environment variable is set $pushBuilds = $false $installer = $false +$symbols = $false if (${env:OPENRCT2.ORG_TOKEN} -ne $null) { $pushBuilds = $true $installer = $true + $symbols = $true } # Write out summary of the build @@ -98,6 +100,18 @@ if ($installer) -CodeSign $codeSign } +if ($symbols) +{ + publish package ` + -Symbols ` + -Server $server ` + -GitTag $tag ` + -GitBranch $env:APPVEYOR_REPO_BRANCH ` + -GitSha1 $env:APPVEYOR_REPO_COMMIT ` + -GitSha1Short $env:APPVEYOR_REPO_COMMIT_SHORT ` + -CodeSign $codeSign +} + if ($pushBuilds) { $versionExtension = "" @@ -127,4 +141,14 @@ if ($pushBuilds) -version $version ` -flavourId 2 } + + # Push symbols + if ($symbols) + { + Write-Host "Sending symbols to OpenRCT2.org" -ForegroundColor Cyan + Push-Build -file ".\artifacts\openrct2-symbols.zip" ` + -name "$pushFileName-symbols.zip" ` + -version $version ` + -flavourId 5 + } } diff --git a/scripts/ps/publish.ps1 b/scripts/ps/publish.ps1 index 5fa9f27952..05b94deddf 100644 --- a/scripts/ps/publish.ps1 +++ b/scripts/ps/publish.ps1 @@ -14,7 +14,8 @@ param ( [string]$GitSha1 = "", [string]$GitSha1Short = "", [bool] $CodeSign = $false, - [switch]$Installer = $false + [switch]$Installer = $false, + [switch]$Symbols = $false ) if ($GitTag -eq "") @@ -97,6 +98,38 @@ function Do-Build() return 0 } +# Symbols +function Do-Symbols() +{ + Write-Host "Publishing OpenRCT2 debug symbols as zip..." -ForegroundColor Cyan + $artifactsDir = "$rootPath\artifacts" + $releaseDir = "$rootPath\bin" + $outZip = "$rootPath\artifacts\openrct2-symbols.zip" + + Copy-Item -Force "$releaseDir\openrct2.pdb" $artifactsDir -ErrorAction Stop + + # Create archive using 7z (renowned for speed and compression) + $7zcmd = "7za" + if (-not (AppExists($7zcmd))) + { + # AppVeyor in particular uses '7z' instead + $7zcmd = "7z" + if (-not (AppExists($7zcmd))) + { + Write-Host "Publish script requires 7z to be in PATH" -ForegroundColor Red + return 1 + } + } + & $7zcmd a -tzip -mx9 $outZip "$artifactsDir\openrct2.pdb" > $null + if ($LASTEXITCODE -ne 0) + { + Write-Host "Failed to create zip." -ForegroundColor Red + return 1 + } + Remove-Item -Force -Recurse "$artifactsDir\openrct2.pdb" -ErrorAction SilentlyContinue + return 0 +} + # Package function Do-Package() { @@ -209,6 +242,10 @@ function Do-Task-Package() { if (($result = (Do-Installer)) -ne 0) { return $result } } + elseif ($Symbols) + { + if (($result = (Do-Symbols)) -ne 0) { return $result } + } else { if (($result = (Do-Package)) -ne 0) { return $result } From 9bb9e1af82904a18dd00a3bd3968dd0f186ab02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 9 Apr 2016 22:11:32 +0200 Subject: [PATCH 8/9] Update changelog & issue template with breakpad entry [ci skip] --- .github/ISSUE_TEMPLATE.md | 3 +++ distribution/changelog.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 061cc965c1..0f27246e28 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -11,6 +11,9 @@ Explanation of the issue... 1. 2. +**Dump file** +If you have a dump file, zip it and drag&drop it here. + **Screenshots / Video:** Drag / drop screenshots here. Use https://vid.me to upload video. diff --git a/distribution/changelog.txt b/distribution/changelog.txt index ea95023887..cd8a212060 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -38,6 +38,7 @@ - Technical: lodepng dropped in return for libpng. - Technical: SDL2 upgraded from 2.0.3 to 2.0.4. - Technical: argparse dropped in return for bespoke command line parsing implementation. +- Technical: Integrated breakpad for (manual) crash reporting - Improve: performance of rendering, particularly for highly populated parks. - Improve: performance of loading parks. - Improve: support for hacked parks. From 51a7e3f278b34f5daab65fcb426943189c3eaa3e Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sat, 9 Apr 2016 23:30:10 +0100 Subject: [PATCH 9/9] clean up and refactor the crash handler --- src/openrct2.c | 8 +- src/platform/crash.cpp | 202 +++++++++++++++++++++++------------------ src/platform/crash.h | 4 +- 3 files changed, 120 insertions(+), 94 deletions(-) diff --git a/src/openrct2.c b/src/openrct2.c index 5d9bf0973e..6f919e89d3 100644 --- a/src/openrct2.c +++ b/src/openrct2.c @@ -33,13 +33,13 @@ #include "network/http.h" #include "network/network.h" #include "openrct2.h" +#include "platform/crash.h" #include "platform/platform.h" #include "ride/ride.h" #include "title.h" #include "util/sawyercoding.h" #include "util/util.h" #include "world/mapgen.h" -#include "platform/crash.h" #if defined(__unix__) #include @@ -185,11 +185,7 @@ bool openrct2_initialise() return false; } - // Exception handling - breakpad - // Uses user data directory for storing dumps - CExceptionHandler eh; - // never free - eh = newCExceptionHandlerSimple(); + crash_init(); if (!openrct2_setup_rct2_segment()) { log_fatal("Unable to load RCT2 data sector"); diff --git a/src/platform/crash.cpp b/src/platform/crash.cpp index 188abc64b0..7503089ceb 100644 --- a/src/platform/crash.cpp +++ b/src/platform/crash.cpp @@ -1,105 +1,135 @@ -#include "crash.h" #include +#include "crash.h" #ifdef USE_BREAKPAD #include -#ifdef __WINDOWS__ -#include -#include -#include -#define BREAKPAD_PATH "." +#if defined(__WINDOWS__) + #include + #include + #include +#elif defined(__LINUX__) + #include + #define BREAKPAD_PATH "/tmp" #else -#error Breakpad support not implemented yet for this platform -#endif __WINDOWS__ - -#ifdef __LINUX__ -#include -#define BREAKPAD_PATH "/tmp" -#endif __LINUX__ + #error Breakpad support not implemented yet for this platform +#endif extern "C" { -#include "platform.h" -#include "../localisation/language.h" -#include "../scenario.h" + #include "../localisation/language.h" + #include "../scenario.h" + #include "platform.h" } +#include "../core/Console.hpp" -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; +#define WSZ(x) L"" x + +static bool OnCrash(const wchar_t * dumpPath, + const wchar_t * miniDumpId, + void * context, + EXCEPTION_POINTERS * exinfo, + MDRawAssertionInfo * assertion, + bool succeeded) +{ + if (!succeeded) + { + constexpr char * DumpFailedMessage = "Failed to create the dump. Nothing left to do. Please file an issue with OpenRCT2 on Github and provide latest save."; + printf("%s\n", DumpFailedMessage); + MessageBoxA(NULL, DumpFailedMessage, OPENRCT2_NAME, MB_OK | MB_ICONERROR); + return succeeded; + } + + wchar_t dumpFilePath[MAX_PATH]; + wchar_t saveFilePath[MAX_PATH]; + wsprintfW(dumpFilePath, L"%s%s.dmp", dumpPath, miniDumpId); + wsprintfW(saveFilePath, L"%s%s.sv6", dumpPath, miniDumpId); + + wprintf(L"Dump Path: %s\n", dumpFilePath); + wprintf(L"Dump Id: %s\n", miniDumpId); + wprintf(L"Version: %s\n", WSZ(OPENRCT2_VERSION)); + wprintf(L"Commit: %s\n", WSZ(OPENRCT2_COMMIT_SHA1_SHORT)); + + utf8 * saveFilePathUTF8 = widechar_to_utf8(saveFilePath); + SDL_RWops * rw = SDL_RWFromFile(saveFilePathUTF8, "wb+"); + free(saveFilePathUTF8); + + bool savedGameDumped = false; + if (rw != NULL) { + scenario_save(rw, 0x80000000); + savedGameDumped = true; + SDL_RWclose(rw); + } + + constexpr wchar_t * MessageFormat = L"A crash has occurred and dump was created at\n%s.\n\nPlease create an issue with OpenRCT2 on Github and provide the dump and save.\n\nVersion: %s\nCommit: %s"; + wchar_t message[MAX_PATH * 2]; + swprintf_s(message, + MessageFormat, + dumpFilePath, + WSZ(OPENRCT2_VERSION), + WSZ(OPENRCT2_COMMIT_SHA1_SHORT)); + + // Cannot use platform_show_messagebox here, it tries to set parent window already dead. + MessageBoxW(NULL, message, WSZ(OPENRCT2_NAME), MB_OK | MB_ICONERROR); + HRESULT coInitializeResult = CoInitialize(NULL); + if (SUCCEEDED(coInitializeResult)) + { + ITEMIDLIST * pidl = ILCreateFromPathW(dumpPath); + ITEMIDLIST * files[2]; + uint32 numFiles = 0; + + files[numFiles++] = ILCreateFromPathW(dumpFilePath); + if (savedGameDumped) + { + files[numFiles++] = ILCreateFromPathW(saveFilePath); + } + if (pidl != nullptr) { + HRESULT result = SHOpenFolderAndSelectItems(pidl, numFiles, (LPCITEMIDLIST *)files, 0); + ILFree(pidl); + for (uint32 i = 0; i < numFiles; i++) + { + ILFree(files[i]); + } + } + CoUninitialize(); + } + + // Return whether the dump was successful + return succeeded; } + +static std::wstring GetDumpDirectory() +{ + char userDirectory[MAX_PATH]; + platform_get_user_directory(userDirectory, NULL); + + wchar_t * userDirectoryW = utf8_to_widechar(userDirectory); + auto result = std::wstring(userDirectoryW); + free(userDirectoryW); + + return result; +} + #endif // USE_BREAKPAD -extern "C" CExceptionHandler newCExceptionHandlerSimple(void) +// Using non-null pipe name here lets breakpad try setting OOP crash handling +constexpr wchar_t * PipeName = L"openrct2-bpad"; + +extern "C" CExceptionHandler crash_init() { #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, - MiniDumpWithDataSegs, - L"openrct2-bpad", // using non-null pipe name here lets breakpad try setting OOP crash handling - 0 )); + // Path must exist and be RW! + auto exHandler = new google_breakpad::ExceptionHandler( + GetDumpDirectory(), + 0, + OnCrash, + 0, + google_breakpad::ExceptionHandler::HANDLER_ALL, + MiniDumpWithDataSegs, + PipeName, + 0); + return reinterpret_cast(exHandler); #else // USE_BREAKPAD - return nullptr; + return nullptr; #endif // USE_BREAKPAD } diff --git a/src/platform/crash.h b/src/platform/crash.h index 0b9938a285..35c0d1f74b 100644 --- a/src/platform/crash.h +++ b/src/platform/crash.h @@ -1,13 +1,13 @@ #ifndef _OPENRCT2_CRASH_ #define _OPENRCT2_CRASH_ -typedef void* CExceptionHandler; +typedef void * CExceptionHandler; #ifdef __cplusplus extern "C" { #endif -CExceptionHandler newCExceptionHandlerSimple(void); + CExceptionHandler crash_init(); #ifdef __cplusplus } #endif