Reliable way to determine working Qt5 without risking a SIGABRT (causing a Python core dump)?

I summarize my investigation results here to make it visible and hope, someone can find a solution for this later (beyond the proposed work-around)... Problem description: ------------------- BiT uses the "QApplication" Qt5 class for its GUI. In the context of the new feature "Diagnostics about Qt5 theme" https://github.com/bit-team/backintime/pull/1469/files#r1254526865 and my upcoming PR to fix all systemtray icon issues @buhtz and me could not find a reliable way to determine if a "QApplication" can be instantiated without causing a Python crash (SIGABRT with core dump) in some cases where Qt5 is unable to initalize itself (e.g. on a headless console-only system or if a non-supported Qt5-plugin is configured). Note: A core dump is snapshot of certain memory regions of the process at the time it crashed. It can be analyzed postmortem using debuggers like GDB. Symptoms: -------- Qt5 aborts Python with this output: > python3 qapp_test.py qt.qpa.xcb: could not connect to display qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found. This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem. Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, xcb. Aborted (core dumped) Minimal reproducible code example: --------------------------------- # File: qapp_test.py # try: from PyQt5.QtWidgets import QApplication, QSystemTrayIcon app = QApplication(['']) if QSystemTrayIcon.isSystemTrayAvailable(): print("systrayicon works") except: print(f"Exception {repr(e)}") Reason: ------- Qt5 uses the Macro "qFatal()" if a QApplication class instance cannot be created: https://doc.qt.io/qt-5/qtglobal.html#qFatal which sends a SIGABRT ("abort") signal on Linux: https://codebrowser.dev/qt5/qtbase/src/corelib/global/qlogging.h.html#168 https://www.qtcentre.org/threads/59684-qFatal-is-killing-my-application-with... TODO Find the exact Qt5 code for this. I could see the SIGABRT in the core dump... This SIGABRT signal is sent from the Python app process to the parent (Python3 Interpreter) and cannot (!) be handled in Python (e.g. via "signal.signal(signal.SIGABRT, signal.SIG_IGN") because Qt5 seems to exit the process immediately. https://docs.python.org/3/library/signal.html > A Python signal handler does not get executed inside the low-level (C) signal handler. > Instead, the low-level signal handler sets a flag which tells the virtual machine > to execute the corresponding Python signal handler at a later point Possible work-around: -------------------- Proposed by @buhtz: Spawn a sub process with above example code and check for the SIGABRT signal (or perhaps an expected return code or output). Impact: - Lower Performance (GUI startup time, diagnostics creation, systray icon creation) - The behaviour of Qt5 may not be the same as in the parent process (e.g. some missing explicit settings) -> to be investigated later

On 2023-07-11 01:34 BiT dev <python@altfeld-im.de> wrote:
Don't we use this approach only when using "--diagnostics"? Or are there other scenarios where we need this? If it is only "--diganostics" I would say "low" performance is not a big deal. To be sure using the same environment as "backintime-qt" we could parse the start-script. Another dirty hack. Or we modify the start script that way that it optionally use a commandline argument "$1" instead of app.py. By default that script would start the qt frontend (via qt/app.py). But if the name of another py-script is givin via argument this is used instead. It gets dirtier. ;)

On Sat, 2023-07-15 at 13:19 +0000, c.buhtz@posteo.jp wrote:
Currently the only other scenario is when loading the systray icon plugin at BiT Qt startup (so it would "only" prolong the startup duration of Bit Qt). This plugin needs to check if Qt5 is available and recognizes a system tray... I have a working solution now and do just a lot of testing in my different VMs by spawning a processs that reduces the soft limit for coredump file sizes via rlimit (basically a kernel system call) to zero: # To suppress the creation of coredump file on Linux # use resource.setrlimit() to set resource.RLIMIT_CORE’s soft limit to 0 # to prevent coredump file creation. # https://docs.python.org/3.10/library/resource.html#resource.RLIMIT_CORE # https://docs.python.org/3.10/library/resource.html#resource.setrlimit # See also the source code of the test.support.SuppressCrashReport() context manager: # if self.resource is not None: # try: # self.old_value = self.resource.getrlimit(self.resource.RLIMIT_CORE) # self.resource.setrlimit(self.resource.RLIMIT_CORE, # (0, self.old_value[1])) # except (ValueError, OSError): # pass # See "man 2 getrlimit" for more details

On 2023-07-11 01:34 BiT dev <python@altfeld-im.de> wrote:
Don't we use this approach only when using "--diagnostics"? Or are there other scenarios where we need this? If it is only "--diganostics" I would say "low" performance is not a big deal. To be sure using the same environment as "backintime-qt" we could parse the start-script. Another dirty hack. Or we modify the start script that way that it optionally use a commandline argument "$1" instead of app.py. By default that script would start the qt frontend (via qt/app.py). But if the name of another py-script is givin via argument this is used instead. It gets dirtier. ;)

On Sat, 2023-07-15 at 13:19 +0000, c.buhtz@posteo.jp wrote:
Currently the only other scenario is when loading the systray icon plugin at BiT Qt startup (so it would "only" prolong the startup duration of Bit Qt). This plugin needs to check if Qt5 is available and recognizes a system tray... I have a working solution now and do just a lot of testing in my different VMs by spawning a processs that reduces the soft limit for coredump file sizes via rlimit (basically a kernel system call) to zero: # To suppress the creation of coredump file on Linux # use resource.setrlimit() to set resource.RLIMIT_CORE’s soft limit to 0 # to prevent coredump file creation. # https://docs.python.org/3.10/library/resource.html#resource.RLIMIT_CORE # https://docs.python.org/3.10/library/resource.html#resource.setrlimit # See also the source code of the test.support.SuppressCrashReport() context manager: # if self.resource is not None: # try: # self.old_value = self.resource.getrlimit(self.resource.RLIMIT_CORE) # self.resource.setrlimit(self.resource.RLIMIT_CORE, # (0, self.old_value[1])) # except (ValueError, OSError): # pass # See "man 2 getrlimit" for more details
participants (2)
-
BiT dev
-
c.buhtz@posteo.jp