Merge pull request #1187 from manongjohn/replace_3rd_party_crashrpt

Replace 3rd party crashrpt
This commit is contained in:
manongjohn 2023-07-12 21:37:51 -04:00 committed by GitHub
commit 017754a47c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 717 additions and 164 deletions

View file

@ -26,12 +26,7 @@ body:
id: logs
attributes:
label: Relevant logs/screenshots
description: If available, please provide relevant logs or screenshots.
placeholder: |
Crash logs or dmp files may be found under the following:
Windows: crash dmp file - C:\Users\yourusername\AppData\Local\Tahoma2D\Tahoma2D\cache\crashrpt\xxxx\crashdump.dmp
crash logs - Event Viewer -> Windows Logs -> Application. Look for Tahoma2D crash report
macOS: crash logs - Console App -> User Reports. Look for Tahoma2D crash report
description: If available, please provide relevant logs, screenshots or short videos. Windows users can add zipped crash dmp files.
validations:
required: false
- type: markdown
@ -45,6 +40,7 @@ body:
label: Software Version
description: What version of the software are you running?
options:
- 1.3.1
- 1.3
- Nightly (Specify below)
- 1.2

View file

@ -46,10 +46,6 @@ jobs:
with:
name: Tahoma2D-win
path: artifact
- uses: actions/upload-artifact@v1
with:
name: debug-symbols.zip
path: toonz\build\debug-symbols.zip
- name: Get Nightly Release Date
if: ${{ github.repository_owner == 'tahoma2d' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
run: |
@ -74,7 +70,7 @@ jobs:
with:
allowUpdates: true
artifactErrorsFailBuild: false
artifacts: toonz\build\Tahoma2D-win.zip,toonz\build\debug-symbols.zip
artifacts: toonz\build\Tahoma2D-win.zip
artifactContentType: "raw"
body: ${{ github.event.head_commit.message }}
name: Latest Nightly ${{ env.NIGHTLYDATE }}
@ -88,7 +84,7 @@ jobs:
with:
allowUpdates: true
artifactErrorsFailBuild: false
artifacts: toonz\build\Tahoma2D-win.zip,toonz\build\debug-symbols.zip
artifacts: toonz\build\Tahoma2D-win.zip
artifactContentType: "raw"
body: ${{ github.event.head_commit.message }}
name: ${{ env.NIGHTLYDATETIME }}

View file

@ -28,10 +28,7 @@ IF EXIST ..\..\thirdparty\canon\Header set WITH_CANON=Y
set WITH_GPHOTO2=N
IF EXIST ..\..\thirdparty\libgphoto2\include set WITH_GPHOTO2=Y
set WITH_CRASHRPT=N
IF EXIST ..\..\thirdparty\crashrpt\include set WITH_CRASHRPT=Y
cmake ..\sources -G %MSVCVERSION% -Ax64 -DQT_PATH=%QT_PATH% -DBOOST_ROOT=%BOOST_ROOT% -DOpenCV_DIR=%OPENCV_DIR% -DWITH_CANON=%WITH_CANON% -DWITH_GPHOTO2=%WITH_GPHOTO2% -DWITH_CRASHRPT=%WITH_CRASHRPT%
cmake ..\sources -G %MSVCVERSION% -Ax64 -DQT_PATH=%QT_PATH% -DBOOST_ROOT=%BOOST_ROOT% -DOpenCV_DIR=%OPENCV_DIR% -DWITH_CANON=%WITH_CANON% -DWITH_GPHOTO2=%WITH_GPHOTO2%
IF EXIST C:\ProgramData\chocolatey\bin\cl.exe (

View file

@ -10,10 +10,6 @@ echo ">>> Copy and configure Tahoma2D installation"
copy /y RelWithDebInfo\*.* Tahoma2D
REM Remove PDB and ILK files
del Tahoma2D\*.pdb
del Tahoma2D\*.ilk
copy /Y ..\..\thirdparty\freeglut\bin\x64\freeglut.dll Tahoma2D
copy /Y ..\..\thirdparty\glew\glew-1.9.0\bin\64bit\glew32.dll Tahoma2D
copy /Y ..\..\thirdparty\libmypaint\dist\64\libiconv-2.dll Tahoma2D
@ -36,11 +32,8 @@ IF EXIST ..\..\thirdparty\libgphoto2\include (
xcopy /Y /E ..\..\thirdparty\libgphoto2\bin Tahoma2D
)
IF EXIST ..\..\thirdparty\crashrpt\include (
copy /Y ..\..\thirdparty\apps\crashrpt\CrashRpt1500.dll Tahoma2D
copy /Y ..\..\thirdparty\apps\crashrpt\CrashSender1500.exe Tahoma2D
copy /Y ..\..\thirdparty\apps\crashrpt\crashrpt_lang.ini Tahoma2D
)
REM Remove ILK files
del Tahoma2D\*.ilk
echo ">>> Copying stuff to Tahoma2D\tahomastuff"
@ -80,14 +73,4 @@ echo ">>> Creating Tahoma2D Windows package"
IF EXIST Tahoma2D-win.zip del Tahoma2D-win.zip
7z a Tahoma2D-win.zip Tahoma2D
IF EXIST ..\..\..\tahoma2d_symbols (
echo ">>> Saving debugging symbols"
mkdir ..\..\..\tahoma2d_symbols\%date:~10,4%-%date:~4,2%-%date:~7,2%
copy /y RelWithDebInfo\*.* ..\..\..\tahoma2d_symbols\%date:~10,4%-%date:~4,2%-%date:~7,2%
) else (
echo ">>> Creating debugging symbols package"
IF EXIST debug-symbols.zip del debug-symbols.zip
7z a debug-symbols.zip RelWithDebInfo\*.*
)
cd ../..

View file

@ -5,17 +5,6 @@ IF NOT EXIST apps mkdir apps
cd apps
echo * > .gitignore
echo ">>> Getting CrashRpt"
IF EXIST crashrpt rmdir /S /Q crashrpt
curl -fsSL -o crashrpt-tahoma2d-win_2019.zip https://github.com/tahoma2d/crashrpt2/releases/download/v1.5.0.0/crashrpt-tahoma2d-win_2019.zip
7z x crashrpt-tahoma2d-win_2019.zip
rename crashrpt-tahoma2d-win_2019 crashrpt
IF EXIST ..\crashrpt\include rmdir /S /Q ..\crashrpt\include
IF EXIST ..\crashrpt\CrashRpt1500.lib del ..\crashrpt\CrashRpt1500.lib
move crashrpt\include ..\crashrpt
move crashrpt\CrashRpt1500.lib ..\crashrpt
echo ">>> Getting FFmpeg"
IF EXIST ffmpeg rmdir /S /Q ffmpeg

View file

@ -1,28 +0,0 @@
CrashRpt
Copyright (c) 2003, The CrashRpt Project Authors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,2 +0,0 @@
include/*
*.lib

View file

@ -1,10 +0,0 @@
For Windows Builds Only!
Copy the include folder from the CrashRpt source to here.
Compile CrashRpt and copy the CrashRptXXXX.lib here also.
When releasing with CrashRpt, include:
CrashRptXXXX.dll
CrashSenderXXXX.exe
crashrpt_lang.ini
dbghelp.dll (optional)

View file

@ -107,7 +107,6 @@ option(WITH_SYSTEM_SUPERLU "Use the system SuperLU library instead of 'thirdpary
option(WITH_CANON "Build with Canon DSLR support - Requires Canon SDK" OFF)
option(WITH_GPHOTO2 "Build with Libgphoto2" OFF)
option(WITH_TRANSLATION "Generate translation projects as well" ON)
option(WITH_CRASHRPT "Build CrashRpt support - Requires CrashRpt Library" OFF)
option(WITH_WINTAB "(Windows only) Build with customized Qt with WinTab support. https://github.com/shun-iwasawa/qt5/releases/tag/v5.15.2_wintab" OFF)
# avoid using again
@ -284,12 +283,6 @@ if(BUILD_ENV_MSVC)
-DGLUT_NO_LIB_PRAGMA
)
if(WITH_CRASHRPT)
include_directories(
${SDKROOT}/crashrpt/include
)
endif()
if(WITH_GPHOTO2)
include_directories(
${SDKROOT}/libgphoto2/include
@ -399,10 +392,6 @@ if(BUILD_ENV_MSVC)
set(MYPAINT_LIB_INCLUDE_DIRS ${SDKROOT}/libmypaint/dist/${PLATFORM}/include/libmypaint)
set(MYPAINT_LIB_LDFLAGS ${SDKROOT}/libmypaint/dist/${PLATFORM}/libmypaint.lib)
if(WITH_CRASHRPT)
set(CRASHRPT_LIB ${SDKROOT}/crashrpt/CrashRpt1500.lib)
endif()
if(WITH_GPHOTO2)
set(GPHOTO2_LIB ${SDKROOT}/libgphoto2/lib/libgphoto2.lib)
set(GPHOTO2_PORT_LIB ${SDKROOT}/libgphoto2/lib/libgphoto2_port.lib)

View file

@ -18,13 +18,6 @@
#include <sstream>
class TStringConvertException final : public TException {
std::string m_string;
public:
TStringConvertException(const std::string str) : m_string(str) {}
};
std::wstring to_wstring(std::string s) {
#ifdef TNZCORE_LIGHT
std::wstring ws;

View file

@ -44,6 +44,7 @@ DVAPI TFilePath getLibraryFolder();
DVAPI TFilePath getPluginsFolder();
DVAPI TFilePath getReslistPath(bool forCleanup);
DVAPI TFilePath getCacheRootFolder();
DVAPI TFilePath getCrashReportFolder();
DVAPI TFilePath getProfileFolder();
DVAPI TFilePath getMyReslistPath(bool forCleanup);

View file

@ -316,6 +316,7 @@ if(BUILD_ENV_MSVC)
winmm.lib
opengl32.lib
glu32.lib
dbghelp.lib
)
elseif(BUILD_ENV_APPLE)
find_library(AUDIOUNIT_LIB AudioUnit)
@ -344,6 +345,7 @@ elseif(BUILD_ENV_UNIXLIKE)
-lmpr
-lwinmm
-lpsapi
-ldbghelp
)
endif()
endif()

View file

@ -30,6 +30,7 @@ set(MOC_HEADERS
commandbar.h
commandbarpopup.h
convertpopup.h
crashhandler.h
duplicatepopup.h
dvdirtreeview.h
dvitemview.h
@ -251,6 +252,7 @@ set(SOURCES
addfilmstripframespopup.cpp
camerasettingspopup.cpp
convertpopup.cpp
crashhandler.cpp
duplicatepopup.cpp
dvdirtreeview.cpp
filebrowserpopup.cpp
@ -449,14 +451,6 @@ if (WITH_CANON)
add_definitions(-DWITH_CANON)
endif()
if (WITH_CRASHRPT)
add_definitions(-DWITH_CRASHRPT)
endif()
if (WITH_GPHOTO2)
add_definitions(-DWITH_GPHOTO2)
endif()
if (WITH_WINTAB AND BUILD_TARGET_WIN AND (PLATFORM EQUAL 64))
add_definitions(-DWITH_WINTAB)
endif()
@ -483,10 +477,6 @@ if(BUILD_ENV_MSVC)
set(EXTRA_LIBS ${EXTRA_LIBS} ${CANON_LIB})
endif()
if(WITH_CRASHRPT)
set(EXTRA_LIBS ${EXTRA_LIBS} ${CRASHRPT_LIB})
endif()
if(WITH_GPHOTO2)
set(EXTRA_LIBS ${EXTRA_LIBS} ${GPHOTO2_LIB} ${GPHOTO2_PORT_LIB} ${GPHOTO2_COMPAT_LIB})
endif()

View file

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

View file

@ -0,0 +1,33 @@
#pragma once
#ifndef CRASHHANDLER_INCLUDED
#define CRASHHANDLER_INCLUDED
#include "tcommon.h"
#include "tfilepath.h"
#include <QDialog>
class CrashHandler : public QDialog {
Q_OBJECT;
TFilePath m_crashFile;
QString m_crashReport;
public:
CrashHandler(QWidget *parent, TFilePath crashFile, QString crashTxt);
void reject();
static void install();
static void reportProjectInfo(bool enableReport);
static void attachParentWindow(QWidget *parent);
static bool trigger(const QString reason, bool showDialog);
public slots:
void copyClipboard();
void openWebpage();
void openFolder();
};
#endif // CRASHHANDLER_INCLUDED

View file

@ -1,9 +1,7 @@
// Soli Deo gloria
#ifdef WITH_CRASHRPT
#include <tchar.h>
#endif
// Tnz6 includes
#include "crashhandler.h"
#include "mainwindow.h"
#include "flipbook.h"
#include "tapp.h"
@ -70,10 +68,6 @@
#include "kis_tablet_support_win8.h"
#ifdef WITH_CRASHRPT
#include "CrashRpt.h"
#endif
#ifdef MACOSX
#include "tipc.h"
#endif
@ -258,18 +252,23 @@ static void script_output(int type, const QString &value) {
int main(int argc, char *argv[]) {
#ifdef Q_OS_WIN
// Enable standard input/output on Windows Platform for debug
BOOL consoleAttached = ::AttachConsole(ATTACH_PARENT_PROCESS);
if (consoleAttached) {
// Enable standard input/output on Windows Platform for debug
if (::AttachConsole(ATTACH_PARENT_PROCESS)) {
freopen("CON", "r", stdin);
freopen("CON", "w", stdout);
freopen("CON", "w", stderr);
atexit([]() {
::FreeConsole();
});
}
#endif
// Build icon map
ThemeManager::getInstance().buildIconPathsMap(":/icons");
// Install signal handlers to catch crashes
CrashHandler::install();
// parsing arguments and qualifiers
TFilePath loadFilePath;
QString argumentLayoutFileName = "";
@ -578,34 +577,6 @@ int main(int argc, char *argv[]) {
// Toonz environment
initToonzEnv(argumentPathValues);
#ifdef WITH_CRASHRPT
std::string str;
CR_INSTALL_INFO pInfo;
memset(&pInfo, 0, sizeof(CR_INSTALL_INFO));
pInfo.cb = sizeof(CR_INSTALL_INFO);
str = TEnv::getApplicationName();
std::wstring wAppName = std::wstring(str.begin(), str.end());
pInfo.pszAppName = wAppName.c_str();
str = TEnv::getApplicationVersion();
std::wstring wAppVersion = std::wstring(str.begin(), str.end());
pInfo.pszAppVersion = wAppVersion.c_str();
TFilePath crashrptCache =
ToonzFolder::getCacheRootFolder() + TFilePath("crashrpt");
str = crashrptCache.getQString().toStdString();
std::wstring wRptdir = std::wstring(str.begin(), str.end());
pInfo.pszErrorReportSaveDir = wRptdir.c_str();
// Install all available exception handlers.
// Don't send reports automaticall, store locally
pInfo.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS | CR_INST_DONT_SEND_REPORT;
crInstall(&pInfo);
#endif
// prepare for 30bit display
if (Preferences::instance()->is30bitDisplayEnabled()) {
QSurfaceFormat sFmt = QSurfaceFormat::defaultFormat();
@ -703,6 +674,8 @@ int main(int argc, char *argv[]) {
/*-- Layoutファイル名をMainWindowのctorに渡す --*/
MainWindow w(argumentLayoutFileName);
CrashHandler::attachParentWindow(&w);
CrashHandler::reportProjectInfo(true);
TFilePath fp = ToonzFolder::getModuleFile("mainwindow.ini");
QSettings settings(toQString(fp), QSettings::IniFormat);
@ -911,15 +884,5 @@ int main(int argc, char *argv[]) {
TUndoManager::manager()->reset();
PreviewFxManager::instance()->reset();
#ifdef _WIN32
if (consoleAttached) {
::FreeConsole();
}
#endif
#ifdef WITH_CRASHRPT
crUninstall();
#endif
return ret;
}

View file

@ -125,6 +125,20 @@ TFilePath ToonzFolder::getCacheRootFolder() {
return (state == OK) ? TFilePath(cacheDir) : TFilePath();
}
TFilePath ToonzFolder::getCrashReportFolder() {
static enum STATE { FIRSTTIME, OK, NG } state = FIRSTTIME;
QString crashDir =
QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) +
"/crash";
if (state == FIRSTTIME) {
if (QDir(crashDir).mkpath("."))
state = OK;
else
state = NG;
}
return (state == OK) ? TFilePath(crashDir) : TFilePath();
}
TFilePath ToonzFolder::getProfileFolder() {
TFilePath fp = getSystemVarPathValue(getSystemVarPrefix() + "PROFILES");
if (fp == TFilePath())