OpenRCT2/src/openrct2/platform/Crash.cpp

174 lines
5.3 KiB
C++
Raw Normal View History

/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
2018-01-18 13:55:34 +01:00
#include "Crash.h"
#ifdef USE_BREAKPAD
2017-02-08 23:17:01 +01:00
#include <memory>
#include <stdio.h>
#if defined(_WIN32)
2018-06-22 23:04:38 +02:00
#include <ShlObj.h>
#include <breakpad/client/windows/handler/exception_handler.h>
#include <string>
#else
2018-06-22 23:04:38 +02:00
#error Breakpad support not implemented yet for this platform
#endif
2018-06-22 23:04:38 +02:00
#include "../Version.h"
#include "../core/Console.hpp"
2018-02-01 18:49:14 +01:00
#include "../localisation/Language.h"
2017-02-08 23:17:01 +01:00
#include "../rct2/S6Exporter.h"
2018-02-01 18:49:14 +01:00
#include "../scenario/Scenario.h"
#include "platform.h"
#define WSZ(x) L"" x
2016-08-28 18:17:20 +02:00
#ifdef OPENRCT2_COMMIT_SHA1_SHORT
2018-06-22 23:04:38 +02:00
const wchar_t* _wszCommitSha1Short = WSZ(OPENRCT2_COMMIT_SHA1_SHORT);
2016-08-28 18:17:20 +02:00
#else
2018-06-22 23:04:38 +02:00
const wchar_t* _wszCommitSha1Short = WSZ("");
2016-08-28 18:17:20 +02:00
#endif
// OPENRCT2_ARCHITECTURE is required to be defined in version.h
2018-06-22 23:04:38 +02:00
const wchar_t* _wszArchitecture = WSZ(OPENRCT2_ARCHITECTURE);
static bool OnCrash(
const wchar_t* dumpPath,
const wchar_t* miniDumpId,
void* context,
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion,
bool succeeded)
{
if (!succeeded)
{
2018-06-22 23:04:38 +02:00
constexpr const char* DumpFailedMessage
= "Failed to create the dump. Please file an issue with OpenRCT2 on GitHub and provide latest save, and provide "
"information about what you did before the crash occurred.";
printf("%s\n", DumpFailedMessage);
if (!gOpenRCT2SilentBreakpad)
{
2017-08-15 10:07:44 +02:00
MessageBoxA(nullptr, DumpFailedMessage, OPENRCT2_NAME, MB_OK | MB_ICONERROR);
}
return succeeded;
}
// 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);
// Try to rename the files
wchar_t dumpFilePathNew[MAX_PATH];
2018-06-22 23:04:38 +02:00
swprintf_s(
dumpFilePathNew,
sizeof(dumpFilePathNew),
L"%s%s(%s_%s).dmp",
dumpPath,
miniDumpId,
_wszCommitSha1Short,
_wszArchitecture);
if (_wrename(dumpFilePath, dumpFilePathNew) == 0)
{
std::wcscpy(dumpFilePath, dumpFilePathNew);
}
// Log information to output
wprintf(L"Dump Path: %s\n", dumpPath);
wprintf(L"Dump File Path: %s\n", dumpFilePath);
wprintf(L"Dump Id: %s\n", miniDumpId);
wprintf(L"Version: %s\n", WSZ(OPENRCT2_VERSION));
2016-08-28 18:17:20 +02:00
wprintf(L"Commit: %s\n", _wszCommitSha1Short);
bool savedGameDumped = false;
2018-06-22 23:04:38 +02:00
utf8* saveFilePathUTF8 = widechar_to_utf8(saveFilePath);
2017-02-08 23:17:01 +01:00
try
{
auto exporter = std::make_unique<S6Exporter>();
exporter->Export();
exporter->SaveGame(saveFilePathUTF8);
savedGameDumped = true;
}
2018-06-22 23:04:38 +02:00
catch (const std::exception&)
2017-02-08 23:17:01 +01:00
{
}
free(saveFilePathUTF8);
if (gOpenRCT2SilentBreakpad)
{
return succeeded;
}
2017-02-08 23:17:01 +01:00
2018-06-22 23:04:38 +02:00
constexpr const wchar_t* MessageFormat
= L"A crash has occurred and a dump was created at\n%s.\n\nPlease file an issue with OpenRCT2 on GitHub, and provide "
L"the dump and saved game there.\n\nVersion: %s\nCommit: %s";
wchar_t message[MAX_PATH * 2];
2018-06-22 23:04:38 +02:00
swprintf_s(message, MessageFormat, dumpFilePath, WSZ(OPENRCT2_VERSION), _wszCommitSha1Short);
// Cannot use platform_show_messagebox here, it tries to set parent window already dead.
2017-08-15 10:07:44 +02:00
MessageBoxW(nullptr, message, WSZ(OPENRCT2_NAME), MB_OK | MB_ICONERROR);
HRESULT coInitializeResult = CoInitialize(nullptr);
if (SUCCEEDED(coInitializeResult))
{
2016-08-28 18:19:13 +02:00
LPITEMIDLIST pidl = ILCreateFromPathW(dumpPath);
LPITEMIDLIST files[2];
uint32_t numFiles = 0;
files[numFiles++] = ILCreateFromPathW(dumpFilePath);
if (savedGameDumped)
{
files[numFiles++] = ILCreateFromPathW(saveFilePath);
}
2018-06-22 23:04:38 +02:00
if (pidl != nullptr)
{
SHOpenFolderAndSelectItems(pidl, numFiles, (LPCITEMIDLIST*)files, 0);
ILFree(pidl);
for (uint32_t i = 0; i < numFiles; i++)
{
ILFree(files[i]);
}
}
CoUninitialize();
}
// Return whether the dump was successful
return succeeded;
2016-04-09 19:21:40 +02:00
}
static std::wstring GetDumpDirectory()
{
char userDirectory[MAX_PATH];
2017-08-15 10:07:44 +02:00
platform_get_user_directory(userDirectory, nullptr, sizeof(userDirectory));
2018-06-22 23:04:38 +02:00
wchar_t* userDirectoryW = utf8_to_widechar(userDirectory);
auto result = std::wstring(userDirectoryW);
free(userDirectoryW);
return result;
}
// Using non-null pipe name here lets breakpad try setting OOP crash handling
2018-06-22 23:04:38 +02:00
constexpr const wchar_t* PipeName = L"openrct2-bpad";
2016-10-06 23:32:10 +02:00
#endif // USE_BREAKPAD
2018-02-01 18:49:14 +01:00
CExceptionHandler crash_init()
{
#ifdef USE_BREAKPAD
// Path must exist and be RW!
auto exHandler = new google_breakpad::ExceptionHandler(
2018-06-22 23:04:38 +02:00
GetDumpDirectory(), 0, OnCrash, 0, google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpWithDataSegs, PipeName, 0);
return reinterpret_cast<CExceptionHandler>(exHandler);
2018-06-22 23:04:38 +02:00
#else // USE_BREAKPAD
return nullptr;
#endif // USE_BREAKPAD
}