[Python-checkins] r47283 - in sandbox/trunk/pdb: Doc/lib/libmpdb.tex README.txt mconnection.py mpdb.py mthread.py test/test_mconnection.py test/test_mpdb.py test/test_mthread.py test/thread_script.py
matt.fleming
python-checkins at python.org
Fri Jul 7 00:27:50 CEST 2006
Author: matt.fleming
Date: Fri Jul 7 00:27:49 2006
New Revision: 47283
Modified:
sandbox/trunk/pdb/Doc/lib/libmpdb.tex
sandbox/trunk/pdb/README.txt
sandbox/trunk/pdb/mconnection.py
sandbox/trunk/pdb/mpdb.py
sandbox/trunk/pdb/mthread.py
sandbox/trunk/pdb/test/test_mconnection.py
sandbox/trunk/pdb/test/test_mpdb.py
sandbox/trunk/pdb/test/test_mthread.py
sandbox/trunk/pdb/test/thread_script.py
Log:
Fix tests cases. When using TCP as a pdbserver connection, setsockopt's
REUSEADDR is on by default. Add some documentation regarding threading and
a little bit on remote debugging. Update README with some things to finish.
Change the the thread_script.py test file so it raises an exception.
Modified: sandbox/trunk/pdb/Doc/lib/libmpdb.tex
==============================================================================
--- sandbox/trunk/pdb/Doc/lib/libmpdb.tex (original)
+++ sandbox/trunk/pdb/Doc/lib/libmpdb.tex Fri Jul 7 00:27:49 2006
@@ -1023,6 +1023,15 @@
This section provides information on Python's thread debugging facilities and
how \module{mpdb} makes use of them.
+\module{mpdb} uses a master-slaves system for handling thread debugging. The
+main debugger (master) is the instance of \class{MPdb} that is
+used by the Python
+Interpreter. The thread that runs the Python Interpreter is a special class
+called \class{_MainThread}. Whenever we refer to the main debugger, this is
+what we are refering to. The \class{MTracer} objects (slaves)
+are responsible for tracing threads that are created in the script being
+debugged.
+
% How does Python facilitate debugging threads
\subsection{Python's Thread Debugging Features}
@@ -1030,8 +1039,26 @@
allows threads to be debugged.
There are two modules in the Python Standard Library that provide thread
-functionality, \ulink{\module{thread}}{http://docs.python.org/lib/module-thread.html} and \ulink{\module{threading}}{http://docs.python.org/lib/module-threading.html}. Threads created with the \module{threading} module can be traced by
-calling \code{threading.settrace()}.
+functionality, \ulink{\module{thread}}{http://docs.python.org/lib/module-thread.html} and \ulink{\module{threading}}{http://docs.python.org/lib/module-threading.html}.
+There are three types of threads in Python,
+
+- threads created with the \module{thread} module. These are low-level threads.
+
+- threads created with the \module{threading} module. These are high-level
+ threads.
+
+- the \class{_MainThread}. This thread is the main thread, it is created
+when the Python interpreter is started. All signals go to this thread.
+
+
+The global trace function for threads created with the \module{threading}
+module can be set by calling \function{threading.settrace()}. We can use
+this method to inspect the frame objects on the stack, for example, to find
+the filename and line number of the code object currently executing. See
+\ulink{The Python Library Reference}
+{http://docs.python.org/lib/debugger-hooks.html} for documentation on how
+debugger's use the global trace function.
+
\subsection{\module{mpdb}'s Thread Debugging Facilities}
@@ -1039,8 +1066,7 @@
on whilst inside the debugger by issuing the command \code{set thread}.
\emph{All the information below can be considered the internals of how mpdb
-works. In other words, this information doesn't provide useful information
-for someone simply wishing to use the debugger for thread debugging.}
+works. It is a programmer's reference.}
When thread debugging is activated in \module{mpdb}, it imports the
\module{mthread} module and calls that modules \function{init()} function,
@@ -1052,17 +1078,30 @@
documentation in the Python Standard Library for more information on how
debuggers interact with trace functions.
-An \class{MTracer} object provides three methods for dealing with the different
-possible events in that occur in the frame object.
-
+An \class{MTracer} object provides methods for dealing with the different
+possible events in that occur in the frame object \ref{mtracer-objects}.
+The main debugger is repsonsible for debugging the \class{_MainThread} object
+and the \class{MTracer} objects are responsible for debugging all other
+threads. When a thread needs to stop exection, it acquires the main debugger's
+\member{lock} variable, which is a \class{Lock} object. The \function{acquire}
+call will block until the lock is obtained. When this lock is obtained
+execution transfers to the thread the \class{MTracer} is running in. This
+means that this thread is now the current thread.
\section{Remote Debugging}
-This section describes how \module{mpdb} handles debugging remotely.
\label{remote-debug}
+This section describes how \module{mpdb} handles debugging remotely.
+
+Remote debugging in \module{mpdb} takes place between a \emph{pdbserver}
+and a client. The pdbserver is analogous to GDB's gdbserver. A pdbserver
+is run on the same machine as the program being debugged. This pdbserver
+then allows clients to connect to it which begins a debugging session. In
+this session all commands come from the client and are executed on the
+pdbserver.
\section{External Process Debugging}
+\label{proc-debug}
This section describes how \module{mpdb} debugs processes that are external
to the process in which \module{mpdb} is being run.
-\label{proc-debug}
\ No newline at end of file
Modified: sandbox/trunk/pdb/README.txt
==============================================================================
--- sandbox/trunk/pdb/README.txt (original)
+++ sandbox/trunk/pdb/README.txt Fri Jul 7 00:27:49 2006
@@ -54,4 +54,8 @@
`run 2' does not pass the argument 2 to the script.
* Allow thread debugging to be turned on with a command line switch.
* Allow reading commands from .mpdbrc file
-* Changed the name of the default history file from ~/.pydbhist to ~/.mpdbhist
\ No newline at end of file
+* Changed the name of the default history file from ~/.pydbhist to ~/.mpdbhist
+* do_return inherited from pydb.gdb.Gdb doesn't use self.stdin for reading
+ input from the user and doesn't work remotely.
+* pdbserver using a TCP connection uses setsockopt() REUSEADDR by default.
+ Need some way to make this configurable. `set reuseaddr' ?
\ No newline at end of file
Modified: sandbox/trunk/pdb/mconnection.py
==============================================================================
--- sandbox/trunk/pdb/mconnection.py (original)
+++ sandbox/trunk/pdb/mconnection.py Fri Jul 7 00:27:49 2006
@@ -100,7 +100,7 @@
self._sock = self.output = self.input = None
MConnectionInterface.__init__(self)
- def connect(self, addr, reuseaddr=False):
+ def connect(self, addr, reuseaddr=True):
"""Set to allow a connection from a client. 'addr' specifies
the hostname and port combination of the server.
"""
Modified: sandbox/trunk/pdb/mpdb.py
==============================================================================
--- sandbox/trunk/pdb/mpdb.py (original)
+++ sandbox/trunk/pdb/mpdb.py Fri Jul 7 00:27:49 2006
@@ -18,10 +18,8 @@
from optparse import OptionParser
import pydb
from pydb.gdb import Restart
-import socket
import sys
import time
-import threading
import traceback
__all__ = ["MPdb", "pdbserver", "target"]
@@ -407,6 +405,26 @@
except:
break
+def thread_debugging(m):
+ """ Setup this debugger to handle threaded applications."""
+ import mthread
+ mthread.init(m)
+ m.user_line = mthread.user_line
+ m.user_call = mthread.user_call
+ m.user_exception = mthread.user_exception
+ m.user_return = mthread.user_return
+ m._program_sys_argv = list(m._sys_argv[2:])
+ m.mainpyfile = m._program_sys_argv[1]
+ while True:
+ try:
+ m._runscript(m.mainpyfile)
+ if m._user_requested_quit: break
+ except Restart:
+ m.msg('Restarting')
+ except:
+ m.msg(traceback.format_exc())
+ break
+
def main():
""" Main entry point to this module. """
opts, args = parse_opts()
@@ -432,7 +450,9 @@
elif opts.pdbserver:
pdbserver(opts.pdbserver, mpdb)
sys.exit()
-
+ elif opts.debug_thread:
+ thread_debugging(mpdb)
+ sys.exit()
while 1:
try:
mpdb._runscript(mainpyfile)
@@ -477,6 +497,8 @@
help="Start the debugger and execute the pdbserver " \
+ "command. The arguments should be of the form," \
+ " 'protocol address scriptname'.")
+ parser.add_option("-d", "--debug-thread", action="store_true",
+ help="Turn on thread debugging.")
(options, args) = parser.parse_args()
return (options, args)
Modified: sandbox/trunk/pdb/mthread.py
==============================================================================
--- sandbox/trunk/pdb/mthread.py (original)
+++ sandbox/trunk/pdb/mthread.py Fri Jul 7 00:27:49 2006
@@ -10,8 +10,7 @@
from pydb.gdb import Gdb
# Global variables
-tracers = [] # The MTracer objects that are tracing threads
-threads = [] # List of threads we're tracing
+tt_dict = {} # Thread,tracer dictionary
# This global variable keeps track of the 'main' debugger which, when one
# of the MTracer objects encounters a breakpoint in its thread, is used to
@@ -26,73 +25,37 @@
a thread's run() method.
"""
def __init__(self):
- Gdb.__init__(self)
+ Gdb.__init__(self, stdout=_main_debugger.stdout)
self.thread = threading.currentThread()
- # Each tracer instance must keep track of its own breakpoints
- self.fncache = {}
- self.lineno = 0
- self.curframe = None
- self.stopframe = None
- self.botframe = None
- self.quitting = False
-
- def trace_dispatch(self, frame, event, arg):
- self.currentframe = frame
- if event == 'line':
- return self.dispatch_line
- if event == 'call':
- self.msg('%s *** call' % self.thread.getName())
- return self.trace_dispatch
- if event == 'return':
- self.msg('%s *** return' % self.thread.getName())
- return self.trace_dispatch
- if event == 'exception':
- self.msg('%s *** exception' % self.thread.getName())
- return self.trace_dispatch
- if event == 'c_call':
- print '*** c_call'
- return self.trace_dispatch
- if event == 'c_exception':
- print '*** c_exception'
- return self.trace_dispatch
- if event == 'c_return':
- print '*** c_return'
- return self.trace_dispatch
- print 'bdb.Bdb.dispatch: unknown debugging event:', repr(event)
- return self.trace_dispatch
-
- def dispatch_line(self, frame, arg):
- if self.stop_here(frame) or self.break_here(frame):
- # Signal to the main debugger that we've hit a breakpoint
- print 'bang'
- _main_debugger.user_line(frame)
- if self.quitting: raise BdbQuit
- return self.trace_dispatch
-
- def dispatch_call(self, frame, arg):
- # XXX 'arg' is no longer used
- if self.botframe is None:
- # First call of dispatch since reset()
- self.botframe = frame.f_back # (CT) Note that this may also be None!
- return self.trace_dispatch
- if not (self.stop_here(frame) or self.break_anywhere(frame)):
- # No need to trace this function
- return # None
- _main_debugger.user_call(frame, arg)
- if self.quitting: raise BdbQuit
- return self.trace_dispatch
-
- def dispatch_return(self, frame, arg):
- if self.stop_here(frame) or frame == self.returnframe:
- _main_debugger.user_return(frame, arg)
- if self.quitting: raise BdbQuit
- return self.trace_dispatch
-
- def dispatch_exception(self, frame, arg):
- if self.stop_here(frame):
- _main_debugger.user_exception(frame, arg)
- if self.quitting: raise BdbQuit
- return self.trace_dispatch
+ self.prompt = '(MPdb: %s)' % self.thread.getName()
+ self.running = True
+ self.reset()
+
+ def user_line(self, frame):
+ """ Override this method from pydb.pydbbdb.Bdb to make
+ it thread-safe.
+ """
+ _main_debugger.lock.acquire()
+ Gdb.user_line(self, frame)
+ _main_debugger.lock.release()
+
+ def user_call(self, frame, args):
+ """ Override pydb.pydbbdb.Bdb.user_call and make it
+ thread-safe.
+ """
+ _main_debugger.lock.acquire()
+ Gdb.user_call(self, frame, args)
+ _main_debugger.lock.release()
+
+ def user_exception(self, frame, (type, value, traceback)):
+ """ Override this method from pydb.pydbbdb.Bdb to make
+ it thread-safe.
+ """
+ _main_debugger.lock.acquire()
+ Gdb.user_exception(self, frame, (type, value,
+ traceback))
+ _main_debugger.lock.release()
+
def trace_dispatch_init(frame, event, arg):
""" This method is called by a sys.settrace when a thread is running
@@ -100,13 +63,14 @@
set this thread's tracing function to that object's trace_dispatch
method.
"""
- global threads, tracers
- threads.append(threading.currentThread())
- t = MTracer()
-
- tracers.append(t)
- threading.settrace(t.trace_dispatch)
- sys.settrace(t.trace_dispatch)
+ global tt_dict
+ tr = MTracer()
+ th = threading.currentThread()
+
+ tt_dict[th] = tr
+
+ threading.settrace(tr.trace_dispatch)
+ sys.settrace(tr.trace_dispatch)
def init(debugger):
@@ -117,11 +81,39 @@
interpreter.
"""
global _main_debugger
- _main_debugger = debugger
- threading.settrace(trace_dispatch_init)
+ if _main_debugger == None:
+ _main_debugger = debugger
+
+ # This lock must be acquired when a MTracer object
+ # places a call to _main_debugger.user_*
+ _main_debugger.lock = threading.Lock()
+ threading.settrace(trace_dispatch_init)
-
+# All the methods below override the methods from MPdb so
+# that they are thread-safe. Every thread must contend for
+# the Lock object on the MPdb instance.
+
+def user_line(frame):
+ _main_debugger.lock.acquire()
+ Gdb.user_line(_main_debugger, frame)
+ _main_debugger.lock.release()
+
+def user_call(frame, arg):
+ _main_debugger.lock.acquire()
+ Gdb.user_call(_main_debugger, frame, arg)
+ _main_debugger.lock.release()
+
+def user_return(frame, return_value):
+ _main_debugger.lock.acquire()
+ Gdb.user_return(_main_debugger, frame, return_value)
+ _main_debugger.lock.release()
+
+def user_exception(frame, (exc_type, exc_value, exc_traceback)):
+ _main_debugger.lock.acquire()
+ Gdb.user_exception(_main_debugger, frame, (exc_type, exc_value,
+ exc_traceback))
+ _main_debugger.lock.release()
Modified: sandbox/trunk/pdb/test/test_mconnection.py
==============================================================================
--- sandbox/trunk/pdb/test/test_mconnection.py (original)
+++ sandbox/trunk/pdb/test/test_mconnection.py Fri Jul 7 00:27:49 2006
@@ -42,6 +42,8 @@
thread.start_new_thread(repeatedConnect, (self.client, __addr__))
self.server.connect(__addr__)
+ self.server.disconnect()
+
def testClientConnectAndRead(self):
"""(tcp) Connect to server and read/write. """
thread.start_new_thread(repeatedConnect, (self.client,__addr__))
@@ -70,6 +72,8 @@
line = self.server.readline()
self.assertEquals('good\n', line, 'Could not read first line.')
+ self.server.disconnect()
+
def testErrorAddressAlreadyInUse(self):
"""(tcp) Test address already in use error. """
thread.start_new_thread(repeatedConnect, (self.client, __addr__))
Modified: sandbox/trunk/pdb/test/test_mpdb.py
==============================================================================
--- sandbox/trunk/pdb/test/test_mpdb.py (original)
+++ sandbox/trunk/pdb/test/test_mpdb.py Fri Jul 7 00:27:49 2006
@@ -93,7 +93,7 @@
def testTarget(self):
""" Test the target command. """
server = MConnectionServerTCP()
- thread.start_new_thread(server.connect, (__addr__,))
+ thread.start_new_thread(server.connect, (__addr__,True))
self.client1 = MPdbTest()
connect_to_target(self.client1)
@@ -168,6 +168,7 @@
server.target = 'remote'
self.client1.onecmd('restart')
self.client1.connection.write('rquit\n')
+ server.disconnect()
for i in range(MAXTRIES):
if server.connection != None: pass
Modified: sandbox/trunk/pdb/test/test_mthread.py
==============================================================================
--- sandbox/trunk/pdb/test/test_mthread.py (original)
+++ sandbox/trunk/pdb/test/test_mthread.py Fri Jul 7 00:27:49 2006
@@ -7,14 +7,13 @@
from test import test_support
sys.path.append('..')
-import mthread
+import mthread, mpdb
class TestThreadDebugging(unittest.TestCase):
def testMthreadInit(self):
""" Test the init method of the mthread file. """
- m = sys.stdout.write
- e = sys.stderr.write
- mthread.init(m, e)
+ m = mpdb.MPdb()
+ mthread.init(m)
def test_main():
test_support.run_unittest(TestThreadDebugging)
Modified: sandbox/trunk/pdb/test/thread_script.py
==============================================================================
--- sandbox/trunk/pdb/test/thread_script.py (original)
+++ sandbox/trunk/pdb/test/thread_script.py Fri Jul 7 00:27:49 2006
@@ -5,14 +5,19 @@
import threading
+def foo():
+ l = [i for i in range(10)]
+ for n in l:
+ print l[n+1]
+
class MyThread(threading.Thread):
def run(self):
- for i in range(10):
- print i
-
+ foo()
+
def func():
- t = MyThread()
- t.start()
+ for i in range(2):
+ t = MyThread()
+ t.start()
t.join()
if __name__ == '__main__':
More information about the Python-checkins
mailing list