crash handler, logger and oengles changes
* save log messages to otclient.txt * fixes in FrameBuffer and HardwareBuffer to work with OpenGLES * possibility to get compilation information from lua (compiler version, build date, build revision, build type) * make crash handler more informatave * handle assert crash signals (SIGABRT)
This commit is contained in:
parent
28633a9e20
commit
3ad97c9eab
|
@ -10,6 +10,7 @@ SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake;${CMAKE_MODULE_PATH}")
|
||||||
OPTION(WINDOWS_CONSOLE "Enables console window on Windows platform" OFF)
|
OPTION(WINDOWS_CONSOLE "Enables console window on Windows platform" OFF)
|
||||||
OPTION(CRASH_HANDLER "Generate crash reports" OFF)
|
OPTION(CRASH_HANDLER "Generate crash reports" OFF)
|
||||||
OPTION(USE_OPENGL_ES2 "Use OpenGL ES 2.0 (for mobiles devices)" OFF)
|
OPTION(USE_OPENGL_ES2 "Use OpenGL ES 2.0 (for mobiles devices)" OFF)
|
||||||
|
SET(BUILD_REVISION "custom" CACHE "Git revision string (intended for releases)" STRING)
|
||||||
|
|
||||||
# set debug as default build type
|
# set debug as default build type
|
||||||
IF(NOT CMAKE_BUILD_TYPE)
|
IF(NOT CMAKE_BUILD_TYPE)
|
||||||
|
@ -47,12 +48,18 @@ SET(CMAKE_C_FLAGS_RELEASE "-Ofast -fomit-frame-pointer")
|
||||||
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -static-libgcc -static-libstdc++ -Wl,--as-needed")
|
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -static-libgcc -static-libstdc++ -Wl,--as-needed")
|
||||||
|
|
||||||
MESSAGE(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
MESSAGE(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||||
|
ADD_DEFINITIONS(-DBUILD_TYPE="${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
|
ADD_DEFINITIONS(-DBUILD_REVISION="${BUILD_REVISION}")
|
||||||
|
MESSAGE(STATUS "Build revision: ${BUILD_REVISION}")
|
||||||
|
|
||||||
IF(USE_OPENGL_ES2)
|
IF(USE_OPENGL_ES2)
|
||||||
MESSAGE(STATUS "Renderer: OpenGL ES 2.0")
|
MESSAGE(STATUS "Renderer: OpenGL ES 2.0")
|
||||||
ELSE()
|
ELSE()
|
||||||
MESSAGE(STATUS "Renderer: OpenGL")
|
MESSAGE(STATUS "Renderer: OpenGL")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
|
|
||||||
IF(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
IF(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||||
ADD_DEFINITIONS(-DDEBUG)
|
ADD_DEFINITIONS(-DDEBUG)
|
||||||
MESSAGE(STATUS "Debug information: ON")
|
MESSAGE(STATUS "Debug information: ON")
|
||||||
|
|
|
@ -67,7 +67,6 @@ Application::~Application()
|
||||||
void Application::init(const std::vector<std::string>& args, int appFlags)
|
void Application::init(const std::vector<std::string>& args, int appFlags)
|
||||||
{
|
{
|
||||||
m_appFlags = appFlags;
|
m_appFlags = appFlags;
|
||||||
logInfo("Starting ", m_appName, " ", m_appVersion);
|
|
||||||
|
|
||||||
// capture exit signals
|
// capture exit signals
|
||||||
signal(SIGTERM, exitSignalHandler);
|
signal(SIGTERM, exitSignalHandler);
|
||||||
|
|
|
@ -49,7 +49,11 @@ public:
|
||||||
bool isStopping() { return m_stopping; }
|
bool isStopping() { return m_stopping; }
|
||||||
const std::string& getName() { return m_appName; }
|
const std::string& getName() { return m_appName; }
|
||||||
const std::string& getVersion() { return m_appVersion; }
|
const std::string& getVersion() { return m_appVersion; }
|
||||||
const std::string& getBuildDate() { return m_appBuildDate; }
|
|
||||||
|
std::string getBuildCompiler() { return BUILD_COMPILER; }
|
||||||
|
std::string getBuildDate() { return BUILD_DATE; }
|
||||||
|
std::string getBuildRevision() { return BUILD_REVISION; }
|
||||||
|
std::string getBuildType() { return BUILD_TYPE; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void render();
|
virtual void render();
|
||||||
|
|
|
@ -28,6 +28,17 @@
|
||||||
#define DEG_TO_RAD (acos(-1)/180.0)
|
#define DEG_TO_RAD (acos(-1)/180.0)
|
||||||
#define RAD_TO_DEC (180.0/acos(-1))
|
#define RAD_TO_DEC (180.0/acos(-1))
|
||||||
|
|
||||||
|
#define BUILD_COMPILER "gcc "__VERSION__
|
||||||
|
#define BUILD_DATE __DATE__
|
||||||
|
|
||||||
|
#ifndef BUILD_REVISION
|
||||||
|
#define BUILD_REVISION "custom"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BUILD_TYPE
|
||||||
|
#define BUILD_TYPE "unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Fw
|
namespace Fw
|
||||||
{
|
{
|
||||||
constexpr float pi = 3.14159265;
|
constexpr float pi = 3.14159265;
|
||||||
|
|
|
@ -37,6 +37,11 @@ void Logger::log(Fw::LogLevel level, const std::string& message)
|
||||||
std::string outmsg = logPrefixes[level] + message;
|
std::string outmsg = logPrefixes[level] + message;
|
||||||
std::cout << outmsg << std::endl;
|
std::cout << outmsg << std::endl;
|
||||||
|
|
||||||
|
if(m_outFile.good()) {
|
||||||
|
m_outFile << outmsg << std::endl;
|
||||||
|
m_outFile.flush();
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t now = std::time(NULL);
|
std::size_t now = std::time(NULL);
|
||||||
m_logMessages.push_back(LogMessage(level, outmsg, now));
|
m_logMessages.push_back(LogMessage(level, outmsg, now));
|
||||||
|
|
||||||
|
@ -72,3 +77,15 @@ void Logger::fireOldMessages()
|
||||||
m_onLog(logMessage.level, logMessage.message, logMessage.when);
|
m_onLog(logMessage.level, logMessage.message, logMessage.when);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Logger::setLogFile(const std::string& file)
|
||||||
|
{
|
||||||
|
m_outFile.open(file.c_str(), std::ios::out | std::ios::app);
|
||||||
|
if(!m_outFile.is_open() || !m_outFile.good()) {
|
||||||
|
logError("Unable to save log to '", file, "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_outFile << "\n== application started at " << Fw::dateTimeString() << std::endl;
|
||||||
|
m_outFile.flush();
|
||||||
|
}
|
||||||
|
|
|
@ -41,11 +41,13 @@ public:
|
||||||
void logFunc(Fw::LogLevel level, const std::string& message, std::string prettyFunction);
|
void logFunc(Fw::LogLevel level, const std::string& message, std::string prettyFunction);
|
||||||
|
|
||||||
void fireOldMessages();
|
void fireOldMessages();
|
||||||
|
void setLogFile(const std::string& file);
|
||||||
void setOnLog(const OnLogCallback& onLog) { m_onLog = onLog; }
|
void setOnLog(const OnLogCallback& onLog) { m_onLog = onLog; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<LogMessage> m_logMessages;
|
std::list<LogMessage> m_logMessages;
|
||||||
OnLogCallback m_onLog;
|
OnLogCallback m_onLog;
|
||||||
|
std::ofstream m_outFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Logger g_logger;
|
extern Logger g_logger;
|
||||||
|
|
|
@ -47,7 +47,9 @@ void FrameBuffer::internalCreate()
|
||||||
glGenFramebuffers(1, &m_fbo);
|
glGenFramebuffers(1, &m_fbo);
|
||||||
if(!m_fbo)
|
if(!m_fbo)
|
||||||
logFatal("Unable to create framebuffer object");
|
logFatal("Unable to create framebuffer object");
|
||||||
} else { // use auxiliar buffers when FBOs are not supported
|
}
|
||||||
|
#ifndef OPENGL_ES2
|
||||||
|
else { // use auxiliar buffers when FBOs are not supported
|
||||||
m_fbo = 0;
|
m_fbo = 0;
|
||||||
if(auxBuffers.size() == 0) {
|
if(auxBuffers.size() == 0) {
|
||||||
int maxAuxs = 0;
|
int maxAuxs = 0;
|
||||||
|
@ -64,15 +66,19 @@ void FrameBuffer::internalCreate()
|
||||||
if(!m_fbo)
|
if(!m_fbo)
|
||||||
logFatal("There is no available auxiliar buffer for a new framebuffer, total AUXs: ", auxBuffers.size()-1);
|
logFatal("There is no available auxiliar buffer for a new framebuffer, total AUXs: ", auxBuffers.size()-1);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameBuffer::~FrameBuffer()
|
FrameBuffer::~FrameBuffer()
|
||||||
{
|
{
|
||||||
if(g_graphics.canUseFBO()) {
|
if(g_graphics.canUseFBO()) {
|
||||||
glDeleteFramebuffers(1, &m_fbo);
|
glDeleteFramebuffers(1, &m_fbo);
|
||||||
} else {
|
}
|
||||||
|
#ifndef OPENGL_ES2
|
||||||
|
else {
|
||||||
auxBuffers[m_fbo] = false;
|
auxBuffers[m_fbo] = false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameBuffer::resize(const Size& size)
|
void FrameBuffer::resize(const Size& size)
|
||||||
|
@ -130,10 +136,7 @@ void FrameBuffer::draw(const Rect& dest, const Rect& src)
|
||||||
|
|
||||||
void FrameBuffer::draw(const Rect& dest)
|
void FrameBuffer::draw(const Rect& dest)
|
||||||
{
|
{
|
||||||
if(g_graphics.canUseFBO())
|
g_painter.drawTexturedRect(dest, m_texture, Rect(0,0, getSize()));
|
||||||
g_painter.drawTexturedRect(dest, m_texture);
|
|
||||||
else
|
|
||||||
g_painter.drawTexturedRect(dest, m_texture, Rect(0, 0, g_window.getSize()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameBuffer::internalBind()
|
void FrameBuffer::internalBind()
|
||||||
|
@ -144,11 +147,14 @@ void FrameBuffer::internalBind()
|
||||||
|
|
||||||
if(g_graphics.canUseFBO()) {
|
if(g_graphics.canUseFBO()) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
|
||||||
} else {
|
}
|
||||||
|
#ifndef OPENGL_ES2
|
||||||
|
else {
|
||||||
int buffer = GL_AUX0 + m_fbo - 1;
|
int buffer = GL_AUX0 + m_fbo - 1;
|
||||||
glDrawBuffer(buffer);
|
glDrawBuffer(buffer);
|
||||||
glReadBuffer(buffer);
|
glReadBuffer(buffer);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
m_prevBoundFbo = boundFbo;
|
m_prevBoundFbo = boundFbo;
|
||||||
boundFbo = m_fbo;
|
boundFbo = m_fbo;
|
||||||
|
@ -159,7 +165,9 @@ void FrameBuffer::internalRelease()
|
||||||
assert(boundFbo == m_fbo);
|
assert(boundFbo == m_fbo);
|
||||||
if(g_graphics.canUseFBO()) {
|
if(g_graphics.canUseFBO()) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, m_prevBoundFbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, m_prevBoundFbo);
|
||||||
} else {
|
}
|
||||||
|
#ifndef OPENGL_ES2
|
||||||
|
else {
|
||||||
m_texture->bind();
|
m_texture->bind();
|
||||||
|
|
||||||
Size size = getSize();
|
Size size = getSize();
|
||||||
|
@ -172,16 +180,19 @@ void FrameBuffer::internalRelease()
|
||||||
glDrawBuffer(buffer);
|
glDrawBuffer(buffer);
|
||||||
glReadBuffer(buffer);
|
glReadBuffer(buffer);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
boundFbo = m_prevBoundFbo;
|
boundFbo = m_prevBoundFbo;
|
||||||
}
|
}
|
||||||
|
|
||||||
Size FrameBuffer::getSize()
|
Size FrameBuffer::getSize()
|
||||||
{
|
{
|
||||||
if(g_graphics.canUseFBO()) {
|
#ifndef OPENGL_ES2
|
||||||
return m_texture->getSize();
|
if(!g_graphics.canUseFBO()) {
|
||||||
} else {
|
|
||||||
// the buffer size is limited by the window size
|
// the buffer size is limited by the window size
|
||||||
return Size(std::min(m_texture->getWidth(), g_window.getWidth()),
|
return Size(std::min(m_texture->getWidth(), g_window.getWidth()),
|
||||||
std::min(m_texture->getHeight(), g_window.getHeight()));
|
std::min(m_texture->getHeight(), g_window.getHeight()));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
return m_texture->getSize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,14 +36,8 @@ public:
|
||||||
|
|
||||||
enum UsagePattern {
|
enum UsagePattern {
|
||||||
StreamDraw = GL_STREAM_DRAW,
|
StreamDraw = GL_STREAM_DRAW,
|
||||||
StreamRead = GL_STREAM_READ,
|
|
||||||
StreamCopy = GL_STREAM_COPY,
|
|
||||||
StaticDraw = GL_STATIC_DRAW,
|
StaticDraw = GL_STATIC_DRAW,
|
||||||
StaticRead = GL_STATIC_READ,
|
DynamicDraw = GL_DYNAMIC_DRAW
|
||||||
StaticCopy = GL_STATIC_COPY,
|
|
||||||
DynamicDraw = GL_DYNAMIC_DRAW,
|
|
||||||
DynamicRead = GL_DYNAMIC_READ,
|
|
||||||
DynamicCopy = GL_DYNAMIC_COPY
|
|
||||||
};
|
};
|
||||||
|
|
||||||
HardwareBuffer(Type type ) {
|
HardwareBuffer(Type type ) {
|
||||||
|
|
|
@ -422,7 +422,10 @@ void Application::registerLuaFunctions()
|
||||||
g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app));
|
g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app));
|
g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app));
|
g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "getBuildDate", std::bind(&Application::getBuildDate, g_app));
|
g_lua.bindClassStaticFunction("g_app", "getBuildDate", std::bind(&Application::getBuildDate, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getBuildRevision", std::bind(&Application::getBuildRevision, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getBuildType", std::bind(&Application::getBuildType, g_app));
|
||||||
|
|
||||||
// ConfigManager
|
// ConfigManager
|
||||||
g_lua.registerStaticClass("g_configs");
|
g_lua.registerStaticClass("g_configs");
|
||||||
|
|
|
@ -36,16 +36,17 @@ void crashHandler(int signum, siginfo_t* info, void* secret)
|
||||||
{
|
{
|
||||||
logError("Application crashed");
|
logError("Application crashed");
|
||||||
|
|
||||||
ucontext_t context = *(ucontext_t*)secret;
|
|
||||||
time_t tnow;
|
|
||||||
char fileName[128];
|
|
||||||
time(&tnow);
|
|
||||||
tm *ts = localtime(&tnow);
|
|
||||||
strftime(fileName, 128, (g_app->getName() + "-crash_-%d-%m-%Y_%H:%M:%S.txt").c_str(), ts);
|
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
ss << Fw::formatString("app name: %s\n", g_app->getName().c_str());
|
||||||
|
ss << Fw::formatString("app version: %s\n", g_app->getVersion().c_str());
|
||||||
|
ss << Fw::formatString("build compiler: %s\n", BUILD_COMPILER);
|
||||||
|
ss << Fw::formatString("build date: %s\n", BUILD_DATE);
|
||||||
|
ss << Fw::formatString("build type: %s\n", BUILD_TYPE);
|
||||||
|
ss << Fw::formatString("build revision: %s\n", BUILD_REVISION);
|
||||||
|
ss << Fw::formatString("crash date: %s\n", Fw::dateTimeString().c_str());
|
||||||
ss.flags(std::ios::hex | std::ios::showbase);
|
ss.flags(std::ios::hex | std::ios::showbase);
|
||||||
|
|
||||||
|
ucontext_t context = *(ucontext_t*)secret;
|
||||||
#if __WORDSIZE == 64
|
#if __WORDSIZE == 64
|
||||||
ss << " at rip = " << context.uc_mcontext.gregs[REG_RIP] << std::endl;
|
ss << " at rip = " << context.uc_mcontext.gregs[REG_RIP] << std::endl;
|
||||||
ss << " rax = " << context.uc_mcontext.gregs[REG_RAX] << std::endl;
|
ss << " rax = " << context.uc_mcontext.gregs[REG_RAX] << std::endl;
|
||||||
|
@ -99,14 +100,21 @@ void crashHandler(int signum, siginfo_t* info, void* secret)
|
||||||
|
|
||||||
logInfo(ss.str());
|
logInfo(ss.str());
|
||||||
|
|
||||||
std::ofstream out(fileName);
|
std::string fileName = "crash_report.txt";
|
||||||
out << ss.str();
|
std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::app);
|
||||||
out.close();
|
if(fout.is_open() && fout.good()) {
|
||||||
logInfo("Crash report saved to file ", fileName);
|
fout << "== application crashed\n";
|
||||||
|
fout << ss.str();
|
||||||
|
fout << "\n";
|
||||||
|
fout.close();
|
||||||
|
logInfo("Crash report saved to file ", fileName.c_str());
|
||||||
|
} else
|
||||||
|
logError("Failed to save crash report!");
|
||||||
|
|
||||||
signal(SIGILL, SIG_DFL);
|
signal(SIGILL, SIG_DFL);
|
||||||
signal(SIGSEGV, SIG_DFL);
|
signal(SIGSEGV, SIG_DFL);
|
||||||
signal(SIGFPE, SIG_DFL);
|
signal(SIGFPE, SIG_DFL);
|
||||||
|
signal(SIGABRT, SIG_DFL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void installCrashHandler()
|
void installCrashHandler()
|
||||||
|
@ -119,4 +127,5 @@ void installCrashHandler()
|
||||||
sigaction(SIGILL, &sa, NULL); // illegal instruction
|
sigaction(SIGILL, &sa, NULL); // illegal instruction
|
||||||
sigaction(SIGSEGV, &sa, NULL); // segmentation fault
|
sigaction(SIGSEGV, &sa, NULL); // segmentation fault
|
||||||
sigaction(SIGFPE, &sa, NULL); // floating-point exception
|
sigaction(SIGFPE, &sa, NULL); // floating-point exception
|
||||||
|
sigaction(SIGABRT, &sa, NULL); // process aborted (asserts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,6 @@ void Stacktrace(LPEXCEPTION_POINTERS e, std::stringstream& ss)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
dwModBase = SymGetModuleBase(process, sf.AddrPC.Offset);
|
dwModBase = SymGetModuleBase(process, sf.AddrPC.Offset);
|
||||||
|
|
||||||
if(dwModBase)
|
if(dwModBase)
|
||||||
GetModuleFileName((HINSTANCE)dwModBase, modname, MAX_PATH);
|
GetModuleFileName((HINSTANCE)dwModBase, modname, MAX_PATH);
|
||||||
else
|
else
|
||||||
|
@ -95,13 +94,10 @@ void Stacktrace(LPEXCEPTION_POINTERS e, std::stringstream& ss)
|
||||||
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
||||||
pSym->MaxNameLength = MAX_PATH;
|
pSym->MaxNameLength = MAX_PATH;
|
||||||
|
|
||||||
if(SymGetSymFromAddr(process, sf.AddrPC.Offset, &Disp, pSym)) {
|
if(SymGetSymFromAddr(process, sf.AddrPC.Offset, &Disp, pSym))
|
||||||
// this is the code path taken on VC if debugging syms are found.
|
ss << Fw::formatString(" %d: %s(%s+%#0lx) [0x%08lX]\n", count, modname, pSym->Name, Disp, sf.AddrPC.Offset);
|
||||||
ss << Fw::formatString(" (%d) %s(%s+%#0lx) [0x%08lX]\n", count, modname, pSym->Name, Disp, sf.AddrPC.Offset);
|
else
|
||||||
} else {
|
ss << Fw::formatString(" %d: %s [0x%08lX]\n", count, modname, sf.AddrPC.Offset);
|
||||||
// this is the code path taken on MinGW, and VC if no debugging syms are found.
|
|
||||||
ss << Fw::formatString(" (%d) %s [0x%08lX]\n", count, modname, sf.AddrPC.Offset);
|
|
||||||
}
|
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
GlobalFree(pSym);
|
GlobalFree(pSym);
|
||||||
|
@ -109,40 +105,38 @@ void Stacktrace(LPEXCEPTION_POINTERS e, std::stringstream& ss)
|
||||||
|
|
||||||
LONG CALLBACK ExceptionHandler(LPEXCEPTION_POINTERS e)
|
LONG CALLBACK ExceptionHandler(LPEXCEPTION_POINTERS e)
|
||||||
{
|
{
|
||||||
char date[32];
|
|
||||||
time_t tnow;
|
|
||||||
time(&tnow);
|
|
||||||
tm *ts = localtime(&tnow);
|
|
||||||
strftime(date, 32, "%b %d %Y %H:%M:%S", ts);
|
|
||||||
|
|
||||||
// generate crash report
|
// generate crash report
|
||||||
SymInitialize(GetCurrentProcess(), 0, TRUE);
|
SymInitialize(GetCurrentProcess(), 0, TRUE);
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "== application crashed\n";
|
ss << "== application crashed\n";
|
||||||
ss << Fw::formatString("app name: %s\n", g_app->getName().c_str());
|
ss << Fw::formatString("app name: %s\n", g_app->getName().c_str());
|
||||||
ss << Fw::formatString("app version: %s\n", g_app->getVersion().c_str());
|
ss << Fw::formatString("app version: %s\n", g_app->getVersion().c_str());
|
||||||
ss << Fw::formatString("app build date: %s\n", g_app->getBuildDate().c_str());
|
ss << Fw::formatString("build compiler: %s\n", BUILD_COMPILER);
|
||||||
ss << Fw::formatString("crash date: %s\n", date);
|
ss << Fw::formatString("build date: %s\n", BUILD_DATE);
|
||||||
|
ss << Fw::formatString("build type: %s\n", BUILD_TYPE);
|
||||||
|
ss << Fw::formatString("build revision: %s\n", BUILD_REVISION);
|
||||||
|
ss << Fw::formatString("crash date: %s\n", Fw::dateTimeString().c_str());
|
||||||
ss << Fw::formatString("exception: %s (0x%08lx)\n", getExceptionName(e->ExceptionRecord->ExceptionCode), e->ExceptionRecord->ExceptionCode);
|
ss << Fw::formatString("exception: %s (0x%08lx)\n", getExceptionName(e->ExceptionRecord->ExceptionCode), e->ExceptionRecord->ExceptionCode);
|
||||||
ss << Fw::formatString("exception address: 0x%08lx\n", (long unsigned int)e->ExceptionRecord->ExceptionAddress);
|
ss << Fw::formatString("exception address: 0x%08lx\n", (long unsigned int)e->ExceptionRecord->ExceptionAddress);
|
||||||
ss << Fw::formatString("backtrace:\n");
|
ss << Fw::formatString(" backtrace:\n");
|
||||||
Stacktrace(e, ss);
|
Stacktrace(e, ss);
|
||||||
ss << "\n";
|
ss << "\n";
|
||||||
SymCleanup(GetCurrentProcess());
|
SymCleanup(GetCurrentProcess());
|
||||||
|
|
||||||
// write stacktrace to crash.txt
|
// print in stdout
|
||||||
|
logInfo(ss.str());
|
||||||
|
|
||||||
|
// write stacktrace to crash_report.txt
|
||||||
char dir[MAX_PATH];
|
char dir[MAX_PATH];
|
||||||
GetCurrentDirectory(sizeof(dir) - 1, dir);
|
GetCurrentDirectory(sizeof(dir) - 1, dir);
|
||||||
std::string fileName = Fw::formatString("%s\\crash_report.txt", dir);
|
std::string fileName = Fw::formatString("%s\\crash_report.txt", dir);
|
||||||
|
std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::app);
|
||||||
|
if(fout.is_open() && fout.good()) {
|
||||||
std::ofstream fout(fileName.c_str(), std::ios_base::out | std::ios_base::ate);
|
fout << ss.str();
|
||||||
fout << ss.str();
|
fout.close();
|
||||||
fout.close();
|
logInfo("Crash report saved to file ", fileName);
|
||||||
|
} else
|
||||||
// print in stdout
|
logError("Failed to save crash report!");
|
||||||
logInfo(ss.str());
|
|
||||||
logInfo("Crash report saved to file ", fileName);
|
|
||||||
|
|
||||||
// inform the user
|
// inform the user
|
||||||
std::string msg = Fw::formatString("The application has crashed.\n\n"
|
std::string msg = Fw::formatString("The application has crashed.\n\n"
|
||||||
|
|
|
@ -290,6 +290,15 @@ inline std::string resolvePath(const std::string& file, std::string sourcePath)
|
||||||
return sourcePath + file;
|
return sourcePath + file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string dateTimeString() {
|
||||||
|
char date[32];
|
||||||
|
std::time_t tnow;
|
||||||
|
std::time(&tnow);
|
||||||
|
std::tm *ts = std::localtime(&tnow);
|
||||||
|
std::strftime(date, 32, "%b %d %Y %H:%M:%S", ts);
|
||||||
|
return std::string(date);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T randomRange(T min, T max);
|
T randomRange(T min, T max);
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,7 @@ namespace Otc
|
||||||
{
|
{
|
||||||
constexpr const char* AppName = "OTClient";
|
constexpr const char* AppName = "OTClient";
|
||||||
constexpr const char* AppCompactName = "otclient";
|
constexpr const char* AppCompactName = "otclient";
|
||||||
constexpr const char* AppVersion = "0.4.0";
|
constexpr const char* AppVersion = "0.4.0_dev";
|
||||||
constexpr const char* AppBuild = "_dev";
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
TILE_PIXELS = 32,
|
TILE_PIXELS = 32,
|
||||||
|
|
|
@ -22,18 +22,24 @@
|
||||||
|
|
||||||
#include "otclient.h"
|
#include "otclient.h"
|
||||||
#include <framework/core/modulemanager.h>
|
#include <framework/core/modulemanager.h>
|
||||||
#include "core/game.h"
|
|
||||||
#include <framework/core/resourcemanager.h>
|
#include <framework/core/resourcemanager.h>
|
||||||
|
#include "core/game.h"
|
||||||
#include "core/map.h"
|
#include "core/map.h"
|
||||||
|
|
||||||
OTClient::OTClient() : Application(Otc::AppCompactName)
|
OTClient::OTClient() : Application(Otc::AppCompactName)
|
||||||
{
|
{
|
||||||
m_appVersion = Fw::formatString("%s%s", Otc::AppVersion, Otc::AppBuild);
|
m_appVersion = Otc::AppVersion;
|
||||||
m_appBuildDate = __DATE__;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OTClient::init(const std::vector<std::string>& args)
|
void OTClient::init(const std::vector<std::string>& args)
|
||||||
{
|
{
|
||||||
|
logInfo(Fw::formatString("%s %s (rev %s) built on %s",
|
||||||
|
Otc::AppName,
|
||||||
|
Otc::AppVersion,
|
||||||
|
BUILD_REVISION,
|
||||||
|
BUILD_DATE));
|
||||||
|
|
||||||
|
g_logger.setLogFile(Fw::formatString("%s.txt", Otc::AppCompactName));
|
||||||
Application::init(args, Fw::AppEnableAll);
|
Application::init(args, Fw::AppEnableAll);
|
||||||
|
|
||||||
g_modules.discoverModules();
|
g_modules.discoverModules();
|
||||||
|
|
Loading…
Reference in New Issue