Add crash handling system using Breakpad (#1439)

This commit is contained in:
Florian Märkl 2019-04-09 09:44:44 +02:00 committed by GitHub
parent b6406e6ac6
commit 1cb314d674
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2001 additions and 39 deletions

View File

@ -45,10 +45,11 @@ install:
before_build: before_build:
- cmd: git submodule update --init - cmd: git submodule update --init
- scripts\prepare_breakpad.bat
# Build config # Build config
build_script: build_script:
- cmd: if defined QMAKE ( call prepare_r2.bat && call build.bat CUTTER_APPVEYOR_R2DEC=true CUTTER_ENABLE_PYTHON=true CUTTER_ENABLE_PYTHON_BINDINGS=true SHIBOKEN_EXECUTABLE="%CUTTER_DEPS_DIR%\pyside\bin\shiboken2.exe" SHIBOKEN_INCLUDEDIR="%CUTTER_DEPS_DIR%/pyside/include/shiboken2" SHIBOKEN_LIBRARY="%CUTTER_DEPS_DIR%/pyside/lib/shiboken2.lib" PYSIDE_INCLUDEDIR="%CUTTER_DEPS_DIR%/pyside/include/PySide2" PYSIDE_LIBRARY="%CUTTER_DEPS_DIR%/pyside/lib/pyside2.lib" PYSIDE_TYPESYSTEMS="%CUTTER_DEPS_DIR%/pyside/share/PySide2/typesystems") - cmd: if defined QMAKE ( call prepare_r2.bat && call build.bat CUTTER_APPVEYOR_R2DEC=true CUTTER_ENABLE_CRASH_REPORTS=true CUTTER_ENABLE_PYTHON=true CUTTER_ENABLE_PYTHON_BINDINGS=true SHIBOKEN_EXECUTABLE="%CUTTER_DEPS_DIR%\pyside\bin\shiboken2.exe" SHIBOKEN_INCLUDEDIR="%CUTTER_DEPS_DIR%/pyside/include/shiboken2" SHIBOKEN_LIBRARY="%CUTTER_DEPS_DIR%/pyside/lib/shiboken2.lib" PYSIDE_INCLUDEDIR="%CUTTER_DEPS_DIR%/pyside/include/PySide2" PYSIDE_LIBRARY="%CUTTER_DEPS_DIR%/pyside/lib/pyside2.lib" PYSIDE_TYPESYSTEMS="%CUTTER_DEPS_DIR%/pyside/share/PySide2/typesystems")
- cmd: if defined MESON ( python meson.py --release --dist=%ARTIFACT_PATH% --backend=%BACKEND% --python ) - cmd: if defined MESON ( python meson.py --release --dist=%ARTIFACT_PATH% --backend=%BACKEND% --python )
after_build: after_build:

2
.gitignore vendored
View File

@ -71,3 +71,5 @@ compile_commands.json
# cutter-deps # cutter-deps
/cutter-deps /cutter-deps
/breakpad

View File

@ -68,8 +68,14 @@ addons:
install: install:
- scripts/fetch_deps.sh - scripts/fetch_deps.sh
- source cutter-deps/env.sh - source cutter-deps/env.sh
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH=/usr/local/opt/llvm/bin:$PATH; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH"; fi export PATH=/usr/local/opt/llvm/bin:$PATH;
source scripts/prepare_breakpad_macos.sh;
fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH";
source scripts/prepare_breakpad_linux.sh;
fi
before_script: before_script:
- git submodule init ; git submodule update - git submodule init ; git submodule update
@ -84,11 +90,13 @@ before_script:
script: script:
- mkdir build - mkdir build
- cd build - cd build
- export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:$CUSTOM_PYTHON_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH"
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
if [[ "$BUILD_SYSTEM" == "qmake" ]]; then if [[ "$BUILD_SYSTEM" == "qmake" ]]; then
qmake qmake
CUTTER_ENABLE_PYTHON=true CUTTER_ENABLE_PYTHON=true
CUTTER_ENABLE_PYTHON_BINDINGS=true CUTTER_ENABLE_PYTHON_BINDINGS=true
CUTTER_ENABLE_CRASH_REPORTS=true
PREFIX=/usr PREFIX=/usr
APPIMAGE=1 APPIMAGE=1
../src && ../src &&
@ -101,6 +109,7 @@ script:
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" -DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
-DCUTTER_ENABLE_PYTHON=ON -DCUTTER_ENABLE_PYTHON=ON
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON -DCUTTER_ENABLE_PYTHON_BINDINGS=ON
-DCUTTER_ENABLE_CRASH_REPORTS=ON
../src && ../src &&
make -j4; make -j4;
fi fi
@ -110,7 +119,9 @@ script:
CUTTER_ENABLE_PYTHON=true CUTTER_ENABLE_PYTHON=true
CUTTER_ENABLE_PYTHON_BINDINGS=true CUTTER_ENABLE_PYTHON_BINDINGS=true
CUTTER_BUNDLE_R2_APPBUNDLE=true CUTTER_BUNDLE_R2_APPBUNDLE=true
CUTTER_ENABLE_CRASH_REPORTS=true
PYTHON_FRAMEWORK_DIR=$CUTTER_DEPS_PYTHON_FRAMEWORK_DIR PYTHON_FRAMEWORK_DIR=$CUTTER_DEPS_PYTHON_FRAMEWORK_DIR
BREAKPAD_FRAMEWORK_DIR=$BREAKPAD_FRAMEWORK_DIR
../src && ../src &&
make -j4; make -j4;
elif [[ "$BUILD_SYSTEM" == "cmake" ]]; then elif [[ "$BUILD_SYSTEM" == "cmake" ]]; then
@ -121,6 +132,8 @@ script:
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" -DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
-DCUTTER_ENABLE_PYTHON=ON -DCUTTER_ENABLE_PYTHON=ON
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON -DCUTTER_ENABLE_PYTHON_BINDINGS=ON
-DCUTTER_ENABLE_CRASH_REPORTS=ON
-DBREAKPAD_FRAMEWORK_DIR="$BREAKPAD_FRAMEWORK_DIR"
../src && ../src &&
make -j4; make -j4;
fi fi
@ -135,6 +148,7 @@ after_success:
macdeployqt Cutter.app -executable=Cutter.app/Contents/MacOS/Cutter -libpath="../Frameworks" && macdeployqt Cutter.app -executable=Cutter.app/Contents/MacOS/Cutter -libpath="../Frameworks" &&
macdeployqt Cutter.app -executable=Cutter.app/Contents/MacOS/Cutter -libpath="../Frameworks" && macdeployqt Cutter.app -executable=Cutter.app/Contents/MacOS/Cutter -libpath="../Frameworks" &&
cp -a "$QTDIR/lib/QtDBus.framework" "$QTDIR/lib/QtPrintSupport.framework" Cutter.app/Contents/Frameworks && cp -a "$QTDIR/lib/QtDBus.framework" "$QTDIR/lib/QtPrintSupport.framework" Cutter.app/Contents/Frameworks &&
cp -a "$BREAKPAD_FRAMEWORK_DIR/Breakpad.framework" Cutter.app/Contents/Frameworks &&
"$TRAVIS_BUILD_DIR/scripts/appbundle_embed_python.sh" "$CUTTER_DEPS_PYTHON_FRAMEWORK_DIR/Python.framework" Cutter.app Cutter.app/Contents/MacOS/Cutter && "$TRAVIS_BUILD_DIR/scripts/appbundle_embed_python.sh" "$CUTTER_DEPS_PYTHON_FRAMEWORK_DIR/Python.framework" Cutter.app Cutter.app/Contents/MacOS/Cutter &&
mv Cutter.app/Contents/MacOS/Cutter Cutter.app/Contents/MacOS/Cutter.bin && mv Cutter.app/Contents/MacOS/Cutter Cutter.app/Contents/MacOS/Cutter.bin &&
cp ../src/macos/Cutter.sh Cutter.app/Contents/MacOS/Cutter && cp ../src/macos/Cutter.sh Cutter.app/Contents/MacOS/Cutter &&

View File

@ -1,5 +1,6 @@
@ECHO off @ECHO off
SETLOCAL ENABLEDELAYEDEXPANSION SETLOCAL ENABLEDELAYEDEXPANSION
SETLOCAL ENABLEEXTENSIONS
IF "%VisualStudioVersion%" == "14.0" ( IF NOT DEFINED Platform SET "Platform=X86" ) IF "%VisualStudioVersion%" == "14.0" ( IF NOT DEFINED Platform SET "Platform=X86" )
FOR /F %%i IN ('powershell -c "\"%Platform%\".toLower()"') DO SET PLATFORM=%%i FOR /F %%i IN ('powershell -c "\"%Platform%\".toLower()"') DO SET PLATFORM=%%i
@ -11,6 +12,7 @@ IF !ERRORLEVEL! NEQ 0 (
SET "R2DIST=r2_dist_%PLATFORM%" SET "R2DIST=r2_dist_%PLATFORM%"
SET "BUILDDIR=build_%PLATFORM%" SET "BUILDDIR=build_%PLATFORM%"
SET "BREAKPAD_SOURCE_DIR=%CD%\src\breakpad\src\src"
ECHO Preparing directory ECHO Preparing directory
RMDIR /S /Q %BUILDDIR% RMDIR /S /Q %BUILDDIR%
@ -21,8 +23,12 @@ FOR %%i in (src\translations\*.ts) DO lrelease %%i
CD %BUILDDIR% CD %BUILDDIR%
IF NOT DEFINED CUTTER_ENABLE_CRASH_REPORTS (
SET "CUTTER_ENABLE_CRASH_REPORTS=false"
)
ECHO Building cutter ECHO Building cutter
qmake %* ..\src\cutter.pro -config release qmake BREAKPAD_SOURCE_DIR=%BREAKPAD_SOURCE_DIR% CUTTER_ENABLE_CRASH_REPORTS=%CUTTER_ENABLE_CRASH_REPORTS% %* ..\src\cutter.pro -config release
IF !ERRORLEVEL! NEQ 0 EXIT /B 1 IF !ERRORLEVEL! NEQ 0 EXIT /B 1
nmake nmake
IF !ERRORLEVEL! NEQ 0 EXIT /B 1 IF !ERRORLEVEL! NEQ 0 EXIT /B 1
@ -34,3 +40,5 @@ XCOPY /S /I ..\%R2DIST%\radare2 cutter\radare2
COPY ..\%R2DIST%\*.dll cutter\ COPY ..\%R2DIST%\*.dll cutter\
windeployqt cutter\cutter.exe windeployqt cutter\cutter.exe
FOR %%i in (..\src\translations\*.qm) DO MOVE "%%~fi" cutter\translations FOR %%i in (..\src\translations\*.qm) DO MOVE "%%~fi" cutter\translations
ENDLOCAL

View File

@ -6,7 +6,7 @@
ERR=0 ERR=0
#### User variables #### #### User variables ####
BUILD="build" BUILD="`pwd`/build"
QMAKE_CONF="" QMAKE_CONF=""
ROOT_DIR=`pwd` ROOT_DIR=`pwd`
@ -63,6 +63,16 @@ find_gmake() {
echo "$gmakepath" echo "$gmakepath"
} }
prepare_breakpad() {
if [[ $OSTYPE == "linux-gnu" ]]; then
source $ROOT_DIR/scripts/prepare_breakpad_linux.sh
export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH"
elif [[ $OSTYPE == "darwin" ]]; then
source $ROOT_DIR/scripts/prepare_breakpad_macos.sh
fi
return 1
}
# Build radare2 # Build radare2
check_r2 check_r2
if [ $? -eq 1 ]; then if [ $? -eq 1 ]; then
@ -86,6 +96,7 @@ fi
$(find_lrelease) ./src/Cutter.pro $(find_lrelease) ./src/Cutter.pro
# Build # Build
prepare_breakpad
mkdir -p "$BUILD" mkdir -p "$BUILD"
cd "$BUILD" || exit 1 cd "$BUILD" || exit 1
$(find_qmake) ../src/Cutter.pro $QMAKE_CONF $(find_qmake) ../src/Cutter.pro $QMAKE_CONF

View File

@ -11,9 +11,11 @@ The “official” way to build Cutter is by using qmake, but there are two
alternatives cmake and meson. alternatives cmake and meson.
In any case, there are obviously some requirements: In any case, there are obviously some requirements:
- Radare2 installed from submodule
- Qt 5.9 or above * Radare2 installed from submodule
- Python3.6 * Qt 5.9 or above
* Python3.6
* Breakpad installed using script (optional, disabled by default)
Before compiling, note that we also provide binaries available for Before compiling, note that we also provide binaries available for
windows/linux/MacOS `here <https://github.com/radareorg/cutter/releases>`_. windows/linux/MacOS `here <https://github.com/radareorg/cutter/releases>`_.
@ -23,9 +25,33 @@ windows/linux/MacOS `here <https://github.com/radareorg/cutter/releases>`_.
Building options Building options
---------------- ----------------
Note that there are two major building options available: Note that there are three major building options available:
- ``CUTTER_ENABLE_PYTHON`` compile with Python support
- ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken2, required for Python plugins! * ``CUTTER_ENABLE_PYTHON`` compile with Python support
* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken2, required for Python plugins!
* ``CUTTER_ENABLE_CRASH_REPORTS`` is used to compile Cutter with crash handling system enabled (Breakpad)
--------------
Preparing Breakpad
-------------------
If you want to build Cutter with crash handling system, you want prepare Breakpad before.
For this simply run one of the scripts (according to your OS) from root Cutter directory:
.. code:: sh
source scripts/prepare_breakpad_linux.sh # Linux
source scripts/prepare_breakpad_macos.sh # MacOS
scripts/prepare_breakpad.bat # Windows
Then if you are building on Linux you want to change ``PKG_CONFIG_PATH`` environment variable
so it contains ``$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig``. For this simply run
.. code:: sh
export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH"
-------------- --------------
@ -73,12 +99,12 @@ Building on Linux
The root for CMake is in src/. In-source builds are **not allowed**, so The root for CMake is in src/. In-source builds are **not allowed**, so
you **must** run CMake from a separate directory: you **must** run CMake from a separate directory:
:: .. code:: sh
cd src cd src
mkdir build mkdir build
cd build cd build
cmake .. cmake .. # Don't forget to provide build options
If all went well, you should now have a working Makefile in your build If all went well, you should now have a working Makefile in your build
directory: directory:

View File

@ -0,0 +1,32 @@
Crash Handling System
=====================
Cutter uses `Breakpad <https://github.com/google/breakpad>`__ as backend
for crash handling.
Crash Handling System is disabled by default to do not interfere developers from debugging.
To enable this system there is building option ``CUTTER_ENABLE_CRASH_REPORTS``.
Solution description
--------------------
There are only 2 source files:
* ``CrashHandler.h``
* ``CrashHandler.cpp``
And API is very simple: only one function - ``initCrashHandler()`` that enables Crash Handling System if
``CUTTER_ENABLE_CRASH_REPORTS`` is true, otherwise does nothing.
As soon as signal is raised ``crashHandler(int signum)`` is called with signal's code as argument.
This function first of all writes crash dump to OS's Temp directory to catch core and memory state
as it was at the crash moment.
Then crash dialog is shown:
.. image :: images/crash-dialog.png
If user chose to create crash dump, prepared dump is moved to directory specified by user.
And then success dialog is shown:
.. image :: images/success-dump-dialog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,34 @@
{
'includes': [
'../../build/common.gypi'
],
'targets': [
{
'target_name': 'build_all',
'type': 'none',
'dependencies': [
'./crash_generation/crash_generation.gyp:*',
'./handler/exception_handler.gyp:*',
'./sender/crash_report_sender.gyp:*',
]
},
{
'target_name': 'common',
'type': 'static_library',
'include_dirs': [
'<(DEPTH)',
],
'direct_dependent_settings': {
'include_dirs': [
'<(DEPTH)',
]
},
'sources': [
'<(DEPTH)/common/windows/guid_string.cc',
'<(DEPTH)/common/windows/guid_string.h',
'<(DEPTH)/common/windows/http_upload.h',
'<(DEPTH)/common/windows/string_utils.cc',
]
}
]
}

1377
scripts/breakpad_macos.patch Normal file

File diff suppressed because it is too large Load Diff

17
scripts/dump_syms.gyp Normal file
View File

@ -0,0 +1,17 @@
{
'includes': [
'../../../build/common.gypi',
],
'targets': [
{
'target_name': 'dump_syms',
'type': 'executable',
'sources': [
'dump_syms.cc',
],
'dependencies': [
'../../../common/windows/common_windows.gyp:common_windows_lib',
],
},
],
}

View File

@ -0,0 +1,29 @@
@ECHO OFF
SET ROOT_DIR=%CD%
powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget https://storage.googleapis.com/chrome-infra/depot_tools.zip -OutFile depot_tools.zip "
7z -bd x %ROOT_DIR%\depot_tools.zip -odepot_tools
powershell -Command "depot_tools\update_depot_tools"
SET BUFF_PATH=%PATH%
SET DEPOT_TOOLS=%ROOT_DIR%\depot_tools
set PATH=%DEPOT_TOOLS%;%BUFF_PATH%
mkdir %ROOT_DIR%\src\breakpad
CD %ROOT_DIR%\src\breakpad
powershell -Command "fetch breakpad"
powershell -Command "gclient sync"
CD %ROOT_DIR%\src\breakpad\src\src\client\windows
DEL %CD%\breakpad_client.gyp
DEL %CD%\breakpad_client.sln
DEL %CD%\common.vcxproj
DEL %CD%\common.vcxproj.filters
DEL %CD%\build_all.vcxproj
COPY %ROOT_DIR%\scripts\breakpad_client.gyp %CD%
CD %ROOT_DIR%\src\breakpad\src\src
powershell -Command "tools\gyp\gyp.bat --no-circular-check client\windows\breakpad_client.gyp -Dwin_release_RuntimeLibrary=2 -Dwin_debug_RuntimeLibrary=2 -Dplatform=%ARCH% -Dconfiguration=release"
set PATH=%BUFF_PATH%
msbuild /m %CD%\client\windows\breakpad_client.sln /p:Configuration=release /p:Platform=%ARCH%
CD %ROOT_DIR%

View File

@ -0,0 +1,9 @@
#!/bin/bash
git clone https://github.com/google/breakpad.git
cd breakpad
git clone https://chromium.googlesource.com/linux-syscall-support src/third_party/lss
CFLAGS=-w CXXFLAGS=-w ./configure --prefix=`pwd`/prefix && make -j4 && make install || exit 1
export CUSTOM_BREAKPAD_PREFIX="`pwd`/prefix"
cd ..

View File

@ -0,0 +1,22 @@
#!/bin/bash
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
DIR="$SCRIPTPATH/.."
cd "$DIR" || exit 1
BREAKPAD_FRAMEWORK_DIR="$DIR/breakpad/framework"
BREAKPAD_DUMP_SYMS_DIR="$DIR/breakpad/bin"
git clone https://github.com/google/breakpad.git || exit 1
mkdir $BREAKPAD_FRAMEWORK_DIR
mkdir $BREAKPAD_DUMP_SYMS_DIR
cd breakpad || exit 1
git checkout 4d550cceca107f36c4bc1ea1126b7d32cc50f424 || exit 1
git apply "$SCRIPTPATH/breakpad_macos.patch" || exit 1
cd src/client/mac/ && xcodebuild -sdk macosx || exit 1
cp -R build/Release/Breakpad.framework "$BREAKPAD_FRAMEWORK_DIR" || exit 1
cd $DIR/breakpad || exit 1
cp -R src/. framework/Breakpad.framework/Headers || exit 1
export BREAKPAD_FRAMEWORK_DIR=$BREAKPAD_FRAMEWORK_DIR
cd $DIR

View File

@ -10,6 +10,7 @@ set(CUTTER_PYTHON_MIN 3.5)
option(CUTTER_ENABLE_PYTHON "Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}." OFF) option(CUTTER_ENABLE_PYTHON "Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}." OFF)
option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken2. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF) option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken2. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF)
option(CUTTER_ENABLE_CRASH_REPORTS "Enable crash report system. Unused if CUTTER_ENABLE_CRASH_REPORTS=OFF" OFF)
if(NOT CUTTER_ENABLE_PYTHON) if(NOT CUTTER_ENABLE_PYTHON)
set(CUTTER_ENABLE_PYTHON_BINDINGS OFF) set(CUTTER_ENABLE_PYTHON_BINDINGS OFF)
@ -93,6 +94,7 @@ message(STATUS "Building Cutter version ${CUTTER_VERSION_FULL}")
message(STATUS "Options:") message(STATUS "Options:")
message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}") message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}")
message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}") message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}")
message(STATUS "- Crash Handling: ${CUTTER_ENABLE_CRASH_REPORTS}")
message(STATUS "") message(STATUS "")
@ -134,6 +136,24 @@ endif()
add_executable(Cutter MACOSX_BUNDLE ${UI_FILES} ${QRC_FILES} ${SOURCE_FILES} ${HEADER_FILES} ${BINDINGS_SOURCE}) add_executable(Cutter MACOSX_BUNDLE ${UI_FILES} ${QRC_FILES} ${SOURCE_FILES} ${HEADER_FILES} ${BINDINGS_SOURCE})
set_target_properties(Cutter PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist") set_target_properties(Cutter PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist")
if(CUTTER_ENABLE_CRASH_REPORTS)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(Cutter Threads::Threads)
add_definitions(-DCUTTER_ENABLE_CRASH_REPORTS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g ")
if(DEFINED BREAKPAD_FRAMEWORK_DIR)
include_directories("${BREAKPAD_FRAMEWORK_DIR}/Breakpad.framework/Headers")
set_target_properties(Cutter PROPERTIES LINK_FLAGS "-Wl,-F${BREAKPAD_FRAMEWORK_DIR}")
target_link_libraries(Cutter "-framework Breakpad")
else()
find_package(Breakpad REQUIRED)
include_directories(${BREAKPAD_INCLUDE_DIRS})
target_link_libraries(Cutter ${BREAKPAD_LINK_LIBRARIES})
endif()
endif()
target_link_libraries(Cutter Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Svg Qt5::Network) target_link_libraries(Cutter Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Svg Qt5::Network)
target_link_libraries(Cutter ${RADARE2_LIBRARIES}) target_link_libraries(Cutter ${RADARE2_LIBRARIES})
if(CUTTER_ENABLE_PYTHON) if(CUTTER_ENABLE_PYTHON)

View File

@ -32,6 +32,8 @@ QT += core gui widgets svg network
QT_CONFIG -= no-pkg-config QT_CONFIG -= no-pkg-config
CONFIG += c++11 CONFIG += c++11
!defined(CUTTER_ENABLE_CRASH_REPORTS, var) CUTTER_ENABLE_CRASH_REPORTS=false
equals(CUTTER_ENABLE_CRASH_REPORTS, true) CONFIG += CUTTER_ENABLE_CRASH_REPORTS
!defined(CUTTER_ENABLE_PYTHON, var) CUTTER_ENABLE_PYTHON=false !defined(CUTTER_ENABLE_PYTHON, var) CUTTER_ENABLE_PYTHON=false
equals(CUTTER_ENABLE_PYTHON, true) CONFIG += CUTTER_ENABLE_PYTHON equals(CUTTER_ENABLE_PYTHON, true) CONFIG += CUTTER_ENABLE_PYTHON
@ -49,6 +51,13 @@ equals(CUTTER_BUNDLE_R2_APPBUNDLE, true) CONFIG += CUTTER_BUNDLE_R2_APPBU
!defined(CUTTER_APPVEYOR_R2DEC, var) CUTTER_APPVEYOR_R2DEC=false !defined(CUTTER_APPVEYOR_R2DEC, var) CUTTER_APPVEYOR_R2DEC=false
equals(CUTTER_APPVEYOR_R2DEC, true) CONFIG += CUTTER_APPVEYOR_R2DEC equals(CUTTER_APPVEYOR_R2DEC, true) CONFIG += CUTTER_APPVEYOR_R2DEC
CUTTER_ENABLE_CRASH_REPORTS {
message("Crash report support enabled.")
DEFINES += CUTTER_ENABLE_CRASH_REPORTS
} else {
message("Crash report support disabled.")
}
CUTTER_ENABLE_PYTHON { CUTTER_ENABLE_PYTHON {
message("Python enabled.") message("Python enabled.")
DEFINES += CUTTER_ENABLE_PYTHON DEFINES += CUTTER_ENABLE_PYTHON
@ -188,6 +197,29 @@ CUTTER_ENABLE_PYTHON {
} }
} }
CUTTER_ENABLE_CRASH_REPORTS {
QMAKE_CXXFLAGS += -g
defined(BREAKPAD_FRAMEWORK_DIR, var)|defined(BREAKPAD_SOURCE_DIR, var) {
defined(BREAKPAD_FRAMEWORK_DIR, var) {
INCLUDEPATH += $$BREAKPAD_FRAMEWORK_DIR/Breakpad.framework/Headers
LIBS += -F$$BREAKPAD_FRAMEWORK_DIR -framework Breakpad
}
defined(BREAKPAD_SOURCE_DIR, var) {
INCLUDEPATH += $$BREAKPAD_SOURCE_DIR
win32 {
LIBS += -L$$quote($$BREAKPAD_SOURCE_DIR\\client\\windows\\release\\lib) -lexception_handler -lcrash_report_sender -lcrash_generation_server -lcrash_generation_client -lcommon
}
unix:LIBS += -L$$BREAKPAD_SOURCE_DIR/client/linux -lbreakpad-client
macos:error("Please use scripts\prepare_breakpad_macos.sh script to provide breakpad framework.")
}
} else {
CONFIG += link_pkgconfig
!packagesExist(breakpad-client) {
error("ERROR: Breakpad could not be found. Make sure it is available to pkg-config.")
}
PKGCONFIG += breakpad-client
}
}
macx:CUTTER_BUNDLE_R2_APPBUNDLE { macx:CUTTER_BUNDLE_R2_APPBUNDLE {
message("Using r2 rom AppBundle") message("Using r2 rom AppBundle")
@ -310,6 +342,8 @@ SOURCES += \
dialogs/LinkTypeDialog.cpp \ dialogs/LinkTypeDialog.cpp \
common/UpdateWorker.cpp \ common/UpdateWorker.cpp \
widgets/MemoryDockWidget.cpp \ widgets/MemoryDockWidget.cpp \
common/CrashHandler.cpp \
common/BugReporting.cpp \
common/HighDpiPixmap.cpp \ common/HighDpiPixmap.cpp \
widgets/GraphGridLayout.cpp widgets/GraphGridLayout.cpp
@ -421,6 +455,7 @@ HEADERS += \
common/RunScriptTask.h \ common/RunScriptTask.h \
common/Json.h \ common/Json.h \
dialogs/EditMethodDialog.h \ dialogs/EditMethodDialog.h \
common/CrashHandler.h \
dialogs/LoadNewTypesDialog.h \ dialogs/LoadNewTypesDialog.h \
widgets/SdbWidget.h \ widgets/SdbWidget.h \
common/PythonManager.h \ common/PythonManager.h \
@ -429,6 +464,7 @@ HEADERS += \
common/UpdateWorker.h \ common/UpdateWorker.h \
dialogs/LinkTypeDialog.h \ dialogs/LinkTypeDialog.h \
widgets/MemoryDockWidget.h \ widgets/MemoryDockWidget.h \
common/BugReporting.h \
common/HighDpiPixmap.h \ common/HighDpiPixmap.h \
widgets/GraphLayout.h \ widgets/GraphLayout.h \
widgets/GraphGridLayout.h widgets/GraphGridLayout.h

View File

@ -3,6 +3,7 @@
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/UpdateWorker.h" #include "common/UpdateWorker.h"
#include "CutterConfig.h" #include "CutterConfig.h"
#include "common/CrashHandler.h"
/** /**
* @brief Migrate Settings used before Cutter 1.8 * @brief Migrate Settings used before Cutter 1.8
@ -20,6 +21,8 @@ static void migrateSettings(QSettings &newSettings)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
initCrashHandler();
qRegisterMetaType<QList<StringDescription>>(); qRegisterMetaType<QList<StringDescription>>();
qRegisterMetaType<QList<FunctionDescription>>(); qRegisterMetaType<QList<FunctionDescription>>();

View File

@ -0,0 +1,46 @@
# - Find Breakpad
#
# BREAKPAD_FOUND - True if Breakpad has been found.
# BREAKPAD_INCLUDE_DIRS - Breakpad include directory
# BREAKPAD_LIBRARIES - List of libraries when using Breakpad.
# BREAKPAD_LIBRARY_DIRS - Breakpad library directories
if(WIN32)
find_path(BREAKPAD_INCLUDE_DIRS
HINTS
"${CMAKE_CURRENT_SOURCE_DIR}/breakpad/prefix/include/breakpad")
set(BREAKPAD_LIBRARY_NAMES
BREAKPAD_CLIENT
BREAKPAD)
set(BREAKPAD_LIBRARIES "")
set(BREAKPAD_LIBRARIES_VARS "")
foreach(libname ${BREAKPAD_LIBRARY_NAMES})
find_library(BREAKPAD_LIBRARY_${libname}
${libname}
HINTS
"${CMAKE_CURRENT_SOURCE_DIR}/breakpad/prefix/lib")
list(APPEND BREAKPAD_LIBRARIES ${BREAKPAD_LIBRARY_${libname}})
list(APPEND BREAKPAD_LIBRARIES_VARS "BREAKPAD_LIBRARY_${libname}")
endforeach()
set(BREAKPAD_LIBRARY_DIRS "")
else()
set(BREAKPAD_CMAKE_PREFIX_PATH_TEMP ${CMAKE_PREFIX_PATH})
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/breakpad/prefix")
find_package(PkgConfig REQUIRED)
pkg_search_module(BREAKPAD REQUIRED breakpad-client)
# reset CMAKE_PREFIX_PATH
set(CMAKE_PREFIX_PATH ${BREAKPAD_CMAKE_PREFIX_PATH_TEMP})
mark_as_advanced(BREAKPAD_CMAKE_PREFIX_PATH_TEMP)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(BREAKPAD REQUIRED_VARS BREAKPAD_LIBRARIES BREAKPAD_INCLUDE_DIRS)
mark_as_advanced(BREAKPAD_LIBRARIES_VARS)

View File

@ -0,0 +1,45 @@
#include "BugReporting.h"
#include "Cutter.h"
#include <QUrl>
#include <QJsonObject>
#include "CutterConfig.h"
#include <QDesktopServices>
void openIssue()
{
QString url, osInfo, format, arch, type;
//Pull in info needed for git issue
osInfo = QSysInfo::productType() + " " +
(QSysInfo::productVersion() == "unknown"
? ""
: QSysInfo::productVersion());
QJsonDocument docu = Core()->getFileInfo();
QJsonObject coreObj = docu.object()["core"].toObject();
QJsonObject binObj = docu.object()["bin"].toObject();
if (!binObj.QJsonObject::isEmpty()) {
format = coreObj["format"].toString();
arch = binObj["arch"].toString();
if (!binObj["type"].isUndefined()) {
type = coreObj["type"].toString();
} else {
type = "N/A";
}
} else {
format = coreObj["format"].toString();
arch = "N/A";
type = "N/A";
}
url =
"https://github.com/radareorg/cutter/issues/new?&body=**Environment information**\n* Operating System: "
+ osInfo + "\n* Cutter version: " + CUTTER_VERSION_FULL +
"\n* File format: " + format + "\n * Arch: " + arch + "\n * Type: " + type +
"\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\n"
"Steps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n"
"4. See error\n\n**Expected behavior**\n"
"A clear and concise description of what you expected to happen.\n\n"
"**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n"
"**Additional context**\nAdd any other context about the problem here.";
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
}

10
src/common/BugReporting.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef CRASHREPORTING_H
#define CRASHREPORTING_H
/**
* @brief Opens issue on Cutter's github page
* with current file and system information.
*/
void openIssue();
#endif // CRASHREPORTING_H

229
src/common/CrashHandler.cpp Normal file
View File

@ -0,0 +1,229 @@
#include "CrashHandler.h"
#ifdef CUTTER_ENABLE_CRASH_REPORTS
#include "BugReporting.h"
#include <QStandardPaths>
#include <QApplication>
#include <QMessageBox>
#include <QPushButton>
#include <QFileDialog>
#include <signal.h>
#include <QString>
#include <QTime>
#include <QFile>
#include <QDir>
#include <QMap>
#if defined (Q_OS_LINUX)
#include "client/linux/handler/exception_handler.h"
#elif defined (Q_OS_WIN32)
#include "client/windows/handler/exception_handler.h"
#elif defined (Q_OS_MACOS)
#include "client/mac/handler/exception_handler.h"
#endif // Q_OS
// Here will be placed crash dump at the first place
// and then moved if needed
#if defined (Q_OS_LINUX) || defined (Q_OS_MACOS)
static std::string tmpLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString();
#else
static std::wstring tmpLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdWString();
#endif
static const QMap<int, QString> sigNumDescription = {
#ifdef SIGSEGV
{ SIGSEGV, "SIGSEGV" },
#endif // SIGSEGV
#ifdef SIGILL
{ SIGILL, "SIGILL" },
#endif // SIGILL
#ifdef SIGFPE
{ SIGFPE, "SIGFPE" },
#endif // SIGFPE
#ifdef SIGABRT
{ SIGABRT, "SIGABRT" },
#endif // SIGABRT
#ifdef SIGBUS
{ SIGBUS, "SIGBUS" },
#endif // SIGBUS
#ifdef SIGPIPE
{ SIGPIPE, "SIGPIPE" },
#endif // SIGPIPE
#ifdef SIGSYS
{ SIGSYS, "SIGSYS" }
#endif // SIGSYS
};
static QString dumpFileFullPath = "";
#ifdef Q_OS_WIN32
// Called if crash dump was successfully created
// Saves path to file
bool callback(const wchar_t *_dump_dir,
const wchar_t *_minidump_id,
void *context, EXCEPTION_POINTERS *exinfo,
MDRawAssertionInfo *assertion,
bool success)
{
QString dir = QString::fromWCharArray(_dump_dir);
QString id = QString::fromWCharArray(_minidump_id);
dumpFileFullPath = QDir(dir).filePath(id + ".dmp");
return true;
}
#elif defined (Q_OS_LINUX)
// Called if crash dump was successfully created
// Saves path to file
bool callback(const google_breakpad::MinidumpDescriptor &md, void *context, bool b)
{
dumpFileFullPath = md.path();
return true;
}
#elif defined (Q_OS_MACOS)
// Called if crash dump was successfully created
// Saves path to file
bool callback(const char *dump_dir, const char *minidump_id, void *context, bool succeeded)
{
QString dir = QString::fromUtf8(dump_dir);
QString id = QString::fromUtf8(minidump_id);
dumpFileFullPath = QDir(dir).filePath(id + ".dmp");
return true;
}
#endif // Q_OS
/**
* @brief Writes minidump and put its name in dumpFileFullPath.
* @return true on succes
*/
bool writeMinidump()
{
bool ok;
#if defined (Q_OS_LINUX) || defined (Q_OS_MACOS)
ok = google_breakpad::ExceptionHandler::WriteMinidump(tmpLocation,
callback,
nullptr);
#elif defined (Q_OS_WIN32)
ok = google_breakpad::ExceptionHandler::WriteMinidump(tmpLocation,
callback,
nullptr);
#endif // Q_OS
return ok;
}
[[noreturn]] void crashHandler(int signum)
{
// As soon as Cutter crashed, crash dump is created, so core and memory state
// is not changed by all stuff with user interation going on below.
bool ok = writeMinidump();
QString err = sigNumDescription.contains(signum) ?
sigNumDescription[signum] :
QObject::tr("undefined");
QMessageBox mb;
mb.setWindowTitle(QObject::tr("Cutter encountered a problem"));
mb.setText(QObject::tr("Cutter received a <b>%1</b> it can't handle and will close.<br/>"
"Would you like to create a crash dump for bug report?"
).arg(err));
mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
mb.button(QMessageBox::Yes)->setText(QObject::tr("Create a crash dump"));
mb.button(QMessageBox::No)->setText(QObject::tr("Do not report"));
mb.setDefaultButton(QMessageBox::Yes);
int ret = mb.exec();
if (ret == QMessageBox::Yes) {
QString dumpSaveFileName;
int placementFailCounter = 0;
do {
placementFailCounter++;
if (placementFailCounter == 4) {
ok = false;
break;
}
dumpSaveFileName = QFileDialog::getSaveFileName(nullptr,
QObject::tr("Choose a directory to save the crash dump in"),
QStandardPaths::writableLocation(QStandardPaths::HomeLocation) +
QDir::separator() +
"Cutter_crash_dump_"
+ QDate().currentDate().toString("dd.MM.yy") + "_"
+ QTime().currentTime().toString() + ".dmp",
QObject::tr("Dump files (*.dmp)"));
if (dumpSaveFileName.isEmpty()) {
exit(3);
}
if (QFile::rename(dumpFileFullPath, dumpSaveFileName)) {
break;
}
QMessageBox::critical(nullptr,
QObject::tr("Error"),
QObject::tr("Error occured during writing to the %1.<br/>"
"Please, make sure you have access to that directory "
"and try again.").arg(QFileInfo(dumpSaveFileName).dir().path()));
} while (true);
if (ok) {
QMessageBox info;
info.setWindowTitle(QObject::tr("Success"));
info.setText(QObject::tr("<a href=\"%1\">Crash dump</a> was successfully created.")
.arg(QFileInfo(dumpSaveFileName).dir().path()));
info.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
info.button(QMessageBox::Yes)->setText(QObject::tr("Open an issue"));
info.button(QMessageBox::No)->setText(QObject::tr("Exit Cutter"));
info.setDefaultButton(QMessageBox::Yes);
int ret = info.exec();
if (ret == QMessageBox::Yes) {
openIssue();
}
} else {
QMessageBox::critical(nullptr,
QObject::tr("Error!"),
QObject::tr("Error occured during crash dump creation."));
}
} else {
QFile f(dumpFileFullPath);
f.remove();
}
_exit(3);
}
void initCrashHandler()
{
#ifdef SIGSEGV
signal(SIGSEGV, crashHandler);
#endif // SIGSEGV
#ifdef SIGILL
signal(SIGILL, crashHandler);
#endif // SIGILL
#ifdef SIGFPE
signal(SIGFPE, crashHandler);
#endif // SIGFPE
#ifdef SIGABRT
signal(SIGABRT, crashHandler);
#endif // SIGABRT
#ifdef SIGBUS
signal(SIGBUS, crashHandler);
#endif // SIGBUS
#ifdef SIGPIPE
signal(SIGPIPE, crashHandler);
#endif // SIGPIPE
#ifdef SIGSYS
signal(SIGSYS, crashHandler);
#endif // SIGSYS
}
#else // CUTTER_ENABLE_CRASH_REPORTS
void initCrashHandler()
{
}
#endif // CUTTER_ENABLE_CRASH_REPORTS

13
src/common/CrashHandler.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef CRASH_HANDLER_H
#define CRASH_HANDLER_H
/**
* @fn void initCrashHandler()
*
* If CUTTER_ENABLE_CRASH_REPORTS is true, initializes
* crash handling and reporting, otherwise does nothing.
*/
void initCrashHandler();
#endif // CRASH_HANDLER_H

View File

@ -9,6 +9,7 @@
#include <QList> #include <QList>
#include <QStringList> #include <QStringList>
#include <QMetaType> #include <QMetaType>
#include "core/CutterCommon.h"
struct FunctionDescription { struct FunctionDescription {
RVA offset; RVA offset;

View File

@ -2,6 +2,7 @@
#include "ui_MainWindow.h" #include "ui_MainWindow.h"
// Common Headers // Common Headers
#include "common/BugReporting.h"
#include "common/Highlighter.h" #include "common/Highlighter.h"
#include "common/HexAsciiHighlighter.h" #include "common/HexAsciiHighlighter.h"
#include "common/Helpers.h" #include "common/Helpers.h"
@ -1112,34 +1113,10 @@ void MainWindow::on_actionAbout_triggered()
a->setAttribute(Qt::WA_DeleteOnClose); a->setAttribute(Qt::WA_DeleteOnClose);
a->open(); a->open();
} }
void MainWindow::on_actionIssue_triggered() void MainWindow::on_actionIssue_triggered()
{ {
QString url, osInfo, format, arch, type; openIssue();
//Pull in info needed for git issue
osInfo = QString(QSysInfo::productType()) + " " + QString(QSysInfo::productVersion());
QJsonDocument docu = Core()->getFileInfo();
QJsonObject coreObj = docu.object()["core"].toObject();
QJsonObject binObj = docu.object()["bin"].toObject();
if (!binObj.QJsonObject::isEmpty()) {
format = coreObj["format"].toString();
arch = binObj["arch"].toString();
if (!binObj["type"].isUndefined()) {
type = coreObj["type"].toString();
} else {
type = "N/A";
}
} else {
format = coreObj["format"].toString();
arch = "N/A";
type = "N/A";
}
url =
"https://github.com/radareorg/cutter/issues/new?&body=**Environment information**\n* Operating System: "
+ osInfo + "\n* Cutter version: " + CUTTER_VERSION_FULL +
"\n* File format: " + format + "\n * Arch: " + arch + "\n * Type: " + type +
"\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Additional context**\nAdd any other context about the problem here.";
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
} }
void MainWindow::on_actionRefresh_Panels_triggered() void MainWindow::on_actionRefresh_Panels_triggered()