Skip to content

Instantly share code, notes, and snippets.

@niccokunzmann
Last active September 27, 2023 08:13
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niccokunzmann/6038331 to your computer and use it in GitHub Desktop.
Save niccokunzmann/6038331 to your computer and use it in GitHub Desktop.
This module prints all hanging threads that are dead locked. It is for Python 2 and 3. Installable: https://pypi.python.org/pypi/hanging_threads
## The MIT License (MIT)
## ---------------------
##
## Copyright (C) 2014 Nicco Kunzmann
##
## https://gist.github.com/niccokunzmann/6038331
##
## Permission is hereby granted, free of charge, to any person obtaining
## a copy of this software and associated documentation files (the "Software"),
## to deal in the Software without restriction, including without limitation
## the rights to use, copy, modify, merge, publish, distribute, sublicense,
## and/or sell copies of the Software, and to permit persons to whom the
## Software is furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
## IN THE SOFTWARE.
"""
copy this code and do
import hanging_threads
If a thread is at the same place for SECONDS_FROZEN then the stacktrace is printed.
This script prints
-------------------- Thread 6628 --------------------
File "hanging_threads.py", line 70, in <module>
time.sleep(3) # TEST
-------------------- Thread 6628 --------------------
File "hanging_threads.py", line 70, in <module>
time.sleep(3) # TEST
"""
import sys
import threading
try:
try:
from threading import _get_ident as get_ident
except ImportError:
from threading import get_ident
except ImportError:
from thread import get_ident
import linecache
import time
SECONDS_FROZEN = 10 # seconds
TESTS_PER_SECOND = 10
def frame2string(frame):
# from module traceback
lineno = frame.f_lineno # or f_lasti
co = frame.f_code
filename = co.co_filename
name = co.co_name
s = ' File "{}", line {}, in {}'.format(filename, lineno, name)
line = linecache.getline(filename, lineno, frame.f_globals).lstrip()
return s + '\n\t' + line
def thread2list(frame):
l = []
while frame:
l.insert(0, frame2string(frame))
frame = frame.f_back
return l
def monitor():
self = get_ident()
old_threads = {}
while 1:
time.sleep(1. / TESTS_PER_SECOND)
now = time.time()
then = now - SECONDS_FROZEN
frames = sys._current_frames()
new_threads = {}
for frame_id, frame in frames.items():
new_threads[frame_id] = thread2list(frame)
for thread_id, frame_list in new_threads.items():
if thread_id == self: continue
if thread_id not in old_threads or \
frame_list != old_threads[thread_id][0]:
new_threads[thread_id] = (frame_list, now)
elif old_threads[thread_id][1] < then:
print_frame_list(frame_list, frame_id)
else:
new_threads[thread_id] = old_threads[thread_id]
old_threads = new_threads
def print_frame_list(frame_list, frame_id):
sys.stderr.write('-' * 20 +
'Thread {}'.format(frame_id).center(20) +
'-' * 20 +
'\n' +
''.join(frame_list))
def start_monitoring():
'''After hanging SECONDS_FROZEN the stack trace of the deadlock is printed automatically.'''
thread = threading.Thread(target = monitor)
thread.daemon = True
thread.start()
return thread
monitoring_thread = start_monitoring()
if __name__ == '__main__':
SECONDS_FROZEN = 1
time.sleep(3) # TEST
@adazi
Copy link

adazi commented Oct 19, 2016

This script is great, and it was a huge help in finding a weird bug in my code, but I had to make some modifications to get it to work with python 2.6.6. For python earlier than 2.7, lines 65 and 100 produce errors because the positional argument specifiers can not be omitted in python earlier than 2.7. This can be fixed by specifying them explicitly.
Changing line 65 from:
s = ' File "{}", line {}, in {}'.format(filename, lineno, name)
to
s = ' File "{0}", line {1}, in {2}'.format(filename, lineno, name)
and lines 99-103 from
sys.stderr.write('-' * 20 + 'Thread {}'.format(frame_id).center(20) + '-' * 20 + '\n' + ''.join(frame_list))
to
sys.stderr.write('-' * 20 + 'Thread {0}'.format(frame_id).center(20) + '-' * 20 + '\n' + ''.join(frame_list))
With these modifications, this script works with python 2.6.6.
I hope this is helpful.

@niccokunzmann
Copy link
Author

@Rickardo987
Copy link

AAAAAAAA THANK YIU!

@earonesty
Copy link

earonesty commented Oct 18, 2019

Python 3.3 now has a built in "faulthandler" module that does this for you.

import faulthandler
faulthandler.enable()

After enabled, you can send a signal to the deadlocked process (like SIGABRT), and it will dump traces of all threads before quitting. You can also schedule regular dumps, or customize the signals or files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment