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
//-----------------------------------------------------------------------------