From de84c3941ce730bec2e97527238a49e527c751ee Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Mon, 28 Jan 2013 22:49:29 +0400 Subject: [PATCH] Implement stack trace dump for all running threads on SIGQUIT This is handy when we're debugging the thread locks: we can try to understand which thread does what and how it was called. Signed-off-by: Eygene Ryabinkin --- Changelog.rst | 3 ++- docs/MANUAL.rst | 5 ++++- offlineimap/init.py | 5 +++++ offlineimap/utils/stacktrace.py | 25 +++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 offlineimap/utils/stacktrace.py diff --git a/Changelog.rst b/Changelog.rst index 7d8992a..0064681 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -7,7 +7,8 @@ ChangeLog WIP (add new stuff for the next release) ======================================== -======= +* Dump stacktrace for all threads on SIGQUIT: ease debugging + of threading and other issues * SIGHUP is now handled as the termination notification rather than the signal to reread the configuration (Dmitrijs Ledkovs) * Honor the timezone of emails (Tobias Thierer) diff --git a/docs/MANUAL.rst b/docs/MANUAL.rst index 0e080a1..15f0625 100644 --- a/docs/MANUAL.rst +++ b/docs/MANUAL.rst @@ -309,7 +309,7 @@ UNIX Signals ============ OfflineImap listens to the unix signals SIGUSR1, SIGUSR2, SIGTERM, -SIGINT, SIGHUP: +SIGINT, SIGHUP, SIGQUIT: If sent a SIGUSR1 it will abort any current (or next future) sleep of all accounts that are configured to "autorefresh". In effect, this will trigger a @@ -326,6 +326,9 @@ in each account, close keep alive connections, remove locks on the accounts and exit. It may take up to 10 seconds, if autorefresh option is used. +SIGQUIT dumps stack traces for all threads and tries to dump process +core. + Folder filtering and nametrans ============================== diff --git a/offlineimap/init.py b/offlineimap/init.py index 94a9a18..e93a382 100644 --- a/offlineimap/init.py +++ b/offlineimap/init.py @@ -28,6 +28,7 @@ from offlineimap import accounts, threadutil, syncmaster from offlineimap.error import OfflineImapError from offlineimap.ui import UI_LIST, setglobalui, getglobalui from offlineimap.CustomConfig import CustomConfigParser +from offlineimap.utils import stacktrace class OfflineImap: @@ -341,12 +342,16 @@ class OfflineImap: getglobalui().warn("Terminating NOW (this may "\ "take a few seconds)...") accounts.Account.set_abort_event(self.config, 3) + elif sig == signal.SIGQUIT: + stacktrace.dump (sys.stderr) + os.abort() signal.signal(signal.SIGHUP,sig_handler) signal.signal(signal.SIGUSR1,sig_handler) signal.signal(signal.SIGUSR2,sig_handler) signal.signal(signal.SIGTERM, sig_handler) signal.signal(signal.SIGINT, sig_handler) + signal.signal(signal.SIGQUIT, sig_handler) #various initializations that need to be performed: offlineimap.mbnames.init(self.config, syncaccounts) diff --git a/offlineimap/utils/stacktrace.py b/offlineimap/utils/stacktrace.py new file mode 100644 index 0000000..7c885b0 --- /dev/null +++ b/offlineimap/utils/stacktrace.py @@ -0,0 +1,25 @@ +# Copyright 2013 Eygene A. Ryabinkin +# Functions to perform stack tracing (for multithreaded programs +# as well as for single-threaded ones). + +import sys +import threading +import traceback + + +def dump(out): + """ Dumps current stack trace into I/O object 'out' """ + id2name = {} + for th in threading.enumerate(): + id2name[th.ident] = th.name + n = 0 + for i, stack in sys._current_frames().items(): + out.write ("\n# Thread #%d (id=%d), %s\n" % \ + (n, i, id2name[i])) + n = n + 1 + for f, lno, name, line in traceback.extract_stack (stack): + out.write ('File: "%s", line %d, in %s' % \ + (f, lno, name)) + if line: + out.write (" %s" % (line.strip())) + out.write ("\n")