New Features: In Tab-View mode, Ctrl-number will take the user to
    the numbered tab view.  Modified files now show an '*' astrisk in
    the view title.  Debugger framework can now support PHP debugging.
    Not important for python development, but at least that means the
    debugger framework is more generalized.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@38852 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
	
		
			
				
	
	
		
			2106 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			2106 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #---------------------------------------------------------------------------
 | |
| # Name:         PHPDebugger.py
 | |
| # Purpose:      php dbg client and supporting code
 | |
| # Author:       Matt Fryer, Kevin Wang
 | |
| # Created:      2/1/06
 | |
| # Copyright:    (c) 2006 ActiveGrid, Inc.
 | |
| # License:      wxWindows License
 | |
| #---------------------------------------------------------------------------
 | |
| 
 | |
| 
 | |
| import os
 | |
| import socket
 | |
| import sys
 | |
| import threading
 | |
| import traceback
 | |
| import wx
 | |
| import DebuggerService
 | |
| import activegrid.util.sysutils as sysutils
 | |
| 
 | |
| 
 | |
| DBGC_REPLY                              = 0x0               # reply to previous DBGA_REQUEST request
 | |
| DBGC_STARTUP                            = 0x0001            # script startup
 | |
| DBGC_END                                = 0x0002            # script done
 | |
| DBGC_BREAKPOINT                         = 0x0003            # user definded breakpoint occured
 | |
| DBGC_STEPINTO_DONE                      = 0x0004            # step to the next statement is completed
 | |
| DBGC_STEPOVER_DONE                      = 0x0005            # step to the next statement is completed
 | |
| DBGC_STEPOUT_DONE                       = 0x0006            # step to the next statement is completed
 | |
| DBGC_EMBEDDED_BREAK                     = 0x0007            # breakpoint caused by DebugBreak() function
 | |
| DBGC_ERROR                              = 0x0010            # error occured
 | |
| DBGC_LOG                                = 0x0011            # logging support
 | |
| DBGC_SID                                = 0x0012            # send SID
 | |
| DBGC_PAUSE                              = 0x0013            # pause current session as soon as possible
 | |
| DBGC_AG_SHUTDOWN_REQ                    = 0x0201            # special ActiveGrid UI shutdown listening thread command
 | |
| 
 | |
| 
 | |
| FRAME_STACK                             = 100000            # "call:stack" - e.g. backtrace
 | |
| FRAME_SOURCE                            = 100100            # source text
 | |
| FRAME_SRC_TREE                          = 100200            # tree of source files
 | |
| FRAME_RAWDATA                           = 100300            # raw data or string
 | |
| FRAME_ERROR                             = 100400            # error notification
 | |
| FRAME_EVAL                              = 100500            # evaluating/watching
 | |
| FRAME_BPS                               = 100600            # set/remove breakpoint
 | |
| FRAME_BPL                               = 100700            # breakpoint(s) request = get the list
 | |
| FRAME_VER                               = 100800            # version request
 | |
| FRAME_SID                               = 100900            # session id info
 | |
| FRAME_SRCLINESINFO                      = 101000            # source lines info
 | |
| FRAME_SRCCTXINFO                        = 101100            # source contexts info
 | |
| FRAME_LOG                               = 101200            # logging
 | |
| FRAME_PROF                              = 101300            # profiler
 | |
| FRAME_PROF_C                            = 101400            # profiler counter/accuracy
 | |
| FRAME_SET_OPT                           = 101500            # set/update options
 | |
| 
 | |
| 
 | |
| DBGF_STARTED                            = 0x0001            # debugger has been started
 | |
| DBGF_FINISHED                           = 0x0002            # DBGC_END notification has been sent
 | |
| DBGF_WAITACK                            = 0x0004            # awaiting replay|request
 | |
| DBGF_UNSYNC                             = 0x0008            # protocol has been unsynchronized
 | |
| DBGF_REQUESTPENDING                     = 0x0010            # Debug session request pending
 | |
| DBGF_REQUESTFOUND                       = 0x0020            # Debug session request found
 | |
| DBGF_REJECTIONFOUND                     = 0x0040            # DBGSESSID=-1 found - session rejection
 | |
| 
 | |
| 
 | |
| E_ERROR                                 = 1 << 0
 | |
| E_WARNING                               = 1 << 1
 | |
| E_PARSE                                 = 1 << 2
 | |
| E_NOTICE                                = 1 << 3
 | |
| E_CORE_ERROR                            = 1 << 4
 | |
| E_CORE_WARNING                          = 1 << 5
 | |
| E_COMPILE_ERROR                         = 1 << 6
 | |
| E_COMPILE_WARNING                       = 1 << 7
 | |
| E_USER_ERROR                            = 1 << 8
 | |
| E_USER_WARNING                          = 1 << 9
 | |
| E_USER_NOTICE                           = 1 << 10
 | |
| 
 | |
| 
 | |
| BPS_DELETED                             = 0
 | |
| BPS_DISABLED                            = 1
 | |
| BPS_ENABLED                             = 2
 | |
| BPS_UNRESOLVED                          = 0x100
 | |
| 
 | |
| 
 | |
| DBG_SYNC                                = 0x5953
 | |
| DBG_SYNC2_STR                           = chr(0) + chr(0) + chr(89) + chr(83)
 | |
| RESPONSE_HEADER_SIZE                    = 16
 | |
| 
 | |
| 
 | |
| _VERBOSE = False
 | |
| def myprint(format, vlist=None):
 | |
|     if _VERBOSE:
 | |
|         if vlist:
 | |
|             print format % vlist
 | |
|         else:
 | |
|             print format
 | |
| 
 | |
| 
 | |
| #
 | |
| # 4 Char's to an Integer
 | |
| #
 | |
| def C4ToInt(ch, startPos):
 | |
|     retval = 0
 | |
|     pos    = startPos
 | |
| 
 | |
|     retval = retval + (CharToInt(ch[pos]) << 24)
 | |
|     pos    = pos + 1
 | |
|     retval = retval + (CharToInt(ch[pos]) << 16)
 | |
|     pos    = pos + 1
 | |
|     retval = retval + (CharToInt(ch[pos]) << 8)
 | |
|     pos    = pos + 1
 | |
|     retval = retval + (CharToInt(ch[pos]) << 0)
 | |
| 
 | |
|     return retval
 | |
| 
 | |
| 
 | |
| def CharToInt(ch):
 | |
|     return int((ord(ch) & 0x00FF));
 | |
| 
 | |
| 
 | |
| #
 | |
| # An Integer to 4 Char's
 | |
| #
 | |
| def IntToC4(num):
 | |
|     retval  = chr((num >> 24) & 0x00FF)
 | |
|     retval += chr((num >> 16) & 0x00FF)
 | |
|     retval += chr((num >> 8 ) & 0x00FF)
 | |
|     retval += chr((num >> 0 ) & 0x00FF)
 | |
| 
 | |
|     return retval
 | |
| 
 | |
| 
 | |
| DBGA_CONTINUE                           = IntToC4(0x8001)
 | |
| DBGA_STOP                               = IntToC4(0x8002)
 | |
| DBGA_STEPINTO                           = IntToC4(0x8003)
 | |
| DBGA_STEPOVER                           = IntToC4(0x8004)
 | |
| DBGA_STEPOUT                            = IntToC4(0x8005)
 | |
| DBGA_IGNORE                             = IntToC4(0x8006)
 | |
| DBGA_REQUEST                            = IntToC4(0x8010)
 | |
| 
 | |
| 
 | |
| def getCommandString(code):
 | |
|     if code == DBGC_REPLY:
 | |
|         return "REPLY"
 | |
|     elif code == DBGC_STARTUP:
 | |
|         return "STARTUP"
 | |
|     elif code == DBGC_END:
 | |
|         return "END"
 | |
|     elif code == DBGC_BREAKPOINT:
 | |
|         return "BREAKPOINT"
 | |
|     elif code == DBGC_STEPINTO_DONE:
 | |
|         return "STEPINTO DONE"
 | |
|     elif code == DBGC_STEPOVER_DONE:
 | |
|         return "STEPOVER DONE"
 | |
|     elif code == DBGC_STEPOUT_DONE:
 | |
|         return "STEPOUT DONE"
 | |
|     elif code == DBGC_EMBEDDED_BREAK:
 | |
|         return "EMBEDDED BREAK"
 | |
|     elif code == DBGC_PAUSE:
 | |
|         return "PAUSE"
 | |
|     elif code == DBGC_ERROR:
 | |
|         return "ERROR"
 | |
|     elif code == DBGC_LOG:
 | |
|         return "LOG"
 | |
|     elif code == DBGC_SID:
 | |
|         return "SEND SID"
 | |
|     elif code == DBGC_AG_SHUTDOWN_REQ:
 | |
|         return "AG SHUTDOWN REQ"
 | |
| 
 | |
| 
 | |
| def reportFlags(flagsValue):
 | |
|     flagsRetVal = ""
 | |
|     if flagsValue & DBGF_STARTED:                  # debugger has been started
 | |
|         flagsRetVal += "started+"
 | |
|     if flagsValue & DBGF_FINISHED:                 # DBGC_END notification has been sent
 | |
|         flagsRetVal += "finished+"
 | |
|     if flagsValue & DBGF_WAITACK:                  # awaiting replay|request
 | |
|         flagsRetVal += "awaiting ack+"
 | |
|     if flagsValue & DBGF_UNSYNC:                   # protocol has been unsynchronized
 | |
|         flagsRetVal += "protocol unsynchronized+"
 | |
|     if flagsValue & DBGF_REQUESTPENDING:           # Debug session request pending
 | |
|         flagsRetVal += "request pending+"
 | |
|     if flagsValue & DBGF_REQUESTFOUND:             # Debug session request found
 | |
|         flagsRetVal += "request found+"
 | |
|     if flagsValue & DBGF_REJECTIONFOUND :          # DBGSESSID=-1 found - session rejection
 | |
|         flagsRetVal += "session rejection+"
 | |
|     return flagsRetVal
 | |
| 
 | |
| 
 | |
| def getErrorTypeString(code):
 | |
|     if code == E_ERROR:
 | |
|         return "[Error]"
 | |
|     elif code == E_WARNING:
 | |
|         return "[Warning]"
 | |
|     elif code == E_PARSE:
 | |
|          return "[Parse Error]"
 | |
|     elif code == E_NOTICE:
 | |
|         return "[Notice]"
 | |
|     elif code == E_CORE_ERROR:
 | |
|         return "[Core Error]"
 | |
|     elif code == E_CORE_WARNING:
 | |
|         return "[Core Warning]"
 | |
|     elif code == E_COMPILE_ERROR:
 | |
|         return  "[Compile Error]"
 | |
|     elif code == E_COMPILE_WARNING:
 | |
|         return  "[Compile Warning]"
 | |
|     elif code == E_USER_ERROR:
 | |
|         return  "[User Error]"
 | |
|     elif code == E_USER_WARNING:
 | |
|         return  "[User Warning]"
 | |
|     elif code == E_USER_NOTICE:
 | |
|         return  "[User Notice]"
 | |
|     else:
 | |
|         return "[Unexpected Error]"
 | |
| 
 | |
| 
 | |
| class ResponseHeader(object):
 | |
|     def __init__(self, conn, blocking = False):
 | |
|         self.isValid = False
 | |
|         receivedData = conn.recv(RESPONSE_HEADER_SIZE, blocking)
 | |
| 
 | |
|         if not receivedData:
 | |
|             myprint("Tried to get %d bytes of PHP DBG header, got None\n" % RESPONSE_HEADER_SIZE)
 | |
|             return
 | |
|         elif len(receivedData) != RESPONSE_HEADER_SIZE:
 | |
|             myprint("Tried to get %d bytes of PHP DBG header, got %d\n" % (RESPONSE_HEADER_SIZE, len(receivedData)))
 | |
|             return
 | |
| 
 | |
|         self.sync    = C4ToInt(receivedData, 0)
 | |
|         self.command = C4ToInt(receivedData, 4)
 | |
|         self.flags   = C4ToInt(receivedData, 8)
 | |
|         self.toRead  = C4ToInt(receivedData, 12)
 | |
| 
 | |
|         myprint("ResponseHeader: sync=%x, command=%s, flags=(%s), toRead=%s\n", (self.sync, getCommandString(self.command), reportFlags(self.flags), self.toRead))
 | |
|         if self.sync != DBG_SYNC:
 | |
|             myprint("Sync wrong for header! Expected %x, got %s\n" % (DBG_SYNC, self.sync))
 | |
|             return
 | |
| 
 | |
|         self.isValid = True
 | |
| 
 | |
| 
 | |
| class ResponsePacketFrame(object):
 | |
|     def __init__(self, conn, size, data, blocking = False):
 | |
|         self.isValid      = False
 | |
|         self.conn         = conn
 | |
|         self.data         = ''
 | |
| 
 | |
|         if data:
 | |
|             self.data     = data
 | |
|             newlyReceived = False
 | |
|         elif conn:
 | |
|             newlyReceived = True
 | |
| 
 | |
|             sizeToReceive = size
 | |
|             while True:
 | |
|                 oneChunk  = conn.recv(sizeToReceive, blocking)
 | |
|                 sizeReceived = len(oneChunk)
 | |
|                 if sizeReceived > 0:
 | |
|                     self.data = self.data + oneChunk
 | |
| 
 | |
|                 if sizeReceived < sizeToReceive:
 | |
|                     sizeToReceive = sizeToReceive - sizeReceived
 | |
|                     continue
 | |
|                 else:
 | |
|                     break
 | |
| 
 | |
|             if len(self.data) != size:
 | |
|                 myprint("Expected to get %d bytes of a PHP DBG packet, got %d\n" % (size, len(self.data)))
 | |
|                 return
 | |
|         else:
 | |
|             return
 | |
| 
 | |
|         self.frameName = C4ToInt(self.data, 0)
 | |
|         self.frameSize = C4ToInt(self.data, 4)
 | |
|         if newlyReceived:
 | |
|             myprint("Newly received ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize))
 | |
|         else:
 | |
|             myprint("Created from existing ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize))
 | |
| 
 | |
|         if self.frameSize == 0:
 | |
|             return
 | |
| 
 | |
|         self.currPos      = 8
 | |
|         self.totalDataLen = len(self.data)
 | |
|         self.length       = 8 + self.frameSize
 | |
|         self.isValid      = True
 | |
|         myprint("new ResponsePacketFrame: currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length)))
 | |
|         return
 | |
| 
 | |
|     def getNextInt(self):
 | |
|         myprint("getNextInt(): currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length)))
 | |
|         if self.isValid and self.currPos + 4 <= self.length:
 | |
|             val = C4ToInt(self.data, self.currPos)
 | |
|             self.currPos = self.currPos + 4
 | |
|             myprint("getNextInt(): got an integar: %s", repr(val))
 | |
|             return val
 | |
|         else:
 | |
|             return self._errorReturn("getNextInt(): no more integar available with current frame: ")
 | |
| 
 | |
|     def getNextString(self, strLen):
 | |
|         endPos = self.currPos + strLen
 | |
|         if self.isValid and endPos <= self.length:
 | |
|             #
 | |
|             # Trim the ending '\0'.  TODO: confirm this applies to all raw string data.
 | |
|             #
 | |
|             str          = self.data[self.currPos:endPos - 1]
 | |
|             self.currPos = endPos
 | |
|             myprint("getNextString(): got a string: %s", str)
 | |
|             return str
 | |
|         else:
 | |
|             return self._errorReturn("getNextString(): no more string available with current frame: ")
 | |
| 
 | |
|     def getNextFrame(self, useAbsolutePos = False):
 | |
|         if useAbsolutePos:
 | |
|             #
 | |
|             # Skip this frame's header (8 bytes for frameSize and frameSize) and frame data (frameSize).
 | |
|             #
 | |
|             self.currPos = self.length
 | |
| 
 | |
|         if self.isValid and self.currPos < self.totalDataLen:
 | |
|             return ResponsePacketFrame(None, None, self.data[self.currPos:])
 | |
|         else:
 | |
|             return self._errorReturn("getNextFrame(): no more frame available with current frame: ")
 | |
| 
 | |
|     def _errorReturn(self, preMsg = ''):
 | |
|         myprint(preMsg + "frameName=%s, frameSize=%s, totalDataLen=%s, length=%s, currPos:%s", (repr(self.frameName), repr(self.frameSize), repr(self.totalDataLen), repr(self.length), repr(self.currPos)))
 | |
|         self.isValid = False
 | |
|         return None
 | |
| 
 | |
| 
 | |
| class PHPDBGFrame(object):
 | |
|     FRAME_HEADER_SIZE = 8
 | |
|     def __init__(self, frameType):
 | |
|         self.frameType = IntToC4(frameType)
 | |
|         self.frameData = ""
 | |
| 
 | |
|     def addInt(self, intVal):
 | |
|         self.frameData = self.frameData + IntToC4(intVal)
 | |
| 
 | |
|     def addChar(self, charVal):
 | |
|         self.frameData = self.frameData + charVal
 | |
| 
 | |
|     def addStr(self, string):
 | |
|         #
 | |
|         # Add the trailing '\0'.
 | |
|         #
 | |
|         self.frameData = self.frameData + string + '\0'
 | |
| 
 | |
|     def getSize(self):
 | |
|         return len(self.frameData) + PHPDBGFrame.FRAME_HEADER_SIZE
 | |
| 
 | |
|     def writeFrame(self, conn):
 | |
|         header = self.frameType + IntToC4(len(self.frameData))
 | |
|         conn.sendall(header)
 | |
|         conn.sendall(self.frameData)
 | |
| 
 | |
| 
 | |
| class PHPDBGPacket(object):
 | |
|     def __init__(self, packetType):
 | |
|         self.header     = DBG_SYNC2_STR + packetType
 | |
|         self.frames     = []
 | |
|         self.packetSize = 0
 | |
| 
 | |
|     def addFrame(self, frame):
 | |
|         self.frames.append(frame)
 | |
|         self.packetSize += frame.getSize()
 | |
| 
 | |
|     def sendPacket(self, conn, flags = 0):
 | |
|         self.header += IntToC4(flags)
 | |
|         self.header += IntToC4(self.packetSize)
 | |
|         conn.sendall(self.header)
 | |
|         for frame in self.frames:
 | |
|             frame.writeFrame(conn)
 | |
| 
 | |
| 
 | |
| class PHPDBGException(Exception):
 | |
|     def __init__(self, msg = None, cause = None):
 | |
|         if (msg == None):
 | |
|             Exception.__init__(self)
 | |
|         elif (cause == None):
 | |
|             Exception.__init__(self, msg)
 | |
|         else:
 | |
|             Exception.__init__(self, "PHPDBGException: message:%s\n, cause:%s" % (msg, cause))
 | |
| 
 | |
| 
 | |
| class PHPDBGConnException(PHPDBGException):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class PHPDebuggerCallback(object):
 | |
|     ACTION_NONE              = 0
 | |
|     ACTION_STOP              = 1
 | |
|     ACTION_LISTEN            = 2
 | |
| 
 | |
|     def __init__(self, ui, service, lsnrHosti, lsnrPorti):
 | |
|         self.ui              = ui
 | |
|         self.service         = service
 | |
|         self.lsnrHost        = lsnrHosti
 | |
|         self.lsnrPort        = lsnrPorti
 | |
|         self.lsnrThr         = None
 | |
|         self.lsnrAction      = PHPDebuggerCallback.ACTION_NONE
 | |
|         self.clearInternals()
 | |
|         self.initLsnrThr()
 | |
| 
 | |
| 
 | |
|     ############################################################################
 | |
|     #                      Public callback functions begin
 | |
|     #
 | |
|     def Start(self):
 | |
|         self.lsnrThr.start()
 | |
| 
 | |
|     def ShutdownServer(self, stopLsnr = True):
 | |
|         #
 | |
|         # First to tell php debugger to stop execution of the current PHP
 | |
|         # program.  Disconnect with php dbg module too.
 | |
|         #
 | |
|         self.stopPhpDbg()
 | |
| 
 | |
|         #
 | |
|         # Stop debug listener.
 | |
|         #
 | |
|         if stopLsnr:
 | |
|             self.stopLsnr()
 | |
| 
 | |
|     def BreakExecution(self):
 | |
|         reqPacket = PHPDBGPacket(IntToC4(DBGC_PAUSE))
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.lsnrThr)
 | |
|             self.awaitAndHandleResponse()
 | |
|         except PHPDBGConnException:
 | |
|             self.currConnFinished()
 | |
|             return
 | |
| 
 | |
|         self.ui.LoadPHPFramesList(self.stackList)
 | |
|         return
 | |
| 
 | |
|     def SingleStep(self):
 | |
|         reqPacket = PHPDBGPacket(DBGA_STEPINTO)
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.lsnrThr)
 | |
|             self.lastCommand = DBGA_STEPINTO
 | |
|             self.awaitAndHandleResponse(blocking = True)
 | |
|         except PHPDBGConnException:
 | |
|             self.currConnFinished()
 | |
|             return
 | |
| 
 | |
|         self.ui.LoadPHPFramesList(self.stackList)
 | |
|         return
 | |
| 
 | |
|     def Next(self):
 | |
|         reqPacket = PHPDBGPacket(DBGA_STEPOVER)
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.lsnrThr)
 | |
|             self.lastCommand = DBGA_STEPOVER
 | |
|             self.awaitAndHandleResponse(blocking = True)
 | |
|         except PHPDBGConnException:
 | |
|             self.currConnFinished()
 | |
|             return
 | |
| 
 | |
|         self.ui.LoadPHPFramesList(self.stackList)
 | |
|         return
 | |
| 
 | |
|     def Continue(self):
 | |
|         reqPacket = PHPDBGPacket(DBGA_CONTINUE)
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.lsnrThr)
 | |
|             self.lastCommand = DBGA_CONTINUE
 | |
|             self.awaitAndHandleResponse(blocking = True)
 | |
|         except PHPDBGConnException:
 | |
|             self.currConnFinished()
 | |
|             return
 | |
| 
 | |
|         self.ui.LoadPHPFramesList(self.stackList)
 | |
|         return
 | |
| 
 | |
|     def Return(self):
 | |
|         reqPacket = PHPDBGPacket(DBGA_STEPOUT)
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.lsnrThr)
 | |
|             self.lastCommand = DBGA_STEPOUT
 | |
|             self.awaitAndHandleResponse(blocking = True)
 | |
|         except PHPDBGConnException:
 | |
|             self.currConnFinished()
 | |
|             return
 | |
| 
 | |
|         self.ui.LoadPHPFramesList(self.stackList)
 | |
|         return
 | |
| 
 | |
|     def PushBreakpoints(self, noRemove = False):
 | |
|         tmpList = []
 | |
|         bps     = self.service.GetMasterBreakpointDict()
 | |
|         for fileName in bps.keys():
 | |
|             if fileName.endswith('.php'):
 | |
|                 lines = bps[fileName]
 | |
|                 if lines:
 | |
|                     for lineNo in lines:
 | |
|                         if lineNo:
 | |
|                             #
 | |
|                             # A tuple (fileName, lineNo) is an item which is
 | |
|                             # used as a key in self.bpDict.
 | |
|                             #
 | |
|                             tmpList.append(self.createBpKey(fileName, lineNo))
 | |
|                             myprint("PushBreakpoints(): global breakpoint \'%s:%i\'", (fileName, lineNo))
 | |
| 
 | |
|         #
 | |
|         # Check to see if we have any new breakpoints added.
 | |
|         #
 | |
|         for oneKey in tmpList:
 | |
|             if not self.bpDict.has_key(oneKey):
 | |
|                 #
 | |
|                 # A new breakpoint.
 | |
|                 #
 | |
|                 newBp = BreakPoint(self, oneKey[0], oneKey[1])
 | |
|                 newBp.addSelf()
 | |
|                 self.bpDict[oneKey] = newBp
 | |
|                 myprint("PushBreakpoints(): newly added global breakpoint \'%s:%i\'", (oneKey[0], oneKey[1]))
 | |
| 
 | |
|         if noRemove:
 | |
|             return
 | |
| 
 | |
|         #
 | |
|         # Check to see if any bp that is in our list, but not in the latest
 | |
|         # global list.  If so, it must have been removed recently in the
 | |
|         # global one.  Remove it from our list and tell php debugger to do
 | |
|         # so as well.
 | |
|         #
 | |
|         toRemoveList = []
 | |
|         for oneKey in self.bpDict.keys():
 | |
|             if tmpList.count(oneKey) == 0:
 | |
|                 toRemoveList.append((oneKey, self.bpDict[oneKey]))
 | |
|                 myprint("PushBreakpoints(): recently removed global breakpoint \'%s:%i\'", (oneKey[0], oneKey[1]))
 | |
| 
 | |
|         for bp in toRemoveList:
 | |
|             bp[1].removeSelf()
 | |
|             del self.bpDict[bp[0]]
 | |
|             myprint("PushBreakpoints(): successfully removed breakpoint \'%s:%i\' from both our local list and php debugger", (bp[0][0], bp[0][1]))
 | |
| 
 | |
|         return
 | |
|     #
 | |
|     #                      Public callback functions end
 | |
|     ############################################################################
 | |
| 
 | |
| 
 | |
|     def newConnEventHandler(self):
 | |
|         #
 | |
|         # Ok, we've got a connection from the php debugger, and some initial
 | |
|         # frame data from it.  Everything is ready and let's make some initial
 | |
|         # actions.
 | |
|         #
 | |
|         self.clearInternals()
 | |
| 
 | |
|         try:
 | |
|             self.awaitAndHandleResponse(self.lsnrThr.getConnHeader())
 | |
|         except PHPDBGConnException:
 | |
|             self.currConnFinished()
 | |
|             return
 | |
| 
 | |
|         self.PushBreakpoints(True)
 | |
|         self.ui.LoadPHPFramesList(self.stackList)
 | |
| 
 | |
|     #
 | |
|     # This could be called when this object is constructed or when self is
 | |
|     # re-initialized after getting a new dbg module connection as a new
 | |
|     # session.
 | |
|     #
 | |
|     def clearInternals(self):
 | |
|         self.stackList       = []
 | |
|         self.errStackList    = []
 | |
|         self.stackFrameIndex = 0
 | |
|         self.isErrStack      = False
 | |
|         self.errStr          = ''
 | |
|         self.modList         = []
 | |
|         self.stopOnError     = True
 | |
|         self.lastCommand     = None
 | |
|         self.evalRet         = ''
 | |
|         self.modDict         = {}
 | |
|         self.bpDict          = {}
 | |
|         self.rawDataDict     = {}
 | |
|         self.sessID          = 0
 | |
|         self.sessType        = 0
 | |
|         self.sessEnded       = False
 | |
|         self.frameCounter    = 1000
 | |
|         self.variableList    = []
 | |
|         self.verMajor        = 0
 | |
|         self.verMinor        = 0
 | |
|         self.verDesc         = None
 | |
| 
 | |
|     def initLsnrThr(self):
 | |
|         self.actionEvent = threading.Event()
 | |
|         self.lsnrThr     = PHPDBGLsnrThr(self, self.lsnrHost, self.lsnrPort, self.actionEvent, self.ui)
 | |
| 
 | |
|     def awaitAndHandleResponse(self, header = None, blocking = False, disable = True, stopping = False):
 | |
|         if disable:
 | |
|             self.ui.DisableWhileDebuggerRunning()
 | |
| 
 | |
|         while self.readResponse(header, blocking) != 0:
 | |
|             myprint("Waiting for response")
 | |
| 
 | |
|         if stopping:
 | |
|             self.ui.DisableAfterStop()
 | |
|         else:
 | |
|             self.ui.EnableWhileDebuggerStopped()
 | |
| 
 | |
|     def requestDBGVersion(self):
 | |
|         #TODO: necessary?
 | |
|         pass
 | |
| 
 | |
|     def getSourceTree(self):
 | |
|         #TODO: necessary?
 | |
|         pass
 | |
| 
 | |
|     def addDBGModName(self):
 | |
|         #TODO: necessary?
 | |
|         pass
 | |
| 
 | |
|     def getNextFrameCounter(self):
 | |
|         self.frameCounter = self.frameCounter + 1
 | |
|         return self.frameCounter
 | |
| 
 | |
|     def getVariables(self, stack):
 | |
|         self.variableList = []
 | |
| 
 | |
|         reqPacket = PHPDBGPacket(DBGA_REQUEST)
 | |
|         reqFrame  = PHPDBGFrame(FRAME_EVAL)
 | |
| 
 | |
|         reqFrame.addInt(0)
 | |
|         reqFrame.addInt(stack.getFrameScopeId())
 | |
|         reqPacket.addFrame(reqFrame)
 | |
|         myprint("PHPDebuggerCallback::getVariables(): about to send eval request")
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.lsnrThr)
 | |
|             self.awaitAndHandleResponse(disable = False)
 | |
|         except PHPDBGConnException:
 | |
|             self.currConnFinished()
 | |
|             return self.variableList
 | |
| 
 | |
|         myprint("PHPDebuggerCallback::getVariables(): evalRet=%s", self.evalRet)
 | |
|         evalStr = PHPDBGEvalString(stack, self.evalRet)
 | |
|         if evalStr:
 | |
|             self.variableList = evalStr.getVars()
 | |
|         myprint("PHPDebuggerCallback::getVariables(): about to return")
 | |
| 
 | |
|         return self.variableList
 | |
| 
 | |
|     def evalBlock(self, stack, evalStr):
 | |
|         reqPacket = PHPDBGPacket(DBGA_REQUEST)
 | |
|         reqFrame1 = PHPDBGFrame(FRAME_EVAL)
 | |
|         reqFrame2 = PHPDBGFrame(FRAME_RAWDATA)
 | |
| 
 | |
|         frameID = self.getNextFrameCounter()
 | |
|         reqFrame1.addInt(frameID)
 | |
|         reqFrame1.addInt(1)
 | |
| 
 | |
|         reqFrame2.addInt(frameID)
 | |
|         reqFrame2.addInt(len(evalStr) + 1)
 | |
|         reqFrame2.addStr(evalString)
 | |
| 
 | |
|         reqPacket.addFrame(reqFrame2)
 | |
|         reqPacket.addFrame(reqFrame1)
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.lsnrThr)
 | |
|             self.awaitAndHandleResponse(disable = False)
 | |
|         except PHPDBGConnException:
 | |
|             self.currConnFinished()
 | |
|             return None
 | |
| 
 | |
|         evalStr = PHPDBGEvalString(stack, self.evalRet)
 | |
| 
 | |
|         return evalStr.getVars()
 | |
| 
 | |
|     def getBPUnderHit(self):
 | |
|         for bp in self.bpDict.values():
 | |
|             if bp.isUnderHit():
 | |
|                 return bp
 | |
| 
 | |
|         return None
 | |
| 
 | |
|     def getRawFrameData(self, frameNo):
 | |
|         if self.rawDataDict.has_key(frameNo):
 | |
|             #
 | |
|             # Once the frameData is consumed, remove it from rawDataDict.
 | |
|             #
 | |
|             return self.rawDataDict.pop(frameNo)
 | |
|         else:
 | |
|             #
 | |
|             # TODO: do we need to handle the case when the raw frame data hasn't
 | |
|             # been received before?
 | |
|             #
 | |
|             return None
 | |
| 
 | |
|     def getModByNum(self, modNum):
 | |
|         if self.modDict.has_key(modNum):
 | |
|             return self.modDict[modNum]
 | |
|         else:
 | |
|             return None
 | |
| 
 | |
|     def getModByFileName(self, fileName):
 | |
|         for mn, fn in self.modDict.iteritems():
 | |
|             if fn == fileName:
 | |
|                 return mn
 | |
| 
 | |
|         return 0
 | |
| 
 | |
|     def setMod(self, modNum, fileName):
 | |
|         if modNum != 0 and fileName:
 | |
|             self.modDict[modNum] = fileName
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def readResponse(self, headeri = None, blockingi = False):
 | |
|         inHeader        = headeri
 | |
|         header          = None
 | |
|         cmdReceived     = 0
 | |
|         isFirstPacket   = True
 | |
|         blocking        = blockingi
 | |
|         self.isErrStack = False
 | |
|         self.rawDataDict.clear()
 | |
| 
 | |
|         while True:
 | |
|             #
 | |
|             # If we have already received the first packet, we can't block any
 | |
|             # more.
 | |
|             #
 | |
|             if not isFirstPacket:
 | |
|                 blocking = False
 | |
| 
 | |
|             #
 | |
|             # If this is the first loop and we have a non-empty header passed in, use it.  Otherwise,
 | |
|             # read in a new header.  For subsequent loops, inHeader is None so we always read a new
 | |
|             # header from the wire.
 | |
|             #
 | |
|             if inHeader:
 | |
|                 header   = inHeader
 | |
|                 inHeader = None
 | |
|             else:
 | |
|                 header   = ResponseHeader(self.lsnrThr, blocking)
 | |
| 
 | |
|             if not header.isValid:
 | |
|                 return 0
 | |
| 
 | |
|             cmdReceived = header.command
 | |
|             frame       = ResponsePacketFrame(self.lsnrThr, header.toRead, None, blocking)
 | |
|             if not frame.isValid:
 | |
|                 return 0
 | |
| 
 | |
|             isFirstPacket     = False
 | |
|             isFirstStackFrame = True
 | |
|             while frame and frame.isValid:
 | |
|                 frameName = frame.frameName
 | |
|                 if frameName == FRAME_STACK:
 | |
|                     if self.isErrStack:
 | |
|                         self.errStackList = self.handleRespFrameStack(self.errStackList, frame, isFirstStackFrame)
 | |
|                     else:
 | |
|                         self.stackList = self.handleRespFrameStack(self.stackList, frame, isFirstStackFrame)
 | |
| 
 | |
|                     if isFirstStackFrame:
 | |
|                         isFirstStackFrame = False
 | |
|                 elif frameName == FRAME_SOURCE:
 | |
|                     self.handleRespFrameSource(frame)
 | |
|                 elif frameName == FRAME_SRC_TREE:
 | |
|                     self.handleRespFrameSrcTree(frame)
 | |
|                 elif frameName == FRAME_RAWDATA:
 | |
|                     self.handleRespFrameRawdata(frame)
 | |
|                 elif frameName == FRAME_ERROR:
 | |
|                     self.handleRespFrameError(frame)
 | |
|                 elif frameName == FRAME_EVAL:
 | |
|                     self.handleRespFrameEval(frame)
 | |
|                 elif frameName == FRAME_BPS:
 | |
|                     self.handleRespFrameBps(frame)
 | |
|                 elif frameName == FRAME_BPL:
 | |
|                     self.handleRespFrameBpl(frame)
 | |
|                 elif frameName == FRAME_VER:
 | |
|                     self.handleRespFrameVer(frame)
 | |
|                 elif frameName == FRAME_SID:
 | |
|                     self.handleRespFrameSid(frame)
 | |
|                 elif frameName == FRAME_SRCLINESINFO:
 | |
|                     self.handleRespFrameSrclinesinfo(frame)
 | |
|                 elif frameName == FRAME_SRCCTXINFO:
 | |
|                     self.handleRespFrameSrcctxinfo(frame)
 | |
|                 elif frameName == FRAME_LOG:
 | |
|                     self.handleRespFrameLog(frame)
 | |
|                 elif frameName == FRAME_PROF:
 | |
|                     self.handleRespFrameProf(frame)
 | |
|                 elif frameName == FRAME_PROF_C:
 | |
|                     self.handleRespFrameProfC(frame)
 | |
|                 elif frameName == FRAME_SET_OPT:
 | |
|                     self.handleRespFrameSetOpt(frame)
 | |
|                 else:
 | |
|                     self.handleRespFrameUnknown(frame)
 | |
|                     return 0
 | |
| 
 | |
|                 #
 | |
|                 # After handling of this frame, force frame to point to the
 | |
|                 # next one based on current frame's absolute size.
 | |
|                 #
 | |
|                 frame = frame.getNextFrame(True)
 | |
| 
 | |
|             if cmdReceived == DBGC_REPLY:
 | |
|                 self.handleRespCmdReply()
 | |
|             elif cmdReceived == DBGC_STARTUP:
 | |
|                 self.handleRespCmdStartup()
 | |
|             elif cmdReceived == DBGC_END:
 | |
|                 self.handleRespCmdEnd()
 | |
|             elif cmdReceived == DBGC_BREAKPOINT:
 | |
|                 self.handleRespCmdBreakpoint()
 | |
|                 cmdReceived = 0
 | |
|             elif cmdReceived == DBGC_STEPINTO_DONE:
 | |
|                 self.handleRespCmdStepintoDone()
 | |
|             elif cmdReceived == DBGC_STEPOVER_DONE:
 | |
|                 self.handleRespCmdStepoverDone()
 | |
|             elif cmdReceived == DBGC_STEPOUT_DONE:
 | |
|                 self.handleRespCmdStepoutDone()
 | |
|             elif cmdReceived == DBGC_EMBEDDED_BREAK:
 | |
|                 self.handleRespCmdEmbeddedBreak()
 | |
|             elif cmdReceived == DBGC_PAUSE:
 | |
|                 self.handleRespCmdPause()
 | |
|             elif cmdReceived == DBGC_ERROR:
 | |
|                 self.handleRespCmdError()
 | |
|             elif cmdReceived == DBGC_LOG:
 | |
|                 self.handleRespCmdLog()
 | |
|             elif cmdReceived == DBGC_SID:
 | |
|                 self.handleRespCmdSid()
 | |
|             else:
 | |
|                 self.handleRespCmdUnknown()
 | |
| 
 | |
|         return cmdReceived
 | |
| 
 | |
|     def handleRespFrameStack(self, stackList, frame, isFirst):
 | |
|         if isFirst:
 | |
|             stackList            = []
 | |
|             self.stackFrameIndex = 0
 | |
| 
 | |
|         lineNo  = frame.getNextInt()
 | |
|         modNo   = frame.getNextInt()
 | |
|         scopeId = frame.getNextInt()
 | |
|         frameId = frame.getNextInt()
 | |
|         if modNo != 0:
 | |
|             newStackFrame = PHPStackFrame(self, self.getModByNum(modNo), lineNo, self.stackFrameIndex, scopeId, self.getRawFrameData(frameId), modNo)
 | |
|             stackList.append(newStackFrame)
 | |
|             self.stackFrameIndex = self.stackFrameIndex + 1
 | |
| 
 | |
|         return stackList
 | |
| 
 | |
|     def handleRespFrameSource(self, frame):
 | |
|         modNo           = frame.getNextInt()
 | |
|         fromFilePos     = frame.getNextInt()
 | |
|         error           = frame.getNextInt()
 | |
|         fullSize        = frame.getNextInt()
 | |
|         fileNameFrameId = frame.getNextInt()
 | |
|         textFrameId     = frame.getNextInt()
 | |
| 
 | |
|         fileName = self.getModByNum(modNo)
 | |
|         if not fileName:
 | |
|             self.setFileMod(modNo, fileNameFrameId)
 | |
| 
 | |
|         #
 | |
|         # TODO: fullSize string and textFrameId are not handled here.
 | |
|         #
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameSrcTree(self, frame):
 | |
|         parentModNo     = frame.getNextInt()
 | |
|         parentLineNo    = frame.getNextInt()
 | |
|         modNo           = frame.getNextInt()
 | |
|         fileNameFrameId = frame.getNextInt()
 | |
| 
 | |
|         fileName = self.getModByNum(modNo)
 | |
|         if not fileName:
 | |
|             self.setFileMod(modNo, fileNameFrameId)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameRawdata(self, frame):
 | |
|         frameNo = frame.getNextInt()
 | |
|         if frameNo > 0:
 | |
|             toRead = frame.getNextInt()
 | |
|             if toRead > 0:
 | |
|                 str = frame.getNextString(toRead)
 | |
|                 self.rawDataDict[frameNo] = str
 | |
|                 myprint("handleRespFrameRawdata(): added \'%d\'=\'%s\' to rawDataDict.", (frameNo, str))
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameError(self, frame):
 | |
|         self.isErrStack = True
 | |
| 
 | |
|         #
 | |
|         # Type of the error.
 | |
|         #
 | |
|         errInt0         = frame.getNextInt()
 | |
|         #
 | |
|         # ID of error message.
 | |
|         #
 | |
|         errInt1         = frame.getNextInt()
 | |
| 
 | |
|         if errInt0 == E_ERROR:
 | |
|             errIdStr = "[Error]"
 | |
|         elif errInt0 == E_WARNING:
 | |
|             errIdStr = "[Warning]"
 | |
|         elif errInt0 == E_PARSE:
 | |
|             errIdStr = "[Parse Error]"
 | |
|         elif errInt0 == E_NOTICE:
 | |
|             errIdStr = "[Notice]"
 | |
|         elif errInt0 == E_CORE_ERROR:
 | |
|             errIdStr = "[Core Error]"
 | |
|         elif errInt0 == E_CORE_WARNING:
 | |
|             errIdStr = "[Core Warning]"
 | |
|         elif errInt0 == E_COMPILE_ERROR:
 | |
|             errIdStr = "[Compile Error]"
 | |
|         elif errInt0 == E_COMPILE_WARNING:
 | |
|             errIdStr = "[Compile Warning]"
 | |
|         elif errInt0 == E_USER_ERROR:
 | |
|             errIdStr = "[User Error]"
 | |
|         elif errInt0 == E_USER_WARNING:
 | |
|             errIdStr = "[User Warning]"
 | |
|         elif errInt0 == E_USER_NOTICE:
 | |
|             errIdStr = "[User Notice]"
 | |
|         else:
 | |
|             errIdStr = "[Unexpected Error]"
 | |
| 
 | |
|         errMsg = self.getRawFrameData(errInt1)
 | |
|         if errMsg and len(errMsg) > 0:
 | |
|             self.errStr = errIdStr + ": " + errMsg + "\n"
 | |
|         else:
 | |
|             self.errStr = errIdStr + ": <Invalid Error Message>\n"
 | |
| 
 | |
|         if not self.stopOnError:
 | |
|             if self.lastCommand == DBGA_CONTINUE:
 | |
|                 self.Continue()
 | |
|             elif self.lastCommand == DBGA_STEPINTO:
 | |
|                 self.SingleStep()
 | |
|             elif self.lastCommand == DBGA_STEPOUT:
 | |
|                 self.Return()
 | |
|             elif self.lastCommand == DBGA_STEPOVER:
 | |
|                 self.Next()
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameEval(self, frame):
 | |
|         evalInt0        = frame.getNextInt()
 | |
|         evalInt1        = frame.getNextInt()
 | |
|         evalInt2        = frame.getNextInt()
 | |
|         self.evalRet    = self.getRawFrameData(evalInt1)
 | |
|         #TODO: is the following necessary?
 | |
|         evalStr         = self.getRawFrameData(evalInt0)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameBps(self, frame):
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameBpl(self, frame):
 | |
|         #
 | |
|         # Get this breakpoint.
 | |
|         #
 | |
|         dbgBp = []
 | |
|         for i in range(10):
 | |
|             dbgBp.append(frame.getNextInt())
 | |
| 
 | |
|         if dbgBp[2] != 0:
 | |
|             #
 | |
|             # If filename is sent, get it from the rawDataDict.
 | |
|             #
 | |
|             fileName = self.getRawFrameData(dbgBp[2])
 | |
|             if not fileName:
 | |
|                 return
 | |
| 
 | |
|             #
 | |
|             # If this filename comes with a mod number, store this
 | |
|             # modNum/fileName into this session's modDict.  Notice it might
 | |
|             # overwrite previous value.
 | |
|             #
 | |
|             if dbgBp[0] != 0:
 | |
|                 self.setMod(dbgBp[0], fileName)
 | |
|         elif dbgBp[0] != 0:
 | |
|             #
 | |
|             # Use modNum to get the fileName.
 | |
|             #
 | |
|             fileName = self.getModByNum(dbgBp[0])
 | |
|             if not fileName:
 | |
|                 return
 | |
|         else:
 | |
|             #
 | |
|             # Couldn't get the filename; nothing we can do with.
 | |
|             #
 | |
|             return
 | |
| 
 | |
|         bpKey = self.createBpKey(fileName, dbgBp[1])
 | |
|         if not self.bpDict.has_key(bpKey):
 | |
|             #
 | |
|             # Not in our bp list?  Anyway, create one for it.
 | |
|             #
 | |
|             ourBp              = BreakPoint(self, fileName, dbgBp[1], dbgBp[0], dbgBp[3], dbgBp[4], dbgBp[5], dbgBp[6], dbgBp[7], dbgBp[8], dbgBp[9])
 | |
|             self.bpDict[bpKey] = ourBp
 | |
|             newlyCreated       = True
 | |
|         else:
 | |
|             ourBp              = self.bpDict[bpKey]
 | |
|             newlyCreated       = False
 | |
| 
 | |
|         #
 | |
|         # Update with the latest bp information.
 | |
|         #
 | |
|         if not newlyCreated:
 | |
|             ourBp.update(dbgBp)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameVer(self, frame):
 | |
|         self.verMajor = frame.getNextInt()
 | |
|         self.verMinor = frame.getNextInt()
 | |
|         verFrameNo    = frame.getNextInt()
 | |
|         self.verDesc  = self.getRawFrameData(verFrameNo)
 | |
|         myprint("respFrameVer: verMajor=%s, verMinor=%s, verDesc=%s", (repr(self.verMajor), repr(self.verMinor), repr(self.verDesc)))
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameSid(self, frame):
 | |
|         self.sessID   = frame.getNextInt()
 | |
|         self.sessType = frame.getNextInt()
 | |
|         myprint("respFrameSid: sessID=%s, sessType=%s", (self.sessID, self.sessType))
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameSrclinesinfo(self, frame):
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameSrcctxinfo(self, frame):
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameLog(self, frame):
 | |
|         #
 | |
|         # TODO:
 | |
|         # Now we don't do much here besides following the protocol to retrieve
 | |
|         # the data.
 | |
|         #
 | |
|         logId           = frame.getNextInt()
 | |
|         logType         = frame.getNextInt()
 | |
|         modNo           = frame.getNextInt()
 | |
|         lineNo          = frame.getNextInt()
 | |
|         fileNameFrameId = frame.getNextInt()
 | |
|         extInfo         = frame.getNextInt()
 | |
| 
 | |
|         fileName = self.getModByNum(modNo)
 | |
|         if not fileName:
 | |
|             self.setFileMod(modNo, fileNameFrameId)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameProf(self, frame):
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameProfC(self, frame):
 | |
|         return
 | |
| 
 | |
|     def handleRespFrameSetOpt(self, frame):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdReply(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdStartup(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdEnd(self):
 | |
|         self.sessEnded = True
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdBreakpoint(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdStepintoDone(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdStepoverDone(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdStepoutDone(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdEmbeddedBreak(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdPause(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdError(self):
 | |
|         self.stackList = []
 | |
| 
 | |
|         if len(self.errStackList) > 0:
 | |
|             self.errStr = self.errStr + "Stack Trace:\n"
 | |
| 
 | |
|         while len(self.errStackList) > 0:
 | |
|             oneStack = self.errStackList.pop()
 | |
|             self.errStr = self.errStr + "%s\n" % oneStack.getLongDisplayStr()
 | |
| 
 | |
|         self.ui.showErrorDialog(self.errStr, "PHP Error")
 | |
|         myprint("Got PHP Error:\n%s", self.errStr)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdLog(self):
 | |
|         return
 | |
| 
 | |
|     def handleRespCmdSid(self):
 | |
|         return
 | |
| 
 | |
|     def setFileMod(self, modNo, fileNameFrameId):
 | |
|         if fileNameFrameId != 0:
 | |
|             fileName = self.getRawFrameData(fileNameFrameId)
 | |
|             if fileName and modNo != 0:
 | |
|                 self.setMod(modNo, fileName)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def createBpKey(self, fileName, lineNo):
 | |
|         #
 | |
|         # This is to work around a bug in dbg module where it changes the path
 | |
|         # names that we pass to it to lower cases.
 | |
|         #
 | |
|         if sysutils.isWindows():
 | |
|             fileName = fileName.lower()
 | |
| 
 | |
|         return (fileName, lineNo)
 | |
| 
 | |
|     def setLsnrAction(self, actioni):
 | |
|         self.lsnrAction = actioni
 | |
|         return
 | |
| 
 | |
|     def getLsnrAction(self):
 | |
|         return self.lsnrAction
 | |
| 
 | |
|     def currConnFinished(self):
 | |
|         self.clearInternals()
 | |
|         self.setLsnrAction(PHPDebuggerCallback.ACTION_LISTEN)
 | |
|         self.actionEvent.set()
 | |
|         self.ui.DisableAfterStop()
 | |
| 
 | |
|     def stopPhpDbg(self):
 | |
|         #
 | |
|         # TODO: should send a request to stop the current running PHP program.
 | |
|         #       should handle network blocking issue correctly, otherwise, we
 | |
|         #       might hang here.
 | |
|         #
 | |
|         reqPacket = PHPDBGPacket(DBGA_STOP)
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.lsnrThr)
 | |
|             self.awaitAndHandleResponse(stopping = True)
 | |
|         except PHPDBGConnException:
 | |
|             pass
 | |
| 
 | |
|         self.currConnFinished()
 | |
|         return
 | |
| 
 | |
|     def stopLsnr(self):
 | |
|         if not self.lsnrThr:
 | |
|             return
 | |
| 
 | |
|         #
 | |
|         # Then we try to stop our listener thread.
 | |
|         #
 | |
|         if self.lsnrThr.hasBeenConnected():
 | |
|             #
 | |
|             # If the listener thread has already accepted a connection from a
 | |
|             # php debug module/client, it is sleeping now and wait for this
 | |
|             # condition object to be set so that it can exit.
 | |
|             #
 | |
|             self.setLsnrAction(PHPDebuggerCallback.ACTION_STOP)
 | |
|             self.actionEvent.set()
 | |
|         else:
 | |
|             #
 | |
|             # If the listener thread has never been connected from a php debug
 | |
|             # module/client, it is still blocking on a accept() call.  We
 | |
|             # connect to it here and send a special shutdown command asking it
 | |
|             # to exit.
 | |
|             #
 | |
|             shutdownMessage = IntToC4(DBG_SYNC) + IntToC4(DBGC_AG_SHUTDOWN_REQ) + IntToC4(0) + IntToC4(0)
 | |
|             tempSocket      = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | |
|             try:
 | |
|                 tempSocket.connect((self.lsnrHost, self.lsnrPort))
 | |
|                 tempSocket.sendall(shutdownMessage)
 | |
|             except:
 | |
|                 myprint("shutdown connection/send message got exception!")
 | |
| 
 | |
|             tempSocket.close()
 | |
| 
 | |
|         self.lsnrThr.join()
 | |
|         self.lsnrThr = None
 | |
| 
 | |
| 
 | |
| class PHPDBGLsnrThr(threading.Thread):
 | |
|     def __init__(self, interfacei, hosti, porti, actionEventi, uii):
 | |
|         threading.Thread.__init__(self)
 | |
|         self.interface          = interfacei
 | |
|         self.svrHost            = hosti
 | |
|         self.svrPort            = porti
 | |
|         self.actionEvent        = actionEventi
 | |
|         self.svrSocket          = None
 | |
|         self.clntConn           = None
 | |
|         self.clntAddr           = None
 | |
|         self.nonBlockingTimeout = 1
 | |
|         self.connHeader         = None
 | |
|         self.ui                 = uii
 | |
| 
 | |
|     def initSvrSocket(self):
 | |
|         self.svrSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | |
|         self.svrSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 | |
|         self.svrSocket.bind((self.svrHost, self.svrPort))
 | |
| 
 | |
|     def waitForClntConn(self):
 | |
|         self.svrSocket.listen(5)
 | |
|         self.clntConn, self.clntAddr = self.svrSocket.accept()
 | |
|         self.clntConn.settimeout(self.nonBlockingTimeout)
 | |
| 
 | |
|     def run(self):
 | |
|         #
 | |
|         # Initialize this server socket.
 | |
|         #
 | |
|         self.initSvrSocket()
 | |
| 
 | |
|         while True:
 | |
|             #
 | |
|             # Block until we get a new connection from a php debug client or our
 | |
|             # debugger ui (with a special shutting down header/command).
 | |
|             #
 | |
|             self.waitForClntConn()
 | |
|     
 | |
|             #
 | |
|             # Ok, a new connection comes in ...  Read the header to see where it
 | |
|             # comes from.
 | |
|             #
 | |
|             self.connHeader = ResponseHeader(self)
 | |
|             if self.connHeader.command == DBGC_AG_SHUTDOWN_REQ:
 | |
|                 #
 | |
|                 # This is a special command coming from our UI asking this
 | |
|                 # thread to exit.  This only happens if after this thread has
 | |
|                 # been waiting for new connections from PHP debug module, no one
 | |
|                 # connects, and UI is ready to shutdown this thread.
 | |
|                 #
 | |
|                 self.shutdown()
 | |
|                 break
 | |
|             else:
 | |
|                 #
 | |
|                 # Tell the main gui thread to handle this new connection.
 | |
|                 #
 | |
|                 wx.CallAfter(self.interface.newConnEventHandler)
 | |
|     
 | |
|                 #
 | |
|                 # From now on, PHPDebuggerCallback will communicate with the php
 | |
|                 # debug module using this thread's clntConn socket.  This thread
 | |
|                 # itself will keep sleeping until get notified to make some
 | |
|                 # actions.
 | |
|                 #
 | |
|                 self.actionEvent.wait()
 | |
|                 self.actionEvent.clear()
 | |
| 
 | |
|                 action = self.interface.getLsnrAction()
 | |
|                 if action == PHPDebuggerCallback.ACTION_STOP:
 | |
|                     self.shutdown()
 | |
|                     break
 | |
|                 elif action == PHPDebuggerCallback.ACTION_LISTEN:
 | |
|                     if self.clntConn:
 | |
|                         self.clntConn.shutdown(socket.SHUT_RDWR)
 | |
|                         self.clntConn.close()
 | |
|                         self.clntConn = None
 | |
| 
 | |
|                     continue
 | |
|                 else:
 | |
|                     continue
 | |
| 
 | |
|     def shutdown(self):
 | |
|         #
 | |
|         # Cleanup and ready to exit.
 | |
|         #
 | |
|         self.clntConn.shutdown(socket.SHUT_RDWR)
 | |
|         self.clntConn.close()
 | |
|         self.svrSocket.close()
 | |
| 
 | |
|     def recv(self, size, blocking = False):
 | |
|         if self.clntConn:
 | |
|             myprint("recv: trying to receive %d bytes of data ...", size)
 | |
|             if blocking:
 | |
|                 self.clntConn.settimeout(None)
 | |
|             else:
 | |
|                 self.clntConn.settimeout(self.nonBlockingTimeout)
 | |
| 
 | |
|             try:
 | |
|                 rData = self.clntConn.recv(size)
 | |
|             except socket.timeout:
 | |
|                 myprint("recv: got timed out")
 | |
|                 rData = None
 | |
|             except:
 | |
|                 myprint("recv: got an unexpected exception: %s", sys.exc_info()[0])
 | |
|                 raise PHPDBGConnException
 | |
| 
 | |
|             return rData
 | |
|         else:
 | |
|             raise PHPDBGConnException
 | |
| 
 | |
|     def sendall(self, message):
 | |
|         if self.clntConn:
 | |
|             try:
 | |
|                 self.clntConn.sendall(message)
 | |
|             except:
 | |
|                 myprint("sendall: got an unexpected exception: %s", sys.exc_info()[0])
 | |
|                 raise PHPDBGConnException
 | |
|         else:
 | |
|             raise PHPDBGConnException
 | |
| 
 | |
|     def hasBeenConnected(self):
 | |
|         return self.clntConn != None
 | |
| 
 | |
|     def getConnHeader(self):
 | |
|         return self.connHeader
 | |
| 
 | |
| 
 | |
| class PHPValue(object):
 | |
|     PEV_NAMES           = ("undefined", "long", "double", "string", "array", "object", "boolean", "resource", "reference", "soft reference", "null")
 | |
|     PEVT_UNKNOWN        = 0
 | |
|     PEVT_LONG           = 1
 | |
|     PEVT_DOUBLE         = 2
 | |
|     PEVT_STRING         = 3
 | |
|     PEVT_ARRAY          = 4
 | |
|     PEVT_OBJECT         = 5
 | |
|     PEVT_BOOLEAN        = 6
 | |
|     PEVT_RESOURCE       = 7
 | |
|     PEVT_REF            = 8
 | |
|     PEVT_SOFTREF        = 9
 | |
|     PEVT_NULL           = 10
 | |
| 
 | |
|     NULL_VALUE_STR      = "NULL"
 | |
|     TRUE_VALUE_STR      = "True"
 | |
|     FALSE_VALUE_STR     = "False"
 | |
|     OBJECT_VALUE_STR    = "<%s> object"
 | |
|     STRING_VALUE_STR    = "\"%s\""
 | |
|     REFERENCE_VALUE_STR = "<reference><%s>"
 | |
|     RESOURCE_VALUE_STR  = "<%s><%s>"
 | |
| 
 | |
|     def __init__(self, frame, type, valueList):
 | |
|         self.fStackFrame = frame
 | |
|         self.fValueType  = type
 | |
| 
 | |
|         if type == self.PEVT_OBJECT:
 | |
|             self.fValueString = self.OBJECT_VALUE_STR % valueList[0]
 | |
|             self.fVariables   = valueList[1:]
 | |
|         elif type == self.PEVT_ARRAY:
 | |
|             self.fValueString = ''
 | |
|             self.fVariables   = valueList
 | |
|         else:
 | |
|             self.fVariables = []
 | |
|             if type == self.PEVT_STRING:
 | |
|                 self.fValueString = self.STRING_VALUE_STR % valueList[0]
 | |
|             elif type == self.PEVT_NULL:
 | |
|                 self.fValueString = self.NULL_VALUE_STR
 | |
|             elif type == self.PEVT_BOOLEAN:
 | |
|                 if valueList[0] == "0":
 | |
|                     self.fValueString = self.FALSE_VALUE_STR
 | |
|                 else:
 | |
|                     self.fValueString = self.TRUE_VALUE_STR
 | |
|             elif type == self.PEVT_REF or type == self.PEVT_SOFTREF:
 | |
|                 self.fValueString = self.REFERENCE_VALUE_STR % valueList[0]
 | |
|             elif type == self.PEVT_RESOURCE:
 | |
|                 self.fValueString = self.RESOURCE_VALUE_STR % (valueList[0], valueList[1])
 | |
|             else:
 | |
|                 self.fValueString = valueList[0]
 | |
| 
 | |
|     def addVariable(self, item):
 | |
|         if item != None:
 | |
|             self.fVariables.append(item)
 | |
| 
 | |
|         return self.fVariables
 | |
| 
 | |
|     def setParent(self, parent):
 | |
|         if self.fVariables != None and len(self.fVariables) > 0:
 | |
|             for item in self.fVariables:
 | |
|                 item.setParent(parent)
 | |
| 
 | |
|     def getReferenceType(self):
 | |
|         return self.fValueType
 | |
| 
 | |
|     def getReferenceTypeName(self):
 | |
|         return self.PEV_NAMES[self.fValueType]
 | |
| 
 | |
|     def setReferenceType(self, type):
 | |
|         self.fValueType = type
 | |
| 
 | |
|     def getValueString(self):
 | |
|         return self.fValueString
 | |
| 
 | |
|     def getChildrenVariables(self):
 | |
|         return self.fVariables
 | |
| 
 | |
|     def hasVariables(self):
 | |
|         return len(self.fVariables) > 0
 | |
| 
 | |
|     def childrenIsSortable(self):
 | |
|         #
 | |
|         # TODO: if self.fValueType != self.PEVT_ARRAY:
 | |
|         #
 | |
|         return True
 | |
| 
 | |
| 
 | |
| class PHPVariable(object):
 | |
|     def __init__(self, frame, parent, valueType, name, valueList):
 | |
|         self.fStackFrame = frame
 | |
|         self.fName       = None
 | |
|         self.fLongName   = None
 | |
|         self.fPureName   = None
 | |
|         self.fValue      = PHPValue(frame, valueType, valueList)
 | |
|         self.fParent     = parent
 | |
|         self.setName(name)
 | |
|         self.setChildrensParent(valueList)
 | |
| 
 | |
|     def setName(self, name):
 | |
|         self.fPureName = name
 | |
| 
 | |
|         type = self.getReferenceType()
 | |
|         if type == PHPValue.PEVT_ARRAY:
 | |
|             numItems = len(self.getChildrenVariables())
 | |
|             self.fName = name + "[" + str(numItems) + "]"
 | |
|         else:
 | |
|             self.fName = name
 | |
| 
 | |
|         if not self.fParent or self.fParent.getName() == None:
 | |
|             self.fLongName = name
 | |
|         else:
 | |
|             self.setLongName()
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def setLongName(self):
 | |
|         parentType = self.fParent.getReferenceType()
 | |
|         if parentType == PHPValue.PEVT_ARRAY:
 | |
|             self.fLongName = self.fParent.getLongName() + "['" + self.fPureName + "']"
 | |
|         elif parentType == PHPValue.PEVT_OBJECT:
 | |
|             self.fLongName = self.fParent.getLongName() + "." + self.fName
 | |
|         else:
 | |
|             self.fLongName = self.fName
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def getValue(self):
 | |
|         return self.fValue
 | |
| 
 | |
|     def getValueString(self):
 | |
|         return self.fValue.getValueString()
 | |
| 
 | |
|     def getChildrenVariables(self):
 | |
|         return self.fValue.getChildrenVariables()
 | |
| 
 | |
|     def getName(self):
 | |
|         return self.fName
 | |
| 
 | |
|     def getParent(self):
 | |
|         return self.fParent
 | |
| 
 | |
|     def setParent(self, parent):
 | |
|         self.fParent = parent
 | |
|         self.setLongName()
 | |
|         return
 | |
| 
 | |
|     def setChildrensParent(self, childrenList):
 | |
|         if self.fValue.hasVariables():
 | |
|             for child in self.fValue.getChildrenVariables():
 | |
|                 child.setParent(self)
 | |
| 
 | |
|     def getLongName(self):
 | |
|         return self.fLongName
 | |
| 
 | |
|     def getReferenceTypeName(self):
 | |
|         return self.fValue.getReferenceTypeName()
 | |
| 
 | |
|     def getReferenceType(self):
 | |
|         return self.fValue.getReferenceType()
 | |
| 
 | |
|     def setReferenceType(self, type):
 | |
|         tp = self.getValue.setReferenceType(type)
 | |
|         return tp
 | |
| 
 | |
|     def setValue(self, expression):
 | |
|         if self.fValue.getReferenceType() == PHPValue.PEVT_STRING:
 | |
|             evalString = self.fLongName + "=\"" + expression + "\""
 | |
|         else:
 | |
|             evalString = self.fLongName + "=" + expression
 | |
| 
 | |
|         vars = self.fStackFrame.getPHPDBGInterface().evalBlock(self.fStackFrame, evalString)
 | |
|         self.fValue = vars[0].fValue
 | |
| 
 | |
|     def toString(self):
 | |
|         rtype = self.getReferenceType()
 | |
|         if rtype == PHPValue.PEVT_ARRAY:
 | |
|             elements = len(self.fValue.getChildrenVariables())
 | |
|             if elements == 0:
 | |
|                 tmpStr = self.getName() + " [no elements]"
 | |
|             elif elements == 1:
 | |
|                 tmpStr = self.getName() + " [1 element]"
 | |
|             else:
 | |
|                 tmpStr = self.getName() + " [" + str(elements) + " elements]"
 | |
|         elif rtype == PHPValue.PEVT_OBJECT:
 | |
|             tmpStr = self.getName() + " [ class: " + self.fValue.getValueString() + "]"
 | |
|         elif rtype == PHPValue.PEVT_STRING:
 | |
|             tmpStr = self.getName() + " = \"" + self.fValue.getValueString() + "\""
 | |
|         else:
 | |
|             tmpStr = self.getName() + " = " + self.fValue.getValueString()
 | |
| 
 | |
|         return tmpStr
 | |
| 
 | |
|     def hasChildren(self):
 | |
|         return self.fValue.hasVariables()
 | |
| 
 | |
|     def childrenIsSortable(self):
 | |
|         return self.fValue.childrenIsSortable()
 | |
| 
 | |
| 
 | |
| class PHPStackFrame(object):
 | |
|     def __init__(self, interface, file, line, frameIndex, scopeId, desc, modNum):
 | |
|         self.interface      = interface
 | |
|         self.fileName       = file
 | |
|         self.lineNo         = line
 | |
|         self.frameIndex     = frameIndex
 | |
|         self.scopeId        = scopeId
 | |
|         self.desc           = desc
 | |
|         self.modNum         = modNum
 | |
|         self.variables      = []
 | |
|         self.shortFileName  = None
 | |
|         self.shortDesc      = None
 | |
|         self.displayStr     = None
 | |
|         self.longDisplayStr = None
 | |
| 
 | |
|         self._getFileNamesAndShortDesc()
 | |
| 
 | |
|         myprint("PHPStackFrame::__init__(): new PHPStackFrame: file=%s, lineNo=%s, frameIndex=%s, scopeId=%s, desc=%s, modNum=%s, shortFileName=%s, shortDesc=%s", (repr(file), repr(line), repr(frameIndex), repr(scopeId), repr(desc), repr(modNum), repr(self.shortFileName), repr(self.shortDesc)))
 | |
| 
 | |
|     def _getFileNamesAndShortDesc(self):
 | |
|         tmp = []
 | |
|         if self.desc:
 | |
|             tmp = self.desc.split("::")
 | |
| 
 | |
|         if self.fileName:
 | |
|             self.shortFileName = os.path.basename(self.fileName)
 | |
|             if len(tmp) == 2:
 | |
|                 self.shortDesc = tmp[1]
 | |
|             elif len(tmp) == 1:
 | |
|                 self.shortDesc = tmp[0]
 | |
|             else:
 | |
|                 self.shortDesc = None
 | |
| 
 | |
|             return
 | |
| 
 | |
|         #
 | |
|         # The fileName is None, we will try our best efforts to get it.
 | |
|         #
 | |
|         if len(tmp) == 2:
 | |
|             #
 | |
|             # We retrieved long finename from the description.  If we haven't
 | |
|             # stored the file mod before, set this one as the new one.
 | |
|             # Otherwise, we prefer to keep the stored one.
 | |
|             #
 | |
|             if self.modNum != 0:
 | |
|                 storedFileName = self.interface.getModByNum(self.modNum)
 | |
|                 if not storedFileName:
 | |
|                     self.interface.setMod(self.modNum, tmp[0])
 | |
| 
 | |
|             self.fileName      = tmp[0]
 | |
|             self.shortFileName = os.path.basename(tmp[0])
 | |
|             self.shortDesc     = tmp[1]
 | |
|         elif len(tmp) == 1:
 | |
|             self.fileName      = None
 | |
|             self.shortFileName = None
 | |
|             self.shortDesc     = tmp[0]
 | |
|         else:
 | |
|             self.shortFileName = None
 | |
|             self.shortDesc     = None
 | |
|             myprint("PHPStackFrame::_getFileNamesAndShortDesc(): something wrong with desc: %s?", self.desc)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def getShortFileName(self):
 | |
|         return self.shortFileName
 | |
| 
 | |
|     def setShortFileName(self, shortFileName):
 | |
|         self.shortFileName = shortFileName
 | |
| 
 | |
|     def getShortDesc(self):
 | |
|         return self.shortDesc
 | |
| 
 | |
|     def getLineNo(self):
 | |
|         return self.lineNo
 | |
| 
 | |
|     def getInterface(self):
 | |
|         return self.interface
 | |
| 
 | |
|     def getVariables(self):
 | |
|         if len(self.variables) == 0:
 | |
|             self.variables = self.interface.getVariables(self)
 | |
| 
 | |
|         return self.variables
 | |
| 
 | |
|     def findVariables(self, s):
 | |
|         if self.hasVariables():
 | |
|             name = "$" + s
 | |
|             for var in self.variables:
 | |
|                 if var.getName() == name:
 | |
|                     return var
 | |
| 
 | |
|         return None
 | |
| 
 | |
|     def hasVariables(self):
 | |
|         if len(self.variables) == 0:
 | |
|             return False
 | |
|         else:
 | |
|             return True
 | |
| 
 | |
|     def getName(self):
 | |
|         if self.getDesc():
 | |
|             return self.getDesc() + " [line: " + str(self.getLineNo()) + "]"
 | |
|         else:
 | |
|             return self.getFileName() + " [line: " + str(self.getLineNo()) + "]"
 | |
| 
 | |
|     def getFileName(self):
 | |
|         return self.fileName
 | |
| 
 | |
|     def setFileName(self, fileName):
 | |
|         self.fileName = fileName
 | |
| 
 | |
|     def setDesc(self, desc):
 | |
|         self.desc = desc
 | |
| 
 | |
|     def getDesc(self):
 | |
|         return self.desc
 | |
| 
 | |
|     def getFrameScopeId(self):
 | |
|         return self.scopeId
 | |
| 
 | |
|     def getFrameIndex(self):
 | |
|         return self.frameIndex
 | |
| 
 | |
|     def getDisplayStr(self, stackList = None):
 | |
|         if self.displayStr:
 | |
|             return self.displayStr
 | |
| 
 | |
|         if not self.shortFileName:
 | |
|             if stackList:
 | |
|                 i = stackList.index(self)
 | |
|                 for j in range(i + 1, len(stackList)):
 | |
|                     self.shortFileName = stackList[j].getShortFileName()
 | |
|                     if self.shortFileName:
 | |
|                         self.fileName = stackList[j].getFileName()
 | |
| 
 | |
|         if self.shortFileName:
 | |
|             if self.shortDesc:
 | |
|                 self.displayStr = "<%s> at %s:%d" % (self.shortDesc, self.shortFileName, self.lineNo)
 | |
|             else:
 | |
|                 self.displayStr = "%s:%d" % (self.shortFileName, self.lineNo)
 | |
|         else:
 | |
|             if self.shortDesc:
 | |
|                 self.displayStr = "<%s>" % self.shortDesc
 | |
|             else:
 | |
|                 self.displayStr = "<internal stack error>"
 | |
| 
 | |
|         return self.displayStr
 | |
| 
 | |
|     def getLongDisplayStr(self):
 | |
|         if self.longDisplayStr:
 | |
|             return self.longDisplayStr
 | |
| 
 | |
|         if self.fileName:
 | |
|             if self.shortDesc:
 | |
|                 self.longDisplayStr = "<%s> at %s:%d" % (self.shortDesc, self.fileName, self.lineNo)
 | |
|             else:
 | |
|                 self.longDisplayStr = "%s:%d" % (self.fileName, self.lineNo)
 | |
|         else:
 | |
|             if self.shortDesc:
 | |
|                 self.longDisplayStr = "<%s>" % self.shortDesc
 | |
|             else:
 | |
|                 self.longDisplayStr = "<internal stack error>"
 | |
| 
 | |
|         return self.longDisplayStr
 | |
| 
 | |
| class BreakPoint(object):
 | |
|     def __init__(self, interface, fileName, lineNo, modNum = 0, state = BPS_ENABLED + BPS_UNRESOLVED, isTemp = 0, hitCount = 0, skipHits = 0, condition = 0, bpId = 0, isUnderHit = 0):
 | |
|         self.interface  = interface
 | |
|         self.fileName   = fileName
 | |
|         self.lineNo     = lineNo
 | |
|         self.bpID       = bpId
 | |
|         self.state      = state
 | |
|         self.isTemp     = isTemp
 | |
|         self.hitCount   = hitCount
 | |
|         self.skipHits   = skipHits
 | |
|         self.condition  = condition
 | |
|         self.isUnderHit = 0
 | |
|         if modNum == 0:
 | |
|             self.modNum = self.interface.getModByFileName(fileName)
 | |
|         else:
 | |
|             self.modNum = modNum
 | |
| 
 | |
|         if self.modNum:
 | |
|             self.fCounterOrZero = 0
 | |
|         else:
 | |
|             self.fCounterOrZero = interface.getNextFrameCounter()
 | |
| 
 | |
|     def sendSelf(self):
 | |
|         reqPacket = PHPDBGPacket(DBGA_REQUEST)
 | |
|         reqFrame1 = PHPDBGFrame(FRAME_BPS)
 | |
| 
 | |
|         if self.modNum:
 | |
|             reqFrame1.addInt(self.modNum)
 | |
|         else:
 | |
|             #
 | |
|             # 0 in modNum to tell to use fileName instead.
 | |
|             #
 | |
|             reqFrame1.addInt(0)
 | |
| 
 | |
|         reqFrame1.addInt(self.lineNo)             # lineNo
 | |
|         reqFrame1.addInt(self.fCounterOrZero)     # fileName frameCounter or 0
 | |
|         reqFrame1.addInt(self.state)              # state
 | |
|         reqFrame1.addInt(self.isTemp)             # isTemp
 | |
|         reqFrame1.addInt(self.hitCount)           # hitCount
 | |
|         reqFrame1.addInt(self.skipHits)           # skipHits
 | |
|         reqFrame1.addInt(self.condition)          # condition
 | |
|         reqFrame1.addInt(self.bpID)               # breakpoint sequence id
 | |
|         reqFrame1.addInt(self.isUnderHit)         # isUnderHit
 | |
| 
 | |
|         if not self.modNum:
 | |
|             reqFrame2 = PHPDBGFrame(FRAME_RAWDATA)
 | |
|             reqFrame2.addInt(self.fCounterOrZero)
 | |
|             reqFrame2.addInt(len(self.fileName) + 1)
 | |
|             reqFrame2.addStr(self.fileName)
 | |
|             reqPacket.addFrame(reqFrame2)
 | |
| 
 | |
|         reqPacket.addFrame(reqFrame1)
 | |
| 
 | |
|         try:
 | |
|             reqPacket.sendPacket(self.interface.lsnrThr)
 | |
|             self.interface.awaitAndHandleResponse()
 | |
|         except PHPDBGConnException:
 | |
|             self.interface.currConnFinished()
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def addSelf(self):
 | |
|         self.sendSelf()
 | |
| 
 | |
|     def removeSelf(self):
 | |
|         self.state = BPS_DISABLED
 | |
|         self.sendSelf()
 | |
| 
 | |
|     def isUnderHit(self):
 | |
|         return self.isUnderHit == 1
 | |
| 
 | |
|     def update(self, dbgBp):
 | |
|         self.modNum     = dbgBp[0]
 | |
|         self.state      = dbgBp[3]
 | |
|         self.isTemp     = dbgBp[4]
 | |
|         self.hitCount   = dbgBp[5]
 | |
|         self.skipHits   = dbgBp[6]
 | |
|         self.condition  = dbgBp[7]
 | |
|         self.bpID       = dbgBp[8]
 | |
|         self.isUnderHit = dbgBp[9]
 | |
| 
 | |
| 
 | |
| class PHPDBGEvalString(object):
 | |
|     def __init__(self, stackFrame, dataStr):
 | |
|         self.stackFrame = stackFrame
 | |
|         self.dataStr    = dataStr
 | |
| 
 | |
|     #
 | |
|     # Get a list of variables under self.stackFrame.
 | |
|     #
 | |
|     def getVars(self):
 | |
|         return self.parseAVariable(isRealVar = False)
 | |
| 
 | |
|     #
 | |
|     # if isRealVar:
 | |
|     #    returnList[0] = The Variable
 | |
|     # else:
 | |
|     #    returnList    = list of variables.
 | |
|     #
 | |
|     def parseAVariable(self, isRealVar = True):
 | |
|         returnList = []
 | |
| 
 | |
|         #
 | |
|         # Get the variable name first.  Notice we ignore this entity's data
 | |
|         # type here.
 | |
|         #
 | |
|         if isRealVar:
 | |
|             nameEntity = self.parseAnEntity()
 | |
|             if not nameEntity or len(nameEntity) != 2 or type(nameEntity[1]) != str:
 | |
|                 myprint("PHPDBGEvalStr::parseAVariable() got a wrong name entity")
 | |
|                 return returnList
 | |
|             else:
 | |
|                 varName = nameEntity[1]
 | |
| 
 | |
|         #
 | |
|         # Get the variable's value.
 | |
|         #
 | |
|         valueEntity = self.parseAnEntity()
 | |
|         if not valueEntity or len(valueEntity) < 1:
 | |
|             myprint("PHPDBGEvalStr::parseAVariable(): couldn't get a variable's value entity.")
 | |
|             return returnList
 | |
| 
 | |
|         #
 | |
|         # This variable's data type.
 | |
|         #
 | |
|         varType = valueEntity[0]
 | |
| 
 | |
|         if isRealVar:
 | |
|             #
 | |
|             # If this is a real variable, return a list which contains only
 | |
|             # this variable item.
 | |
|             #
 | |
|             #valueEntity = valueEntity[1:]
 | |
|             variable    = PHPVariable(self.stackFrame, None, varType, varName, valueEntity[1:])
 | |
|             #myprint("xxxxCreated variable varName=%s, valueEntity=%s", (repr(varName), repr(valueEntity[1])))
 | |
|             myprint("xxxxCreated variable: %s", repr(variable.toString()))
 | |
|             returnList.append(variable)
 | |
|         else:
 | |
|             #
 | |
|             # If this is a root variable container, returns a list of
 | |
|             # variables under the root.  Do a sanity check here.
 | |
|             #
 | |
|             if valueEntity[0] != PHPValue.PEVT_ARRAY:
 | |
|                 myprint("PHPDBGEvalStr::parseAVariable(): failed to parse the root variable container.")
 | |
|             else:
 | |
|                 returnList = valueEntity[1:]
 | |
| 
 | |
|         return returnList
 | |
| 
 | |
|     #
 | |
|     # An entity could be a variable's name or its value.
 | |
|     #
 | |
|     # returnList[0]  = variable data type
 | |
|     # returnList[1:] = the real list
 | |
|     #
 | |
|     def parseAnEntity(self):
 | |
|         if not self.dataStr or len(self.dataStr) < 2  or (self.dataStr[1] != ':' and self.dataStr[1] != ';'):
 | |
|             myprint("PHPDBGEvalStr::parseAnEntity(): failed to parse %s.", repr(self.dataStr))
 | |
|             return None
 | |
| 
 | |
|         returnList    = []
 | |
|         typeChar      = self.dataStr[0]
 | |
|         self.dataStr  = self.dataStr[2:]
 | |
|         if typeChar   == 'i':
 | |
|             returnList.append(PHPValue.PEVT_LONG)
 | |
|             self.parseInt(returnList)
 | |
|         elif typeChar == 'a':
 | |
|             returnList.append(PHPValue.PEVT_ARRAY)
 | |
|             self.parseArray(returnList)
 | |
|         elif typeChar == 's':
 | |
|             returnList.append(PHPValue.PEVT_STRING)
 | |
|             self.parseString(returnList)
 | |
|         elif typeChar == 'O':
 | |
|             returnList.append(PHPValue.PEVT_OBJECT)
 | |
|             self.parseObject(returnList)
 | |
|         elif typeChar == 'r':
 | |
|             returnList.append(PHPValue.PEVT_SOFTREF)
 | |
|             self.parseReference(returnList, isSoftRef = True)
 | |
|         elif typeChar == 'R':
 | |
|             returnList.append(PHPValue.PEVT_REF)
 | |
|             self.parseReference(returnList, isSoftRef = False)
 | |
|         elif typeChar == 'b':
 | |
|             returnList.append(PHPValue.PEVT_BOOLEAN)
 | |
|             self.parseBoolean(returnList)
 | |
|         elif typeChar == 'd':
 | |
|             returnList.append(PHPValue.PEVT_DOUBLE)
 | |
|             self.parseDouble(returnList)
 | |
|         elif typeChar == 'z':
 | |
|             returnList.append(PHPValue.PEVT_RESOURCE)
 | |
|             self.parseResource(returnList)
 | |
|         elif typeChar == 'N':
 | |
|             returnList.append(PHPValue.PEVT_NULL)
 | |
|             self.parseNull(returnList)
 | |
|         else:
 | |
|             myprint("PHPDBGEvalStr::parseAnEntity(): unknown data type: %s", typeChar)
 | |
| 
 | |
|         return returnList
 | |
| 
 | |
|     def parseInt(self, returnList):
 | |
|         myprint("enter parseInt().")
 | |
|         returnList.append(self.getAnIntStr(';'))
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def parseArray(self, returnList):
 | |
|         myprint("enter parseArray().")
 | |
|         #
 | |
|         # The shortest array is 'a:0:{}'.
 | |
|         #
 | |
|         if len(self.dataStr) < 4:
 | |
|             myprint("PHPDBGEvalStr::parseArray(): failed (1) to parse an array: %s.", repr(self.dataStr))
 | |
|             return
 | |
| 
 | |
|         expectedNumItems = self.getAnInt(':')
 | |
|         if len(self.dataStr) < 2 or self.dataStr[0] != '{':
 | |
|             myprint("PHPDBGEvalStr::parseArray(): failed (3) to parse an array: %s.", repr(self.dataStr))
 | |
|             return
 | |
| 
 | |
|         self.dataStr = self.dataStr[1:]
 | |
|         varList      = []
 | |
|         while self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] != '}':
 | |
|             tmpList = self.parseAVariable()
 | |
|             if not tmpList or len(tmpList) != 1 or not tmpList[0]:
 | |
|                 myprint("PHPDBGEvalStr::parseArray(): failed (4) to parse an array.  dataStr=%s.", repr(self.dataStr))
 | |
|                 break
 | |
|             else:
 | |
|                 varList.append(tmpList[0])
 | |
| 
 | |
|         if expectedNumItems != len(varList):
 | |
|             myprint("PHPDBGEvalStr::parseArray(): failed (5) expected no. of items=%d, but got %d", (expectedNumItems, len(varList)))
 | |
| 
 | |
|         #
 | |
|         # An array should end with a '}'.
 | |
|         #
 | |
|         if self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] == '}':
 | |
|             self.dataStr = self.dataStr[1:]
 | |
|             returnList.extend(varList)
 | |
|         else:
 | |
|             myprint("PHPDBGEvalStr::parseArray(): failed (6) to parse an array.  dataStr=%s.", repr(self.dataStr))
 | |
| 
 | |
|         myprint("parseArray() ends.")
 | |
|         return
 | |
| 
 | |
|     def parseString(self, returnList, endChar = ';'):
 | |
|         myprint("enter parseString().")
 | |
|         #
 | |
|         # The shortest string is 's:<str_len>:"<str>"<endChar>'.
 | |
|         #
 | |
|         if len(self.dataStr) < 5:
 | |
|             myprint("PHPDBGEvalStr::parseString(): failed (1) to parse a string.  dataStr=%s.", repr(self.dataStr))
 | |
|             return
 | |
| 
 | |
|         expectedStrLen = self.getAnInt(':')
 | |
|         if len(self.dataStr) < expectedStrLen + 3 or self.dataStr[0] != '"':
 | |
|             myprint("PHPDBGEvalStr::parseString(): failed (3) to parse a string.  dataStr=%s.", repr(self.dataStr))
 | |
|             return
 | |
| 
 | |
|         strValue = self.dataStr[1:expectedStrLen + 1]
 | |
|         if self.dataStr[expectedStrLen + 1:expectedStrLen + 3] != '"' + endChar:
 | |
|             myprint("PHPDBGEvalStr::parseString(): failed (4) to parse a string.  dataStr=%s.", repr(self.dataStr))
 | |
|             return
 | |
| 
 | |
|         #
 | |
|         # Skip the starting double quote, real string, ending double quote, and ending semicolon.
 | |
|         #
 | |
|         self.dataStr = self.dataStr[expectedStrLen + 3:]
 | |
|         returnList.append(strValue)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def parseObject(self, returnList):
 | |
|         #
 | |
|         # A simple sanity check.  The shortest object is:
 | |
|         # 'O:<class_name_len>:"<class_name>":<num_of_items>:{<list_of_items>}'
 | |
|         #
 | |
|         if not self.dataStr or len(self.dataStr) < 10:
 | |
|             myprint("PHPDBGEvalStr::parseObject(): failed (1) to parse an object: %s.", repr(self.dataStr))
 | |
| 
 | |
|         #
 | |
|         # Get the class name in classNameList[0].
 | |
|         #
 | |
|         classNameList = []
 | |
|         self.parseString(classNameList, ':')
 | |
| 
 | |
|         expectedNumItems = self.getAnInt(':')
 | |
|         if len(self.dataStr) < 2 or self.dataStr[0] != '{':
 | |
|             myprint("PHPDBGEvalStr::parseObject(): failed (2) to parse an object: %s.", repr(self.dataStr))
 | |
|             return
 | |
| 
 | |
|         self.dataStr = self.dataStr[1:]
 | |
|         varList      = []
 | |
|         while self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] != '}':
 | |
|             tmpList = self.parseAVariable()
 | |
|             if not tmpList or len(tmpList) != 1 or not tmpList[0]:
 | |
|                 myprint("PHPDBGEvalStr::parseObject(): failed (3) to parse an object.  dataStr=%s.", repr(self.dataStr))
 | |
|                 break
 | |
|             else:
 | |
|                 varList.append(tmpList[0])
 | |
| 
 | |
|         if expectedNumItems != len(varList):
 | |
|             myprint("PHPDBGEvalStr::parseObject(): failed (4) expected no. of items=%d, but got %d", (expectedNumItems, len(varList)))
 | |
| 
 | |
|         #
 | |
|         # An object should end with a '}'.
 | |
|         #
 | |
|         if self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] == '}':
 | |
|             self.dataStr = self.dataStr[1:]
 | |
|             returnList.append(classNameList[0])
 | |
|             returnList.extend(varList)
 | |
|         else:
 | |
|             myprint("PHPDBGEvalStr::parseObject(): failed (6) to parse an object.  dataStr=%s.", repr(self.dataStr))
 | |
| 
 | |
|         myprint("parseObject() ends.")
 | |
|         return
 | |
| 
 | |
|     def parseReference(self, returnList, isSoftRef):
 | |
|         myprint("enter parseReference().")
 | |
|         intStr = self.getAnIntStr(';')
 | |
|         if intStr:
 | |
|             returnList.append(intStr)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def parseBoolean(self, returnList):
 | |
|         tmpBooleanStr = self.getAnIntStr(';')
 | |
|         returnList.append(tmpBooleanStr)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def parseDouble(self, returnList):
 | |
|         tmpStr = self.getAStrTillEndChar(';')
 | |
|         if tmpStr:
 | |
|             returnList.append(tmpStr)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def parseResource(self, returnList):
 | |
|         tmpList = []
 | |
|         self.parseString(tmpList, ':')
 | |
| 
 | |
|         if len(tmpList) == 1:
 | |
|             returnList.extend(tmpList)
 | |
|         else:
 | |
|             return
 | |
| 
 | |
|         resourceId = self.getAnIntStr(';')
 | |
|         if resourceId:
 | |
|             returnList.append(resourceId)
 | |
| 
 | |
|         return
 | |
| 
 | |
|     def parseNull(self, returnList):
 | |
|         return
 | |
| 
 | |
|     def getAStrTillEndChar(self, endChar):
 | |
|         if len(self.dataStr) < 1:
 | |
|             myprint("PHPDBGEvalStr::getAStrTillEndChar(): no more data string to work with.")
 | |
|             return
 | |
| 
 | |
|         i = self.findNextChar(self.dataStr, endChar)
 | |
|         if i == -1:
 | |
|             myprint("PHPDBGEvalStr::getAStrTillEndChar(): no double/float string supplied.")
 | |
|             return
 | |
| 
 | |
|         tmpStr       = self.dataStr[:i]
 | |
|         self.dataStr = self.dataStr[i + 1:]
 | |
| 
 | |
|         if self.isFloat(tmpStr):
 | |
|             return tmpStr
 | |
|         else:
 | |
|             myprint("PHPDBGEvalStr::getAStrTillEndChar(): parsing error.  tried to get an float number, but get %s.", tmpStr)
 | |
|             return None
 | |
| 
 | |
|     def getAnInt(self, endChar):
 | |
|         tmpStr = self.getAnIntStr(endChar)
 | |
|         if tmpStr:
 | |
|             return int(tmpStr)
 | |
|         else:
 | |
|             return 0
 | |
| 
 | |
|     def getAnIntStr(self, endChar):
 | |
|         if len(self.dataStr) == 0:
 | |
|             myprint("PHPDBGEvalStr::getAnIntStr(): no more data string to work with.")
 | |
|             return
 | |
| 
 | |
|         i = self.findNextChar(self.dataStr, endChar)
 | |
|         if i == -1:
 | |
|             tmpStr = self.dataStr
 | |
|             self.dataStr = ''
 | |
|         else:
 | |
|             tmpStr = self.dataStr[:i]
 | |
|             self.dataStr = self.dataStr[i + 1:]
 | |
| 
 | |
|         if self.isInt(tmpStr):
 | |
|             return tmpStr
 | |
|         else:
 | |
|             myprint("PHPDBGEvalStr::getAnIntStr(): parsing error.  tried to get an integer, but get %s.", tmpStr)
 | |
|             return None
 | |
| 
 | |
|     def isInt(self, aStr):
 | |
|         try:
 | |
|             int(aStr)
 | |
|         except ValueError:
 | |
|             return False
 | |
| 
 | |
|         return True
 | |
| 
 | |
|     def isFloat(self, aStr):
 | |
|         try:
 | |
|             float(aStr)
 | |
|         except ValueError:
 | |
|             return False
 | |
| 
 | |
|         return True
 | |
| 
 | |
|     def findNextChar(self, aStr, aChar):
 | |
|         try:
 | |
|             index = aStr.index(aChar)
 | |
|         except ValueError:
 | |
|             index = -1
 | |
| 
 | |
|         return index
 |