Skip to content

Commit

Permalink
Merge pull request OpenRCT2#8073 from janisozaur/breakpad-upload
Browse files Browse the repository at this point in the history
Auto-upload crashes to backtrace.io
  • Loading branch information
janisozaur authored Oct 26, 2018
2 parents 2fccd4c + 76b6bd8 commit 80b7575
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 13 deletions.
2 changes: 1 addition & 1 deletion openrct2.common.props
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<AdditionalOptions>/utf-8 /std:c++17 /permissive- /Zc:externConstexpr</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>wininet.lib;imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/OPT:NOLBR /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
Expand Down
8 changes: 4 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ We also need need scenarios to distribute with the game, when the time comes. Fo

Companies that kindly allow us to use their stuff:

| DigitalOcean | JetBrains | AppVeyor | Travis-CI |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [![do_logo_vertical_blue svg](https://user-images.githubusercontent.com/550290/36508276-8b572f0e-175c-11e8-8622-9febbce756b2.png)](https://www.digitalocean.com/) | [![jetbrains](https://user-images.githubusercontent.com/550290/36413299-0e0985ea-161e-11e8-8a01-3ef523b5905b.png)](https://www.jetbrains.com/) | [![AppVeyor](https://user-images.githubusercontent.com/550290/36508339-be413216-175c-11e8-97d8-760ced0931e8.png)](https://www.appveyor.com/) | [![Travis](https://raw.githubusercontent.com/travis-ci/docs-travis-ci-com/4b14eeab25ce8ca9164e177bfb60782a8535a822/images/travis-mascot-200px.png)](https://travis-ci.org/) |
| Hosting of various services | CLion and other products | MSVC CI | Linux + macOS CI |
| [DigitalOcean](https://www.digitalocean.com/) | [JetBrains](https://www.jetbrains.com/) | [AppVeyor](https://www.appveyor.com/) | [Travis-CI](https://travis-ci.org/) | [Backtrace](https://backtrace.io/) |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|
| [![do_logo_vertical_blue svg](https://user-images.githubusercontent.com/550290/36508276-8b572f0e-175c-11e8-8622-9febbce756b2.png)](https://www.digitalocean.com/) | [![jetbrains](https://user-images.githubusercontent.com/550290/36413299-0e0985ea-161e-11e8-8a01-3ef523b5905b.png)](https://www.jetbrains.com/) | [![AppVeyor](https://user-images.githubusercontent.com/550290/36508339-be413216-175c-11e8-97d8-760ced0931e8.png)](https://www.appveyor.com/) | [![Travis](https://raw.githubusercontent.com/travis-ci/docs-travis-ci-com/4b14eeab25ce8ca9164e177bfb60782a8535a822/images/travis-mascot-200px.png)](https://travis-ci.org/) | [![backtrace](https://user-images.githubusercontent.com/550290/47113259-d0647680-d258-11e8-97c3-1a2c6bde6d11.png)](https://backtrace.io/) |
| Hosting of various services | CLion and other products | MSVC CI | Linux + macOS CI | Minidump uploads and inspection |
5 changes: 5 additions & 0 deletions scripts/ps/appveyor_deploy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ if ($nottesting -and $notvs2015)
if (${env:APPVEYOR_REPO_TAG} -eq "true" -or ${env:APPVEYOR_REPO_BRANCH} -match "^develop$|^push/")
{
msbuild openrct2.proj /t:UploadArtifacts /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
# curl is sometimes aliased so be explicit
curl.exe --data-binary @bin/openrct2-dll.pdb 'https://openrct2.sp.backtrace.io:6098/post?format=symbols&token=e9e6d681fafdeac9f6131b4b59a155d54bebad567a8c0380d70643f4414819f5&upload_file=openrct2-dll.pdb'
curl.exe --data-binary @bin/openrct2-win.pdb 'https://openrct2.sp.backtrace.io:6098/post?format=symbols&token=e9e6d681fafdeac9f6131b4b59a155d54bebad567a8c0380d70643f4414819f5&upload_file=openrct2-win.pdb'
curl.exe --data-binary @bin/openrct2.dll 'https://openrct2.sp.backtrace.io:6098/post?format=symbols&token=e9e6d681fafdeac9f6131b4b59a155d54bebad567a8c0380d70643f4414819f5&upload_file=openrct2.dll'
curl.exe --data-binary @bin/openrct2.exe 'https://openrct2.sp.backtrace.io:6098/post?format=symbols&token=e9e6d681fafdeac9f6131b4b59a155d54bebad567a8c0380d70643f4414819f5&upload_file=openrct2.exe'
}
else
{
Expand Down
94 changes: 87 additions & 7 deletions src/openrct2/platform/Crash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,26 @@
#include "Crash.h"

#ifdef USE_BREAKPAD
# include <map>
# include <memory>
# include <stdio.h>

# if defined(_WIN32)
# include <ShlObj.h>
# include <client/windows/handler/exception_handler.h>
# include <common/windows/http_upload.h>
# include <string>
# else
# error Breakpad support not implemented yet for this platform
# endif

# include "../Version.h"
# include "../core/Console.hpp"
# include "../core/String.hpp"
# include "../localisation/Language.h"
# include "../rct2/S6Exporter.h"
# include "../scenario/Scenario.h"
# include "../util/Util.h"
# include "platform.h"

# define WSZ(x) L"" x
Expand All @@ -39,6 +43,34 @@ const wchar_t* _wszCommitSha1Short = WSZ("");
// OPENRCT2_ARCHITECTURE is required to be defined in version.h
const wchar_t* _wszArchitecture = WSZ(OPENRCT2_ARCHITECTURE);

// Note: uploading gzipped crash dumps manually requires specifying
// 'Content-Encoding: gzip' header in HTTP request, but we cannot do that,
// so just hope the file name with '.gz' suffix is enough.
// For docs on uplading to backtrace.io check
// https://documentation.backtrace.io/product_integration_minidump_breakpad/
static bool UploadMinidump(const wchar_t* dumpPath, int& error, std::wstring& response)
{
std::wstring url(L"https://openrct2.sp.backtrace.io:6098/"
L"post?format=minidump&token=f9c5e640d498f15dbe902eab3e822e472af9270d5b0cbdc269cee65a926bf306");
std::map<std::wstring, std::wstring> parameters;
std::map<std::wstring, std::wstring> files;
parameters[L"product_name"] = L"openrct2";
// In case of releases this can be empty
if (wcslen(_wszCommitSha1Short) > 0)
{
parameters[L"commit"] = _wszCommitSha1Short;
}
else
{
parameters[L"commit"] = String::ToUtf16(gVersionInfoFull);
}
files[L"upload_file_minidump"] = dumpPath;
int timeout = 10000;
bool success = google_breakpad::HTTPUpload::SendRequest(url, parameters, files, &timeout, &response, &error);
wprintf(L"Success = %d, error = %d, response = %s\n", success, error, response.c_str());
return success;
}

static bool OnCrash(
const wchar_t* dumpPath, const wchar_t* miniDumpId, void* context, EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion, bool succeeded)
Expand All @@ -59,19 +91,39 @@ static bool OnCrash(
// Get filenames
wchar_t dumpFilePath[MAX_PATH];
wchar_t saveFilePath[MAX_PATH];
swprintf_s(dumpFilePath, sizeof(dumpFilePath), L"%s/%s.dmp", dumpPath, miniDumpId);
swprintf_s(saveFilePath, sizeof(saveFilePath), L"%s/%s.sv6", dumpPath, miniDumpId);
swprintf_s(dumpFilePath, sizeof(dumpFilePath), L"%s\\%s.dmp", dumpPath, miniDumpId);
swprintf_s(saveFilePath, sizeof(saveFilePath), L"%s\\%s.sv6", dumpPath, miniDumpId);
const wchar_t* minidumpToUpload = dumpFilePath;

// Try to rename the files
wchar_t dumpFilePathNew[MAX_PATH];
swprintf_s(
dumpFilePathNew, sizeof(dumpFilePathNew), L"%s/%s(%s_%s).dmp", dumpPath, miniDumpId, _wszCommitSha1Short,
dumpFilePathNew, sizeof(dumpFilePathNew), L"%s\\%s(%s_%s).dmp", dumpPath, miniDumpId, _wszCommitSha1Short,
_wszArchitecture);

wchar_t dumpFilePathGZIP[MAX_PATH];
swprintf_s(dumpFilePathGZIP, sizeof(dumpFilePathGZIP), L"%s.gz", dumpFilePathNew);

// TODO: enable gzip compression once supported on backtrace.io
/*
FILE* input = _wfopen(dumpFilePath, L"rb");
FILE* dest = _wfopen(dumpFilePathGZIP, L"wb");
if (util_gzip_compress(input, dest))
{
minidumpToUpload = dumpFilePathGZIP;
}
fclose(input);
fclose(dest);
*/

// Try to rename the files
if (_wrename(dumpFilePath, dumpFilePathNew) == 0)
{
std::wcscpy(dumpFilePath, dumpFilePathNew);
}

// Compress to gzip-compatible stream

// Log information to output
wprintf(L"Dump Path: %s\n", dumpPath);
wprintf(L"Dump File Path: %s\n", dumpFilePath);
Expand All @@ -95,25 +147,53 @@ static bool OnCrash(

if (gOpenRCT2SilentBreakpad)
{
int error;
std::wstring response;
UploadMinidump(minidumpToUpload, error, response);
return succeeded;
}

constexpr const wchar_t* MessageFormat = L"A crash has occurred and a dump was created at\n%s.\n\nPlease file an issue "
L"with OpenRCT2 on GitHub, and provide "
L"the dump and saved game there.\n\nVersion: %s\nCommit: %s";
L"the dump and saved game there.\n\nVersion: %s\nCommit: %s\n\n"
L"We would like to upload the crash dump for automated analysis, do you agree?\n"
L"The automated analysis is done by courtesy of https://backtrace.io/";
wchar_t message[MAX_PATH * 2];
swprintf_s(message, MessageFormat, dumpFilePath, WSZ(OPENRCT2_VERSION), _wszCommitSha1Short);

// Cannot use platform_show_messagebox here, it tries to set parent window already dead.
MessageBoxW(nullptr, message, WSZ(OPENRCT2_NAME), MB_OK | MB_ICONERROR);
int answer = MessageBoxW(nullptr, message, WSZ(OPENRCT2_NAME), MB_YESNO | MB_ICONERROR);
if (answer == IDYES)
{
int error;
std::wstring response;
bool ok = UploadMinidump(minidumpToUpload, error, response);
if (!ok)
{
const wchar_t* MessageFormat2 = L"There was a problem while uploading the dump. Please upload it manually to "
L"GitHub. It should be highlighted for you once you close this message.\n"
L"Please provide following information as well:\n"
L"Error code = %d\n"
L"Response = %s";
swprintf_s(message, MessageFormat2, error, response.c_str());
MessageBoxW(nullptr, message, WSZ(OPENRCT2_NAME), MB_OK | MB_ICONERROR);
}
else
{
MessageBoxW(nullptr, L"Dump uploaded succesfully.", WSZ(OPENRCT2_NAME), MB_OK | MB_ICONINFORMATION);
}
}
HRESULT coInitializeResult = CoInitialize(nullptr);
if (SUCCEEDED(coInitializeResult))
{
LPITEMIDLIST pidl = ILCreateFromPathW(dumpPath);
LPITEMIDLIST files[2];
LPITEMIDLIST files[3];
uint32_t numFiles = 0;

files[numFiles++] = ILCreateFromPathW(dumpFilePath);
// There should be no need to check if this file exists, if it doesn't
// it simply shouldn't get selected.
files[numFiles++] = ILCreateFromPathW(dumpFilePathGZIP);
if (savedGameDumped)
{
files[numFiles++] = ILCreateFromPathW(saveFilePath);
Expand Down
58 changes: 58 additions & 0 deletions src/openrct2/util/Util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,64 @@ uint8_t* util_zlib_deflate(const uint8_t* data, size_t data_in_size, size_t* dat
return buffer;
}

// Compress the source to gzip-compatible stream, write to dest.
// Mainly used for compressing the crashdumps
bool util_gzip_compress(FILE* source, FILE* dest)
{
if (source == nullptr || dest == nullptr)
{
return false;
}
int ret, flush;
size_t have;
z_stream strm{};
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
int windowBits = 15;
int GZIP_ENCODING = 16;
ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits | GZIP_ENCODING, 8, Z_DEFAULT_STRATEGY);
if (ret != Z_OK)
{
log_error("Failed to initialise stream");
return false;
}
do
{
strm.avail_in = uInt(fread(in, 1, CHUNK, source));
if (ferror(source))
{
deflateEnd(&strm);
log_error("Failed to read data from source");
return false;
}
flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
strm.next_in = in;
do
{
strm.avail_out = CHUNK;
strm.next_out = out;
ret = deflate(&strm, flush);
if (ret == Z_STREAM_ERROR)
{
log_error("Failed to compress data");
return false;
}
have = CHUNK - strm.avail_out;
if (fwrite(out, 1, have, dest) != have || ferror(dest))
{
deflateEnd(&strm);
log_error("Failed to write data to destination");
return false;
}
} while (strm.avail_out == 0);
} while (flush != Z_FINISH);
deflateEnd(&strm);
return true;
}

// Type-independent code left as macro to reduce duplicate code.
#define add_clamp_body(value, value_to_add, min_cap, max_cap) \
if ((value_to_add > 0) && (value > (max_cap - (value_to_add)))) \
Expand Down
4 changes: 3 additions & 1 deletion src/openrct2/util/Util.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

#include "../common.h"

#include <time.h>
#include <cstdio>
#include <ctime>

int32_t squaredmetres_to_squaredfeet(int32_t squaredMetres);
int32_t metres_to_feet(int32_t metres);
Expand Down Expand Up @@ -56,6 +57,7 @@ uint32_t util_rand();

uint8_t* util_zlib_deflate(const uint8_t* data, size_t data_in_size, size_t* data_out_size);
uint8_t* util_zlib_inflate(uint8_t* data, size_t data_in_size, size_t* data_out_size);
bool util_gzip_compress(FILE* source, FILE* dest);

int8_t add_clamp_int8_t(int8_t value, int8_t value_to_add);
int16_t add_clamp_int16_t(int16_t value, int16_t value_to_add);
Expand Down

0 comments on commit 80b7575

Please sign in to comment.