diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d338f0fe..2ea361f7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,146 +1,147 @@ include (CheckFunctionExists) check_function_exists("strsignal" HAVE_STRSIGNAL) check_function_exists("uname" HAVE_UNAME) if (NOT DEBUG_PACKAGE_INSTALLER_NAME) set (DEBUG_PACKAGE_INSTALLER_NAME "installdbgsymbols.sh") endif () configure_file (config-drkonqi.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-drkonqi.h ) add_subdirectory( bugzillaintegration/libbugzilla ) add_subdirectory( data ) add_subdirectory( parser ) if ( WIN32 ) find_package(KDEWin REQUIRED) # for finding drkonqi_debug.h include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory( kdbgwin ) endif () set(drkonqi_SRCS drkonqidialog.cpp statuswidget.cpp aboutbugreportingdialog.cpp backtraceratingwidget.cpp backtracewidget.cpp backtracegenerator.cpp drkonqi.cpp drkonqibackends.cpp detachedprocessmonitor.cpp debugpackageinstaller.cpp systeminformation.cpp crashedapplication.cpp debugger.cpp debuggerlaunchers.cpp ptracer.cpp debuggermanager.cpp applicationdetailsexamples.cpp statusnotifier.cpp ) ki18n_wrap_ui(drkonqi_SRCS ui/maindialog.ui ui/backtracewidget.ui ) # if BACKTRACE_PARSER_DEBUG is enabled, it will show both the # parsed and the unparsed backtrace in the backtrace widget. # Comment this out for release. #add_definitions(-DBACKTRACE_PARSER_DEBUG) set(drkonqi_SRCS ${drkonqi_SRCS} bugzillaintegration/assistantpage_bugzilla_version.cpp bugzillaintegration/bugzillalib.cpp bugzillaintegration/reportassistantdialog.cpp bugzillaintegration/reportassistantpage.cpp bugzillaintegration/reportassistantpages_base.cpp bugzillaintegration/reportassistantpages_bugzilla.cpp bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp bugzillaintegration/reportinterface.cpp bugzillaintegration/productmapping.cpp bugzillaintegration/parsebugbacktraces.cpp bugzillaintegration/duplicatefinderjob.cpp ) ki18n_wrap_ui(drkonqi_SRCS bugzillaintegration/ui/assistantpage_introduction.ui bugzillaintegration/ui/assistantpage_bugawareness.ui bugzillaintegration/ui/assistantpage_conclusions.ui bugzillaintegration/ui/assistantpage_conclusions_dialog.ui bugzillaintegration/ui/assistantpage_bugzilla_login.ui bugzillaintegration/ui/assistantpage_bugzilla_duplicates.ui bugzillaintegration/ui/assistantpage_bugzilla_duplicates_dialog.ui bugzillaintegration/ui/assistantpage_bugzilla_duplicates_dialog_confirmation.ui bugzillaintegration/ui/assistantpage_bugzilla_information.ui bugzillaintegration/ui/assistantpage_bugzilla_preview.ui bugzillaintegration/ui/assistantpage_bugzilla_send.ui bugzillaintegration/ui/assistantpage_bugzilla_version.ui ) ecm_qt_declare_logging_category(drkonqi_SRCS HEADER drkonqi_debug.h IDENTIFIER DRKONQI_LOG CATEGORY_NAME org.kde.drkonqi) # transient static lib we can use to link autotests against add_library(DrKonqiInternal STATIC ${drkonqi_SRCS}) target_link_libraries(DrKonqiInternal KF5::I18n KF5::CoreAddons KF5::Service KF5::ConfigWidgets KF5::JobWidgets KF5::KIOCore + KF5::KIOGui KF5::Crash KF5::Completion KF5::WindowSystem Qt5::DBus KF5::WidgetsAddons KF5::Wallet KF5::Notifications # for status notifier KF5::IdleTime # hide status notifier only if user saw it KF5::SyntaxHighlighting # Backtrace Highlighting drkonqi_backtrace_parser qbugzilla ) if (${Qt5X11Extras_FOUND}) target_link_libraries(DrKonqiInternal Qt5::X11Extras ) endif() if (WIN32) target_link_libraries(DrKonqiInternal kdewin) endif() add_executable(drkonqi main.cpp) ecm_mark_nongui_executable(drkonqi) target_link_libraries(drkonqi DrKonqiInternal) install(TARGETS drkonqi DESTINATION ${KDE_INSTALL_LIBEXECDIR}) configure_file(org.kde.drkonqi.desktop.cmake ${CMAKE_BINARY_DIR}/src/org.kde.drkonqi.desktop) install(PROGRAMS ${CMAKE_BINARY_DIR}/src/org.kde.drkonqi.desktop DESTINATION ${KDE_INSTALL_APPDIR}) add_subdirectory( tests ) add_subdirectory(bugzillaintegration/libbugzilla/autotests) if(BUILD_COVERAGE AND BUILD_TESTING) # coverage without tests make no sense add_custom_target(coverage) add_custom_command(TARGET coverage COMMAND ctest COMMAND lcov -o ${CMAKE_BINARY_DIR}/lcov.report -c -d ${CMAKE_BINARY_DIR} COMMAND genhtml -o ${CMAKE_BINARY_DIR}/coverage ${CMAKE_BINARY_DIR}/lcov.report COMMAND find ${CMAKE_BINARY_DIR} -iname *.gcda | xargs rm -v DEPENDS test ) add_custom_target(show-coverage COMMAND xdg-open ${CMAKE_BINARY_DIR}/coverage/index.html DEPENDS coverage ) endif() diff --git a/src/crashedapplication.cpp b/src/crashedapplication.cpp index 3b59549b..b8ad0a2f 100644 --- a/src/crashedapplication.cpp +++ b/src/crashedapplication.cpp @@ -1,184 +1,181 @@ /* Copyright (C) 2009 George Kiagiadakis This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "crashedapplication.h" #if HAVE_STRSIGNAL && defined(Q_OS_UNIX) # include # include # include #else # if defined(Q_OS_UNIX) # include # else # include # endif #endif -#include +#include CrashedApplication::CrashedApplication(QObject *parent) : QObject(parent), m_restarted(false) { } CrashedApplication::~CrashedApplication() { } QString CrashedApplication::name() const { return m_name.isEmpty() ? fakeExecutableBaseName() : m_name; } QFileInfo CrashedApplication::executable() const { return m_executable; } QString CrashedApplication::fakeExecutableBaseName() const { if (!m_fakeBaseName.isEmpty()) { return m_fakeBaseName; } else { return m_executable.baseName(); } } QString CrashedApplication::version() const { return m_version; } BugReportAddress CrashedApplication::bugReportAddress() const { return m_reportAddress; } int CrashedApplication::pid() const { return m_pid; } int CrashedApplication::signalNumber() const { return m_signalNumber; } QString CrashedApplication::signalName() const { #if HAVE_STRSIGNAL && defined(Q_OS_UNIX) const char * oldLocale = std::setlocale(LC_MESSAGES, nullptr); char * savedLocale; if (oldLocale) { savedLocale = strdup(oldLocale); } else { savedLocale = nullptr; } std::setlocale(LC_MESSAGES, "C"); const char *name = strsignal(m_signalNumber); std::setlocale(LC_MESSAGES, savedLocale); std::free(savedLocale); return QString::fromLocal8Bit(name ? name : "Unknown"); #else switch (m_signalNumber) { # if defined(Q_OS_UNIX) case SIGILL: return QLatin1String("SIGILL"); case SIGABRT: return QLatin1String("SIGABRT"); case SIGFPE: return QLatin1String("SIGFPE"); case SIGSEGV: return QLatin1String("SIGSEGV"); case SIGBUS: return QLatin1String("SIGBUS"); # else case EXCEPTION_ACCESS_VIOLATION: return QLatin1String("EXCEPTION_ACCESS_VIOLATION"); case EXCEPTION_DATATYPE_MISALIGNMENT: return QLatin1String("EXCEPTION_DATATYPE_MISALIGNMENT"); case EXCEPTION_BREAKPOINT: return QLatin1String("EXCEPTION_BREAKPOINT"); case EXCEPTION_SINGLE_STEP: return QLatin1String("EXCEPTION_SINGLE_STEP"); case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return QLatin1String("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); case EXCEPTION_FLT_DENORMAL_OPERAND: return QLatin1String("EXCEPTION_FLT_DENORMAL_OPERAND"); case EXCEPTION_FLT_DIVIDE_BY_ZERO: return QLatin1String("EXCEPTION_FLT_DIVIDE_BY_ZERO"); case EXCEPTION_FLT_INEXACT_RESULT: return QLatin1String("EXCEPTION_FLT_INEXACT_RESULT"); case EXCEPTION_FLT_INVALID_OPERATION: return QLatin1String("EXCEPTION_FLT_INVALID_OPERATION"); case EXCEPTION_FLT_OVERFLOW: return QLatin1String("EXCEPTION_FLT_OVERFLOW"); case EXCEPTION_FLT_STACK_CHECK: return QLatin1String("EXCEPTION_FLT_STACK_CHECK"); case EXCEPTION_FLT_UNDERFLOW: return QLatin1String("EXCEPTION_FLT_UNDERFLOW"); case EXCEPTION_INT_DIVIDE_BY_ZERO: return QLatin1String("EXCEPTION_INT_DIVIDE_BY_ZERO"); case EXCEPTION_INT_OVERFLOW: return QLatin1String("EXCEPTION_INT_OVERFLOW"); case EXCEPTION_PRIV_INSTRUCTION: return QLatin1String("EXCEPTION_PRIV_INSTRUCTION"); case EXCEPTION_IN_PAGE_ERROR: return QLatin1String("EXCEPTION_IN_PAGE_ERROR"); case EXCEPTION_ILLEGAL_INSTRUCTION: return QLatin1String("EXCEPTION_ILLEGAL_INSTRUCTION"); case EXCEPTION_NONCONTINUABLE_EXCEPTION: return QLatin1String("EXCEPTION_NONCONTINUABLE_EXCEPTION"); case EXCEPTION_STACK_OVERFLOW: return QLatin1String("EXCEPTION_STACK_OVERFLOW"); case EXCEPTION_INVALID_DISPOSITION: return QLatin1String("EXCEPTION_INVALID_DISPOSITION"); # endif default: return QLatin1String("Unknown"); } #endif } bool CrashedApplication::hasBeenRestarted() const { return m_restarted; } int CrashedApplication::thread() const { return m_thread; } const QDateTime& CrashedApplication::datetime() const { return m_datetime; } bool CrashedApplication::hasDeletedFiles() const { return m_hasDeletedFiles; } void CrashedApplication::restart() { if (m_restarted) { return; } - //start the application via kdeinit, as it needs to have a pristine environment and - //KProcess::startDetached() can't start a new process with custom environment variables. - // if m_fakeBaseName is set, this means m_executable is the path to kdeinit4 - // so we need to use the fakeBaseName to restart the app - const int ret = KToolInvocation::kdeinitExec(!m_fakeBaseName.isEmpty() ? m_fakeBaseName : m_executable.absoluteFilePath()); - const bool success = (ret == 0); - - m_restarted = success; - emit restarted(success); + auto *job = new KIO::CommandLauncherJob(!m_fakeBaseName.isEmpty() ? m_fakeBaseName : m_executable.absoluteFilePath()); + connect(job, &KIO::CommandLauncherJob::result, this, [this](KJob *job) { + m_restarted = !job->error(); + emit restarted(m_restarted); + }); + job->start(); } QString getSuggestedKCrashFilename(const CrashedApplication* app) { QString filename = app->fakeExecutableBaseName() + QLatin1Char('-') + app->datetime().toString(QStringLiteral("yyyyMMdd-hhmmss")) + QStringLiteral(".kcrash.txt"); if (filename.contains(QLatin1Char('/'))) { filename = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1); } return filename; } diff --git a/src/drkonqi.cpp b/src/drkonqi.cpp index 058299da..68cb0e5c 100644 --- a/src/drkonqi.cpp +++ b/src/drkonqi.cpp @@ -1,425 +1,414 @@ /* Copyright (C) 2009 George Kiagiadakis This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Parts of this code were originally under the following license: * Copyright (C) 2000-2003 Hans Petter Bieker * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include "drkonqi.h" #include "drkonqi_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include "systeminformation.h" #include "crashedapplication.h" #include "drkonqibackends.h" #include "debuggermanager.h" #include "backtracegenerator.h" DrKonqi::DrKonqi() : m_signal(0) , m_pid(0) - , m_kdeinit(false) , m_safer(false) , m_restarted(false) , m_keepRunning(false) , m_thread(0) { m_backend = new KCrashBackend(); m_systemInformation = new SystemInformation(); } DrKonqi::~DrKonqi() { delete m_systemInformation; delete m_backend; } //static DrKonqi *DrKonqi::instance() { static DrKonqi drKonqiInstance; return &drKonqiInstance; } //based on KCrashDelaySetHandler from kdeui/util/kcrash.cpp class EnableCrashCatchingDelayed : public QObject { public: EnableCrashCatchingDelayed() { startTimer(10000); // 10 s } protected: void timerEvent(QTimerEvent *event) override { qCDebug(DRKONQI_LOG) << "Enabling drkonqi crash catching"; KCrash::setDrKonqiEnabled(true); killTimer(event->timerId()); this->deleteLater(); } }; bool DrKonqi::init() { if (!instance()->m_backend->init()) { return false; } else { //all ok, continue initialization // Set drkonqi to handle its own crashes, but only if the crashed app // is not drkonqi already. If it is drkonqi, delay enabling crash catching // to prevent recursive crashes (in case it crashes at startup) if (crashedApplication()->fakeExecutableBaseName() != QLatin1String("drkonqi")) { qCDebug(DRKONQI_LOG) << "Enabling drkonqi crash catching"; KCrash::setDrKonqiEnabled(true); } else { new EnableCrashCatchingDelayed; } return true; } } //static SystemInformation *DrKonqi::systemInformation() { return instance()->m_systemInformation; } //static DebuggerManager* DrKonqi::debuggerManager() { return instance()->m_backend->debuggerManager(); } //static CrashedApplication *DrKonqi::crashedApplication() { return instance()->m_backend->crashedApplication(); } //static void DrKonqi::saveReport(const QString & reportText, QWidget *parent) { if (isSafer()) { QTemporaryFile tf; tf.setFileTemplate(QStringLiteral("XXXXXX.kcrash.txt")); tf.setAutoRemove(false); if (tf.open()) { QTextStream textStream(&tf); textStream << reportText; textStream.flush(); KMessageBox::information(parent, xi18nc("@info", "Report saved to %1.", tf.fileName())); } else { KMessageBox::sorry(parent, i18nc("@info","Could not create a file in which to save the report.")); } } else { QString defname = getSuggestedKCrashFilename(crashedApplication()); QPointer dlg(new QFileDialog(parent, defname)); dlg->selectFile(defname); dlg->setWindowTitle(i18nc("@title:window","Select Filename")); dlg->setAcceptMode(QFileDialog::AcceptSave); dlg->setFileMode(QFileDialog::AnyFile); dlg->setOption(QFileDialog::DontResolveSymlinks, false); if (dlg->exec() != QDialog::Accepted) { return; } if (!dlg) { //Dialog is invalid, it was probably deleted (ex. via DBus call) //return and do not crash return; } QUrl fileUrl; if(!dlg->selectedUrls().isEmpty()) fileUrl = dlg->selectedUrls().first(); delete dlg; if (fileUrl.isValid()) { QTemporaryFile tf; if (tf.open()) { QTextStream ts(&tf); ts << reportText; ts.flush(); } else { KMessageBox::sorry(parent, xi18nc("@info","Cannot open file %1 " "for writing.", tf.fileName())); return; } // QFileDialog was run with confirmOverwrite, so we can safely // overwrite as necessary. KIO::FileCopyJob* job = KIO::file_copy(QUrl::fromLocalFile(tf.fileName()), fileUrl, -1, KIO::DefaultFlags | KIO::Overwrite); KJobWidgets::setWindow(job, parent); if (!job->exec()) { KMessageBox::sorry(parent, job->errorString()); } } } } // Helper functions for the shutdownSaveReport class ShutdownHelper : public QObject { Q_OBJECT public: QString shutdownSaveString; void removeOldFilesIn(QDir &dir) { auto fileList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::SortFlag::Time | QDir::Reversed); for(int i = fileList.size(); i >= 10; i--) { auto currentFile = fileList.takeFirst(); dir.remove(currentFile.fileName()); } } void saveReportAndQuit() { const QString dirname = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); // Try to create the directory to save the logs, if we can't open the directory, // just bail out. no need to hold the shutdown process. QDir dir(dirname); if (!dir.mkpath(dirname)) { qApp->quit(); } removeOldFilesIn(dir); const QString defname = dirname + QLatin1Char('/') + QStringLiteral("pid-") + QString::number(DrKonqi::pid()) + QLatin1Char('-') + getSuggestedKCrashFilename(DrKonqi::crashedApplication()); QFile shutdownSaveFile(defname); if (shutdownSaveFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream ts(&shutdownSaveFile); ts << shutdownSaveString; ts.flush(); shutdownSaveFile.close(); } deleteLater(); qApp->quit(); } void appendNewLine(const QString& newLine) { shutdownSaveString += newLine; } }; void DrKonqi::shutdownSaveReport() { auto btGenerator = instance()->debuggerManager()->backtraceGenerator(); auto shutdownHelper = new ShutdownHelper(); QObject::connect(btGenerator, &BacktraceGenerator::done, shutdownHelper, &ShutdownHelper::saveReportAndQuit); QObject::connect(btGenerator, &BacktraceGenerator::someError, shutdownHelper, &ShutdownHelper::saveReportAndQuit); QObject::connect(btGenerator, &BacktraceGenerator::failedToStart, shutdownHelper, &ShutdownHelper::saveReportAndQuit); QObject::connect(btGenerator, &BacktraceGenerator::newLine, shutdownHelper, &ShutdownHelper::appendNewLine); btGenerator->start(); } void DrKonqi::setSignal(int signal) { instance()->m_signal = signal; } void DrKonqi::setAppName(const QString &appName) { instance()->m_appName = appName; } void DrKonqi::setAppPath(const QString &appPath) { instance()->m_appPath = appPath; } void DrKonqi::setAppVersion(const QString &appVersion) { instance()->m_appVersion = appVersion; } void DrKonqi::setBugAddress(const QString &bugAddress) { instance()->m_bugAddress = bugAddress; } void DrKonqi::setProgramName(const QString &programName) { instance()->m_programName = programName; } void DrKonqi::setPid(int pid) { instance()->m_pid = pid; } -void DrKonqi::setKdeinit(bool kdeinit) -{ - instance()->m_kdeinit = kdeinit; -} - void DrKonqi::setSafer(bool safer) { instance()->m_safer = safer; } void DrKonqi::setRestarted(bool restarted) { instance()->m_restarted = restarted; } void DrKonqi::setKeepRunning(bool keepRunning) { instance()->m_keepRunning = keepRunning; } void DrKonqi::setThread(int thread) { instance()->m_thread = thread; } int DrKonqi::signal() { return instance()->m_signal; } const QString &DrKonqi::appName() { return instance()->m_appName; } const QString &DrKonqi::appPath() { return instance()->m_appPath; } const QString &DrKonqi::appVersion() { return instance()->m_appVersion; } const QString &DrKonqi::bugAddress() { return instance()->m_bugAddress; } const QString &DrKonqi::programName() { return instance()->m_programName; } int DrKonqi::pid() { return instance()->m_pid; } -bool DrKonqi::isKdeinit() -{ - return instance()->m_kdeinit; -} - bool DrKonqi::isSafer() { return instance()->m_safer; } bool DrKonqi::isRestarted() { return instance()->m_restarted; } bool DrKonqi::isKeepRunning() { return instance()->m_keepRunning; } int DrKonqi::thread() { return instance()->m_thread; } bool DrKonqi::ignoreQuality() { static bool ignore = qEnvironmentVariableIsSet("DRKONQI_IGNORE_QUALITY") || qEnvironmentVariableIsSet("DRKONQI_TEST_MODE"); return ignore; } const QString &DrKonqi::kdeBugzillaURL() { // WARNING: for practical reasons this cannot use the shared instance // Initing the instances requires knowing the URL already, so we'd have // an init loop. Use a local static instead. Otherwise we'd crash on // initialization of global statics derived from our return value. // Always copy into the local static and return that! static QString url; if (!url.isEmpty()) { return url; } url = QString::fromLocal8Bit(qgetenv("DRKONQI_KDE_BUGZILLA_URL")); if (!url.isEmpty()) { return url; } if (qEnvironmentVariableIsSet("DRKONQI_TEST_MODE")) { url = QStringLiteral("https://bugstest.kde.org/"); } else { url = QStringLiteral("https://bugs.kde.org/"); } return url; } #include "drkonqi.moc" diff --git a/src/drkonqi.h b/src/drkonqi.h index 0bab1527..a71ac153 100644 --- a/src/drkonqi.h +++ b/src/drkonqi.h @@ -1,90 +1,87 @@ /* Copyright (C) 2009 George Kiagiadakis This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRKONQI_H #define DRKONQI_H #include class QWidget; class SystemInformation; class DebuggerManager; class CrashedApplication; class AbstractDrKonqiBackend; class DrKonqi { public: static bool init(); static SystemInformation *systemInformation(); static DebuggerManager *debuggerManager(); static CrashedApplication *crashedApplication(); static void saveReport(const QString & reportText, QWidget *parent = nullptr); static void shutdownSaveReport(); static void setSignal(int signal); static void setAppName(const QString &appName); static void setAppPath(const QString &appPath); static void setAppVersion(const QString &appVersion); static void setBugAddress(const QString &bugAddress); static void setProgramName(const QString &programName); static void setPid(int pid); - static void setKdeinit(bool kdeinit); static void setSafer(bool safer); static void setRestarted(bool restarted); static void setKeepRunning(bool keepRunning); static void setThread(int thread); static int signal(); static const QString &appName(); static const QString &appPath(); static const QString &appVersion(); static const QString &bugAddress(); static const QString &programName(); static int pid(); - static bool isKdeinit(); static bool isSafer(); static bool isRestarted(); static bool isKeepRunning(); static int thread(); static bool ignoreQuality(); static const QString &kdeBugzillaURL(); private: DrKonqi(); ~DrKonqi(); static DrKonqi *instance(); SystemInformation *m_systemInformation = nullptr; AbstractDrKonqiBackend *m_backend = nullptr; int m_signal; QString m_appName; QString m_appPath; QString m_appVersion; QString m_bugAddress; QString m_programName; int m_pid; - bool m_kdeinit; bool m_safer; bool m_restarted; bool m_keepRunning; int m_thread; }; #endif diff --git a/src/drkonqibackends.cpp b/src/drkonqibackends.cpp index e38a4bd5..6b36b64d 100644 --- a/src/drkonqibackends.cpp +++ b/src/drkonqibackends.cpp @@ -1,283 +1,277 @@ /* Copyright (C) 2009 George Kiagiadakis This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "drkonqibackends.h" #include #include #include #include #include #include #include #include #include #include #include #include "drkonqi_debug.h" #include "crashedapplication.h" #include "debugger.h" #include "debuggermanager.h" #include "backtracegenerator.h" #include "drkonqi.h" #ifdef Q_OS_MACOS #include #endif AbstractDrKonqiBackend::~AbstractDrKonqiBackend() { } bool AbstractDrKonqiBackend::init() { m_crashedApplication = constructCrashedApplication(); m_debuggerManager = constructDebuggerManager(); return true; } KCrashBackend::KCrashBackend() : QObject(), AbstractDrKonqiBackend(), m_state(ProcessRunning) { } KCrashBackend::~KCrashBackend() { continueAttachedProcess(); } bool KCrashBackend::init() { AbstractDrKonqiBackend::init(); //check whether the attached process exists and whether we have permissions to inspect it if (crashedApplication()->pid() <= 0) { qCWarning(DRKONQI_LOG) << "Invalid pid specified"; return false; } #if !defined(Q_OS_WIN32) if (::kill(crashedApplication()->pid(), 0) < 0) { switch (errno) { case EPERM: qCWarning(DRKONQI_LOG) << "DrKonqi doesn't have permissions to inspect the specified process"; break; case ESRCH: qCWarning(DRKONQI_LOG) << "The specified process does not exist."; break; default: break; } return false; } //--keeprunning means: generate backtrace instantly and let the process continue execution if(DrKonqi::isKeepRunning()) { stopAttachedProcess(); debuggerManager()->backtraceGenerator()->start(); connect(debuggerManager(), &DebuggerManager::debuggerFinished, this, &KCrashBackend::continueAttachedProcess); } else { connect(debuggerManager(), &DebuggerManager::debuggerStarting, this, &KCrashBackend::onDebuggerStarting); connect(debuggerManager(), &DebuggerManager::debuggerFinished, this, &KCrashBackend::onDebuggerFinished); //stop the process to avoid high cpu usage by other threads (bug 175362). //if the process was started by kdeinit, we need to wait a bit for KCrash //to reach the alarm(0); call in kdeui/util/kcrash.cpp line 406 or else //if we stop it before this call, pending alarm signals will kill the //process when we try to continue it. QTimer::singleShot(2000, this, &KCrashBackend::stopAttachedProcess); } #endif //Handle drkonqi crashes s_pid = crashedApplication()->pid(); //copy pid for use by the crash handler, so that it is safer KCrash::setEmergencySaveFunction(emergencySaveFunction); return true; } CrashedApplication *KCrashBackend::constructCrashedApplication() { CrashedApplication *a = new CrashedApplication(this); a->m_datetime = QDateTime::currentDateTime(); a->m_name = DrKonqi::programName(); a->m_version = DrKonqi::appVersion(); a->m_reportAddress = BugReportAddress(DrKonqi::bugAddress()); a->m_pid = DrKonqi::pid(); a->m_signalNumber = DrKonqi::signal(); a->m_restarted = DrKonqi::isRestarted(); a->m_thread = DrKonqi::thread(); //try to determine the executable that crashed const QString procPath(QStringLiteral("/proc/%1").arg(a->m_pid)); const QString exeProcPath(procPath + QStringLiteral("/exe")); if (QFileInfo(exeProcPath).exists()) { //on linux, the fastest and most reliable way is to get the path from /proc qCDebug(DRKONQI_LOG) << "Using /proc to determine executable path"; const QString exePath = QFile::symLinkTarget(exeProcPath); a->m_executable.setFile(exePath); - if (DrKonqi::isKdeinit() || - a->m_executable.fileName().startsWith(QLatin1String("python")) ) { + if (a->m_executable.fileName().startsWith(QLatin1String("python"))) { a->m_fakeBaseName = DrKonqi::appName(); } QDir mapFilesDir(procPath + QStringLiteral("/map_files")); mapFilesDir.setFilter(mapFilesDir.filter() | QDir::System); // proc is system! // "/bin/foo (deleted)" is how the kernel tells us that a file has been deleted since // it was mmap'd. QRegularExpression expression(QStringLiteral("(?.+) \\(deleted\\)$")); // For the map_files we filter only .so files to ensure that // we don't trip over cache files or the like, as a result we // manually need to check if the main exe was deleted and add // it. // NB: includes .so* and .py* since we also implicitly support snakes to // a degree QRegularExpression soExpression(QStringLiteral("(?.+\\.(so|py)([^/]*)) \\(deleted\\)$")); bool hasDeletedFiles = false; const auto exeMatch = expression.match(exePath); if (exeMatch.isValid() && exeMatch.hasMatch()) { hasDeletedFiles = true; } const auto list = mapFilesDir.entryInfoList(); for (auto it = list.constBegin(); !hasDeletedFiles && it != list.constEnd(); ++it) { const auto match = soExpression.match(it->symLinkTarget()); if (!match.isValid() || !match.hasMatch()) { continue; } const QString path = match.captured(QStringLiteral("path")); if (path.startsWith(QStringLiteral("/memfd"))) { // Qml.so's JIT shows up under memfd. This is a false positive against our regex. continue; } hasDeletedFiles = true; } a->m_hasDeletedFiles = hasDeletedFiles; qCDebug(DRKONQI_LOG) << "exe" << exePath << "has deleted files:" << hasDeletedFiles; } else { - if ( DrKonqi::isKdeinit() ) { - a->m_executable = QFileInfo(QStandardPaths::findExecutable(QStringLiteral("kdeinit5"))); - a->m_fakeBaseName = DrKonqi::appName(); + QFileInfo execPath(DrKonqi::appName()); + if ( execPath.isAbsolute() ) { + a->m_executable = execPath; + } else if ( !DrKonqi::appPath().isEmpty() ) { + QDir execDir(DrKonqi::appPath()); + a->m_executable = execDir.absoluteFilePath(execPath.fileName()); } else { - QFileInfo execPath(DrKonqi::appName()); - if ( execPath.isAbsolute() ) { - a->m_executable = execPath; - } else if ( !DrKonqi::appPath().isEmpty() ) { - QDir execDir(DrKonqi::appPath()); - a->m_executable = execDir.absoluteFilePath(execPath.fileName()); - } else { - a->m_executable = QFileInfo(QStandardPaths::findExecutable(execPath.fileName())); - } + a->m_executable = QFileInfo(QStandardPaths::findExecutable(execPath.fileName())); } } qCDebug(DRKONQI_LOG) << "Executable is:" << a->m_executable.absoluteFilePath(); qCDebug(DRKONQI_LOG) << "Executable exists:" << a->m_executable.exists(); return a; } DebuggerManager *KCrashBackend::constructDebuggerManager() { QList internalDebuggers = Debugger::availableInternalDebuggers(QStringLiteral("KCrash")); KConfigGroup config(KSharedConfig::openConfig(), "DrKonqi"); #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED > 1070 QString defaultDebuggerName = config.readEntry("Debugger", QStringLiteral("lldb")); #elif !defined(Q_OS_WIN) QString defaultDebuggerName = config.readEntry("Debugger", QStringLiteral("gdb")); #else QString defaultDebuggerName = config.readEntry("Debugger", QStringLiteral("cdb")); #endif Debugger firstKnownGoodDebugger, preferredDebugger; foreach (const Debugger & debugger, internalDebuggers) { qCDebug(DRKONQI_LOG) << "Check debugger if" << debugger.displayName() << "[" << debugger.codeName() << "]" << "is installed:" << debugger.isInstalled(); if (!firstKnownGoodDebugger.isValid() && debugger.isInstalled()) { firstKnownGoodDebugger = debugger; } if (debugger.codeName() == defaultDebuggerName) { preferredDebugger = debugger; } if (firstKnownGoodDebugger.isValid() && preferredDebugger.isValid()) { break; } } if (!preferredDebugger.isInstalled()) { if (firstKnownGoodDebugger.isValid()) { preferredDebugger = firstKnownGoodDebugger; } else { qCWarning(DRKONQI_LOG) << "Unable to find an internal debugger that can work with the KCrash backend"; } } qCDebug(DRKONQI_LOG) << "Using debugger:" << preferredDebugger.codeName(); return new DebuggerManager(preferredDebugger, Debugger::availableExternalDebuggers(QStringLiteral("KCrash")), this); } void KCrashBackend::stopAttachedProcess() { if (m_state == ProcessRunning) { qCDebug(DRKONQI_LOG) << "Sending SIGSTOP to process"; ::kill(crashedApplication()->pid(), SIGSTOP); m_state = ProcessStopped; } } void KCrashBackend::continueAttachedProcess() { if (m_state == ProcessStopped) { qCDebug(DRKONQI_LOG) << "Sending SIGCONT to process"; ::kill(crashedApplication()->pid(), SIGCONT); m_state = ProcessRunning; } } void KCrashBackend::onDebuggerStarting() { continueAttachedProcess(); m_state = DebuggerRunning; } void KCrashBackend::onDebuggerFinished() { m_state = ProcessRunning; stopAttachedProcess(); } //static qint64 KCrashBackend::s_pid = 0; //static void KCrashBackend::emergencySaveFunction(int signal) { // In case drkonqi itself crashes, we need to get rid of the process being debugged, // so we kill it, no matter what its state was. Q_UNUSED(signal); ::kill(s_pid, SIGKILL); } diff --git a/src/main.cpp b/src/main.cpp index af9e64a6..9a984ee4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,243 +1,242 @@ /***************************************************************** * drkonqi - The KDE Crash Handler * * Copyright (C) 2000-2003 Hans Petter Bieker * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. *****************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_X11 #include #endif #ifdef Q_OS_MACOS #include #endif #include "config-drkonqi.h" #include "drkonqi.h" #include "drkonqidialog.h" #include "statusnotifier.h" #include "debuggermanager.h" #include "backtracegenerator.h" static const char version[] = PROJECT_VERSION; static const char description[] = I18N_NOOP("The KDE Crash Handler gives the user feedback " "if a program has crashed."); namespace { void openDrKonqiDialog () { DrKonqiDialog *w = new DrKonqiDialog(); QObject::connect(qApp, &QCoreApplication::aboutToQuit, w, &QObject::deleteLater); QObject::connect(w, &DrKonqiDialog::rejected, qApp, &QApplication::quit); w->show(); #ifdef Q_OS_MACOS KWindowSystem::forceActiveWindow(w->winId()); #endif } void requestDrKonqiDialog(bool restarted, bool interactionAllowed) { StatusNotifier *statusNotifier = new StatusNotifier(); statusNotifier->setActivationAllowed(interactionAllowed); if (interactionAllowed) { statusNotifier->show(); } if (!restarted) { statusNotifier->notify(); } QObject::connect(statusNotifier, &StatusNotifier::expired, qApp, &QApplication::quit); QObject::connect(statusNotifier, &StatusNotifier::activated, &openDrKonqiDialog); } bool isShuttingDown() { if (QDBusConnection::sessionBus().isConnected()) { QDBusInterface remoteApp( QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QStringLiteral("org.kde.KSMServerInterface" )); QDBusReply reply = remoteApp.call( QStringLiteral("isShuttingDown") ); return reply.isValid() ? reply.value() : false; } return false; } } int main(int argc, char* argv[]) { #ifndef Q_OS_WIN //krazy:exclude=cpp // Drop privs. setgid(getgid()); if (setuid(getuid()) < 0 && geteuid() != getuid()) { exit(255); } #endif QApplication app(argc, argv); app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); KLocalizedString::setApplicationDomain("drkonqi5"); // Prevent KApplication from setting the crash handler. We will set it later... setenv("KDE_DEBUG", "true", 1); // Session management is not needed, do not even connect in order to survive longer than ksmserver. unsetenv("SESSION_MANAGER"); KAboutData aboutData(QStringLiteral("drkonqi"), i18n("The KDE Crash Handler"), QString::fromLatin1(version), i18n(description), KAboutLicense::GPL, i18n("(C) 2000-2018, The DrKonqi Authors")); aboutData.addAuthor(i18nc("@info:credit","Hans Petter Bieker"), QString(), QStringLiteral("bieker@kde.org")); aboutData.addAuthor(i18nc("@info:credit","Dario Andres Rodriguez"), QString(), QStringLiteral("andresbajotierra@gmail.com")); aboutData.addAuthor(i18nc("@info:credit","George Kiagiadakis"), QString(), QStringLiteral("gkiagia@users.sourceforge.net")); aboutData.addAuthor(i18nc("@info:credit","A. L. Spehr"), QString(), QStringLiteral("spehr@kde.org")); KAboutData::setApplicationData(aboutData); app.setWindowIcon(QIcon::fromTheme(QStringLiteral("tools-report-bug"), app.windowIcon())); QCommandLineParser parser; aboutData.setupCommandLine(&parser); const QCommandLineOption signalOption(QStringLiteral("signal"), i18nc("@info:shell","The signal that was caught"), QStringLiteral("number")); const QCommandLineOption appNameOption(QStringLiteral("appname"), i18nc("@info:shell"," of the program"), QStringLiteral("name")); const QCommandLineOption appPathOption(QStringLiteral("apppath"), i18nc("@info:shell"," to the executable"), QStringLiteral("path")); const QCommandLineOption appVersionOption(QStringLiteral("appversion"), i18nc("@info:shell","The of the program"), QStringLiteral("version")); const QCommandLineOption bugAddressOption(QStringLiteral("bugaddress"), i18nc("@info:shell","The bug
to use"), QStringLiteral("address")); const QCommandLineOption programNameOption(QStringLiteral("programname"), i18nc("@info:shell","Translated of the program"), QStringLiteral("name")); const QCommandLineOption pidOption(QStringLiteral("pid"), i18nc("@info:shell","The of the program"), QStringLiteral("pid")); const QCommandLineOption startupIdOption(QStringLiteral("startupid"), i18nc("@info:shell","Startup of the program"), QStringLiteral("id")); - const QCommandLineOption kdeinitOption(QStringLiteral("kdeinit"), i18nc("@info:shell","The program was started by kdeinit")); + const QCommandLineOption kdeinitOption(QStringLiteral("kdeinit"), i18nc("@info:shell","Deprecated. Does nothing.")); const QCommandLineOption saferOption(QStringLiteral("safer"), i18nc("@info:shell","Disable arbitrary disk access")); const QCommandLineOption restartedOption(QStringLiteral("restarted"), i18nc("@info:shell","The program has already been restarted")); const QCommandLineOption keepRunningOption(QStringLiteral("keeprunning"), i18nc("@info:shell","Keep the program running and generate " "the backtrace at startup")); const QCommandLineOption threadOption(QStringLiteral("thread"), i18nc("@info:shell","The of the failing thread"), QStringLiteral("threadid")); const QCommandLineOption dialogOption(QStringLiteral("dialog"), i18nc("@info:shell","Do not show a notification but launch the debug dialog directly")); parser.addOptions({ signalOption, appNameOption, appPathOption, appVersionOption, bugAddressOption, programNameOption, pidOption, startupIdOption, kdeinitOption, saferOption, restartedOption, keepRunningOption, threadOption, dialogOption }); // Add all unknown options but make sure to print a warning. // This enables older DrKonqi's to run by newer KCrash instances with // possibly different/new options. // KCrash can always send all options it knows to send and be sure that // DrKonqi will not explode on them. If an option is not known here it's // either too old or too new. // // To implement this smartly we'll ::parse all arguments, and then ::process // them again once we have injected no-op options for all unknown ones. // This allows ::process to still do common argument handling for --version // as well as standard error handling. if (!parser.parse(app.arguments())) { for (const QString &option : parser.unknownOptionNames()) { qWarning() << "Unknown option" << option << " - ignoring it."; parser.addOption(QCommandLineOption(option)); } } parser.process(app); aboutData.processCommandLine(&parser); DrKonqi::setSignal(parser.value(signalOption).toInt()); DrKonqi::setAppName(parser.value(appNameOption)); DrKonqi::setAppPath(parser.value(appPathOption)); DrKonqi::setAppVersion(parser.value(appVersionOption)); DrKonqi::setBugAddress(parser.value(bugAddressOption)); DrKonqi::setProgramName(parser.value(programNameOption)); DrKonqi::setPid(parser.value(pidOption).toInt()); - DrKonqi::setKdeinit(parser.isSet(kdeinitOption)); DrKonqi::setSafer(parser.isSet(saferOption)); DrKonqi::setRestarted(parser.isSet(restartedOption)); DrKonqi::setKeepRunning(parser.isSet(keepRunningOption)); DrKonqi::setThread(parser.value(threadOption).toInt()); auto forceDialog = parser.isSet(dialogOption); #if HAVE_X11 const QString startupId = parser.value(startupIdOption); if (!startupId.isEmpty()) { QX11Info::setNextStartupId(startupId.toUtf8()); } #endif if (!DrKonqi::init()) { return 1; } app.setQuitOnLastWindowClosed(false); const bool restarted = parser.isSet(restartedOption); // Whether the user should be encouraged to file a bug report const bool interactionAllowed = KConfigGroup(KSharedConfig::openConfig(), "General").readEntry("InteractionAllowed", true); const bool shuttingDown = isShuttingDown(); // For automatically restarted services or during shutdown, do nothing in case no interaction is allowed if (!forceDialog && !interactionAllowed && (restarted || shuttingDown)) { return 0; } // if no notification service is running (eg. shell crashed, or other desktop environment) // and we didn't auto-restart the app, open DrKonqi dialog instead of showing an SNI // and emitting a desktop notification. if (shuttingDown) { DrKonqi::shutdownSaveReport(); } else if (forceDialog || (!restarted && !StatusNotifier::notificationServiceRegistered())) { openDrKonqiDialog(); } else { requestDrKonqiDialog(restarted, interactionAllowed); } return app.exec(); } diff --git a/src/tests/crashtest/crashtest.cpp b/src/tests/crashtest/crashtest.cpp index 0feab695..2d2d226a 100644 --- a/src/tests/crashtest/crashtest.cpp +++ b/src/tests/crashtest/crashtest.cpp @@ -1,171 +1,170 @@ /***************************************************************** * drkonqi - The KDE Crash Handler * * Copyright (C) 2000-2002 David Faure * Copyright (C) 2000-2002 Waldo Bastian * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. *****************************************************************/ // Let's crash. #include #include #include #include #include #include #include enum CrashType { Crash, Malloc, Div0, Assert, QAssert, Threads, FatalErrorMessage }; struct SomeStruct { int foo() { return ret; } int ret; }; void do_crash() { SomeStruct *obj = nullptr; int ret = obj->foo(); printf("result = %d\n", ret); } void do_malloc() { delete (char*)0xdead; } void do_div0() { volatile int a = 99; volatile int b = 10; volatile int c = a / ( b - 10 ); printf("result = %d\n", c); } void do_assert() { assert(false); } void do_qassert() { Q_ASSERT(false); } void map_function(const QString & s) { while ( s != QLatin1String("thread 4") ) {} do_crash(); } void do_threads() { QStringList foo; foo << QStringLiteral("thread 1") << QStringLiteral("thread 2") << QStringLiteral("thread 3") << QStringLiteral("thread 4") << QStringLiteral("thread 5"); QThreadPool::globalInstance()->setMaxThreadCount(5); QtConcurrent::blockingMap(foo, map_function); } void do_fatalErrorMessage() { KCrash::setErrorMessage(QStringLiteral("So long, my friends...")); qFatal("So long!\n"); } void level4(int t) { if (t == Malloc) do_malloc(); else if (t == Div0) do_div0(); else if (t == Assert) do_assert(); else if (t == QAssert) do_qassert(); else if (t == Threads) do_threads(); else if (t == FatalErrorMessage) do_fatalErrorMessage(); else do_crash(); } void level3(int t) { level4(t); } void level2(int t) { level3(t); } void level1(int t) { level2(t); } int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); KAboutData aboutData(QStringLiteral("crashtest"), QStringLiteral("Crash Test for DrKonqi"), QStringLiteral("1.1"), QStringLiteral("Crash Test for DrKonqi"), KAboutLicense::GPL, QStringLiteral("(c) 2000-2002 David Faure, Waldo Bastian")); QCommandLineParser parser; parser.addOption(QCommandLineOption(QStringLiteral("autorestart"), QStringLiteral("Automatically restart"))); - parser.addOption(QCommandLineOption(QStringLiteral("kdeinit"), QStringLiteral("Start DrKonqi using kdeinit"))); parser.addPositionalArgument(QStringLiteral("type"), QStringLiteral("Type of crash."), QStringLiteral("crash|malloc|div0|assert|threads|fatal")); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); // Start drkonqi directly by default so that drkonqi's output goes to the console. KCrash::CrashFlags flags = KCrash::AlwaysDirectly; // This can be disabled to be able to test kcrash's real default behavior. if (parser.isSet(QStringLiteral("kdeinit"))) flags &= ~KCrash::AlwaysDirectly; if (parser.isSet(QStringLiteral("autorestart"))) flags |= KCrash::AutoRestart; KCrash::setFlags(flags); QByteArray type = parser.positionalArguments().isEmpty() ? QByteArray() : parser.positionalArguments().first().toUtf8(); int crashtype = Crash; if (type == "malloc") crashtype = Malloc; else if (type == "div0") crashtype = Div0; else if (type == "assert") crashtype = Assert; else if (type == "qassert") crashtype = QAssert; else if (type == "threads") crashtype = Threads; else if (type == "fatal") crashtype = FatalErrorMessage; level1(crashtype); return app.exec(); }