Integration with breakpad; crash report handler dialog #5425

Signed-off-by: Max Risuhin <risuhin.max@gmail.com>
This commit is contained in:
Max Risuhin 2018-08-30 01:45:19 +03:00
parent 6353039171
commit 76ace61d17
No known key found for this signature in database
GPG Key ID: BF733F5ACA0B4448
12 changed files with 377 additions and 27 deletions

View File

@ -26,6 +26,7 @@ external_modules_dir = [
'node_modules/react-native-keychain/desktop',
'node_modules/react-native-securerandom/desktop',
'modules/react-native-status/desktop',
'node_modules/google-breakpad',
]
external_fonts = [
@ -145,7 +146,10 @@ timeout(90) {
sh 'cp -r assets/share/assets StatusIm.app/Contents/MacOs'
sh 'chmod +x StatusIm.app/Contents/MacOs/ubuntu-server'
sh 'cp ../desktop/bin/StatusIm StatusIm.app/Contents/MacOs'
sh 'cp ../desktop/reportApp/reportApp StatusIm.app/Contents/MacOs'
sh 'install_name_tool -add_rpath "@executable_path/../Frameworks" StatusIm.app/Contents/MacOs/reportApp'
sh 'cp -f ../deployment/macos/Info.plist StatusIm.app/Contents'
sh 'cp -f ../deployment/macos/qt.conf StatusIm.app/Contents/MacOs'
sh """
macdeployqt StatusIm.app -verbose=1 -dmg \\
-qmldir='${workspace}/node_modules/react-native/ReactQt/runtime/src/qml/'
@ -212,6 +216,7 @@ timeout(90) {
sh "cp -r ./deployment/linux/usr ${packageFolder}/AppDir"
sh "cp ./deployment/linux/.env ${packageFolder}/AppDir"
sh "cp ./desktop/bin/StatusIm ${packageFolder}/AppDir/usr/bin"
sh "cp ./desktop/reportApp/reportApp ${packageFolder}/AppDir/usr/bin"
sh 'wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage'
sh 'chmod a+x ./linuxdeployqt-continuous-x86_64.AppImage'
@ -219,6 +224,12 @@ timeout(90) {
sh 'rm -f StatusIm-x86_64.AppImage'
sh "ldd ${packageFolder}/AppDir/usr/bin/StatusIm"
sh """
./linuxdeployqt-continuous-x86_64.AppImage \\
${packageFolder}/AppDir/usr/bin/reportApp \\
-verbose=3 -always-overwrite -no-strip -no-translations -qmake=${qt_bin}/qmake \\
-qmldir='${workspace}/desktop/reportApp'
"""
sh """
./linuxdeployqt-continuous-x86_64.AppImage \\
${packageFolder}/AppDir/usr/share/applications/StatusIm.desktop \\

View File

@ -11,6 +11,7 @@ external_modules_dir = [
'node_modules/react-native-keychain/desktop',
'node_modules/react-native-securerandom/desktop',
'modules/react-native-status/desktop',
'node_modules/google-breakpad',
]
external_fonts = [
@ -116,6 +117,7 @@ def bundleLinux(type = 'nightly') {
sh "cp -r ./deployment/linux/usr ${packageFolder}/AppDir"
sh "cp ./deployment/linux/.env ${packageFolder}/AppDir"
sh "cp ./desktop/bin/StatusIm ${packageFolder}/AppDir/usr/bin"
sh "cp ./desktop/reportApp/reportApp ${packageFolder}/AppDir/usr/bin"
sh 'wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage'
sh 'chmod a+x ./linuxdeployqt-continuous-x86_64.AppImage'
@ -123,6 +125,12 @@ def bundleLinux(type = 'nightly') {
sh 'rm -f StatusIm-x86_64.AppImage'
sh "ldd ${packageFolder}/AppDir/usr/bin/StatusIm"
sh """
./linuxdeployqt-continuous-x86_64.AppImage \\
${packageFolder}/AppDir/usr/bin/reportApp \\
-verbose=3 -always-overwrite -no-strip -no-translations -qmake=${qtBin}/qmake \\
-qmldir='${workspace}/desktop/reportApp'
"""
sh """
./linuxdeployqt-continuous-x86_64.AppImage \\
${packageFolder}/AppDir/usr/share/applications/StatusIm.desktop \\
@ -177,6 +185,9 @@ def bundleMacOS(type = 'nightly') {
sh 'cp -r assets/share/assets StatusIm.app/Contents/MacOs'
sh 'chmod +x StatusIm.app/Contents/MacOs/ubuntu-server'
sh 'cp ../desktop/bin/StatusIm StatusIm.app/Contents/MacOs'
sh 'cp ../desktop/reportApp/reportApp StatusIm.app/Contents/MacOs'
sh 'cp -f ../deployment/macos/qt.conf StatusIm.app/Contents/MacOs'
sh 'install_name_tool -add_rpath "@executable_path/../Frameworks" StatusIm.app/Contents/MacOs/reportApp'
sh 'cp -f ../deployment/macos/Info.plist StatusIm.app/Contents'
sh """
macdeployqt StatusIm.app -verbose=1 -dmg \\

4
deployment/macos/qt.conf Normal file
View File

@ -0,0 +1,4 @@
[Paths]
Plugins = ../PlugIns
Imports = ../Resources/qml
Qml2Imports = ../Resources/qml

View File

@ -20,6 +20,8 @@ foreach(external_module ${EXTERNAL_MODULES_DIR})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../${external_module} ${CMAKE_CURRENT_BINARY_DIR}/${external_module})
endforeach(external_module)
add_subdirectory(reportApp)
# APPLICATION_MAIN_CPP_PATH contains absolute path to generated template copy of main.cpp for application executable
get_filename_component(APPLICATION_MAIN_CPP_PATH main.cpp ABSOLUTE)

View File

@ -12,8 +12,8 @@
#include <QCommandLineParser>
#include <QDirIterator>
#include <QFontDatabase>
#include <QFile>
#include <QFontDatabase>
#include <QGuiApplication>
#include <QProcess>
#include <QQuickView>
@ -26,16 +26,22 @@
#include "rootview.h"
#include "utilities.h"
#include "exceptionglobalhandler.h"
#ifdef BUILD_FOR_BUNDLE
#include <QMutexLocker>
QStringList consoleOutputStrings;
bool ubuntuServerStarted = false;
QMutex consoleOutputMutex;
QProcess *g_ubuntuServerProcess = nullptr;
#endif
const int MAIN_WINDOW_WIDTH = 1024;
const int MAIN_WINDOW_HEIGHT = 768;
const QString CRASH_REPORT_EXECUTABLE = QStringLiteral("reportApp");
const QString CRASH_REPORT_EXECUTABLE_RELATIVE_PATH =
QStringLiteral("/../reportApp");
// TODO: some way to change while running
class ReactNativeProperties : public QObject {
@ -143,13 +149,22 @@ void writeLogsToFile();
void loadFontsFromResources() {
QDirIterator it(":", QDirIterator::Subdirectories);
while (it.hasNext()) {
QString resourceFile = it.next();
if (resourceFile.endsWith(".otf", Qt::CaseInsensitive) || resourceFile.endsWith(".ttf", Qt::CaseInsensitive)) {
QFontDatabase::addApplicationFont(resourceFile);
}
QDirIterator it(":", QDirIterator::Subdirectories);
while (it.hasNext()) {
QString resourceFile = it.next();
if (resourceFile.endsWith(".otf", Qt::CaseInsensitive) ||
resourceFile.endsWith(".ttf", Qt::CaseInsensitive)) {
QFontDatabase::addApplicationFont(resourceFile);
}
}
}
void exceptionPostHandledCallback() {
#ifdef BUILD_FOR_BUNDLE
if (g_ubuntuServerProcess) {
g_ubuntuServerProcess->kill();
}
#endif
}
int main(int argc, char **argv) {
@ -157,6 +172,17 @@ int main(int argc, char **argv) {
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
app.setApplicationName("StatusIm");
QString appPath = QCoreApplication::applicationDirPath();
#ifndef BUILD_FOR_BUNDLE
appPath.append(CRASH_REPORT_EXECUTABLE_RELATIVE_PATH);
#endif
ExceptionGlobalHandler exceptionHandler(appPath + QDir::separator() +
CRASH_REPORT_EXECUTABLE,
exceptionPostHandledCallback);
Q_INIT_RESOURCE(react_resources);
loadFontsFromResources();
@ -242,35 +268,41 @@ void writeLogsToFile() {
}
void runUbuntuServer() {
QProcess *process = new QProcess();
process->setWorkingDirectory(getDataStoragePath());
process->setProgram(QGuiApplication::applicationDirPath() + "/ubuntu-server");
QObject::connect(process, &QProcess::errorOccurred,
g_ubuntuServerProcess = new QProcess();
g_ubuntuServerProcess->setWorkingDirectory(getDataStoragePath());
g_ubuntuServerProcess->setProgram(QGuiApplication::applicationDirPath() +
"/ubuntu-server");
QObject::connect(g_ubuntuServerProcess, &QProcess::errorOccurred,
[=](QProcess::ProcessError) {
qDebug() << "process name: " << process->program();
qDebug() << "process error: " << process->errorString();
qDebug() << "process name: "
<< g_ubuntuServerProcess->program();
qDebug() << "process error: "
<< g_ubuntuServerProcess->errorString();
});
QObject::connect(process, &QProcess::readyReadStandardOutput, [=] {
qDebug() << "ubuntu-server std: "
<< process->readAllStandardOutput().trimmed();
});
QObject::connect(process, &QProcess::readyReadStandardError, [=] {
QString output = process->readAllStandardError().trimmed();
qDebug() << "ubuntu-server err: " << output;
if (output.contains("Server starting")) {
ubuntuServerStarted = true;
}
});
QObject::connect(
g_ubuntuServerProcess, &QProcess::readyReadStandardOutput, [=] {
qDebug() << "ubuntu-server std: "
<< g_ubuntuServerProcess->readAllStandardOutput().trimmed();
});
QObject::connect(
g_ubuntuServerProcess, &QProcess::readyReadStandardError, [=] {
QString output =
g_ubuntuServerProcess->readAllStandardError().trimmed();
qDebug() << "ubuntu-server err: " << output;
if (output.contains("Server starting")) {
ubuntuServerStarted = true;
}
});
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit,
[=]() {
qDebug() << "Kill ubuntu server";
process->kill();
g_ubuntuServerProcess->kill();
});
qDebug() << "starting ubuntu server...";
process->start();
g_ubuntuServerProcess->start();
qDebug() << "wait for started...";
while (!ubuntuServerStarted) {

View File

@ -0,0 +1,36 @@
# Copyright (c) 2017-present, Status Research and Development GmbH.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
set(APP_NAME "reportApp")
find_package(Qt5Core REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5Quick REQUIRED)
find_package(Qt5WebSockets REQUIRED)
find_package(Qt5Svg REQUIRED)
set(MAIN_CPP_SOURCE reportpublisher.cpp
reportpublisher.cpp
main.cpp)
add_executable(
${APP_NAME}
${MAIN_CPP_SOURCE}
main.qrc
)
set(USED_QT_MODULES Core Qml Quick WebSockets Svg)
qt5_use_modules(${APP_NAME} ${USED_QT_MODULES})
set(REACT_NATIVE_DESKTOP_EXTERNAL_PROJECT_DEPS ${REACT_NATIVE_DESKTOP_EXTERNAL_PROJECT_DEPS} reportApp PARENT_SCOPE)

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2017-present, Status Research and Development GmbH.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include <QDebug>
#include <QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
#include "reportpublisher.h"
const int MAIN_WINDOW_WIDTH = 1024;
const int MAIN_WINDOW_HEIGHT = 768;
const int INPUT_ARGUMENTS_COUNT = 5;
int main(int argc, char **argv) {
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
if (argc != INPUT_ARGUMENTS_COUNT) {
return 1;
}
app.setApplicationName("Crash Report");
ReportPublisher reportPublisher(argv[1], argv[2]);
QQuickView view;
view.rootContext()->setContextProperty("reportPublisher", &reportPublisher);
view.setSource(QUrl("qrc:///main.qml"));
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.resize(MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT);
view.show();
return app.exec();
}

View File

@ -0,0 +1,76 @@
/**
* Copyright (c) 2017-present, Status Research and Development GmbH.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
import QtQuick 2.4
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Rectangle {
id: root
width: 384
height: 640
ColumnLayout {
anchors.centerIn: parent
Text {
Layout.alignment: Qt.AlignCenter
text: "Oh, no! StatusIm application just crashed!"
font.bold: true
font.pointSize: 25
}
Text {
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 20
text: "Please report us crash log files to allow us fix the issue!"
font.bold: true
font.pointSize: 20
}
RowLayout {
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 40
spacing: 25
Button {
Layout.minimumWidth: 150
text: "Report (highly appreciated)"
onClicked: reportPublisher.submit()
}
Button {
text: "Restart and Quit"
onClicked: reportPublisher.restartAndQuit()
}
Button {
text: "Just Quit"
onClicked: reportPublisher.quit()
}
}
RowLayout {
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 100
TextEdit {
readOnly: true
Layout.maximumWidth: 500
wrapMode: TextEdit.Wrap
selectByMouse: true
font.pointSize: 12
text: "Log files directory:\n" + reportPublisher.resolveDataStoragePath()
}
Button {
text: "View"
onClicked: reportPublisher.showDirectory()
}
}
}
}

View File

@ -0,0 +1,5 @@
<RCC>
<qresource>
<file>main.qml</file>
</qresource>
</RCC>

View File

@ -0,0 +1,89 @@
/**
* Copyright (c) 2017-present, Status Research and Development GmbH.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "reportpublisher.h"
#include <QApplication>
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QFile>
#include <QProcess>
#include <QUrl>
const QString REPORT_SUBMIT_URL =
QStringLiteral("https://goo.gl/forms/0705ZN0EMW3xLDpI2");
ReportPublisher::ReportPublisher(QString minidumpFilePath,
QString crashedExecutablePath, QObject *parent)
: QObject(parent), m_minidumpFilePath(minidumpFilePath),
m_crashedExecutablePath(crashedExecutablePath) {}
void ReportPublisher::submit() {
QDesktopServices::openUrl(QUrl(REPORT_SUBMIT_URL));
showDirectory();
}
void ReportPublisher::restartAndQuit() {
QString appPath = m_crashedExecutablePath;
#ifdef Q_OS_MACOS
QFileInfo crashedExecutableFileInfo(m_crashedExecutablePath);
QString fullPath = crashedExecutableFileInfo.dir().absolutePath();
const QString bundleExtension = QStringLiteral(".app");
if (fullPath.contains(bundleExtension)) {
appPath = fullPath.left(fullPath.indexOf(bundleExtension) +
bundleExtension.size());
}
QString cmd = QString("open %1").arg(appPath);
#elif defined(Q_OS_LINUX)
QString cmd = appPath;
#endif
QProcess::startDetached(cmd);
qApp->quit();
}
void ReportPublisher::quit() { qApp->quit(); }
void ReportPublisher::showDirectory() {
QString dataStoragePath = resolveDataStoragePath();
if (!m_logFilesPrepared) {
m_logFilesPrepared = prepareReportFiles(dataStoragePath);
}
QDesktopServices::openUrl(
QUrl("file://" + dataStoragePath, QUrl::TolerantMode));
}
bool ReportPublisher::prepareReportFiles(QString reportDirPath) {
QFileInfo minidumpFileInfo(m_minidumpFilePath);
QFileInfo crashedExecutableFileInfo(m_crashedExecutablePath);
if (!minidumpFileInfo.exists() || !crashedExecutableFileInfo.exists())
return false;
return QFile::copy(m_minidumpFilePath,
reportDirPath + QDir::separator() + "crash.dmp") &&
QFile::copy(m_crashedExecutablePath,
reportDirPath + QDir::separator() +
crashedExecutableFileInfo.fileName());
}
QString ReportPublisher::resolveDataStoragePath() {
QFileInfo minidumpFileInfo(m_minidumpFilePath);
QString dataStoragePath =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
QDir::separator() + minidumpFileInfo.baseName();
QDir dir(dataStoragePath);
if (!dir.exists()) {
dir.mkpath(".");
}
return dataStoragePath;
}

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2017-present, Status Research and Development GmbH.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#ifndef REPORTPUBLISHER
#define REPORTPUBLISHER
#include <QObject>
#include <QString>
class ReportPublisher : public QObject {
Q_OBJECT
public:
ReportPublisher(QString minidumpFilePath, QString crashedExecutablePath, QObject* parent = 0);
Q_INVOKABLE void submit();
Q_INVOKABLE void restartAndQuit();
Q_INVOKABLE void quit();
Q_INVOKABLE void showDirectory();
Q_INVOKABLE QString resolveDataStoragePath();
private:
bool prepareReportFiles(QString reportDirPath);
QString m_minidumpFilePath;
QString m_crashedExecutablePath;
bool m_logFilesPrepared = false;
};
#endif // REPORTPUBLISHER

View File

@ -14,7 +14,8 @@
"node_modules/react-native-webview-bridge/desktop",
"node_modules/react-native-keychain/desktop",
"node_modules/react-native-securerandom/desktop",
"modules/react-native-status/desktop"
"modules/react-native-status/desktop",
"node_modules/google-breakpad"
],
"desktopFonts": [
"../../../../../resources/fonts/SF-Pro-Text-Regular.otf",
@ -37,6 +38,7 @@
"dns.js": "1.0.1",
"emojilib": "2.2.9",
"events": "1.1.1",
"google-breakpad": "git+https://github.com/status-im/google-breakpad.git",
"homoglyph-finder": "1.1.1",
"identicon.js": "github:status-im/identicon.js",
"instabug-reactnative": "2.12.0",