[Pydev-cvs] org.python.pydev.debug/pysrc pydevd_comm.py,NONE,1.1 pydevd.py,1.27,1.28
Brought to you by:
fabioz
From: Fabio Z. <fa...@us...> - 2006-01-18 15:18:27
|
Update of /cvsroot/pydev/org.python.pydev.debug/pysrc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11670/pysrc Modified Files: pydevd.py Added Files: pydevd_comm.py Log Message: some debugger improvements Index: pydevd.py =================================================================== RCS file: /cvsroot/pydev/org.python.pydev.debug/pysrc/pydevd.py,v retrieving revision 1.27 retrieving revision 1.28 diff -C2 -d -r1.27 -r1.28 *** pydevd.py 13 Jan 2006 20:42:54 -0000 1.27 --- pydevd.py 18 Jan 2006 15:18:18 -0000 1.28 *************** *** 1,436 **** ! """ pydevd - a debugging daemon ! This is the daemon you launch for python remote debugging. ! ! Protocol: ! each command has a format: ! id\tsequence-num\ttext ! id: protocol command number ! sequence-num: each request has a sequence number. Sequence numbers ! originating at the debugger are odd, sequence numbers originating ! at the daemon are even. Every response uses the same sequence number ! as the request. ! payload: it is protocol dependent. When response is a complex structure, it ! is returned as XML. Each attribute value is urlencoded, and then the whole ! payload is urlencoded again to prevent stray characters corrupting protocol/xml encodings ! ! Commands: ! ! NUMBER NAME FROM* ARGUMENTS RESPONSE NOTE ! 100 series: program execution ! 101 RUN RDB - - ! 102 LIST_THREADS RDB RETURN with XML listing of all threads ! 103 THREAD_CREATE PYDB - XML with thread information ! 104 THREAD_KILL RDB id kills the thread ! PYDB id nofies RDB that thread was killed ! 105 THREAD_SUSPEND RDB XML of the stack, suspends the thread ! reason for suspension ! PYDB id notifies RDB that thread was suspended ! 106 THREAD_RUN RDB id resume the thread ! PYDB id \t reason notifies RDB that thread was resumed ! 107 STEP_INTO RDB thread_id ! 108 STEP_OVER RDB thread_id ! 109 STEP_RETURN RDB thread_id ! 110 GET_VARIABLE RDB var_locator GET_VARIABLE with XML of var content ! see code for definition ! 111 SET_BREAK RDB file/line of the breakpoint ! 112 REMOVE_BREAK RDB file/line of the return ! 113 EVALUATE_EXPRESSION RDB expression result of evaluating the expression ! ! 500 series diagnostics/ok ! 901 VERSION either Version string (1.0) Currently just used at startup ! 902 RETURN either Depends on caller - ! 900 series: errors ! 501 ERROR either - This is reserved for unexpected errors. ! ! * RDB - remote debugger, the java end ! * PYDB - pydevd, the python end ! """ ! import os.path ! ! ! try: ! __setFalse = False ! except: ! False = 0 ! True = 1 ! ! ! import time ! import sys ! import threading ! import Queue as PydevQueue ! from socket import socket ! from socket import AF_INET ! from socket import SOCK_STREAM ! from socket import error ! import urllib ! import string ! import pydevd_vars ! ! VERSION_STRING = "1.0" ! ! CMD_RUN = 101 ! CMD_LIST_THREADS = 102 ! CMD_THREAD_CREATE = 103 ! CMD_THREAD_KILL = 104 ! CMD_THREAD_SUSPEND = 105 ! CMD_THREAD_RUN = 106 ! CMD_STEP_INTO = 107 ! CMD_STEP_OVER = 108 ! CMD_STEP_RETURN = 109 ! CMD_GET_VARIABLE = 110 ! CMD_SET_BREAK = 111 ! CMD_REMOVE_BREAK = 112 ! CMD_EVALUATE_EXPRESSION = 113 ! CMD_VERSION = 501 ! CMD_RETURN = 502 ! CMD_ERROR = 901 ! ! DONT_TRACE = ['pydevd.py' , 'threading.py'] - Debugger = None connected = False ! __all__ = (); ! ! #--------------------------------------------------------------------------------------------------- UTILITIES ! ! pydevd_trace = 0 ! ! def pydevd_log(level, s): ! """ levels are: ! 0 most serious warnings/errors ! 1 warnings/significant events ! 2 informational trace ! """ ! if level <= pydevd_trace: ! print >>sys.stderr, s ! ! ! def NormFile(filename): ! try: ! rPath = os.path.realpath ! except: ! # jython does not support os.path.realpath ! # realpath is a no-op on systems without islink support ! rPath = os.path.abspath ! return os.path.normcase(rPath(filename)) ! ! ! #----------------------------------------------------------------------------------- SOCKET UTILITIES - READER ! class ReaderThread(threading.Thread): ! """ reader thread reads and dispatches commands in an infinite loop """ ! def __init__(self, sock): ! threading.Thread.__init__(self) ! self.sock = sock ! self.setName("pydevd.Reader") ! self.setDaemon(True) ! ! def run(self): ! sys.settrace(None) # no debugging on this thread ! buffer = "" try: ! while True: ! buffer += self.sock.recv(1024) ! while buffer.find('\n') != -1: ! [command, buffer] = buffer.split('\n', 1) ! pydevd_log(1, "received command " + command) ! args = command.split('\t', 2) ! PyDB.instance.processNetCommand(int(args[0]), int(args[1]), args[2]) ! except: ! print >>sys.stderr, "Exception in reader thread" ! raise ! ! #----------------------------------------------------------------------------------- SOCKET UTILITIES - WRITER ! class WriterThread(threading.Thread): ! """ writer thread writes out the commands in an infinite loop """ ! def __init__(self, sock): threading.Thread.__init__(self) ! self.sock = sock ! self.setName("pydevd.Writer") self.setDaemon(True) ! self.cmdQueue = PydevQueue.Queue() ! if type=='python': ! self.timeout = 0 ! else: ! self.timeout = 0.1 ! ! def addCommand(self, cmd): ! """ cmd is NetCommand """ ! self.cmdQueue.put(cmd) ! def run(self): - """ just loop and write responses """ sys.settrace(None) # no debugging on this thread ! try: ! while True: ! cmd = self.cmdQueue.get(1) ! out = cmd.getOutgoing() ! pydevd_log(1, "sending cmd " + out) ! bytesSent = self.sock.send(out) #TODO: this does not guarantee that all message are sent (and jython does not have a send all) ! time.sleep(self.timeout) ! except Exception, e: ! print >>sys.stderr, "Exception in writer thread", str(e) ! except: ! print >>sys.stderr, "Exception in writer thread" ! raise ! ! ! ! ! ! ! ! #------------------------------------------------------------------------------------ MANY COMMUNICATION STUFF ! ! class NetCommand: ! """ Commands received/sent over the network. ! ! Command can represent command received from the debugger, ! or one to be sent by daemon. ! """ ! next_seq = 0 # sequence numbers ! ! def __init__(self, id, seq, text): ! """ smart handling of paramaters ! if sequence is 0, new sequence will be generated ! if text has carriage returns they'll be replaced""" ! self.id = id ! if (seq == 0): seq = self.getNextSeq() ! self.seq = seq ! self.text = text ! self.outgoing = self.makeMessage(id, seq, text) ! ! def getNextSeq(self): ! """ returns next sequence number """ ! NetCommand.next_seq += 2 ! return NetCommand.next_seq ! ! def getOutgoing(self): ! """ returns the outgoing message""" ! return self.outgoing ! ! def makeMessage(self, cmd, seq, payload): ! encoded = urllib.quote(str(payload), '/<>_=" \t') ! return str(cmd) + '\t' + str(seq) + '\t' + encoded + "\n" ! ! class NetCommandFactory: ! ! def __init_(self): ! self.next_seq = 0 ! ! def threadToXML(self, thread): ! """ thread information as XML """ ! name = pydevd_vars.makeValidXmlValue(thread.getName()) ! cmdText = '<thread name="%s" id="%s" />' % (urllib.quote(name), id(thread) ) ! return cmdText ! ! def makeErrorMessage(self, seq, text): ! cmd = NetCommand(CMD_ERROR, seq, text) ! print >>sys.stderr, "Error: ", text ! return cmd; ! ! def makeThreadCreatedMessage(self,thread): ! cmdText = "<xml>" + self.threadToXML(thread) + "</xml>" ! return NetCommand(CMD_THREAD_CREATE, 0, cmdText) ! ! def makeListThreadsMessage(self, seq): ! """ returns thread listing as XML """ ! try: ! t = threading.enumerate() ! cmdText = "<xml>" ! for i in t: ! cmdText += self.threadToXML(i) ! cmdText += "</xml>" ! return NetCommand(CMD_RETURN, seq, cmdText) ! except: ! return self.makeErrorMessage(seq, sys.exc_info()[0]) ! ! def makeVersionMessage(self, seq): ! try: ! return NetCommand(CMD_VERSION, seq, VERSION_STRING) ! except: ! return self.makeErrorMessage(seq, sys.exc_info()[0]) ! ! def makeThreadKilledMessage(self, id): ! try: ! return NetCommand(CMD_THREAD_KILL, 0, str(id)) ! except: ! return self.makeErrorMessage(0, sys.exc_info()[0]) ! ! def makeThreadSuspendMessage(self, thread_id, frame, stop_reason): ! ! """ <xml> ! <thread id="id" stop_reason="reason"> ! <frame id="id" name="functionName " file="file" line="line"> ! <var variable stuffff.... ! </frame> ! </thread> ! """ ! try: ! # print "makeThreadSuspendMessage" ! cmdTextList = ["<xml>"] ! cmdTextList.append('<thread id="%s" stop_reason="%s">' % (str(thread_id), str(stop_reason))) ! ! curFrame = frame ! while curFrame: ! #print cmdText ! myId = str(id(curFrame)) ! #print "id is ", myId ! ! myName = curFrame.f_code.co_name ! #print "name is ", myName ! ! myFile = NormFile( curFrame.f_code.co_filename ) ! #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame) ! #print "file is ", myFile ! ! myLine = str(curFrame.f_lineno) ! #print "line is ", myLine ! ! variables = pydevd_vars.frameVarsToXML(curFrame) ! ! cmdTextList.append( '<frame id="%s" name="%s" ' % (myId , pydevd_vars.makeValidXmlValue(myName))) ! cmdTextList.append( 'file="%s" line="%s">"' % (urllib.quote(myFile, '/>_= \t'), myLine)) ! cmdTextList.append( variables ) ! cmdTextList.append( "</frame>" ) ! curFrame = curFrame.f_back ! ! cmdTextList.append( "</thread></xml>" ) ! cmdText = ''.join(cmdTextList) ! ! return NetCommand(CMD_THREAD_SUSPEND, 0, cmdText) ! except: ! return self.makeErrorMessage(0, str(sys.exc_info()[0])) ! ! def makeThreadRunMessage(self, id, reason): ! try: ! return NetCommand(CMD_THREAD_RUN, 0, str(id) + "\t" + str(reason)) ! except: ! return self.makeErrorMessage(0, sys.exc_info()[0]) ! ! def makeGetVariableMessage(self, seq, payload): ! try: ! return NetCommand(CMD_GET_VARIABLE, seq, payload) ! except Exception, e: ! return self.makeErrorMessage(seq, str(e)) ! - def makeEvaluateExpressionMessage(self, seq, payload): - try: - return NetCommand(CMD_EVALUATE_EXPRESSION, seq, payload) - except Exception, e: - return self.makeErrorMessage(seq, str(e)) - - INTERNAL_TERMINATE_THREAD = 1 - INTERNAL_SUSPEND_THREAD = 2 - - class InternalThreadCommand: - """ internal commands are generated/executed by the debugger. - - The reason for their existence is that some commands have to be executed - on specific threads. These are the InternalThreadCommands that get - get posted to PyDB.cmdQueue. - """ - def __init__(self, id, payload): - self.id = id - - def doIt(self, dbg): - print "you have to override doIt" - - class InternalTerminateThread: - def __init__(self, thread_id): - self.thread_id = thread_id - - def doIt(self, dbg): - pydevd_log(1, "killing " + str(self.thread_id)) - cmd = dbg.cmdFactory.makeThreadKilledMessage(self.thread_id) - dbg.writer.addCommand(cmd) - time.sleep(0.1) - sys.exit() - - class InternalGetVariable: - """ gets the value of a variable """ - def __init__(self, seq, thread, frame_id, scope, attrs): - self.sequence = seq - self.thread = thread - self.frame_id = frame_id - self.scope = scope - self.attributes = attrs - - def doIt(self, dbg): - """ Converts request into python variable """ - # print "InternalGetVariable" - try: - xml = "<xml>" - valDict = pydevd_vars.resolveCompoundVariable(self.thread, self.frame_id, self.scope, self.attributes) - keys = valDict.keys() - keys.sort() - for k in keys: - xml += pydevd_vars.varToXML(valDict[k], str(k)) - xml += "</xml>" - # print >>sys.stderr, "done to xml" - cmd = dbg.cmdFactory.makeGetVariableMessage(self.sequence, xml) - # print >>sys.stderr, "sending command" - dbg.writer.addCommand(cmd) - except Exception: - exc_info = sys.exc_info() - import StringIO - s = StringIO.StringIO() - import traceback;traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], file = s) - cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error resolving variables " + s.getvalue()) - - # cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error resolving variables " + str(e)) - dbg.writer.addCommand(cmd) - - - class InternalEvaluateExpression: - """ gets the value of a variable """ - def __init__(self, seq, thread, frame_id, expression): - self.sequence = seq - self.thread = thread - self.frame_id = frame_id - self.expression = expression - - def doIt(self, dbg): - """ Converts request into python variable """ - try: - # print "InternalEvaluteExpression" - result = pydevd_vars.evaluateExpression( self.thread, self.frame_id, self.expression ) - xml = "<xml>" - xml += pydevd_vars.varToXML(result, "") - xml += "</xml>" - # print >>sys.stderr, "done to xml" - cmd = dbg.cmdFactory.makeEvaluateExpressionMessage(self.sequence, xml) - # print >>sys.stderr, "sending command" - dbg.writer.addCommand(cmd) - except Exception, e: - cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + str(e)) - dbg.writer.addCommand(cmd) - import traceback - traceback.print_exc(file=sys.stderr) - - - def pydevd_findThreadById(thread_id): - try: - int_id = int(thread_id) - # print >>sys.stderr, "enumerating" - # there was a deadlock here when I did not remove the tracing function when thread was dead - threads = threading.enumerate() - # print >>sys.stderr, "done enumerating" - for i in threads: - if int_id == id(i): return i - print >>sys.stderr, "could not find thread" - except: - print >>sys.stderr, "unexpected exceiton if findThreadById" - return None - - - - #---------------------------------------------------------------------------------------- THIS IS THE DEBUGGER --- 1,40 ---- ! from pydevd_comm import * ! DONT_TRACE = ('pydevd.py' , 'threading.py', 'pydevd_vars.py') connected = False ! ! class PyDBCtx: ! ctxs = dict() ! @staticmethod ! def GetCtx(frame): try: ! return PyDBCtx.ctxs[frame.f_code] ! except KeyError: ! ctx = PyDBCtx(frame) ! PyDBCtx.ctxs[frame.f_code] = ctx ! return ctx ! def __init__(self, frame): ! self.filename = NormFile(frame.f_code.co_filename) ! self.base = os.path.basename( self.filename ) ! ! ! class PyDBCommandThread(threading.Thread): ! ! def __init__(self, pyDb): threading.Thread.__init__(self) ! self.pyDb = pyDb self.setDaemon(True) ! def run(self): sys.settrace(None) # no debugging on this thread ! while True: ! self.pyDb.processInternalCommands() ! time.sleep(0.1) #---------------------------------------------------------------------------------------- THIS IS THE DEBUGGER *************** *** 450,459 **** """ - instance = None STATE_RUN = 1 STATE_SUSPEND = 2 # thread states def __init__(self): ! PyDB.instance = self self.reader = None self.writer = None --- 54,62 ---- """ STATE_RUN = 1 STATE_SUSPEND = 2 # thread states def __init__(self): ! SetGlobalDebugger(self) self.reader = None self.writer = None *************** *** 463,466 **** --- 66,70 ---- self.breakpoints = {} self.readyToRun = False + self.currentThread = None def initializeNetwork(self, sock): *************** *** 496,503 **** def connect(self, host, port): ! if (host): self.startClient(host, port) else: self.startServer(port) def getInternalQueue(self, thread_id): --- 100,109 ---- def connect(self, host, port): ! if host: self.startClient(host, port) else: self.startServer(port) + + PyDBCommandThread(self).start() def getInternalQueue(self, thread_id): *************** *** 514,522 **** if id(t) == thread_id: cmd = self.cmdFactory.makeThreadCreatedMessage(t) ! if (cmd): pydevd_log(2, "found a new thread " + str(thread_id)) self.writer.addCommand(cmd) else: pydevd_log(0, "could not find thread by id to register") return self.cmdQueue[thread_id] --- 120,129 ---- if id(t) == thread_id: cmd = self.cmdFactory.makeThreadCreatedMessage(t) ! if cmd: pydevd_log(2, "found a new thread " + str(thread_id)) self.writer.addCommand(cmd) else: pydevd_log(0, "could not find thread by id to register") + return self.cmdQueue[thread_id] *************** *** 524,529 **** """ if thread_id is *, post to all """ # print "Posting internal command for ", str(thread_id) ! if (thread_id == "*"): ! for k in self.cmdQueue.keys(): self.cmdQueue[k].put(int_cmd) else: queue = self.getInternalQueue(thread_id) --- 131,138 ---- """ if thread_id is *, post to all """ # print "Posting internal command for ", str(thread_id) ! if thread_id == "*": ! for k in self.cmdQueue.keys(): ! self.cmdQueue[k].put(int_cmd) ! else: queue = self.getInternalQueue(thread_id) *************** *** 531,542 **** def processInternalCommands(self): ! queue = self.getInternalQueue(id(threading.currentThread())) ! try: ! while (True): ! int_cmd = queue.get(False) ! pydevd_log(2, "processign internal command " + str(int_cmd)) ! int_cmd.doIt(self) ! except PydevQueue.Empty: ! pass # this is how we exit def processNetCommand(self, id, seq, text): --- 140,152 ---- def processInternalCommands(self): ! if self.currentThread is not None: ! queue = self.getInternalQueue(id(self.currentThread)) ! try: ! while (True): ! int_cmd = queue.get(False) ! pydevd_log(2, "processign internal command " + str(int_cmd)) ! int_cmd.doIt(self) ! except PydevQueue.Empty: ! pass # this is how we exit def processNetCommand(self, id, seq, text): *************** *** 594,613 **** elif id == CMD_SET_BREAK: - # #command to add some breakpoint. - # #text is file\tline. Add to breakpoints dictionary - # file, line = text.split('\t', 1) - # file = NormFile(file) - # line = int(line) - # - # if self.breakpoints.has_key(file): - # breakDict = self.breakpoints[file] - # - # else: - # breakDict = {} - # - # breakDict[line] = True - # self.breakpoints[file] = breakDict - # pydevd_log(1, "Set breakpoint at %s %s" % (file, line)) - #command to add some breakpoint. # text is file\tline. Add to breakpoints dictionary --- 204,207 ---- *************** *** 735,749 **** pydev_step_cmo ''' ! if event not in ['call', 'line', 'return']: return None ! filename = NormFile(frame.f_code.co_filename) ! base = os.path.basename( filename ) if base in DONT_TRACE: #we don't want to debug pydevd or threading return None ! self.processInternalCommands() ! t = threading.currentThread() # if thread is not alive, cancel trace_dispatch processing --- 329,346 ---- pydev_step_cmo ''' ! if event not in ('call', 'line', 'return'): return None ! ctx = PyDBCtx.GetCtx(frame) ! filename = ctx.filename ! base = ctx.base ! if base in DONT_TRACE: #we don't want to debug pydevd or threading return None ! # self.processInternalCommands() ! t = threading.currentThread() + self.currentThread = t # if thread is not alive, cancel trace_dispatch processing *************** *** 786,790 **** condition = self.breakpoints[filename][line][1] ! if condition != None: try: val = eval(condition, frame.f_globals, frame.f_locals) --- 383,387 ---- condition = self.breakpoints[filename][line][1] ! if condition is not None: try: val = eval(condition, frame.f_globals, frame.f_locals) --- NEW FILE: pydevd_comm.py --- """ pydevd - a debugging daemon This is the daemon you launch for python remote debugging. Protocol: each command has a format: id\tsequence-num\ttext id: protocol command number sequence-num: each request has a sequence number. Sequence numbers originating at the debugger are odd, sequence numbers originating at the daemon are even. Every response uses the same sequence number as the request. payload: it is protocol dependent. When response is a complex structure, it is returned as XML. Each attribute value is urlencoded, and then the whole payload is urlencoded again to prevent stray characters corrupting protocol/xml encodings Commands: NUMBER NAME FROM* ARGUMENTS RESPONSE NOTE 100 series: program execution 101 RUN RDB - - 102 LIST_THREADS RDB RETURN with XML listing of all threads 103 THREAD_CREATE PYDB - XML with thread information 104 THREAD_KILL RDB id kills the thread PYDB id nofies RDB that thread was killed 105 THREAD_SUSPEND RDB XML of the stack, suspends the thread reason for suspension PYDB id notifies RDB that thread was suspended 106 THREAD_RUN RDB id resume the thread PYDB id \t reason notifies RDB that thread was resumed 107 STEP_INTO RDB thread_id 108 STEP_OVER RDB thread_id 109 STEP_RETURN RDB thread_id 110 GET_VARIABLE RDB var_locator GET_VARIABLE with XML of var content see code for definition 111 SET_BREAK RDB file/line of the breakpoint 112 REMOVE_BREAK RDB file/line of the return 113 EVALUATE_EXPRESSION RDB expression result of evaluating the expression 500 series diagnostics/ok 901 VERSION either Version string (1.0) Currently just used at startup 902 RETURN either Depends on caller - 900 series: errors 501 ERROR either - This is reserved for unexpected errors. * RDB - remote debugger, the java end * PYDB - pydevd, the python end """ try: __setFalse = False except: False = 0 True = 1 import os.path import time import sys import threading import Queue as PydevQueue from socket import socket from socket import AF_INET from socket import SOCK_STREAM from socket import error import urllib import string import pydevd_vars CMD_RUN = 101 CMD_LIST_THREADS = 102 CMD_THREAD_CREATE = 103 CMD_THREAD_KILL = 104 CMD_THREAD_SUSPEND = 105 CMD_THREAD_RUN = 106 CMD_STEP_INTO = 107 CMD_STEP_OVER = 108 CMD_STEP_RETURN = 109 CMD_GET_VARIABLE = 110 CMD_SET_BREAK = 111 CMD_REMOVE_BREAK = 112 CMD_EVALUATE_EXPRESSION = 113 CMD_VERSION = 501 CMD_RETURN = 502 CMD_ERROR = 901 VERSION_STRING = "1.0" #--------------------------------------------------------------------------------------------------- UTILITIES pydevd_trace = 0 def pydevd_log(level, s): """ levels are: 0 most serious warnings/errors 1 warnings/significant events 2 informational trace """ if level <= pydevd_trace: print >>sys.stderr, s def NormFile(filename): try: rPath = os.path.realpath except: # jython does not support os.path.realpath # realpath is a no-op on systems without islink support rPath = os.path.abspath return os.path.normcase(rPath(filename)) def SetGlobalDebugger(dbg): global globalDbg globalDbg = dbg #------------------------------------------------------------------- ACTUAL COMM class ReaderThread(threading.Thread): """ reader thread reads and dispatches commands in an infinite loop """ def __init__(self, sock): threading.Thread.__init__(self) self.sock = sock self.setName("pydevd.Reader") self.setDaemon(True) def run(self): sys.settrace(None) # no debugging on this thread buffer = "" try: while True: buffer += self.sock.recv(1024) while buffer.find('\n') != -1: [command, buffer] = buffer.split('\n', 1) pydevd_log(1, "received command " + command) args = command.split('\t', 2) globalDbg.processNetCommand(int(args[0]), int(args[1]), args[2]) except: print >>sys.stderr, "Exception in reader thread" raise #----------------------------------------------------------------------------------- SOCKET UTILITIES - WRITER class WriterThread(threading.Thread): """ writer thread writes out the commands in an infinite loop """ def __init__(self, sock): threading.Thread.__init__(self) self.sock = sock self.setName("pydevd.Writer") self.setDaemon(True) self.cmdQueue = PydevQueue.Queue() if type=='python': self.timeout = 0 else: self.timeout = 0.1 def addCommand(self, cmd): """ cmd is NetCommand """ self.cmdQueue.put(cmd) def run(self): """ just loop and write responses """ sys.settrace(None) # no debugging on this thread try: while True: cmd = self.cmdQueue.get(1) out = cmd.getOutgoing() pydevd_log(1, "sending cmd " + out) bytesSent = self.sock.send(out) #TODO: this does not guarantee that all message are sent (and jython does not have a send all) time.sleep(self.timeout) except Exception, e: print >>sys.stderr, "Exception in writer thread", str(e) except: print >>sys.stderr, "Exception in writer thread" raise #------------------------------------------------------------------------------------ MANY COMMUNICATION STUFF class NetCommand: """ Commands received/sent over the network. Command can represent command received from the debugger, or one to be sent by daemon. """ next_seq = 0 # sequence numbers def __init__(self, id, seq, text): """ smart handling of paramaters if sequence is 0, new sequence will be generated if text has carriage returns they'll be replaced""" self.id = id if (seq == 0): seq = self.getNextSeq() self.seq = seq self.text = text self.outgoing = self.makeMessage(id, seq, text) def getNextSeq(self): """ returns next sequence number """ NetCommand.next_seq += 2 return NetCommand.next_seq def getOutgoing(self): """ returns the outgoing message""" return self.outgoing def makeMessage(self, cmd, seq, payload): encoded = urllib.quote(str(payload), '/<>_=" \t') return str(cmd) + '\t' + str(seq) + '\t' + encoded + "\n" class NetCommandFactory: def __init_(self): self.next_seq = 0 def threadToXML(self, thread): """ thread information as XML """ name = pydevd_vars.makeValidXmlValue(thread.getName()) cmdText = '<thread name="%s" id="%s" />' % (urllib.quote(name), id(thread) ) return cmdText def makeErrorMessage(self, seq, text): cmd = NetCommand(CMD_ERROR, seq, text) print >>sys.stderr, "Error: ", text return cmd; def makeThreadCreatedMessage(self,thread): cmdText = "<xml>" + self.threadToXML(thread) + "</xml>" return NetCommand(CMD_THREAD_CREATE, 0, cmdText) def makeListThreadsMessage(self, seq): """ returns thread listing as XML """ try: t = threading.enumerate() cmdText = "<xml>" for i in t: cmdText += self.threadToXML(i) cmdText += "</xml>" return NetCommand(CMD_RETURN, seq, cmdText) except: return self.makeErrorMessage(seq, sys.exc_info()[0]) def makeVersionMessage(self, seq): try: return NetCommand(CMD_VERSION, seq, VERSION_STRING) except: return self.makeErrorMessage(seq, sys.exc_info()[0]) def makeThreadKilledMessage(self, id): try: return NetCommand(CMD_THREAD_KILL, 0, str(id)) except: return self.makeErrorMessage(0, sys.exc_info()[0]) def makeThreadSuspendMessage(self, thread_id, frame, stop_reason): """ <xml> <thread id="id" stop_reason="reason"> <frame id="id" name="functionName " file="file" line="line"> <var variable stuffff.... </frame> </thread> """ try: # print "makeThreadSuspendMessage" cmdTextList = ["<xml>"] cmdTextList.append('<thread id="%s" stop_reason="%s">' % (str(thread_id), str(stop_reason))) curFrame = frame while curFrame: #print cmdText myId = str(id(curFrame)) #print "id is ", myId myName = curFrame.f_code.co_name #print "name is ", myName myFile = NormFile( curFrame.f_code.co_filename ) #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame) #print "file is ", myFile myLine = str(curFrame.f_lineno) #print "line is ", myLine variables = pydevd_vars.frameVarsToXML(curFrame) cmdTextList.append( '<frame id="%s" name="%s" ' % (myId , pydevd_vars.makeValidXmlValue(myName))) cmdTextList.append( 'file="%s" line="%s">"' % (urllib.quote(myFile, '/>_= \t'), myLine)) cmdTextList.append( variables ) cmdTextList.append( "</frame>" ) curFrame = curFrame.f_back cmdTextList.append( "</thread></xml>" ) cmdText = ''.join(cmdTextList) return NetCommand(CMD_THREAD_SUSPEND, 0, cmdText) except: return self.makeErrorMessage(0, str(sys.exc_info()[0])) def makeThreadRunMessage(self, id, reason): try: return NetCommand(CMD_THREAD_RUN, 0, str(id) + "\t" + str(reason)) except: return self.makeErrorMessage(0, sys.exc_info()[0]) def makeGetVariableMessage(self, seq, payload): try: return NetCommand(CMD_GET_VARIABLE, seq, payload) except Exception, e: return self.makeErrorMessage(seq, str(e)) def makeEvaluateExpressionMessage(self, seq, payload): try: return NetCommand(CMD_EVALUATE_EXPRESSION, seq, payload) except Exception, e: return self.makeErrorMessage(seq, str(e)) INTERNAL_TERMINATE_THREAD = 1 INTERNAL_SUSPEND_THREAD = 2 class InternalThreadCommand: """ internal commands are generated/executed by the debugger. The reason for their existence is that some commands have to be executed on specific threads. These are the InternalThreadCommands that get get posted to PyDB.cmdQueue. """ def __init__(self, id, payload): self.id = id def doIt(self, dbg): print "you have to override doIt" class InternalTerminateThread: def __init__(self, thread_id): self.thread_id = thread_id def doIt(self, dbg): pydevd_log(1, "killing " + str(self.thread_id)) cmd = dbg.cmdFactory.makeThreadKilledMessage(self.thread_id) dbg.writer.addCommand(cmd) time.sleep(0.1) sys.exit() class InternalGetVariable: """ gets the value of a variable """ def __init__(self, seq, thread, frame_id, scope, attrs): self.sequence = seq self.thread = thread self.frame_id = frame_id self.scope = scope self.attributes = attrs def doIt(self, dbg): """ Converts request into python variable """ # print "InternalGetVariable" try: xml = "<xml>" valDict = pydevd_vars.resolveCompoundVariable(self.thread, self.frame_id, self.scope, self.attributes) keys = valDict.keys() keys.sort() for k in keys: xml += pydevd_vars.varToXML(valDict[k], str(k)) xml += "</xml>" # print >>sys.stderr, "done to xml" cmd = dbg.cmdFactory.makeGetVariableMessage(self.sequence, xml) # print >>sys.stderr, "sending command" dbg.writer.addCommand(cmd) except Exception: exc_info = sys.exc_info() import StringIO s = StringIO.StringIO() import traceback;traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], file = s) cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error resolving variables " + s.getvalue()) # cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error resolving variables " + str(e)) dbg.writer.addCommand(cmd) class InternalEvaluateExpression: """ gets the value of a variable """ def __init__(self, seq, thread, frame_id, expression): self.sequence = seq self.thread = thread self.frame_id = frame_id self.expression = expression def doIt(self, dbg): """ Converts request into python variable """ try: # print "InternalEvaluteExpression" result = pydevd_vars.evaluateExpression( self.thread, self.frame_id, self.expression ) xml = "<xml>" xml += pydevd_vars.varToXML(result, "") xml += "</xml>" # print >>sys.stderr, "done to xml" cmd = dbg.cmdFactory.makeEvaluateExpressionMessage(self.sequence, xml) # print >>sys.stderr, "sending command" dbg.writer.addCommand(cmd) except Exception, e: cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + str(e)) dbg.writer.addCommand(cmd) import traceback traceback.print_exc(file=sys.stderr) def pydevd_findThreadById(thread_id): try: int_id = int(thread_id) # print >>sys.stderr, "enumerating" # there was a deadlock here when I did not remove the tracing function when thread was dead threads = threading.enumerate() # print >>sys.stderr, "done enumerating" for i in threads: if int_id == id(i): return i print >>sys.stderr, "could not find thread" except: print >>sys.stderr, "unexpected exceiton if findThreadById" return None |