diff options
author | Robert Griebl <robert.griebl@qt.io> | 2024-03-06 15:32:08 +0100 |
---|---|---|
committer | Robert Griebl <robert.griebl@qt.io> | 2024-03-07 09:27:50 +0100 |
commit | 23b39347af03a40af4d19be9793f0f93a691a78f (patch) | |
tree | edcb8693798a1eb4d45756cf74e7f34dfc10eae2 | |
parent | ea49063b02733e3ec8caaf39a610fb70d24d5b0a (diff) |
Fix exception handling after Q*Application::exec()
Uncaught exceptions thrown after we call exec() are originating
from user code and we should not be catching those in our top-level
catch handler. This will just print "ERROR: <what()>" without any
context and make it look like the error is coming from the AM itself.
Instead we need to run the event loop without a try/catch handler, so
stray exception from user code go directly to our set_terminate handler
in the CrashHandler class, which preserves as much context as possible
and prints out a lot more useful meta data for debugging.
Change-Id: Icc0432d5a6c5db85de439cafab89d05aa88b5891
Pick-to: 6.7 6.6 6.5
Reviewed-by: Dominik Holland <dominik.holland@qt.io>
-rw-r--r-- | examples/applicationmanager/custom-appman/custom-appman.cpp | 19 | ||||
-rw-r--r-- | examples/applicationmanager/custom-appman/doc/src/custom-appman.qdoc | 4 | ||||
-rw-r--r-- | src/main-lib/main.h | 2 | ||||
-rw-r--r-- | src/main-lib/mainmacro.h | 23 | ||||
-rw-r--r-- | src/tools/appman/appman.cpp | 33 | ||||
-rw-r--r-- | src/tools/launcher-qml/launcher-qml.cpp | 10 |
6 files changed, 51 insertions, 40 deletions
diff --git a/examples/applicationmanager/custom-appman/custom-appman.cpp b/examples/applicationmanager/custom-appman/custom-appman.cpp index 28f7a1b9..3ec4c6e5 100644 --- a/examples/applicationmanager/custom-appman/custom-appman.cpp +++ b/examples/applicationmanager/custom-appman/custom-appman.cpp @@ -23,21 +23,24 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) QCoreApplication::setApplicationName(u"Custom Application Manager"_s); QCoreApplication::setApplicationVersion(u"0.1"_s); - try { - Main a(argc, argv, Main::InitFlag::ForkSudoServer | Main::InitFlag::InitializeLogging); + std::unique_ptr<Main> a; + std::unique_ptr<Configuration> cfg; - Configuration cfg; - cfg.parseWithArguments(QCoreApplication::arguments()); + try { + a = std::make_unique<Main>(argc, argv, Main::InitFlag::ForkSudoServer + | Main::InitFlag::InitializeLogging); + cfg = std::make_unique<Configuration>(); + cfg->parseWithArguments(QCoreApplication::arguments()); - a.setup(&cfg); - a.loadQml(cfg.loadDummyData()); - a.showWindow(cfg.fullscreen() && !cfg.noFullscreen()); + a->setup(cfg.get()); + a->loadQml(); + a->showWindow(cfg->fullscreen() && !cfg->noFullscreen()); - return Main::exec(); } catch (const std::exception &e) { qCritical() << "ERROR:" << e.what(); return 2; } + return Main::exec(); } #endif diff --git a/examples/applicationmanager/custom-appman/doc/src/custom-appman.qdoc b/examples/applicationmanager/custom-appman/doc/src/custom-appman.qdoc index ee6c1b82..55f9fc4f 100644 --- a/examples/applicationmanager/custom-appman/doc/src/custom-appman.qdoc +++ b/examples/applicationmanager/custom-appman/doc/src/custom-appman.qdoc @@ -36,7 +36,7 @@ them is both intended and required, so we need to set the define \c AM_COMPILING The following is a breakdown of the minimal code necessary: \quotefromfile applicationmanager/custom-appman/custom-appman.cpp -\skipto #include +\skipto #include <QtAppManMain/main.h> \printuntil QT_USE_NAMESPACE_AM The application manager is split into functional building blocks. These include statements @@ -49,7 +49,7 @@ the application manager's symbols are namespaced - \c QT_USE_NAMESPACE_AM expand Generally, it's a good idea to set an application name and version. -\printuntil Main a +\printuntil a = std::make_unique<Main> This \c try block is the heart of the custom application manager. You need to create a \c Main object, which is a class derived from QGuiApplication (or QApplication, if widgets support is diff --git a/src/main-lib/main.h b/src/main-lib/main.h index fc2e2672..ddb6704e 100644 --- a/src/main-lib/main.h +++ b/src/main-lib/main.h @@ -72,7 +72,7 @@ public: bool isRunningOnEmbedded() const; void setup(const Configuration *cfg) noexcept(false); - void loadQml(bool loadDummyData) noexcept(false); + void loadQml(bool loadDummyData = false) noexcept(false); void showWindow(bool showFullscreen); Q_INVOKABLE void shutDown(int exitCode = 0); diff --git a/src/main-lib/mainmacro.h b/src/main-lib/mainmacro.h index 2b99fcba..f27af6a6 100644 --- a/src/main-lib/mainmacro.h +++ b/src/main-lib/mainmacro.h @@ -14,21 +14,24 @@ QT_END_NAMESPACE_AM #define QT_AM_MAIN() \ Q_DECL_EXPORT int main(int argc, char *argv[]) \ { \ + std::unique_ptr<QtAM::Main> a; \ + std::unique_ptr<QtAM::Configuration> cfg; \ +\ try { \ - QtAM::Main a(argc, argv, QtAM::Main::InitFlag::ForkSudoServer | QtAM::Main::InitFlag::InitializeLogging); \ - \ - QtAM::Configuration cfg; \ - cfg.parseWithArguments(QCoreApplication::arguments()); \ - \ - a.setup(&cfg); \ - a.loadQml(cfg.loadDummyData()); \ - a.showWindow(cfg.fullscreen() && !cfg.noFullscreen()); \ - \ - return QtAM::MainBase::exec(); \ + a = std::make_unique<QtAM::Main>(argc, argv, QtAM::Main::InitFlag::ForkSudoServer \ + | QtAM::Main::InitFlag::InitializeLogging); \ + cfg = std::make_unique<QtAM::Configuration>(); \ + cfg->parseWithArguments(QCoreApplication::arguments()); \ +\ + a->setup(cfg.get()); \ + a->loadQml(); \ + a->showWindow(cfg->fullscreen() && !cfg->noFullscreen()); \ } catch (const std::exception &e) { \ qCritical() << "ERROR:" << e.what(); \ return 2; \ } \ +\ + return QtAM::Main::exec(); \ } #endif // MAINMACRO_H diff --git a/src/tools/appman/appman.cpp b/src/tools/appman/appman.cpp index 06cc3ce6..02f32424 100644 --- a/src/tools/appman/appman.cpp +++ b/src/tools/appman/appman.cpp @@ -46,26 +46,31 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain(u"qt-project.org"_s); QCoreApplication::setApplicationVersion(QString::fromLatin1(QT_AM_VERSION_STR)); - try { - Main a(argc, argv, Main::InitFlag::ForkSudoServer | Main::InitFlag::InitializeLogging); - - Configuration cfg(additionalDescription, onlyOnePositionalArgument); - cfg.parseWithArguments(QCoreApplication::arguments()); + std::unique_ptr<Main> a; + std::unique_ptr<Configuration> cfg; -#if defined(AM_TESTRUNNER) - TestRunner::setup(&cfg); -#endif - a.setup(&cfg); - a.loadQml(cfg.loadDummyData()); - a.showWindow(cfg.fullscreen() && !cfg.noFullscreen()); + try { + a = std::make_unique<Main>(argc, argv, Main::InitFlag::ForkSudoServer + | Main::InitFlag::InitializeLogging); + cfg = std::make_unique<Configuration>(additionalDescription, onlyOnePositionalArgument); + cfg->parseWithArguments(QCoreApplication::arguments()); #if defined(AM_TESTRUNNER) - return TestRunner::exec(a.qmlEngine()); -#else - return Main::exec(); + TestRunner::setup(cfg.get()); #endif + a->setup(cfg.get()); + a->loadQml(cfg->loadDummyData()); + a->showWindow(cfg->fullscreen() && !cfg->noFullscreen()); } catch (const Exception &e) { qCCritical(LogSystem).noquote() << "ERROR:" << e.errorString(); return 2; } + + // we want the exec() outside of the try/catch block, so stray user exceptions trigger the + // CrashHandler's set_terminate callback. +#if defined(AM_TESTRUNNER) + return TestRunner::exec(a->qmlEngine()); +#else + return Main::exec(); +#endif } diff --git a/src/tools/launcher-qml/launcher-qml.cpp b/src/tools/launcher-qml/launcher-qml.cpp index c0953385..55b2a945 100644 --- a/src/tools/launcher-qml/launcher-qml.cpp +++ b/src/tools/launcher-qml/launcher-qml.cpp @@ -82,6 +82,7 @@ int main(int argc, char *argv[]) Logging::initialize(); std::unique_ptr<ApplicationMain> am; + std::unique_ptr<Controller> controller; // this needs to die BEFORE qApp does try { const QString socket = QDir(qEnvironmentVariable("XDG_RUNTIME_DIR")) @@ -116,8 +117,6 @@ int main(int argc, char *argv[]) StartupTimer::instance()->checkpoint("after basic initialization"); - std::unique_ptr<Controller> controller; // this needs to die BEFORE qApp does - if (!directLoadManifest.isEmpty()) { QString directLoadAppId; qsizetype appPos = directLoadManifest.indexOf(u"@"_s); @@ -136,13 +135,14 @@ int main(int argc, char *argv[]) StartupTimer::instance()->checkpoint("after dbus initialization"); controller.reset(new Controller(am.get(), quicklaunched)); } - - return am->exec(); - } catch (const std::exception &e) { qCCritical(LogQmlRuntime) << "ERROR:" << e.what(); return 2; } + + // we want the exec() outside of the try/catch block, so stray user exceptions trigger the + // CrashHandler's set_terminate callback. + return ApplicationMainBase::exec(); } Controller::Controller(ApplicationMain *am, bool quickLaunched) |