1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-06 06:32:56 +01:00

Merge pull request #3236 from janisozaur/breakpad

Implement crash / exception handling using Google Breakpad. This also includes updating the AppVeyor script to upload symbols.
This commit is contained in:
Ted John
2016-04-10 00:16:08 +01:00
16 changed files with 264 additions and 16 deletions

View File

@@ -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.

View File

@@ -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)

View File

@@ -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 = "<group>"; };
001085EE1C90FD030075A2AD /* textinputbuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = textinputbuffer.c; sourceTree = "<group>"; };
001085EF1C90FD030075A2AD /* textinputbuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = textinputbuffer.h; sourceTree = "<group>"; };
C62A08D41C787C2A00F3AA76 /* drawing_fast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drawing_fast.cpp; sourceTree = "<group>"; };
@@ -655,7 +657,8 @@
D4EC47711C26342F0024B507 /* title.c */,
D4EC47721C26342F0024B507 /* title.h */,
D4163F671C2A044D00B83136 /* version.h */,
);
a9793fe06a4244938f5d4b61 /* crash.cpp */,
);
name = Sources;
sourceTree = "<group>";
};
@@ -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 */

View File

@@ -28,3 +28,5 @@ artifacts:
name: OpenRCT2-portable
- path: .\artifacts\*.exe
name: OpenRCT2-installer
- path: .\artifacts\openrct2-symbols.zip
name: OpenRCT2 debug symbols

View File

@@ -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.

View File

@@ -87,6 +87,7 @@
<ClCompile Include="src\openrct2.c" />
<ClCompile Include="src\peep\peep.c" />
<ClCompile Include="src\peep\staff.c" />
<ClCompile Include="src\platform\crash.cpp" />
<ClCompile Include="src\platform\linux.c" />
<ClCompile Include="src\platform\posix.c" />
<ClCompile Include="src\platform\shared.c" />
@@ -261,6 +262,7 @@
<ClInclude Include="src\openrct2.h" />
<ClInclude Include="src\peep\peep.h" />
<ClInclude Include="src\peep\staff.h" />
<ClInclude Include="src\platform\crash.h" />
<ClInclude Include="src\platform\platform.h" />
<ClInclude Include="src\rct1.h" />
<ClInclude Include="src\rct2.h" />
@@ -329,20 +331,26 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(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)</IncludePath>
<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)</IncludePath>
<LibraryPath>$(SolutionDir)lib;$(LibraryPath)</LibraryPath>
<OutDir>$(SolutionDir)bin\</OutDir>
<IntDir>$(SolutionDir)obj\$(ProjectName)\$(Configuration)\</IntDir>
<LinkIncremental />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>$(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)</IncludePath>
<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)</IncludePath>
<LibraryPath>$(SolutionDir)lib;$(LibraryPath)</LibraryPath>
<OutDir>$(SolutionDir)bin\</OutDir>
<IntDir>$(SolutionDir)obj\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Breakpad)'=='true'">
<ClCompile>
<PreprocessorDefinitions>USE_BREAKPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<DisableSpecificWarnings>4091;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
@@ -363,6 +371,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<DisableSpecificWarnings>4091;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<WarningLevel>Level3</WarningLevel>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
@@ -388,4 +397,4 @@
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@@ -587,6 +587,7 @@
<ClCompile Include="src\core\textinputbuffer.c">
<Filter>Source\Core</Filter>
</ClCompile>
<ClCompile Include="src\platform\crash.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\management\award.h">
@@ -896,5 +897,6 @@
<ClInclude Include="src\core\textinputbuffer.h">
<Filter>Source\Core</Filter>
</ClInclude>
<ClInclude Include="src\platform\crash.h" />
</ItemGroup>
</Project>
</Project>

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -14,7 +14,8 @@ param (
[string]$GitSha1 = "",
[string]$GitSha1Short = "",
[bool] $CodeSign = $false,
[switch]$Installer = $false
[switch]$Installer = $false,
[switch]$Symbols = $false
)
if ($GitTag -eq "")
@@ -77,7 +78,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
@@ -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 }

View File

@@ -33,6 +33,7 @@
#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"
@@ -184,6 +185,8 @@ bool openrct2_initialise()
return false;
}
crash_init();
if (!openrct2_setup_rct2_segment()) {
log_fatal("Unable to load RCT2 data sector");
return false;

135
src/platform/crash.cpp Normal file
View File

@@ -0,0 +1,135 @@
#include <SDL_platform.h>
#include "crash.h"
#ifdef USE_BREAKPAD
#include <stdio.h>
#if defined(__WINDOWS__)
#include <breakpad/client/windows/handler/exception_handler.h>
#include <string>
#include <ShlObj.h>
#elif defined(__LINUX__)
#include <breakpad/client/linux/handler/exception_handler.h>
#define BREAKPAD_PATH "/tmp"
#else
#error Breakpad support not implemented yet for this platform
#endif
extern "C" {
#include "../localisation/language.h"
#include "../scenario.h"
#include "platform.h"
}
#include "../core/Console.hpp"
#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
// 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
// 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<CExceptionHandler>(exHandler);
#else // USE_BREAKPAD
return nullptr;
#endif // USE_BREAKPAD
}

15
src/platform/crash.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef _OPENRCT2_CRASH_
#define _OPENRCT2_CRASH_
typedef void * CExceptionHandler;
#ifdef __cplusplus
extern "C"
{
#endif
CExceptionHandler crash_init();
#ifdef __cplusplus
}
#endif
#endif /* _OPENRCT2_CRASH_ */

View File

@@ -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 };

View File

@@ -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);
}