Source From Here
Question
I just want to see the state of the process, is it possible to attach a console into the process, so I can invoke functions inside the process and see some of the global variables.
How-To
If you have access to the program's source-code, you can add this functionality relatively easily. See Recipe 576515: Debugging a running python process by interrupting and providing an interactive prompt (Python):
A simple user case can be referred as below. Firstly, our fake main program zombe.py:
- zombe.py
Which will use while loop to keep running as zombie. Then is the module which will help us intercept the running Python process for debugging:
- DebugPy.py
Then you can use it this way:
Another implementation of roughly the same concept is provided by rconsole. From the documentation:
Question
I just want to see the state of the process, is it possible to attach a console into the process, so I can invoke functions inside the process and see some of the global variables.
How-To
If you have access to the program's source-code, you can add this functionality relatively easily. See Recipe 576515: Debugging a running python process by interrupting and providing an interactive prompt (Python):
A simple user case can be referred as below. Firstly, our fake main program zombe.py:
- zombe.py
- #!/usr/bin/env python
- import time
- import DebugPy
- import pdb
- import sys
- cnt=0
- DebugPy.listen() # This is key point for the main program being able to be intercepted by Linux signal
- def test():
- print("This is testing function in zombe")
- while True:
- cnt = cnt + 1
- print("Count %d...(sleep 10 min)" % (cnt))
- time.sleep(1*60)
- DebugPy.py
- #!/usr/bin/env python
- 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)
Another implementation of roughly the same concept is provided by rconsole. From the documentation:
沒有留言:
張貼留言