Skip to content

Instantly share code, notes, and snippets.

@alexcouper
Created June 12, 2014 11:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexcouper/2d8aef11df4b426ac700 to your computer and use it in GitHub Desktop.
Save alexcouper/2d8aef11df4b426ac700 to your computer and use it in GitHub Desktop.
Debug live running process
import time
def fred():
return '2'
def do_it():
my_list = []
count = 0
while 1:
count += 1
if count % 10 == 0:
my_list.append(count)
time.sleep(0.1)
if __name__ == '__main__':
from debugmode import listen
listen()
do_it()
try:
import readline # For readline input support
except:
pass
import sys, os, traceback, signal, codeop, cStringIO, cPickle, tempfile
def pipename(pid):
"""Return name of pipe to use"""
return os.path.join(tempfile.gettempdir(), 'debug-%d' % pid)
class NamedPipe(object):
def __init__(self, name, end=0, mode=0666):
"""Open a pair of pipes, name.in and name.out for communication
with another process. One process should pass 1 for end, and the
other 0. Data is marshalled with pickle."""
self.in_name, self.out_name = name +'.in', name +'.out',
try: os.mkfifo(self.in_name,mode)
except OSError: pass
try: os.mkfifo(self.out_name,mode)
except OSError: pass
# NOTE: The order the ends are opened in is important - both ends
# of pipe 1 must be opened before the second pipe can be opened.
if end:
self.inp = open(self.out_name,'r')
self.out = open(self.in_name,'w')
else:
self.out = open(self.out_name,'w')
self.inp = open(self.in_name,'r')
self._open = True
def is_open(self):
return not (self.inp.closed or self.out.closed)
def put(self,msg):
if self.is_open():
data = cPickle.dumps(msg,1)
self.out.write("%d\n" % len(data))
self.out.write(data)
self.out.flush()
else:
raise Exception("Pipe closed")
def get(self):
txt=self.inp.readline()
if not txt:
self.inp.close()
else:
l = int(txt)
data=self.inp.read(l)
if len(data) < l: self.inp.close()
return cPickle.loads(data) # Convert back to python object.
def close(self):
self.inp.close()
self.out.close()
try: os.remove(self.in_name)
except OSError: pass
try: os.remove(self.out_name)
except OSError: pass
def __del__(self):
self.close()
def remote_debug(sig,frame):
"""Handler to allow process to be remotely debugged."""
def _raiseEx(ex):
"""Raise specified exception in the remote process"""
_raiseEx.ex = ex
_raiseEx.ex = None
try:
# Provide some useful functions.
locs = {'_raiseEx' : _raiseEx}
locs.update(frame.f_locals) # Unless shadowed.
globs = frame.f_globals
pid = os.getpid() # Use pipe name based on pid
pipe = NamedPipe(pipename(pid))
old_stdout, old_stderr = sys.stdout, sys.stderr
txt = ''
pipe.put("Interrupting process at following point:\n" +
''.join(traceback.format_stack(frame)) + ">>> ")
try:
while pipe.is_open() and _raiseEx.ex is None:
line = pipe.get()
if line is None: continue # EOF
txt += line
try:
code = codeop.compile_command(txt)
if code:
sys.stdout = cStringIO.StringIO()
sys.stderr = sys.stdout
exec code in globs,locs
txt = ''
pipe.put(sys.stdout.getvalue() + '>>> ')
else:
pipe.put('... ')
except:
txt='' # May be syntax err.
sys.stdout = cStringIO.StringIO()
sys.stderr = sys.stdout
traceback.print_exc()
pipe.put(sys.stdout.getvalue() + '>>> ')
finally:
sys.stdout = old_stdout # Restore redirected output.
sys.stderr = old_stderr
pipe.close()
except Exception: # Don't allow debug exceptions to propogate to real program.
traceback.print_exc()
if _raiseEx.ex is not None: raise _raiseEx.ex
def debug_process(pid):
"""Interrupt a running process and debug it."""
os.kill(pid, signal.SIGUSR1) # Signal process.
pipe = NamedPipe(pipename(pid), 1)
try:
while pipe.is_open():
txt=raw_input(pipe.get()) + '\n'
pipe.put(txt)
except EOFError:
pass # Exit.
pipe.close()
def listen():
signal.signal(signal.SIGUSR1, remote_debug) # Register for remote debugging.
if __name__=='__main__':
if len(sys.argv) != 2:
print "Error: Must provide process id to debug"
else:
pid = int(sys.argv[1])
debug_process(pid)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment