crash reporter: add EarlyExceptionsQueue
`util.send_exception_to_crash_reporter` is now useful and can be transparently used even before the exception hook is set up.
This commit is contained in:
@@ -24,6 +24,7 @@ import json
|
|||||||
import locale
|
import locale
|
||||||
import traceback
|
import traceback
|
||||||
import sys
|
import sys
|
||||||
|
import queue
|
||||||
|
|
||||||
from .version import ELECTRUM_VERSION
|
from .version import ELECTRUM_VERSION
|
||||||
from . import constants
|
from . import constants
|
||||||
@@ -131,6 +132,43 @@ class BaseCrashReporter(Logger):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class EarlyExceptionsQueue:
|
||||||
|
"""Helper singleton for explicitly sending exceptions to crash reporter.
|
||||||
|
|
||||||
|
Typically the GUIs set up an "exception hook" that catches all otherwise
|
||||||
|
uncaught exceptions (which unroll the stack of a thread completely).
|
||||||
|
This class provides methods to report *any* exception, and queueing logic
|
||||||
|
that delays processing until the exception hook is set up.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_is_exc_hook_ready = False
|
||||||
|
_exc_queue = queue.Queue()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_hook_as_ready(cls):
|
||||||
|
if cls._is_exc_hook_ready:
|
||||||
|
return
|
||||||
|
cls._is_exc_hook_ready = True
|
||||||
|
while cls._exc_queue.qsize() > 0:
|
||||||
|
e = cls._exc_queue.get()
|
||||||
|
cls._send_exception_to_crash_reporter(e)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def send_exception_to_crash_reporter(cls, e: BaseException):
|
||||||
|
if cls._is_exc_hook_ready:
|
||||||
|
cls._send_exception_to_crash_reporter(e)
|
||||||
|
else:
|
||||||
|
cls._exc_queue.put(e)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _send_exception_to_crash_reporter(e: BaseException):
|
||||||
|
assert EarlyExceptionsQueue._is_exc_hook_ready
|
||||||
|
sys.excepthook(type(e), e, e.__traceback__)
|
||||||
|
|
||||||
|
|
||||||
|
send_exception_to_crash_reporter = EarlyExceptionsQueue.send_exception_to_crash_reporter
|
||||||
|
|
||||||
|
|
||||||
def trigger_crash():
|
def trigger_crash():
|
||||||
# note: do not change the type of the exception, the message,
|
# note: do not change the type of the exception, the message,
|
||||||
# or the name of this method. All reports generated through this
|
# or the name of this method. All reports generated through this
|
||||||
|
|||||||
@@ -487,8 +487,6 @@ class Daemon(Logger):
|
|||||||
if self.config.get('use_gossip', False):
|
if self.config.get('use_gossip', False):
|
||||||
self.network.start_gossip()
|
self.network.start_gossip()
|
||||||
|
|
||||||
self.exception = None # type: Optional[Exception]
|
|
||||||
|
|
||||||
self._stop_entered = False
|
self._stop_entered = False
|
||||||
self._stopping_soon_or_errored = threading.Event()
|
self._stopping_soon_or_errored = threading.Event()
|
||||||
self._stopped_event = threading.Event()
|
self._stopped_event = threading.Event()
|
||||||
@@ -508,7 +506,6 @@ class Daemon(Logger):
|
|||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.exception("taskgroup died.")
|
self.logger.exception("taskgroup died.")
|
||||||
self.exception = e
|
|
||||||
util.send_exception_to_crash_reporter(e)
|
util.send_exception_to_crash_reporter(e)
|
||||||
finally:
|
finally:
|
||||||
self.logger.info("taskgroup stopped.")
|
self.logger.info("taskgroup stopped.")
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from kivy.utils import platform
|
|||||||
|
|
||||||
from electrum.gui.kivy.i18n import _
|
from electrum.gui.kivy.i18n import _
|
||||||
|
|
||||||
from electrum.base_crash_reporter import BaseCrashReporter
|
from electrum.base_crash_reporter import BaseCrashReporter, EarlyExceptionsQueue
|
||||||
from electrum.logging import Logger
|
from electrum.logging import Logger
|
||||||
|
|
||||||
|
|
||||||
@@ -185,6 +185,7 @@ class ExceptionHook(base.ExceptionHandler, Logger):
|
|||||||
base.ExceptionManager.add_handler(self)
|
base.ExceptionManager.add_handler(self)
|
||||||
# For everything else:
|
# For everything else:
|
||||||
sys.excepthook = lambda exctype, value, tb: self.handle_exception(value)
|
sys.excepthook = lambda exctype, value, tb: self.handle_exception(value)
|
||||||
|
EarlyExceptionsQueue.set_hook_as_ready()
|
||||||
|
|
||||||
def handle_exception(self, _inst):
|
def handle_exception(self, _inst):
|
||||||
exc_info = sys.exc_info()
|
exc_info = sys.exc_info()
|
||||||
|
|||||||
@@ -403,8 +403,6 @@ class ElectrumGui(Logger):
|
|||||||
signal.signal(signal.SIGINT, lambda *args: self.app.quit())
|
signal.signal(signal.SIGINT, lambda *args: self.app.quit())
|
||||||
# hook for crash reporter
|
# hook for crash reporter
|
||||||
Exception_Hook.maybe_setup(config=self.config)
|
Exception_Hook.maybe_setup(config=self.config)
|
||||||
if self.daemon.exception: # if daemon errored too early, replay that now:
|
|
||||||
send_exception_to_crash_reporter(self.daemon.exception)
|
|
||||||
# first-start network-setup
|
# first-start network-setup
|
||||||
try:
|
try:
|
||||||
self.init_network()
|
self.init_network()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ from PyQt5.QtWidgets import (QWidget, QLabel, QPushButton, QTextEdit,
|
|||||||
QMessageBox, QHBoxLayout, QVBoxLayout)
|
QMessageBox, QHBoxLayout, QVBoxLayout)
|
||||||
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.base_crash_reporter import BaseCrashReporter
|
from electrum.base_crash_reporter import BaseCrashReporter, EarlyExceptionsQueue
|
||||||
from electrum.logging import Logger
|
from electrum.logging import Logger
|
||||||
from electrum import constants
|
from electrum import constants
|
||||||
from electrum.network import Network
|
from electrum.network import Network
|
||||||
@@ -172,6 +172,7 @@ class Exception_Hook(QObject, Logger):
|
|||||||
|
|
||||||
sys.excepthook = self.handler
|
sys.excepthook = self.handler
|
||||||
self._report_exception.connect(_show_window)
|
self._report_exception.connect(_show_window)
|
||||||
|
EarlyExceptionsQueue.set_hook_as_ready()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def maybe_setup(cls, *, config: 'SimpleConfig', wallet: 'Abstract_Wallet' = None) -> None:
|
def maybe_setup(cls, *, config: 'SimpleConfig', wallet: 'Abstract_Wallet' = None) -> None:
|
||||||
|
|||||||
@@ -1112,7 +1112,8 @@ def setup_thread_excepthook():
|
|||||||
|
|
||||||
|
|
||||||
def send_exception_to_crash_reporter(e: BaseException):
|
def send_exception_to_crash_reporter(e: BaseException):
|
||||||
sys.excepthook(type(e), e, e.__traceback__)
|
from .base_crash_reporter import send_exception_to_crash_reporter
|
||||||
|
send_exception_to_crash_reporter(e)
|
||||||
|
|
||||||
|
|
||||||
def versiontuple(v):
|
def versiontuple(v):
|
||||||
|
|||||||
Reference in New Issue
Block a user