add a full stack of all thread dump upon EXIT or KILL signal in thread debug mode
Note that the stacks are grouped if similar, and the current process (the one handling the signal) is identified and reports where it was before the signal. This can be quite handy when wanting to debug thread locks for instance. Signed-off-by: Valentin Lab <valentin.lab@kalysto.org> Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
parent
86a91f28c0
commit
4087f3a4e7
@ -31,6 +31,9 @@ from offlineimap.ui import UI_LIST, setglobalui, getglobalui
|
||||
from offlineimap.CustomConfig import CustomConfigParser
|
||||
from offlineimap.utils import stacktrace
|
||||
|
||||
import traceback
|
||||
import collections
|
||||
|
||||
|
||||
class OfflineImap:
|
||||
"""The main class that encapsulates the high level use of OfflineImap.
|
||||
@ -272,6 +275,42 @@ class OfflineImap:
|
||||
self.config = config
|
||||
return (options, args)
|
||||
|
||||
def __dumpstacks(self, context=1, sighandler_deep=2):
|
||||
""" Signal handler: dump a stack trace for each existing thread."""
|
||||
|
||||
currentThreadId = threading.currentThread().ident
|
||||
|
||||
def unique_count(l):
|
||||
d = collections.defaultdict(lambda: 0)
|
||||
for v in l:
|
||||
d[tuple(v)] += 1
|
||||
return list((k, v) for k, v in d.iteritems())
|
||||
|
||||
stack_displays = []
|
||||
for threadId, stack in sys._current_frames().items():
|
||||
stack_display = []
|
||||
for filename, lineno, name, line in traceback.extract_stack(stack):
|
||||
stack_display.append(' File: "%s", line %d, in %s'
|
||||
% (filename, lineno, name))
|
||||
if line:
|
||||
stack_display.append(" %s" % (line.strip()))
|
||||
if currentThreadId == threadId:
|
||||
stack_display = stack_display[:- (sighandler_deep * 2)]
|
||||
stack_display.append(' => Stopped to handle current signal. ')
|
||||
stack_displays.append(stack_display)
|
||||
stacks = unique_count(stack_displays)
|
||||
print "** Thread List:\n"
|
||||
for stack, times in stacks:
|
||||
if times == 1:
|
||||
msg = "%s Thread is at:\n%s\n"
|
||||
else:
|
||||
msg = "%s Threads are at:\n%s\n"
|
||||
self.ui.debug('thread', msg % (times, '\n'.join(stack[- (context * 2):])))
|
||||
|
||||
self.ui.debug('thread', "Dumped a total of %d Threads." %
|
||||
len(sys._current_frames().keys()))
|
||||
|
||||
|
||||
def __sync(self, options):
|
||||
"""Invoke the correct single/multithread syncing
|
||||
|
||||
@ -321,6 +360,8 @@ class OfflineImap:
|
||||
getglobalui().warn("Terminating NOW (this may "\
|
||||
"take a few seconds)...")
|
||||
accounts.Account.set_abort_event(self.config, 3)
|
||||
if 'thread' in self.ui.debuglist:
|
||||
self.__dumpstacks(5)
|
||||
elif sig == signal.SIGQUIT:
|
||||
stacktrace.dump(sys.stderr)
|
||||
os.abort()
|
||||
|
Loading…
Reference in New Issue
Block a user