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 logging
|
||||
import subprocess
|
||||
import faulthandler
|
||||
import configparser
|
||||
from bdb import BdbQuit
|
||||
from functools import partial
|
||||
@ -54,7 +55,7 @@ from qutebrowser.network.networkmanager import NetworkManager
|
||||
from qutebrowser.config.config import ConfigManager
|
||||
from qutebrowser.keyinput.modeman import ModeManager
|
||||
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.keyparser import PassthroughKeyParser
|
||||
from qutebrowser.commands.managers import CommandManager, SearchManager
|
||||
@ -94,6 +95,8 @@ class QuteBrowser(QApplication):
|
||||
_shutting_down: True if we're currently shutting down.
|
||||
_quit_status: The current quitting status.
|
||||
_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.
|
||||
@ -110,6 +113,8 @@ class QuteBrowser(QApplication):
|
||||
self._opened_urls = []
|
||||
self._shutting_down = False
|
||||
self._keyparsers = None
|
||||
self._crashdlg = None
|
||||
self._crashlogfile = None
|
||||
self.messagebridge = None
|
||||
self.stateconfig = None
|
||||
self.modeman = None
|
||||
@ -123,6 +128,7 @@ class QuteBrowser(QApplication):
|
||||
self._init_misc()
|
||||
actute_warning()
|
||||
self._init_config()
|
||||
self._handle_segfault()
|
||||
self._init_modes()
|
||||
websettings.init()
|
||||
proxy.init()
|
||||
@ -145,6 +151,9 @@ class QuteBrowser(QApplication):
|
||||
timer = QTimer.singleShot(0, self._process_init_args)
|
||||
self._timers.append(timer)
|
||||
|
||||
if self._crashdlg is not None:
|
||||
self._crashdlg.raise_()
|
||||
|
||||
def _parse_args(self):
|
||||
"""Parse command line options.
|
||||
|
||||
@ -238,6 +247,26 @@ class QuteBrowser(QApplication):
|
||||
self.setApplicationVersion(qutebrowser.__version__)
|
||||
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):
|
||||
"""Initialisation of the qutebrowser commands.
|
||||
|
||||
@ -427,8 +456,8 @@ class QuteBrowser(QApplication):
|
||||
except TypeError:
|
||||
logging.exception("Preventing shutdown failed.")
|
||||
QApplication.closeAllWindows()
|
||||
dlg = ExceptionCrashDialog(pages, history, exc)
|
||||
ret = dlg.exec_()
|
||||
self._crashdlg = ExceptionCrashDialog(pages, history, exc)
|
||||
ret = self._crashdlg.exec_()
|
||||
if ret == QDialog.Accepted: # restore
|
||||
self.restart(shutdown=False, pages=pages)
|
||||
# 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
|
||||
# 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
|
||||
# re-enable faulthandler to write to a file. Then we can also display
|
||||
# crashes to the user at the next start.
|
||||
# Later when we have our data dir available we re-enable faulthandler
|
||||
# to write to a file so we can display a crash to the user at the next
|
||||
# start.
|
||||
return
|
||||
faulthandler.enable()
|
||||
if hasattr(faulthandler, 'register') and hasattr(signal, 'SIGUSR1'):
|
||||
|
@ -176,29 +176,64 @@ class ExceptionCrashDialog(_CrashDialog):
|
||||
self._btn_quit = QPushButton()
|
||||
self._btn_quit.setText("Quit")
|
||||
self._btn_quit.clicked.connect(self.reject)
|
||||
self._hbox.addWidget(self._btn_quit)
|
||||
self._btn_pastebin = QPushButton()
|
||||
self._btn_pastebin.setText("Pastebin")
|
||||
self._btn_pastebin.clicked.connect(self.pastebin)
|
||||
self._hbox.addWidget(self._btn_quit)
|
||||
self._hbox.addWidget(self._btn_pastebin)
|
||||
if self._pages:
|
||||
self._btn_restore = QPushButton()
|
||||
self._btn_restore.setText("Restore tabs")
|
||||
self._btn_restore.clicked.connect(self.accept)
|
||||
self._btn_restore.setDefault(True)
|
||||
self._hbox.addWidget(self._btn_restore)
|
||||
self._hbox.addWidget(self._btn_pastebin)
|
||||
|
||||
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()
|
||||
self._crash_info += [
|
||||
("Exception", ''.join(traceback.format_exception(*self._exc))),
|
||||
("Open Pages", '\n'.join(self._pages)),
|
||||
("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