User-friendly abort error messages

master
krzys-h 2015-08-15 17:07:21 +02:00 committed by Piotr Dziwinski
parent d703eb7165
commit f21025b526
8 changed files with 221 additions and 8 deletions

View File

@ -44,6 +44,12 @@ endif()
set(COLOBOT_VERSION_FULL "${COLOBOT_VERSION_MAJOR}.${COLOBOT_VERSION_MINOR}.${COLOBOT_VERSION_REVISION}${COLOBOT_VERSION_UNRELEASED}${COLOBOT_VERSION_RELEASE_CODENAME}")
message(STATUS "Building Colobot \"${COLOBOT_VERSION_CODENAME}\" (${COLOBOT_VERSION_FULL})")
set(BUILD_NUMBER 0)
if(NOT "$ENV{BUILD_NUMBER}" STREQUAL "")
set(BUILD_NUMBER "$ENV{BUILD_NUMBER}")
message(STATUS "CI build #${BUILD_NUMBER}")
endif()
##
# Platform detection and some related checks
@ -214,7 +220,7 @@ endif()
# Warn about development build
if(DEV_BUILD)
message("Building with development extensions")
message(STATUS "Building with development extensions")
endif()

View File

@ -78,6 +78,7 @@ set(BASE_SOURCES
app/controller.cpp
app/input.cpp
app/pausemanager.cpp
app/signal_handlers.cpp
app/system.cpp
app/${SYSTEM_CPP_MODULE}
app/system_other.cpp

View File

@ -25,6 +25,7 @@
#include "common/config.h"
#include "app/app.h"
#include "app/signal_handlers.h"
#include "app/system.h"
#if PLATFORM_WINDOWS
#include "app/system_windows.h"
@ -89,7 +90,7 @@ extern "C"
int SDL_MAIN_FUNC(int argc, char *argv[])
{
CLogger logger; // single istance of logger
CLogger logger; // single instance of logger
// Workaround for character encoding in argv on Windows
#if PLATFORM_WINDOWS
@ -120,20 +121,22 @@ int SDL_MAIN_FUNC(int argc, char *argv[])
LocalFree(wargv);
#endif
logger.Info("%s starting\n", COLOBOT_FULLNAME);
auto systemUtils = CSystemUtils::Create(); // platform-specific utils
systemUtils->Init();
CSignalHandlers::Init(systemUtils.get());
CResourceManager manager(argv[0]);
// Initialize static string arrays
InitializeRestext();
InitializeEventTypeTexts();
logger.Info("%s starting\n", COLOBOT_FULLNAME);
int code = 0;
while (true)
{
auto systemUtils = CSystemUtils::Create(); // platform-specific utils
systemUtils->Init();
CApplication app(systemUtils.get()); // single instance of the application
ParseArgsStatus status = app.ParseArguments(argc, argv);
@ -173,4 +176,3 @@ int SDL_MAIN_FUNC(int argc, char *argv[])
}
} // extern "C"

129
src/app/signal_handlers.cpp Normal file
View File

@ -0,0 +1,129 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2015, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "app/signal_handlers.h"
#include "common/config.h"
#include "app/system.h"
#include "common/stringutils.h"
#include "level/robotmain.h"
#include <csignal>
#include <sstream>
#include <iostream>
CSystemUtils* CSignalHandlers::m_systemUtils = nullptr;
void CSignalHandlers::Init(CSystemUtils* systemUtils)
{
m_systemUtils = systemUtils;
signal(SIGSEGV, SignalHandler);
signal(SIGABRT, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGILL, SignalHandler);
std::set_terminate(UnhandledExceptionHandler);
}
void CSignalHandlers::SignalHandler(int sig)
{
std::string signalStr = StrUtils::ToString(signal);
switch(sig)
{
case SIGSEGV: signalStr = "SIGSEGV, segmentation fault"; break;
case SIGABRT: signalStr = "SIGABRT, abort"; break;
case SIGFPE: signalStr = "SIGFPE, arithmetic exception"; break;
case SIGILL: signalStr = "SIGILL, illegal instruction"; break;
}
ReportError(signalStr);
}
// TODO: How portable across compilers is this?
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
std::string demangle(const char* name) {
int status;
std::unique_ptr<char[], void(*)(void*)> result {
abi::__cxa_demangle(name, nullptr, nullptr, &status),
std::free
};
return result != nullptr ? result.get() : name;
}
// END OF TODO
void CSignalHandlers::UnhandledExceptionHandler()
{
std::exception_ptr exptr = std::current_exception();
try
{
std::rethrow_exception(exptr);
}
catch (const std::exception& e)
{
std::stringstream ss;
ss << "Type: " << demangle(typeid(e).name()) << std::endl;
ss << "Message: " << e.what();
ReportError(ss.str());
}
catch (...)
{
ReportError("Unknown unhandled exception (not inherited from std::exception)");
}
}
void CSignalHandlers::ReportError(const std::string& errorMessage)
{
std::stringstream msg;
msg << "Unhandled exception occured!" << std::endl;
msg << "==============================" << std::endl;
msg << errorMessage << std::endl;
msg << "==============================" << std::endl;
msg << std::endl;
msg << "This is usually caused by a bug. Please report this on http://github.com/colobot/colobot/issues" << std::endl;
msg << "including information on what you were doing before this happened and all the information below." << std::endl;
msg << "==============================" << std::endl;
#if BUILD_NUMBER == 0
// COLOBOT_VERSION_DISPLAY doesn't update if you don't run CMake after "git pull"
msg << "You seem to be running a custom compilation of version " << COLOBOT_VERSION_DISPLAY << ", but please verify that." << std::endl;
#else
msg << "You are running version " << COLOBOT_VERSION_DISPLAY << " from CI build #" << BUILD_NUMBER << std::endl;
#endif
msg << std::endl;
if (!CRobotMain::IsCreated())
{
msg << "CRobotMain instance does not seem to exist" << std::endl;
}
else
{
CRobotMain* robotMain = CRobotMain::GetInstancePointer();
msg << "The game was in phase " << PhaseToString(robotMain->GetPhase()) << " (ID=" << robotMain->GetPhase() << ")" << std::endl;
msg << "Last started level was: category=" << GetLevelCategoryDir(robotMain->GetLevelCategory()) << " chap=" << robotMain->GetLevelChap() << " rank=" << robotMain->GetLevelRank() << std::endl;
}
msg << "==============================" << std::endl;
msg << std::endl;
msg << "Sorry for inconvenience!";
std::cerr << std::endl << msg.str() << std::endl;
m_systemUtils->SystemDialog(SDT_ERROR, "Unhandled exception occured!", msg.str());
exit(1);
}

38
src/app/signal_handlers.h Normal file
View File

@ -0,0 +1,38 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2015, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsiteс.ch; http://colobot.info; http://github.com/colobot
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#pragma once
#include <string>
class CSystemUtils;
class CSignalHandlers
{
public:
static void Init(CSystemUtils* systemUtils);
private:
static void SignalHandler(int sig);
static void UnhandledExceptionHandler();
static void ReportError(const std::string& errorMessage);
private:
static CSystemUtils* m_systemUtils;
};

View File

@ -32,3 +32,4 @@
#define COLOBOT_DEFAULT_DATADIR "@COLOBOT_INSTALL_DATA_DIR@"
#define COLOBOT_I18N_DIR "@COLOBOT_INSTALL_I18N_DIR@"
#define BUILD_NUMBER @BUILD_NUMBER@

View File

@ -323,6 +323,35 @@ void CRobotMain::LoadConfigFile()
m_settings->LoadSettings();
}
std::string PhaseToString(Phase phase)
{
if (phase == PHASE_WELCOME1) return "PHASE_WELCOME1";
if (phase == PHASE_WELCOME2) return "PHASE_WELCOME2";
if (phase == PHASE_WELCOME3) return "PHASE_WELCOME3";
if (phase == PHASE_PLAYER_SELECT) return "PHASE_PLAYER_SELECT";
if (phase == PHASE_APPERANCE) return "PHASE_APPERANCE";
if (phase == PHASE_MAIN_MENU) return "PHASE_MAIN_MENU";
if (phase == PHASE_LEVEL_LIST) return "PHASE_LEVEL_LIST";
if (phase == PHASE_SIMUL) return "PHASE_SIMUL";
if (phase == PHASE_SETUPd) return "PHASE_SETUPd";
if (phase == PHASE_SETUPg) return "PHASE_SETUPg";
if (phase == PHASE_SETUPp) return "PHASE_SETUPp";
if (phase == PHASE_SETUPc) return "PHASE_SETUPc";
if (phase == PHASE_SETUPs) return "PHASE_SETUPs";
if (phase == PHASE_SETUPds) return "PHASE_SETUPds";
if (phase == PHASE_SETUPgs) return "PHASE_SETUPgs";
if (phase == PHASE_SETUPps) return "PHASE_SETUPps";
if (phase == PHASE_SETUPcs) return "PHASE_SETUPcs";
if (phase == PHASE_SETUPss) return "PHASE_SETUPss";
if (phase == PHASE_WRITEs) return "PHASE_WRITEs";
if (phase == PHASE_READ) return "PHASE_READ";
if (phase == PHASE_READs) return "PHASE_READs";
if (phase == PHASE_WIN) return "PHASE_WIN";
if (phase == PHASE_LOST) return "PHASE_LOST";
if (phase == PHASE_QUIT_SCREEN) return "PHASE_QUIT_SCREEN";
return "(unknown)";
}
bool IsInSimulationConfigPhase(Phase phase)
{
return (phase >= PHASE_SETUPds && phase <= PHASE_SETUPss) || phase == PHASE_READs || phase == PHASE_WRITEs;
@ -579,6 +608,11 @@ void CRobotMain::ChangePhase(Phase phase)
m_engine->LoadAllTextures();
}
Phase CRobotMain::GetPhase()
{
return m_phase;
}
//! Processes an event
bool CRobotMain::ProcessEvent(Event &event)
{

View File

@ -72,6 +72,7 @@ enum Phase
PHASE_LOST,
PHASE_QUIT_SCREEN,
};
std::string PhaseToString(Phase phase);
bool IsInSimulationConfigPhase(Phase phase);
bool IsPhaseWithWorld(Phase phase);
bool IsMainMenuPhase(Phase phase);
@ -166,6 +167,7 @@ public:
void ChangePhase(Phase phase);
bool ProcessEvent(Event &event);
Phase GetPhase();
bool CreateShortcuts();
void ScenePerso();