Display error dialog when started after segfault
This commit is contained in:
parent
8fe4000e41
commit
659fe5126b
@ -31,6 +31,7 @@ import sys
|
|||||||
import types
|
import types
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import faulthandler
|
||||||
import configparser
|
import configparser
|
||||||
from bdb import BdbQuit
|
from bdb import BdbQuit
|
||||||
from functools import partial
|
from functools import partial
|
||||||
@ -54,7 +55,7 @@ from qutebrowser.network.networkmanager import NetworkManager
|
|||||||
from qutebrowser.config.config import ConfigManager
|
from qutebrowser.config.config import ConfigManager
|
||||||
from qutebrowser.keyinput.modeman import ModeManager
|
from qutebrowser.keyinput.modeman import ModeManager
|
||||||
from qutebrowser.widgets.mainwindow import MainWindow
|
from qutebrowser.widgets.mainwindow import MainWindow
|
||||||
from qutebrowser.widgets.crash import ExceptionCrashDialog
|
from qutebrowser.widgets.crash import ExceptionCrashDialog, FatalCrashDialog
|
||||||
from qutebrowser.keyinput.modeparsers import NormalKeyParser, HintKeyParser
|
from qutebrowser.keyinput.modeparsers import NormalKeyParser, HintKeyParser
|
||||||
from qutebrowser.keyinput.keyparser import PassthroughKeyParser
|
from qutebrowser.keyinput.keyparser import PassthroughKeyParser
|
||||||
from qutebrowser.commands.managers import CommandManager, SearchManager
|
from qutebrowser.commands.managers import CommandManager, SearchManager
|
||||||
@ -94,6 +95,8 @@ class QuteBrowser(QApplication):
|
|||||||
_shutting_down: True if we're currently shutting down.
|
_shutting_down: True if we're currently shutting down.
|
||||||
_quit_status: The current quitting status.
|
_quit_status: The current quitting status.
|
||||||
_opened_urls: List of opened URLs.
|
_opened_urls: List of opened URLs.
|
||||||
|
_crashdlg: The crash dialog currently open.
|
||||||
|
_crashlogfile: A file handler to the fatal crash logfile.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# This also holds all our globals, so we're a bit over the top here.
|
# This also holds all our globals, so we're a bit over the top here.
|
||||||
@ -110,6 +113,8 @@ class QuteBrowser(QApplication):
|
|||||||
self._opened_urls = []
|
self._opened_urls = []
|
||||||
self._shutting_down = False
|
self._shutting_down = False
|
||||||
self._keyparsers = None
|
self._keyparsers = None
|
||||||
|
self._crashdlg = None
|
||||||
|
self._crashlogfile = None
|
||||||
self.messagebridge = None
|
self.messagebridge = None
|
||||||
self.stateconfig = None
|
self.stateconfig = None
|
||||||
self.modeman = None
|
self.modeman = None
|
||||||
@ -123,6 +128,7 @@ class QuteBrowser(QApplication):
|
|||||||
self._init_misc()
|
self._init_misc()
|
||||||
actute_warning()
|
actute_warning()
|
||||||
self._init_config()
|
self._init_config()
|
||||||
|
self._handle_segfault()
|
||||||
self._init_modes()
|
self._init_modes()
|
||||||
websettings.init()
|
websettings.init()
|
||||||
proxy.init()
|
proxy.init()
|
||||||
@ -145,6 +151,9 @@ class QuteBrowser(QApplication):
|
|||||||
timer = QTimer.singleShot(0, self._process_init_args)
|
timer = QTimer.singleShot(0, self._process_init_args)
|
||||||
self._timers.append(timer)
|
self._timers.append(timer)
|
||||||
|
|
||||||
|
if self._crashdlg is not None:
|
||||||
|
self._crashdlg.raise_()
|
||||||
|
|
||||||
def _parse_args(self):
|
def _parse_args(self):
|
||||||
"""Parse command line options.
|
"""Parse command line options.
|
||||||
|
|
||||||
@ -238,6 +247,26 @@ class QuteBrowser(QApplication):
|
|||||||
self.setApplicationVersion(qutebrowser.__version__)
|
self.setApplicationVersion(qutebrowser.__version__)
|
||||||
self.messagebridge = MessageBridge()
|
self.messagebridge = MessageBridge()
|
||||||
|
|
||||||
|
def _handle_segfault(self):
|
||||||
|
"""Handle a segfault from a previous run."""
|
||||||
|
logname = os.path.join(get_standard_dir(QStandardPaths.DataLocation),
|
||||||
|
'crash.log')
|
||||||
|
# First check if an old logfile exists.
|
||||||
|
if os.path.exists(logname):
|
||||||
|
with open(logname, 'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
# Just in case FatalCrashDialog crashes... -.-
|
||||||
|
os.remove(logname)
|
||||||
|
if data:
|
||||||
|
self._crashdlg = FatalCrashDialog(data)
|
||||||
|
self._crashdlg.show()
|
||||||
|
# Start a new logfile and redirect faulthandler to it
|
||||||
|
self._crashlogfile = open(logname, 'w') # This also truncates.
|
||||||
|
faulthandler.enable(self._crashlogfile)
|
||||||
|
if hasattr(faulthandler, 'register') and hasattr(signal, 'SIGUSR1'):
|
||||||
|
# If available, we also want a traceback on SIGUSR1.
|
||||||
|
faulthandler.register(signal.SIGUSR1) # pylint: disable=no-member
|
||||||
|
|
||||||
def _init_cmds(self):
|
def _init_cmds(self):
|
||||||
"""Initialisation of the qutebrowser commands.
|
"""Initialisation of the qutebrowser commands.
|
||||||
|
|
||||||
@ -427,8 +456,8 @@ class QuteBrowser(QApplication):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
logging.exception("Preventing shutdown failed.")
|
logging.exception("Preventing shutdown failed.")
|
||||||
QApplication.closeAllWindows()
|
QApplication.closeAllWindows()
|
||||||
dlg = ExceptionCrashDialog(pages, history, exc)
|
self._crashdlg = ExceptionCrashDialog(pages, history, exc)
|
||||||
ret = dlg.exec_()
|
ret = self._crashdlg.exec_()
|
||||||
if ret == QDialog.Accepted: # restore
|
if ret == QDialog.Accepted: # restore
|
||||||
self.restart(shutdown=False, pages=pages)
|
self.restart(shutdown=False, pages=pages)
|
||||||
# We might risk a segfault here, but that's better than continuing to
|
# We might risk a segfault here, but that's better than continuing to
|
||||||
|
@ -39,9 +39,9 @@ def init_faulthandler():
|
|||||||
# If we'd enable faulthandler in that case, we just get a weird
|
# If we'd enable faulthandler in that case, we just get a weird
|
||||||
# exception, so we don't enable faulthandler if we have no stdout.
|
# exception, so we don't enable faulthandler if we have no stdout.
|
||||||
#
|
#
|
||||||
# FIXME at the point we have our config/data dirs, we probably should
|
# Later when we have our data dir available we re-enable faulthandler
|
||||||
# re-enable faulthandler to write to a file. Then we can also display
|
# to write to a file so we can display a crash to the user at the next
|
||||||
# crashes to the user at the next start.
|
# start.
|
||||||
return
|
return
|
||||||
faulthandler.enable()
|
faulthandler.enable()
|
||||||
if hasattr(faulthandler, 'register') and hasattr(signal, 'SIGUSR1'):
|
if hasattr(faulthandler, 'register') and hasattr(signal, 'SIGUSR1'):
|
||||||
|
@ -176,29 +176,64 @@ class ExceptionCrashDialog(_CrashDialog):
|
|||||||
self._btn_quit = QPushButton()
|
self._btn_quit = QPushButton()
|
||||||
self._btn_quit.setText("Quit")
|
self._btn_quit.setText("Quit")
|
||||||
self._btn_quit.clicked.connect(self.reject)
|
self._btn_quit.clicked.connect(self.reject)
|
||||||
|
self._hbox.addWidget(self._btn_quit)
|
||||||
self._btn_pastebin = QPushButton()
|
self._btn_pastebin = QPushButton()
|
||||||
self._btn_pastebin.setText("Pastebin")
|
self._btn_pastebin.setText("Pastebin")
|
||||||
self._btn_pastebin.clicked.connect(self.pastebin)
|
self._btn_pastebin.clicked.connect(self.pastebin)
|
||||||
self._hbox.addWidget(self._btn_quit)
|
self._hbox.addWidget(self._btn_pastebin)
|
||||||
if self._pages:
|
if self._pages:
|
||||||
self._btn_restore = QPushButton()
|
self._btn_restore = QPushButton()
|
||||||
self._btn_restore.setText("Restore tabs")
|
self._btn_restore.setText("Restore tabs")
|
||||||
self._btn_restore.clicked.connect(self.accept)
|
self._btn_restore.clicked.connect(self.accept)
|
||||||
self._btn_restore.setDefault(True)
|
self._btn_restore.setDefault(True)
|
||||||
self._hbox.addWidget(self._btn_restore)
|
self._hbox.addWidget(self._btn_restore)
|
||||||
self._hbox.addWidget(self._btn_pastebin)
|
|
||||||
|
|
||||||
def _gather_crash_info(self):
|
def _gather_crash_info(self):
|
||||||
"""Gather crash information to display.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
pages: A list of the open pages (URLs as strings)
|
|
||||||
cmdhist: A list with the command history (as strings)
|
|
||||||
exc: An exception tuple (type, value, traceback)
|
|
||||||
"""
|
|
||||||
super()._gather_crash_info()
|
super()._gather_crash_info()
|
||||||
self._crash_info += [
|
self._crash_info += [
|
||||||
("Exception", ''.join(traceback.format_exception(*self._exc))),
|
("Exception", ''.join(traceback.format_exception(*self._exc))),
|
||||||
("Open Pages", '\n'.join(self._pages)),
|
("Open Pages", '\n'.join(self._pages)),
|
||||||
("Command history", '\n'.join(self._cmdhist)),
|
("Command history", '\n'.join(self._cmdhist)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class FatalCrashDialog(_CrashDialog):
|
||||||
|
|
||||||
|
"""Dialog which gets shown when a fatal error occured.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
_log: The log text to display.
|
||||||
|
_btn_ok: The OK button.
|
||||||
|
_btn_pastebin: The pastebin button.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, log):
|
||||||
|
self._log = log
|
||||||
|
self._btn_ok = None
|
||||||
|
self._btn_pastebin = None
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def _init_text(self):
|
||||||
|
super()._init_text()
|
||||||
|
text = ("qutebrowser was restarted after a fatal crash.<br/>"
|
||||||
|
"Please click on 'pastebin' or send the data below to "
|
||||||
|
"<a href='mailto:crash@qutebrowser.org'>"
|
||||||
|
"crash@qutebrowser.org</a>.<br/><br/>")
|
||||||
|
self._lbl.setText(text)
|
||||||
|
|
||||||
|
def _init_buttons(self):
|
||||||
|
super()._init_buttons()
|
||||||
|
self._btn_ok = QPushButton()
|
||||||
|
self._btn_ok.setText("OK")
|
||||||
|
self._btn_ok.clicked.connect(self.accept)
|
||||||
|
self._hbox.addWidget(self._btn_ok)
|
||||||
|
self._btn_pastebin = QPushButton()
|
||||||
|
self._btn_pastebin.setText("Pastebin")
|
||||||
|
self._btn_pastebin.clicked.connect(self.pastebin)
|
||||||
|
self._hbox.addWidget(self._btn_pastebin)
|
||||||
|
|
||||||
|
def _gather_crash_info(self):
|
||||||
|
super()._gather_crash_info()
|
||||||
|
self._crash_info += [
|
||||||
|
("Fault log", self._log),
|
||||||
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user