User-friendly abort error messages
parent
d703eb7165
commit
f21025b526
|
@ -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}")
|
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})")
|
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
|
# Platform detection and some related checks
|
||||||
|
@ -214,7 +220,7 @@ endif()
|
||||||
|
|
||||||
# Warn about development build
|
# Warn about development build
|
||||||
if(DEV_BUILD)
|
if(DEV_BUILD)
|
||||||
message("Building with development extensions")
|
message(STATUS "Building with development extensions")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ set(BASE_SOURCES
|
||||||
app/controller.cpp
|
app/controller.cpp
|
||||||
app/input.cpp
|
app/input.cpp
|
||||||
app/pausemanager.cpp
|
app/pausemanager.cpp
|
||||||
|
app/signal_handlers.cpp
|
||||||
app/system.cpp
|
app/system.cpp
|
||||||
app/${SYSTEM_CPP_MODULE}
|
app/${SYSTEM_CPP_MODULE}
|
||||||
app/system_other.cpp
|
app/system_other.cpp
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
|
|
||||||
#include "app/app.h"
|
#include "app/app.h"
|
||||||
|
#include "app/signal_handlers.h"
|
||||||
#include "app/system.h"
|
#include "app/system.h"
|
||||||
#if PLATFORM_WINDOWS
|
#if PLATFORM_WINDOWS
|
||||||
#include "app/system_windows.h"
|
#include "app/system_windows.h"
|
||||||
|
@ -89,7 +90,7 @@ extern "C"
|
||||||
|
|
||||||
int SDL_MAIN_FUNC(int argc, char *argv[])
|
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
|
// Workaround for character encoding in argv on Windows
|
||||||
#if PLATFORM_WINDOWS
|
#if PLATFORM_WINDOWS
|
||||||
|
@ -120,20 +121,22 @@ int SDL_MAIN_FUNC(int argc, char *argv[])
|
||||||
LocalFree(wargv);
|
LocalFree(wargv);
|
||||||
#endif
|
#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]);
|
CResourceManager manager(argv[0]);
|
||||||
|
|
||||||
// Initialize static string arrays
|
// Initialize static string arrays
|
||||||
InitializeRestext();
|
InitializeRestext();
|
||||||
InitializeEventTypeTexts();
|
InitializeEventTypeTexts();
|
||||||
|
|
||||||
logger.Info("%s starting\n", COLOBOT_FULLNAME);
|
|
||||||
|
|
||||||
int code = 0;
|
int code = 0;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
auto systemUtils = CSystemUtils::Create(); // platform-specific utils
|
|
||||||
systemUtils->Init();
|
|
||||||
|
|
||||||
CApplication app(systemUtils.get()); // single instance of the application
|
CApplication app(systemUtils.get()); // single instance of the application
|
||||||
|
|
||||||
ParseArgsStatus status = app.ParseArguments(argc, argv);
|
ParseArgsStatus status = app.ParseArguments(argc, argv);
|
||||||
|
@ -173,4 +176,3 @@ int SDL_MAIN_FUNC(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -32,3 +32,4 @@
|
||||||
#define COLOBOT_DEFAULT_DATADIR "@COLOBOT_INSTALL_DATA_DIR@"
|
#define COLOBOT_DEFAULT_DATADIR "@COLOBOT_INSTALL_DATA_DIR@"
|
||||||
#define COLOBOT_I18N_DIR "@COLOBOT_INSTALL_I18N_DIR@"
|
#define COLOBOT_I18N_DIR "@COLOBOT_INSTALL_I18N_DIR@"
|
||||||
|
|
||||||
|
#define BUILD_NUMBER @BUILD_NUMBER@
|
||||||
|
|
|
@ -323,6 +323,35 @@ void CRobotMain::LoadConfigFile()
|
||||||
m_settings->LoadSettings();
|
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)
|
bool IsInSimulationConfigPhase(Phase phase)
|
||||||
{
|
{
|
||||||
return (phase >= PHASE_SETUPds && phase <= PHASE_SETUPss) || phase == PHASE_READs || phase == PHASE_WRITEs;
|
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();
|
m_engine->LoadAllTextures();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Phase CRobotMain::GetPhase()
|
||||||
|
{
|
||||||
|
return m_phase;
|
||||||
|
}
|
||||||
|
|
||||||
//! Processes an event
|
//! Processes an event
|
||||||
bool CRobotMain::ProcessEvent(Event &event)
|
bool CRobotMain::ProcessEvent(Event &event)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,6 +72,7 @@ enum Phase
|
||||||
PHASE_LOST,
|
PHASE_LOST,
|
||||||
PHASE_QUIT_SCREEN,
|
PHASE_QUIT_SCREEN,
|
||||||
};
|
};
|
||||||
|
std::string PhaseToString(Phase phase);
|
||||||
bool IsInSimulationConfigPhase(Phase phase);
|
bool IsInSimulationConfigPhase(Phase phase);
|
||||||
bool IsPhaseWithWorld(Phase phase);
|
bool IsPhaseWithWorld(Phase phase);
|
||||||
bool IsMainMenuPhase(Phase phase);
|
bool IsMainMenuPhase(Phase phase);
|
||||||
|
@ -166,6 +167,7 @@ public:
|
||||||
|
|
||||||
void ChangePhase(Phase phase);
|
void ChangePhase(Phase phase);
|
||||||
bool ProcessEvent(Event &event);
|
bool ProcessEvent(Event &event);
|
||||||
|
Phase GetPhase();
|
||||||
|
|
||||||
bool CreateShortcuts();
|
bool CreateShortcuts();
|
||||||
void ScenePerso();
|
void ScenePerso();
|
||||||
|
|
Loading…
Reference in New Issue