From 3cb061d119e952c2442bcd7ec68398253e45ecb4 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 | 2 ++ docs/MANUAL.rst | 5 ++++- offlineimap/init.py | 5 +++++ offlineimap/utils/stacktrace.py | 25 +++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 offlineimap/utils/stacktrace.py diff --git a/Changelog.rst b/Changelog.rst index 6172b34..f8d48c6 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -7,6 +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 * Avoid lockups for IMAP synchronizations running with the "-1" command-line switch (X-Ryl669 ) * SIGHUP is now handled as the termination notification rather than 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 430118c..dee97c6 100644 --- a/offlineimap/init.py +++ b/offlineimap/init.py @@ -29,6 +29,7 @@ from offlineimap.error import OfflineImapError from offlineimap.ui import UI_LIST, setglobalui, getglobalui from offlineimap.CustomConfig import CustomConfigParser from offlineimap.utils import depot +from offlineimap.utils import stacktrace class OfflineImap: @@ -343,12 +344,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") -- 1.8.1