From 2d95052477b7b54fceec09fbb7c58bbee7ac3664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 20 Oct 2018 23:54:59 +0200 Subject: [PATCH] Compress minidumps with gzip --- src/openrct2/platform/Crash.cpp | 42 +++++++++++++++++++----- src/openrct2/util/Util.cpp | 58 +++++++++++++++++++++++++++++++++ src/openrct2/util/Util.h | 4 ++- 3 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/openrct2/platform/Crash.cpp b/src/openrct2/platform/Crash.cpp index e94c657908..6f17253e86 100644 --- a/src/openrct2/platform/Crash.cpp +++ b/src/openrct2/platform/Crash.cpp @@ -29,6 +29,7 @@ # include "../localisation/Language.h" # include "../rct2/S6Exporter.h" # include "../scenario/Scenario.h" +# include "../util/Util.h" # include "platform.h" # define WSZ(x) L"" x @@ -42,21 +43,26 @@ 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) { - std::wstring url( - L"https://submit.backtrace.io/openrct2/f9c5e640d498f15dbe902eab3e822e472af9270d5b0cbdc269cee65a926bf306/minidump"); + std::wstring url(L"https://openrct2.sp.backtrace.io:6098/" + L"post?format=minidump&token=f9c5e640d498f15dbe902eab3e822e472af9270d5b0cbdc269cee65a926bf306"); std::map parameters; std::map files; parameters[L"product_name"] = L"openrct2"; // In case of releases this can be empty if (wcslen(_wszCommitSha1Short) > 0) { - parameters[L"version"] = _wszCommitSha1Short; + parameters[L"commit"] = _wszCommitSha1Short; } else { - parameters[L"version"] = String::ToUtf16(gVersionInfoFull); + parameters[L"commit"] = String::ToUtf16(gVersionInfoFull); } files[L"upload_file_minidump"] = dumpPath; std::wstring response; @@ -89,17 +95,34 @@ static bool OnCrash( 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); + 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, _wszArchitecture); + + wchar_t dumpFilePathGZIP[MAX_PATH]; + swprintf_s(dumpFilePathGZIP, sizeof(dumpFilePathGZIP), L"%s.gz", dumpFilePathNew); + + 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); @@ -123,7 +146,7 @@ static bool OnCrash( if (gOpenRCT2SilentBreakpad) { - UploadMinidump(dumpFilePath); + UploadMinidump(minidumpToUpload); return succeeded; } @@ -139,16 +162,19 @@ static bool OnCrash( int answer = MessageBoxW(nullptr, message, WSZ(OPENRCT2_NAME), MB_YESNO | MB_ICONERROR); if (answer == IDYES) { - UploadMinidump(dumpFilePath); + UploadMinidump(minidumpToUpload); } 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); diff --git a/src/openrct2/util/Util.cpp b/src/openrct2/util/Util.cpp index ff58a84a42..500b83e0b5 100644 --- a/src/openrct2/util/Util.cpp +++ b/src/openrct2/util/Util.cpp @@ -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)))) \ diff --git a/src/openrct2/util/Util.h b/src/openrct2/util/Util.h index 462e46308a..d8c45030e2 100644 --- a/src/openrct2/util/Util.h +++ b/src/openrct2/util/Util.h @@ -12,7 +12,8 @@ #include "../common.h" -#include +#include +#include int32_t squaredmetres_to_squaredfeet(int32_t squaredMetres); int32_t metres_to_feet(int32_t metres); @@ -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);