Browse Source

stacktracer.py: small fixes and clean-up

master
SomberNight 2 years ago
parent
commit
59a6690986
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 64
      electrum/utils/stacktracer.py

64
electrum/utils/stacktracer.py

@ -26,31 +26,43 @@
"""Stack tracer for multi-threaded applications. """Stack tracer for multi-threaded applications.
Useful for debugging deadlocks and hangs.
Usage: Usage:
import stacktracer
stacktracer.trace_start("trace.html", interval=5)
...
stacktracer.trace_stop()
import stacktracer This will create a file named "trace.html" showing the stack traces of all threads,
stacktracer.start_trace("trace.html",interval=5,auto=True) # Set auto flag to always update file! updated every 5 seconds.
....
stacktracer.stop_trace()
""" """
import os
import sys import sys
import threading
import time
import traceback import traceback
from typing import Optional
# 3rd-party dependency:
from pygments import highlight from pygments import highlight
from pygments.lexers import PythonLexer from pygments.lexers import PythonLexer
from pygments.formatters import HtmlFormatter from pygments.formatters import HtmlFormatter
# Taken from http://bzimmer.ziclix.com/2008/12/17/python-thread-dumps/ def _thread_from_id(ident) -> Optional[threading.Thread]:
return threading._active.get(ident)
def stacktraces(): def stacktraces():
"""Taken from http://bzimmer.ziclix.com/2008/12/17/python-thread-dumps/"""
code = [] code = []
for threadId, stack in sys._current_frames().items(): for thread_id, stack in sys._current_frames().items():
code.append("\n# ThreadID: %s" % threadId) thread = _thread_from_id(thread_id)
code.append(f"\n# thread_id={thread_id}. thread={thread}")
for filename, lineno, name, line in traceback.extract_stack(stack): for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('File: "%s", line %d, in %s' % (filename, lineno, name)) code.append(f'File: "{filename}", line {lineno}, in {name}')
if line: if line:
code.append(" %s" % (line.strip())) code.append(" %s" % (line.strip()))
@ -61,14 +73,11 @@ def stacktraces():
)) ))
# This part was made by nagylzs
import os
import time
import threading
class TraceDumper(threading.Thread): class TraceDumper(threading.Thread):
"""Dump stack traces into a given file periodically.""" """Dump stack traces into a given file periodically.
# written by nagylzs
"""
def __init__(self, fpath, interval, auto): def __init__(self, fpath, interval, auto):
""" """
@ -86,10 +95,10 @@ class TraceDumper(threading.Thread):
threading.Thread.__init__(self) threading.Thread.__init__(self)
def run(self): def run(self):
while not self.stop_requested.isSet(): while not self.stop_requested.is_set():
time.sleep(self.interval) time.sleep(self.interval)
if self.auto or not os.path.isfile(self.fpath): if self.auto or not os.path.isfile(self.fpath):
self.stacktraces() self.dump_stacktraces()
def stop(self): def stop(self):
self.stop_requested.set() self.stop_requested.set()
@ -97,26 +106,23 @@ class TraceDumper(threading.Thread):
try: try:
if os.path.isfile(self.fpath): if os.path.isfile(self.fpath):
os.unlink(self.fpath) os.unlink(self.fpath)
except: except OSError:
pass pass
def stacktraces(self): def dump_stacktraces(self):
fout = file(self.fpath, "wb+") with open(self.fpath, "w+") as fout:
try:
fout.write(stacktraces()) fout.write(stacktraces())
finally:
fout.close()
_tracer = None _tracer = None # type: Optional[TraceDumper]
def trace_start(fpath, interval=5, auto=True): def trace_start(fpath, interval=5, *, auto=True):
"""Start tracing into the given file.""" """Start tracing into the given file."""
global _tracer global _tracer
if _tracer is None: if _tracer is None:
_tracer = TraceDumper(fpath, interval, auto) _tracer = TraceDumper(fpath, interval, auto)
_tracer.setDaemon(True) _tracer.daemon = True
_tracer.start() _tracer.start()
else: else:
raise Exception("Already tracing to %s" % _tracer.fpath) raise Exception("Already tracing to %s" % _tracer.fpath)
@ -128,5 +134,5 @@ def trace_stop():
if _tracer is None: if _tracer is None:
raise Exception("Not tracing, cannot stop.") raise Exception("Not tracing, cannot stop.")
else: else:
_trace.stop() _tracer.stop()
_trace = None _tracer = None

Loading…
Cancel
Save