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}")
|
||||
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()
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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_I18N_DIR "@COLOBOT_INSTALL_I18N_DIR@"
|
||||
|
||||
#define BUILD_NUMBER @BUILD_NUMBER@
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue