123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- """Stack tracer for multi-threaded applications.
- Usage:
- import stacktracer
- stacktracer.start_trace("trace.html",interval=5,auto=True) # Set auto flag to always update file!
- ....
- stacktracer.stop_trace()
- """
- import sys
- import traceback
- from pygments import highlight
- from pygments.lexers import PythonLexer
- from pygments.formatters import HtmlFormatter
- # Taken from http://bzimmer.ziclix.com/2008/12/17/python-thread-dumps/
- def stacktraces():
- code = []
- for threadId, stack in sys._current_frames().items():
- code.append("\n# ThreadID: %s" % threadId)
- for filename, lineno, name, line in traceback.extract_stack(stack):
- code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
- if line:
- code.append(" %s" % (line.strip()))
- return highlight("\n".join(code), PythonLexer(), HtmlFormatter(
- full=False,
- # style="native",
- noclasses=True,
- ))
- # This part was made by nagylzs
- import os
- import time
- import threading
- class TraceDumper(threading.Thread):
- """Dump stack traces into a given file periodically."""
- def __init__(self, fpath, interval, auto):
- """
- @param fpath: File path to output HTML (stack trace file)
- @param auto: Set flag (True) to update trace continuously.
- Clear flag (False) to update only if file not exists.
- (Then delete the file to force update.)
- @param interval: In seconds: how often to update the trace file.
- """
- assert (interval > 0.1)
- self.auto = auto
- self.interval = interval
- self.fpath = os.path.abspath(fpath)
- self.stop_requested = threading.Event()
- threading.Thread.__init__(self)
- def run(self):
- while not self.stop_requested.isSet():
- time.sleep(self.interval)
- if self.auto or not os.path.isfile(self.fpath):
- self.stacktraces()
- def stop(self):
- self.stop_requested.set()
- self.join()
- try:
- if os.path.isfile(self.fpath):
- os.unlink(self.fpath)
- except:
- pass
- def stacktraces(self):
- fout = open(self.fpath, "w+")
- try:
- fout.write(stacktraces())
- finally:
- fout.close()
- _tracer = None
- def trace_start(fpath, interval=5, auto=True):
- """Start tracing into the given file."""
- global _tracer
- if _tracer is None:
- _tracer = TraceDumper(fpath, interval, auto)
- _tracer.setDaemon(True)
- _tracer.start()
- else:
- raise Exception("Already tracing to %s" % _tracer.fpath)
- def trace_stop():
- """Stop tracing."""
- global _tracer
- if _tracer is None:
- raise Exception("Not tracing, cannot stop.")
- else:
- _tracer.stop()
- _tracer = None
|