OpenRCT2/src/platform/crash.cpp

173 lines
5.6 KiB
C++
Raw Normal View History

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#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>
#else
#error Breakpad support not implemented yet for this platform
#endif
2016-04-09 19:21:40 +02:00
extern "C" {
#include "../localisation/language.h"
#include "../scenario.h"
#include "platform.h"
}
#include "../core/Console.hpp"
#define WSZ(x) L"" x
2016-08-28 18:17:20 +02:00
#ifdef OPENRCT2_COMMIT_SHA1_SHORT
const wchar_t *_wszCommitSha1Short = WSZ(OPENRCT2_COMMIT_SHA1_SHORT);
#else
const wchar_t *_wszCommitSha1Short = WSZ("");
#endif
static bool OnCrash(const wchar_t * dumpPath,
const wchar_t * miniDumpId,
void * context,
EXCEPTION_POINTERS * exinfo,
MDRawAssertionInfo * assertion,
bool succeeded)
{
if (!succeeded)
{
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 occured.";
printf("%s\n", DumpFailedMessage);
if (!gOpenRCT2SilentBreakpad)
{
MessageBoxA(NULL, 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];
swprintf_s(dumpFilePathNew, sizeof(dumpFilePathNew), L"%s%s(%s).dmp", dumpPath, miniDumpId, _wszCommitSha1Short);
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);
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);
}
if (gOpenRCT2SilentBreakpad)
{
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 with OpenRCT2 on GitHub, and provide the dump and saved game there.\n\nVersion: %s\nCommit: %s";
wchar_t message[MAX_PATH * 2];
swprintf_s(message,
MessageFormat,
dumpFilePath,
WSZ(OPENRCT2_VERSION),
2016-08-28 18:17:20 +02:00
_wszCommitSha1Short);
// 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))
{
2016-08-28 18:19:13 +02:00
LPITEMIDLIST pidl = ILCreateFromPathW(dumpPath);
LPITEMIDLIST 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;
2016-04-09 19:21:40 +02:00
}
static std::wstring GetDumpDirectory()
{
char userDirectory[MAX_PATH];
platform_get_user_directory(userDirectory, NULL, sizeof(userDirectory));
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
2016-04-10 20:51:44 +02:00
constexpr const wchar_t * PipeName = L"openrct2-bpad";
2016-10-06 23:32:10 +02:00
#endif // USE_BREAKPAD
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
}