📄 phpdebugger.py
字号:
#---------------------------------------------------------------------------
# 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -