|
justburner |
a66c51 |
#include "crashhandler.h"
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
#include <inttypes.h></inttypes.h>
|
|
justburner |
a66c51 |
#include <signal.h></signal.h>
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
#ifdef _WIN32
|
|
justburner |
a66c51 |
#include <windows.h></windows.h>
|
|
justburner |
a66c51 |
#include <winbase.h></winbase.h>
|
|
justburner |
a66c51 |
#include <dbghelp.h></dbghelp.h>
|
|
justburner |
541192 |
#include <psapi.h></psapi.h>
|
|
justburner |
a66c51 |
#else
|
|
justburner |
a66c51 |
#include <execinfo.h></execinfo.h>
|
|
justburner |
a66c51 |
#include <signal.h></signal.h>
|
|
justburner |
a66c51 |
#include <unistd.h></unistd.h>
|
|
justburner |
a66c51 |
#include <err.h></err.h>
|
|
justburner |
a66c51 |
#include <regex></regex>
|
|
justburner |
a66c51 |
#endif
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
#include "tgl.h"
|
|
justburner |
a66c51 |
#include "tapp.h"
|
|
justburner |
a66c51 |
#include "tenv.h"
|
|
justburner |
a66c51 |
#include "tconvert.h"
|
|
justburner |
41a8e8 |
#include "texception.h"
|
|
justburner |
a66c51 |
#include "tfilepath_io.h"
|
|
justburner |
a66c51 |
#include "toonz/toonzfolders.h"
|
|
justburner |
a66c51 |
#include "toonz/tproject.h"
|
|
justburner |
a66c51 |
#include "toonz/tscenehandle.h"
|
|
justburner |
a66c51 |
#include "toonz/toonzscene.h"
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
#include <qoperatingsystemversion></qoperatingsystemversion>
|
|
justburner |
a66c51 |
#include <qdesktopservices></qdesktopservices>
|
|
justburner |
a66c51 |
#include <qapplication></qapplication>
|
|
justburner |
a66c51 |
#include <qclipboard></qclipboard>
|
|
justburner |
a66c51 |
#include <qthread></qthread>
|
|
justburner |
a66c51 |
#include <qmainwindow></qmainwindow>
|
|
justburner |
a66c51 |
#include <qmessagebox></qmessagebox>
|
|
justburner |
a66c51 |
#include <qcloseevent></qcloseevent>
|
|
justburner |
a66c51 |
#include <qdialog></qdialog>
|
|
justburner |
a66c51 |
#include <qlayout></qlayout>
|
|
justburner |
a66c51 |
#include <qlabel></qlabel>
|
|
justburner |
a66c51 |
#include <qtextedit></qtextedit>
|
|
justburner |
a66c51 |
#include <qpushbutton></qpushbutton>
|
|
justburner |
a66c51 |
|
|
justburner |
bc2c3b |
static QWidget *s_parentWindow = NULL;
|
|
justburner |
bc2c3b |
static bool s_reportProjInfo = false;
|
|
justburner |
bc2c3b |
|
|
justburner |
a66c51 |
//-----------------------------------------------------------------------------
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
static const char *filenameOnly(const char *path) {
|
|
justburner |
541192 |
for (int i = strlen(path); i >= 0; --i) {
|
|
justburner |
541192 |
if (path[i] == '\\' || path[i] == '/') return path + i + 1;
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
return path;
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
// Windows platform functions
|
|
justburner |
541192 |
|
|
justburner |
541192 |
#ifdef _WIN32
|
|
justburner |
541192 |
|
|
justburner |
541192 |
#define HAS_MINIDUMP
|
|
justburner |
541192 |
static bool generateMinidump(TFilePath dumpFile) {
|
|
justburner |
541192 |
HANDLE hDumpFile = CreateFileW(dumpFile.getWideString().c_str(),
|
|
justburner |
541192 |
GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
|
|
justburner |
541192 |
if (hDumpFile == INVALID_HANDLE_VALUE) return false;
|
|
justburner |
541192 |
|
|
justburner |
541192 |
MINIDUMP_EXCEPTION_INFORMATION mdei;
|
|
justburner |
541192 |
mdei.ThreadId = GetCurrentThreadId();
|
|
justburner |
541192 |
mdei.ExceptionPointers = NULL;
|
|
justburner |
541192 |
mdei.ClientPointers = FALSE;
|
|
justburner |
541192 |
|
|
justburner |
541192 |
if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile,
|
|
justburner |
541192 |
MiniDumpNormal, &mdei, 0, NULL)) {
|
|
justburner |
541192 |
CloseHandle(hDumpFile);
|
|
justburner |
541192 |
return true;
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
|
|
justburner |
541192 |
return false;
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
#define HAS_MODULES
|
|
justburner |
541192 |
static void printModules(std::string &out) {
|
|
justburner |
541192 |
HANDLE hProcess = GetCurrentProcess();
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
HMODULE modules[1024];
|
|
justburner |
541192 |
DWORD size;
|
|
justburner |
541192 |
if (EnumProcessModules(hProcess, modules, sizeof(modules), &size)) {
|
|
justburner |
541192 |
for (unsigned int i = 0; i < size / sizeof(HMODULE); i++) {
|
|
justburner |
541192 |
char moduleName[512];
|
|
justburner |
541192 |
GetModuleFileNameA(modules[i], moduleName, 512);
|
|
justburner |
541192 |
out.append(moduleName);
|
|
justburner |
541192 |
out.append("\n");
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
|
|
justburner |
541192 |
#define HAS_BACKTRACE
|
|
justburner |
541192 |
static void printBacktrace(std::string &out) {
|
|
justburner |
a66c51 |
int frameStack = 0;
|
|
justburner |
541192 |
int frameSkip = 3;
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
HANDLE hProcess = GetCurrentProcess();
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES |
|
|
justburner |
541192 |
SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_LINES |
|
|
justburner |
541192 |
SYMOPT_UNDNAME);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
SymInitialize(hProcess, NULL, TRUE);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
CONTEXT context;
|
|
justburner |
a66c51 |
RtlCaptureContext(&context);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
char sourceSymMem[sizeof(IMAGEHLP_SYMBOL64) + 1025];
|
|
justburner |
a66c51 |
PIMAGEHLP_SYMBOL64 sourceSym = (PIMAGEHLP_SYMBOL64)&sourceSymMem;
|
|
justburner |
a66c51 |
memset(sourceSymMem, 0, sizeof(sourceSymMem));
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
IMAGEHLP_LINE64 sourceInfo;
|
|
justburner |
a66c51 |
memset(&sourceInfo, 0, sizeof(IMAGEHLP_LINE64));
|
|
justburner |
a66c51 |
sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
IMAGEHLP_MODULE64 moduleInfo;
|
|
justburner |
541192 |
memset(&moduleInfo, 0, sizeof(moduleInfo));
|
|
justburner |
541192 |
moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
|
|
justburner |
541192 |
|
|
justburner |
a66c51 |
STACKFRAME64 stackframe;
|
|
justburner |
a66c51 |
memset(&stackframe, 0, sizeof(STACKFRAME64));
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
#ifdef _WIN64
|
|
justburner |
a66c51 |
int machineType = IMAGE_FILE_MACHINE_AMD64;
|
|
justburner |
a66c51 |
stackframe.AddrPC.Offset = context.Rip;
|
|
justburner |
a66c51 |
stackframe.AddrPC.Mode = AddrModeFlat;
|
|
justburner |
a66c51 |
stackframe.AddrStack.Offset = context.Rsp;
|
|
justburner |
a66c51 |
stackframe.AddrStack.Mode = AddrModeFlat;
|
|
justburner |
a66c51 |
stackframe.AddrFrame.Offset = context.Rbp;
|
|
justburner |
a66c51 |
stackframe.AddrFrame.Mode = AddrModeFlat;
|
|
justburner |
a66c51 |
#else
|
|
justburner |
a66c51 |
int machineType = IMAGE_FILE_MACHINE_I386;
|
|
justburner |
a66c51 |
stackframe.AddrPC.Offset = context.Eip;
|
|
justburner |
a66c51 |
stackframe.AddrPC.Mode = AddrModeFlat;
|
|
justburner |
a66c51 |
stackframe.AddrStack.Offset = context.Esp;
|
|
justburner |
a66c51 |
stackframe.AddrStack.Mode = AddrModeFlat;
|
|
justburner |
a66c51 |
stackframe.AddrFrame.Offset = context.Ebp;
|
|
justburner |
a66c51 |
stackframe.AddrFrame.Mode = AddrModeFlat;
|
|
justburner |
a66c51 |
#endif
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
HANDLE hThread = GetCurrentThread();
|
|
justburner |
a66c51 |
while (StackWalk64(machineType, hProcess, hThread, &stackframe, &context,
|
|
justburner |
a66c51 |
NULL, SymFunctionTableAccess64, SymGetModuleBase64,
|
|
justburner |
a66c51 |
NULL)) {
|
|
justburner |
a66c51 |
// Skip first frames since they point to this function
|
|
justburner |
a66c51 |
if (frameStack++ < frameSkip) continue;
|
|
justburner |
a66c51 |
char numStr[32];
|
|
justburner |
a66c51 |
memset(numStr, 0, sizeof(numStr));
|
|
justburner |
a66c51 |
sprintf(numStr, "%3i> ", frameStack - frameSkip);
|
|
justburner |
a66c51 |
out.append(numStr);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
sourceSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
|
justburner |
a66c51 |
sourceSym->MaxNameLength = 1024;
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
// Get symbol name
|
|
justburner |
a66c51 |
DWORD64 displacement64;
|
|
justburner |
a66c51 |
if (SymGetSymFromAddr64(hProcess, (ULONG64)stackframe.AddrPC.Offset,
|
|
justburner |
a66c51 |
&displacement64, sourceSym)) {
|
|
justburner |
a66c51 |
out.append(sourceSym->Name);
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
// Get module filename
|
|
justburner |
541192 |
char moduleFile[512];
|
|
justburner |
541192 |
MEMORY_BASIC_INFORMATION mbi;
|
|
justburner |
541192 |
VirtualQuery((LPCVOID)stackframe.AddrPC.Offset, &mbi, sizeof(mbi));
|
|
justburner |
541192 |
GetModuleFileNameA((HMODULE)mbi.AllocationBase, moduleFile, 512);
|
|
justburner |
541192 |
|
|
justburner |
a66c51 |
// Get source filename and line
|
|
justburner |
a66c51 |
DWORD displacement32;
|
|
justburner |
a66c51 |
if (SymGetLineFromAddr64(hProcess, stackframe.AddrPC.Offset,
|
|
justburner |
a66c51 |
&displacement32, &sourceInfo) != FALSE) {
|
|
justburner |
a66c51 |
out.append(" {");
|
|
justburner |
541192 |
out.append(filenameOnly(sourceInfo.FileName));
|
|
justburner |
a66c51 |
out.append(":");
|
|
justburner |
a66c51 |
out.append(std::to_string(sourceInfo.LineNumber));
|
|
justburner |
a66c51 |
out.append("}");
|
|
justburner |
a66c51 |
} else {
|
|
justburner |
a66c51 |
memset(numStr, 0, sizeof(numStr));
|
|
justburner |
a66c51 |
sprintf(numStr, " [0x%" PRIx64 "]", stackframe.AddrPC.Offset);
|
|
justburner |
a66c51 |
out.append(numStr);
|
|
justburner |
a66c51 |
}
|
|
justburner |
541192 |
out.append(" <");
|
|
justburner |
541192 |
out.append(filenameOnly(moduleFile));
|
|
justburner |
541192 |
out.append(">");
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
out.append("\n");
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
SymCleanup(hProcess);
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
|
|
justburner |
541192 |
LONG WINAPI exceptionHandler(PEXCEPTION_POINTERS info) {
|
|
justburner |
bc2c3b |
static volatile bool handling = false;
|
|
justburner |
541192 |
|
|
justburner |
bc2c3b |
const char *reason = "Unknown";
|
|
justburner |
541192 |
switch (info->ExceptionRecord->ExceptionCode) {
|
|
justburner |
541192 |
case EXCEPTION_ACCESS_VIOLATION:
|
|
justburner |
541192 |
reason = "EXCEPTION_ACCESS_VIOLATION";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
justburner |
541192 |
reason = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
justburner |
541192 |
reason = "EXCEPTION_DATATYPE_MISALIGNMENT";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
justburner |
541192 |
reason = "EXCEPTION_FLT_DENORMAL_OPERAND";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
justburner |
541192 |
reason = "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
justburner |
541192 |
reason = "EXCEPTION_FLT_INEXACT_RESULT";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
justburner |
541192 |
reason = "EXCEPTION_FLT_INVALID_OPERATION";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_FLT_OVERFLOW:
|
|
justburner |
541192 |
reason = "EXCEPTION_FLT_OVERFLOW";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_FLT_STACK_CHECK:
|
|
justburner |
541192 |
reason = "EXCEPTION_FLT_STACK_CHECK";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_FLT_UNDERFLOW:
|
|
justburner |
541192 |
reason = "EXCEPTION_FLT_UNDERFLOW";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
justburner |
541192 |
reason = "EXCEPTION_ILLEGAL_INSTRUCTION";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_IN_PAGE_ERROR:
|
|
justburner |
541192 |
reason = "EXCEPTION_IN_PAGE_ERROR";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
justburner |
541192 |
reason = "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_INVALID_DISPOSITION:
|
|
justburner |
541192 |
reason = "EXCEPTION_INVALID_DISPOSITION";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
justburner |
541192 |
reason = "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_PRIV_INSTRUCTION:
|
|
justburner |
541192 |
reason = "EXCEPTION_PRIV_INSTRUCTION";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
case EXCEPTION_STACK_OVERFLOW:
|
|
justburner |
541192 |
reason = "EXCEPTION_STACK_OVERFLOW";
|
|
justburner |
541192 |
break;
|
|
justburner |
541192 |
default:
|
|
justburner |
41a8e8 |
return EXCEPTION_CONTINUE_SEARCH;
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
|
|
justburner |
bc2c3b |
// Avoid new exceptions inside the crash handler
|
|
justburner |
bc2c3b |
if (handling) return EXCEPTION_CONTINUE_SEARCH;
|
|
justburner |
bc2c3b |
|
|
justburner |
bc2c3b |
handling = true;
|
|
justburner |
541192 |
if (CrashHandler::trigger(reason, true)) _Exit(1);
|
|
justburner |
bc2c3b |
handling = false;
|
|
justburner |
541192 |
|
|
justburner |
41a8e8 |
return EXCEPTION_CONTINUE_SEARCH;
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
|
|
justburner |
541192 |
#endif
|
|
justburner |
541192 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
// Linux and Mac OS X platform functions
|
|
justburner |
541192 |
|
|
justburner |
541192 |
#ifndef _WIN32
|
|
justburner |
541192 |
|
|
justburner |
541192 |
static bool sh(std::string &out, const char *cmd) {
|
|
justburner |
541192 |
char buffer[128];
|
|
justburner |
541192 |
FILE *p = popen(cmd, "r");
|
|
justburner |
541192 |
if (p == NULL) return false;
|
|
justburner |
541192 |
while (fgets(buffer, 128, p)) out.append(buffer);
|
|
justburner |
541192 |
pclose(p);
|
|
justburner |
541192 |
return true;
|
|
justburner |
541192 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
|
|
justburner |
541192 |
static bool addr2line(std::string &out, const char *exepath, const char *addr) {
|
|
justburner |
541192 |
char cmd[512];
|
|
justburner |
541192 |
#ifdef OSX
|
|
justburner |
541192 |
sprintf(cmd, "atos -o \"%.400s\" %s 2>&1", exepath, addr);
|
|
justburner |
a66c51 |
#else
|
|
justburner |
541192 |
sprintf(cmd, "addr2line -f -p -e \"%.400s\" %s 2>&1", exepath, addr);
|
|
justburner |
541192 |
#endif
|
|
justburner |
541192 |
return sh(out, cmd);
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
|
|
justburner |
541192 |
static bool generateMinidump(TFilePath dumpFile) { return false; }
|
|
justburner |
541192 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
|
|
justburner |
541192 |
static void printModules(std::string &out) {}
|
|
justburner |
541192 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
|
|
justburner |
541192 |
#define HAS_BACKTRACE
|
|
justburner |
541192 |
static void printBacktrace(std::string &out) {
|
|
justburner |
541192 |
int frameStack = 0;
|
|
justburner |
541192 |
int frameSkip = 3;
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
const int size = 256;
|
|
justburner |
a66c51 |
void *buffer[size];
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
// Get executable path
|
|
justburner |
a66c51 |
char exepath[512];
|
|
justburner |
a66c51 |
memset(exepath, 0, 512);
|
|
justburner |
541192 |
if (readlink("/proc/self/exe", exepath, 512) < 0)
|
|
justburner |
541192 |
fprintf(stderr, "Couldn't get exe path\n");
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
// Back trace
|
|
justburner |
a66c51 |
int nptrs = backtrace(buffer, size);
|
|
justburner |
a66c51 |
char **bts = backtrace_symbols(buffer, nptrs);
|
|
justburner |
a66c51 |
std::regex re("\\[(.+)\\]");
|
|
justburner |
a66c51 |
if (bts) {
|
|
justburner |
a66c51 |
for (int i = 0; i < nptrs; ++i) {
|
|
justburner |
a66c51 |
// Skip first frames since they point to this function
|
|
justburner |
a66c51 |
if (frameStack++ < frameSkip) continue;
|
|
justburner |
a66c51 |
char numStr[32];
|
|
justburner |
a66c51 |
memset(numStr, 0, sizeof(numStr));
|
|
justburner |
a66c51 |
sprintf(numStr, "%3i> ", frameStack - frameSkip);
|
|
justburner |
a66c51 |
out.append(numStr);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
std::string sym = bts[i];
|
|
justburner |
a66c51 |
std::string line;
|
|
justburner |
a66c51 |
std::smatch ms;
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
bool found = false;
|
|
justburner |
a66c51 |
if (std::regex_search(sym, ms, re)) {
|
|
justburner |
a66c51 |
std::string addr = ms[1];
|
|
justburner |
a66c51 |
if (addr2line(line, exepath, addr.c_str())) {
|
|
justburner |
a66c51 |
found = (line.rfind("??", 0) != 0);
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
out.append(found ? line : (sym + "\n"));
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
free(bts);
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
void signalHandler(int sig) {
|
|
justburner |
bc2c3b |
static volatile bool handling = false;
|
|
justburner |
a66c51 |
|
|
justburner |
bc2c3b |
const char *reason = "Unknown";
|
|
justburner |
a66c51 |
switch (sig) {
|
|
justburner |
a66c51 |
case SIGABRT:
|
|
justburner |
a66c51 |
reason = "(SIGABRT) Usually caused by an abort() or assert()";
|
|
justburner |
a66c51 |
break;
|
|
justburner |
a66c51 |
case SIGFPE:
|
|
justburner |
a66c51 |
reason = "(SIGFPE) Arithmetic exception, such as divide by zero";
|
|
justburner |
a66c51 |
break;
|
|
justburner |
a66c51 |
case SIGILL:
|
|
justburner |
a66c51 |
reason = "(SIGILL) Illegal instruction";
|
|
justburner |
a66c51 |
break;
|
|
justburner |
a66c51 |
case SIGINT:
|
|
justburner |
a66c51 |
reason = "(SIGINT) Interactive attention signal, (usually ctrl+c)";
|
|
justburner |
a66c51 |
break;
|
|
justburner |
a66c51 |
case SIGSEGV:
|
|
justburner |
a66c51 |
reason = "(SIGSEGV) Segmentation Fault";
|
|
justburner |
a66c51 |
break;
|
|
justburner |
a66c51 |
case SIGTERM:
|
|
justburner |
a66c51 |
reason = "(SIGTERM) A termination request was sent to the program";
|
|
justburner |
a66c51 |
break;
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
bc2c3b |
// Avoid new signals inside the crash handler
|
|
justburner |
bc2c3b |
if (handling) return;
|
|
justburner |
bc2c3b |
|
|
justburner |
bc2c3b |
handling = true;
|
|
justburner |
a66c51 |
if (CrashHandler::trigger(reason, true)) _Exit(1);
|
|
justburner |
bc2c3b |
handling = false;
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
541192 |
#endif
|
|
justburner |
541192 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
|
|
justburner |
541192 |
static void printSysInfo(std::string &out) {
|
|
justburner |
541192 |
out.append("Build ABI: " + QSysInfo::buildAbi().toStdString() + "\n");
|
|
justburner |
541192 |
out.append("Operating System: " + QSysInfo::prettyProductName().toStdString() + "\n");
|
|
justburner |
541192 |
out.append("OS Kernel: " + QSysInfo::kernelVersion().toStdString() + "\n");
|
|
justburner |
541192 |
out.append("CPU Threads: " + std::to_string(QThread::idealThreadCount()) + "\n");
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
|
|
justburner |
541192 |
//-----------------------------------------------------------------------------
|
|
justburner |
541192 |
|
|
justburner |
541192 |
static void printGPUInfo(std::string &out) {
|
|
justburner |
bc2c3b |
const char *gpuVendorName = (const char *)glGetString(GL_VENDOR);
|
|
justburner |
bc2c3b |
const char *gpuModelName = (const char *)glGetString(GL_RENDERER);
|
|
justburner |
bc2c3b |
const char *gpuVersion = (const char *)glGetString(GL_VERSION);
|
|
justburner |
bc2c3b |
if (gpuVendorName)
|
|
justburner |
bc2c3b |
out.append("GPU Vendor: " + std::string(gpuVendorName) + "\n");
|
|
justburner |
bc2c3b |
if (gpuModelName)
|
|
justburner |
bc2c3b |
out.append("GPU Model: " + std::string(gpuModelName) + "\n");
|
|
justburner |
bc2c3b |
if (gpuVersion)
|
|
justburner |
bc2c3b |
out.append("GPU Version: " + std::string(gpuVersion) + "\n");
|
|
justburner |
541192 |
}
|
|
justburner |
541192 |
|
|
justburner |
a66c51 |
//-----------------------------------------------------------------------------
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
CrashHandler::CrashHandler(QWidget *parent, TFilePath crashFile, QString crashReport)
|
|
justburner |
a66c51 |
: QDialog(parent), m_crashFile(crashFile), m_crashReport(crashReport) {
|
|
justburner |
a66c51 |
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
QStringList sl;
|
|
justburner |
a66c51 |
sl.append(tr("OpenToonz crashed unexpectedly."));
|
|
justburner |
a66c51 |
sl.append("");
|
|
justburner |
a66c51 |
sl.append(tr("A crash report has been generated."));
|
|
justburner |
a66c51 |
sl.append(
|
|
justburner |
a66c51 |
tr("To report, click 'Open Issue Webpage' to access OpenToonz's Issues "
|
|
justburner |
a66c51 |
"page on GitHub."));
|
|
justburner |
a66c51 |
sl.append(tr("Click on the 'New issue' button and fill out the form."));
|
|
justburner |
a66c51 |
sl.append("");
|
|
justburner |
a66c51 |
sl.append(tr("System Configuration and Problem Details:"));
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
QLabel *headtext = new QLabel(sl.join(" "));
|
|
justburner |
a66c51 |
headtext->setTextFormat(Qt::RichText);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
QTextEdit *reportTxt = new QTextEdit();
|
|
justburner |
a66c51 |
reportTxt->setText(crashReport);
|
|
justburner |
a66c51 |
reportTxt->setReadOnly(true);
|
|
justburner |
a66c51 |
reportTxt->setLineWrapMode(QTextEdit::LineWrapMode::NoWrap);
|
|
justburner |
a66c51 |
reportTxt->setStyleSheet(
|
|
justburner |
a66c51 |
"background:white;\ncolor:black;\nborder:1 solid black;");
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
QVBoxLayout *mainLayout = new QVBoxLayout();
|
|
justburner |
a66c51 |
QHBoxLayout *buttonsLay = new QHBoxLayout();
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
QPushButton *copyBtn = new QPushButton(tr("Copy to Clipboard"));
|
|
justburner |
a66c51 |
QPushButton *webBtn = new QPushButton(tr("Open Issue Webpage"));
|
|
justburner |
4635e8 |
QPushButton *folderBtn = new QPushButton(tr("Open Reports Folder"));
|
|
justburner |
a66c51 |
QPushButton *closeBtn = new QPushButton(tr("Close Application"));
|
|
justburner |
a66c51 |
buttonsLay->addWidget(copyBtn);
|
|
justburner |
a66c51 |
buttonsLay->addWidget(webBtn);
|
|
justburner |
4635e8 |
buttonsLay->addWidget(folderBtn);
|
|
justburner |
a66c51 |
buttonsLay->addWidget(closeBtn);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
mainLayout->addWidget(headtext);
|
|
justburner |
a66c51 |
mainLayout->addWidget(reportTxt);
|
|
justburner |
a66c51 |
mainLayout->addLayout(buttonsLay);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
bool ret = connect(copyBtn, SIGNAL(clicked()), this, SLOT(copyClipboard()));
|
|
justburner |
4635e8 |
ret = ret && connect(webBtn, SIGNAL(clicked()), this, SLOT(openWebpage()));
|
|
justburner |
4635e8 |
ret = ret && connect(folderBtn, SIGNAL(clicked()), this, SLOT(openFolder()));
|
|
justburner |
4635e8 |
ret = ret && connect(closeBtn, SIGNAL(clicked()), this, SLOT(accept()));
|
|
justburner |
a66c51 |
if (!ret) throw TException();
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
setWindowTitle(tr("OpenToonz crashed!"));
|
|
justburner |
a66c51 |
setLayout(mainLayout);
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
void CrashHandler::reject() {
|
|
justburner |
a66c51 |
QStringList sl;
|
|
justburner |
a66c51 |
sl.append(tr("Application is in unstable state and must be restarted."));
|
|
justburner |
4635e8 |
sl.append(tr("Resuming is not recommended and may lead to an unrecoverable crash."));
|
|
justburner |
a66c51 |
sl.append(tr("Ignore advice and try to resume program?"));
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
QMessageBox::StandardButton reply =
|
|
justburner |
a66c51 |
QMessageBox::question(this, tr("Ignore crash?"), sl.join("\n"),
|
|
justburner |
a66c51 |
QMessageBox::Yes | QMessageBox::No);
|
|
justburner |
a66c51 |
if (reply == QMessageBox::Yes) {
|
|
justburner |
a66c51 |
QDialog::reject();
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
//-----------------------------------------------------------------------------
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
void CrashHandler::copyClipboard() {
|
|
justburner |
a66c51 |
QClipboard *clipboard = QApplication::clipboard();
|
|
justburner |
a66c51 |
clipboard->setText(m_crashReport);
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
//-----------------------------------------------------------------------------
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
void CrashHandler::openWebpage() {
|
|
justburner |
a66c51 |
QDesktopServices::openUrl(QUrl("https://github.com/opentoonz/opentoonz/issues"));
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
//-----------------------------------------------------------------------------
|
|
justburner |
a66c51 |
|
|
justburner |
4635e8 |
void CrashHandler::openFolder() {
|
|
justburner |
4635e8 |
TFilePath fp = ToonzFolder::getCrashReportFolder();
|
|
justburner |
4635e8 |
QDesktopServices::openUrl(QUrl("file:///" + fp.getQString()));
|
|
justburner |
4635e8 |
}
|
|
justburner |
4635e8 |
|
|
justburner |
4635e8 |
//-----------------------------------------------------------------------------
|
|
justburner |
4635e8 |
|
|
justburner |
a66c51 |
void CrashHandler::install() {
|
|
justburner |
541192 |
#ifdef _WIN32
|
|
justburner |
541192 |
// std library seems to override this
|
|
justburner |
541192 |
//SetUnhandledExceptionFilter(exceptionHandler);
|
|
justburner |
541192 |
|
|
justburner |
541192 |
void *handler = AddVectoredExceptionHandler(0, exceptionHandler);
|
|
justburner |
541192 |
assert(handler != NULL);
|
|
justburner |
541192 |
//RemoveVectoredExceptionHandler(handler);
|
|
justburner |
541192 |
#else
|
|
justburner |
541192 |
signal(SIGABRT, signalHandler);
|
|
justburner |
541192 |
signal(SIGFPE, signalHandler);
|
|
justburner |
541192 |
signal(SIGILL, signalHandler);
|
|
justburner |
541192 |
signal(SIGINT, signalHandler);
|
|
justburner |
541192 |
signal(SIGSEGV, signalHandler);
|
|
justburner |
541192 |
signal(SIGTERM, signalHandler);
|
|
justburner |
541192 |
#endif
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
//-----------------------------------------------------------------------------
|
|
justburner |
a66c51 |
|
|
justburner |
bc2c3b |
void CrashHandler::reportProjectInfo(bool enableReport) {
|
|
justburner |
bc2c3b |
s_reportProjInfo = enableReport;
|
|
justburner |
bc2c3b |
}
|
|
justburner |
bc2c3b |
|
|
justburner |
bc2c3b |
//-----------------------------------------------------------------------------
|
|
justburner |
bc2c3b |
|
|
justburner |
bc2c3b |
void CrashHandler::attachParentWindow(QWidget *parent) {
|
|
justburner |
bc2c3b |
s_parentWindow = parent;
|
|
justburner |
bc2c3b |
}
|
|
justburner |
bc2c3b |
|
|
justburner |
bc2c3b |
//-----------------------------------------------------------------------------
|
|
justburner |
bc2c3b |
|
|
justburner |
a66c51 |
bool CrashHandler::trigger(const QString reason, bool showDialog) {
|
|
justburner |
541192 |
char fileName[128];
|
|
justburner |
541192 |
char dumpName[128];
|
|
justburner |
541192 |
char dateName[128];
|
|
justburner |
a66c51 |
std::string out;
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
// Get time and build filename
|
|
justburner |
a66c51 |
time_t acc_time;
|
|
justburner |
a66c51 |
time(&acc_time);
|
|
justburner |
a66c51 |
struct tm *tm = localtime(&acc_time);
|
|
justburner |
a66c51 |
strftime(dateName, 128, "%Y-%m-%d %H:%M:%S", tm);
|
|
justburner |
a66c51 |
strftime(fileName, 128, "Crash-%Y%m%d-%H%M%S.log", tm);
|
|
justburner |
541192 |
strftime(dumpName, 128, "Crash-%Y%m%d-%H%M%S.dmp", tm);
|
|
justburner |
541192 |
TFilePath fpCrsh = ToonzFolder::getCrashReportFolder() + fileName;
|
|
justburner |
541192 |
TFilePath fpDump = ToonzFolder::getCrashReportFolder() + dumpName;
|
|
justburner |
541192 |
|
|
justburner |
541192 |
// Generate minidump
|
|
justburner |
541192 |
bool minidump = generateMinidump(fpDump);
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
// Generate report
|
|
justburner |
a66c51 |
try {
|
|
justburner |
a66c51 |
out.append(TEnv::getApplicationFullName() + " (Build " + __DATE__ ")\n");
|
|
justburner |
a66c51 |
out.append("\nReport Date: ");
|
|
justburner |
a66c51 |
out.append(dateName);
|
|
justburner |
a66c51 |
out.append("\nCrash Reason: ");
|
|
justburner |
a66c51 |
out.append(reason.toStdString());
|
|
justburner |
a66c51 |
out.append("\n\n");
|
|
justburner |
541192 |
printSysInfo(out);
|
|
justburner |
a66c51 |
out.append("\n");
|
|
justburner |
541192 |
printGPUInfo(out);
|
|
justburner |
a66c51 |
out.append("\nCrash File: ");
|
|
justburner |
541192 |
out.append(fpCrsh.getQString().toStdString());
|
|
justburner |
541192 |
#ifdef HAS_MINIDUMP
|
|
justburner |
541192 |
out.append("\nMini Dump File: ");
|
|
justburner |
541192 |
if (minidump)
|
|
justburner |
541192 |
out.append(fpDump.getQString().toStdString());
|
|
justburner |
541192 |
else
|
|
justburner |
541192 |
out.append("Failed");
|
|
justburner |
541192 |
#endif
|
|
justburner |
41a8e8 |
out.append("\n");
|
|
justburner |
41a8e8 |
} catch (...) {
|
|
justburner |
41a8e8 |
}
|
|
justburner |
41a8e8 |
try {
|
|
justburner |
bc2c3b |
if (s_reportProjInfo) {
|
|
justburner |
bc2c3b |
TProjectManager *pm = TProjectManager::instance();
|
|
justburner |
bc2c3b |
TApp *app = TApp::instance();
|
|
justburner |
41a8e8 |
|
|
justburner |
bc2c3b |
TProjectP currentProject = pm->getCurrentProject();
|
|
justburner |
bc2c3b |
TFilePath projectPath = currentProject->getProjectPath();
|
|
justburner |
41a8e8 |
|
|
justburner |
bc2c3b |
ToonzScene *currentScene = app->getCurrentScene()->getScene();
|
|
justburner |
bc2c3b |
std::wstring sceneName = currentScene->getSceneName();
|
|
justburner |
41a8e8 |
|
|
justburner |
bc2c3b |
out.append("\nApplication Dir: ");
|
|
justburner |
bc2c3b |
out.append(QCoreApplication::applicationDirPath().toStdString());
|
|
justburner |
bc2c3b |
out.append("\nStuff Dir: ");
|
|
justburner |
bc2c3b |
out.append(TEnv::getStuffDir().getQString().toStdString());
|
|
justburner |
bc2c3b |
out.append("\n");
|
|
justburner |
bc2c3b |
out.append("\nProject Name: ");
|
|
justburner |
bc2c3b |
out.append(currentProject->getName().getQString().toStdString());
|
|
justburner |
bc2c3b |
out.append("\nScene Name: ");
|
|
justburner |
bc2c3b |
out.append(QString::fromStdWString(sceneName).toStdString());
|
|
justburner |
bc2c3b |
out.append("\nProject Path: ");
|
|
justburner |
bc2c3b |
out.append(projectPath.getQString().toStdString());
|
|
justburner |
bc2c3b |
out.append("\nScene Path: ");
|
|
justburner |
bc2c3b |
out.append(currentScene->getScenePath().getQString().toStdString());
|
|
justburner |
bc2c3b |
out.append("\n");
|
|
justburner |
bc2c3b |
}
|
|
justburner |
41a8e8 |
} catch (...) {
|
|
justburner |
41a8e8 |
}
|
|
justburner |
541192 |
#ifdef HAS_MODULES
|
|
justburner |
41a8e8 |
try {
|
|
justburner |
541192 |
out.append("\n==== Modules ====\n");
|
|
justburner |
541192 |
printModules(out);
|
|
justburner |
541192 |
out.append("==== End ====\n");
|
|
justburner |
41a8e8 |
} catch (...) {
|
|
justburner |
41a8e8 |
}
|
|
justburner |
541192 |
#endif
|
|
justburner |
541192 |
#ifdef HAS_BACKTRACE
|
|
justburner |
41a8e8 |
try {
|
|
justburner |
541192 |
out.append("\n==== Backtrace ====\n");
|
|
justburner |
541192 |
printBacktrace(out);
|
|
justburner |
a66c51 |
out.append("==== End ====\n");
|
|
justburner |
a66c51 |
} catch (...) {
|
|
justburner |
a66c51 |
}
|
|
justburner |
41a8e8 |
#endif
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
// Save to crash information to file
|
|
justburner |
541192 |
FILE *fw = fopen(fpCrsh, "w");
|
|
justburner |
a66c51 |
if (fw != NULL) {
|
|
justburner |
a66c51 |
fwrite(out.c_str(), 1, out.size(), fw);
|
|
justburner |
a66c51 |
fclose(fw);
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
if (showDialog) {
|
|
justburner |
a66c51 |
// Show crash handler dialog
|
|
justburner |
bc2c3b |
CrashHandler crashdialog(s_parentWindow, fpCrsh, QString::fromStdString(out));
|
|
justburner |
a66c51 |
return crashdialog.exec() != QDialog::Rejected;
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
return true;
|
|
justburner |
a66c51 |
}
|
|
justburner |
a66c51 |
|
|
justburner |
a66c51 |
//-----------------------------------------------------------------------------
|