]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/PHPDebugger.py
DocView and ActiveGrid IDE updates from Morgan Hua:
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / PHPDebugger.py
1 #---------------------------------------------------------------------------
2 # Name: PHPDebugger.py
3 # Purpose: php dbg client and supporting code
4 # Author: Matt Fryer, Kevin Wang
5 # Created: 2/1/06
6 # Copyright: (c) 2006 ActiveGrid, Inc.
7 # License: wxWindows License
8 #---------------------------------------------------------------------------
9
10
11 import os
12 import socket
13 import sys
14 import threading
15 import traceback
16 import wx
17 import DebuggerService
18 import activegrid.util.sysutils as sysutils
19
20
21 DBGC_REPLY = 0x0 # reply to previous DBGA_REQUEST request
22 DBGC_STARTUP = 0x0001 # script startup
23 DBGC_END = 0x0002 # script done
24 DBGC_BREAKPOINT = 0x0003 # user definded breakpoint occured
25 DBGC_STEPINTO_DONE = 0x0004 # step to the next statement is completed
26 DBGC_STEPOVER_DONE = 0x0005 # step to the next statement is completed
27 DBGC_STEPOUT_DONE = 0x0006 # step to the next statement is completed
28 DBGC_EMBEDDED_BREAK = 0x0007 # breakpoint caused by DebugBreak() function
29 DBGC_ERROR = 0x0010 # error occured
30 DBGC_LOG = 0x0011 # logging support
31 DBGC_SID = 0x0012 # send SID
32 DBGC_PAUSE = 0x0013 # pause current session as soon as possible
33 DBGC_AG_SHUTDOWN_REQ = 0x0201 # special ActiveGrid UI shutdown listening thread command
34
35
36 FRAME_STACK = 100000 # "call:stack" - e.g. backtrace
37 FRAME_SOURCE = 100100 # source text
38 FRAME_SRC_TREE = 100200 # tree of source files
39 FRAME_RAWDATA = 100300 # raw data or string
40 FRAME_ERROR = 100400 # error notification
41 FRAME_EVAL = 100500 # evaluating/watching
42 FRAME_BPS = 100600 # set/remove breakpoint
43 FRAME_BPL = 100700 # breakpoint(s) request = get the list
44 FRAME_VER = 100800 # version request
45 FRAME_SID = 100900 # session id info
46 FRAME_SRCLINESINFO = 101000 # source lines info
47 FRAME_SRCCTXINFO = 101100 # source contexts info
48 FRAME_LOG = 101200 # logging
49 FRAME_PROF = 101300 # profiler
50 FRAME_PROF_C = 101400 # profiler counter/accuracy
51 FRAME_SET_OPT = 101500 # set/update options
52
53
54 DBGF_STARTED = 0x0001 # debugger has been started
55 DBGF_FINISHED = 0x0002 # DBGC_END notification has been sent
56 DBGF_WAITACK = 0x0004 # awaiting replay|request
57 DBGF_UNSYNC = 0x0008 # protocol has been unsynchronized
58 DBGF_REQUESTPENDING = 0x0010 # Debug session request pending
59 DBGF_REQUESTFOUND = 0x0020 # Debug session request found
60 DBGF_REJECTIONFOUND = 0x0040 # DBGSESSID=-1 found - session rejection
61
62
63 E_ERROR = 1 << 0
64 E_WARNING = 1 << 1
65 E_PARSE = 1 << 2
66 E_NOTICE = 1 << 3
67 E_CORE_ERROR = 1 << 4
68 E_CORE_WARNING = 1 << 5
69 E_COMPILE_ERROR = 1 << 6
70 E_COMPILE_WARNING = 1 << 7
71 E_USER_ERROR = 1 << 8
72 E_USER_WARNING = 1 << 9
73 E_USER_NOTICE = 1 << 10
74
75
76 BPS_DELETED = 0
77 BPS_DISABLED = 1
78 BPS_ENABLED = 2
79 BPS_UNRESOLVED = 0x100
80
81
82 DBG_SYNC = 0x5953
83 DBG_SYNC2_STR = chr(0) + chr(0) + chr(89) + chr(83)
84 RESPONSE_HEADER_SIZE = 16
85
86
87 _VERBOSE = False
88 def myprint(format, vlist=None):
89 if _VERBOSE:
90 if vlist:
91 print format % vlist
92 else:
93 print format
94
95
96 #
97 # 4 Char's to an Integer
98 #
99 def C4ToInt(ch, startPos):
100 retval = 0
101 pos = startPos
102
103 retval = retval + (CharToInt(ch[pos]) << 24)
104 pos = pos + 1
105 retval = retval + (CharToInt(ch[pos]) << 16)
106 pos = pos + 1
107 retval = retval + (CharToInt(ch[pos]) << 8)
108 pos = pos + 1
109 retval = retval + (CharToInt(ch[pos]) << 0)
110
111 return retval
112
113
114 def CharToInt(ch):
115 return int((ord(ch) & 0x00FF));
116
117
118 #
119 # An Integer to 4 Char's
120 #
121 def IntToC4(num):
122 retval = chr((num >> 24) & 0x00FF)
123 retval += chr((num >> 16) & 0x00FF)
124 retval += chr((num >> 8 ) & 0x00FF)
125 retval += chr((num >> 0 ) & 0x00FF)
126
127 return retval
128
129
130 DBGA_CONTINUE = IntToC4(0x8001)
131 DBGA_STOP = IntToC4(0x8002)
132 DBGA_STEPINTO = IntToC4(0x8003)
133 DBGA_STEPOVER = IntToC4(0x8004)
134 DBGA_STEPOUT = IntToC4(0x8005)
135 DBGA_IGNORE = IntToC4(0x8006)
136 DBGA_REQUEST = IntToC4(0x8010)
137
138
139 def getCommandString(code):
140 if code == DBGC_REPLY:
141 return "REPLY"
142 elif code == DBGC_STARTUP:
143 return "STARTUP"
144 elif code == DBGC_END:
145 return "END"
146 elif code == DBGC_BREAKPOINT:
147 return "BREAKPOINT"
148 elif code == DBGC_STEPINTO_DONE:
149 return "STEPINTO DONE"
150 elif code == DBGC_STEPOVER_DONE:
151 return "STEPOVER DONE"
152 elif code == DBGC_STEPOUT_DONE:
153 return "STEPOUT DONE"
154 elif code == DBGC_EMBEDDED_BREAK:
155 return "EMBEDDED BREAK"
156 elif code == DBGC_PAUSE:
157 return "PAUSE"
158 elif code == DBGC_ERROR:
159 return "ERROR"
160 elif code == DBGC_LOG:
161 return "LOG"
162 elif code == DBGC_SID:
163 return "SEND SID"
164 elif code == DBGC_AG_SHUTDOWN_REQ:
165 return "AG SHUTDOWN REQ"
166
167
168 def reportFlags(flagsValue):
169 flagsRetVal = ""
170 if flagsValue & DBGF_STARTED: # debugger has been started
171 flagsRetVal += "started+"
172 if flagsValue & DBGF_FINISHED: # DBGC_END notification has been sent
173 flagsRetVal += "finished+"
174 if flagsValue & DBGF_WAITACK: # awaiting replay|request
175 flagsRetVal += "awaiting ack+"
176 if flagsValue & DBGF_UNSYNC: # protocol has been unsynchronized
177 flagsRetVal += "protocol unsynchronized+"
178 if flagsValue & DBGF_REQUESTPENDING: # Debug session request pending
179 flagsRetVal += "request pending+"
180 if flagsValue & DBGF_REQUESTFOUND: # Debug session request found
181 flagsRetVal += "request found+"
182 if flagsValue & DBGF_REJECTIONFOUND : # DBGSESSID=-1 found - session rejection
183 flagsRetVal += "session rejection+"
184 return flagsRetVal
185
186
187 def getErrorTypeString(code):
188 if code == E_ERROR:
189 return "[Error]"
190 elif code == E_WARNING:
191 return "[Warning]"
192 elif code == E_PARSE:
193 return "[Parse Error]"
194 elif code == E_NOTICE:
195 return "[Notice]"
196 elif code == E_CORE_ERROR:
197 return "[Core Error]"
198 elif code == E_CORE_WARNING:
199 return "[Core Warning]"
200 elif code == E_COMPILE_ERROR:
201 return "[Compile Error]"
202 elif code == E_COMPILE_WARNING:
203 return "[Compile Warning]"
204 elif code == E_USER_ERROR:
205 return "[User Error]"
206 elif code == E_USER_WARNING:
207 return "[User Warning]"
208 elif code == E_USER_NOTICE:
209 return "[User Notice]"
210 else:
211 return "[Unexpected Error]"
212
213
214 class ResponseHeader(object):
215 def __init__(self, conn, blocking = False):
216 self.isValid = False
217 receivedData = conn.recv(RESPONSE_HEADER_SIZE, blocking)
218
219 if not receivedData:
220 myprint("Tried to get %d bytes of PHP DBG header, got None\n" % RESPONSE_HEADER_SIZE)
221 return
222 elif len(receivedData) != RESPONSE_HEADER_SIZE:
223 myprint("Tried to get %d bytes of PHP DBG header, got %d\n" % (RESPONSE_HEADER_SIZE, len(receivedData)))
224 return
225
226 self.sync = C4ToInt(receivedData, 0)
227 self.command = C4ToInt(receivedData, 4)
228 self.flags = C4ToInt(receivedData, 8)
229 self.toRead = C4ToInt(receivedData, 12)
230
231 myprint("ResponseHeader: sync=%x, command=%s, flags=(%s), toRead=%s\n", (self.sync, getCommandString(self.command), reportFlags(self.flags), self.toRead))
232 if self.sync != DBG_SYNC:
233 myprint("Sync wrong for header! Expected %x, got %s\n" % (DBG_SYNC, self.sync))
234 return
235
236 self.isValid = True
237
238
239 class ResponsePacketFrame(object):
240 def __init__(self, conn, size, data, blocking = False):
241 self.isValid = False
242 self.conn = conn
243 self.data = ''
244
245 if data:
246 self.data = data
247 newlyReceived = False
248 elif conn:
249 newlyReceived = True
250
251 sizeToReceive = size
252 while True:
253 oneChunk = conn.recv(sizeToReceive, blocking)
254 sizeReceived = len(oneChunk)
255 if sizeReceived > 0:
256 self.data = self.data + oneChunk
257
258 if sizeReceived < sizeToReceive:
259 sizeToReceive = sizeToReceive - sizeReceived
260 continue
261 else:
262 break
263
264 if len(self.data) != size:
265 myprint("Expected to get %d bytes of a PHP DBG packet, got %d\n" % (size, len(self.data)))
266 return
267 else:
268 return
269
270 self.frameName = C4ToInt(self.data, 0)
271 self.frameSize = C4ToInt(self.data, 4)
272 if newlyReceived:
273 myprint("Newly received ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize))
274 else:
275 myprint("Created from existing ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize))
276
277 if self.frameSize == 0:
278 return
279
280 self.currPos = 8
281 self.totalDataLen = len(self.data)
282 self.length = 8 + self.frameSize
283 self.isValid = True
284 myprint("new ResponsePacketFrame: currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length)))
285 return
286
287 def getNextInt(self):
288 myprint("getNextInt(): currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length)))
289 if self.isValid and self.currPos + 4 <= self.length:
290 val = C4ToInt(self.data, self.currPos)
291 self.currPos = self.currPos + 4
292 myprint("getNextInt(): got an integar: %s", repr(val))
293 return val
294 else:
295 return self._errorReturn("getNextInt(): no more integar available with current frame: ")
296
297 def getNextString(self, strLen):
298 endPos = self.currPos + strLen
299 if self.isValid and endPos <= self.length:
300 #
301 # Trim the ending '\0'. TODO: confirm this applies to all raw string data.
302 #
303 str = self.data[self.currPos:endPos - 1]
304 self.currPos = endPos
305 myprint("getNextString(): got a string: %s", str)
306 return str
307 else:
308 return self._errorReturn("getNextString(): no more string available with current frame: ")
309
310 def getNextFrame(self, useAbsolutePos = False):
311 if useAbsolutePos:
312 #
313 # Skip this frame's header (8 bytes for frameSize and frameSize) and frame data (frameSize).
314 #
315 self.currPos = self.length
316
317 if self.isValid and self.currPos < self.totalDataLen:
318 return ResponsePacketFrame(None, None, self.data[self.currPos:])
319 else:
320 return self._errorReturn("getNextFrame(): no more frame available with current frame: ")
321
322 def _errorReturn(self, preMsg = ''):
323 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)))
324 self.isValid = False
325 return None
326
327
328 class PHPDBGFrame(object):
329 FRAME_HEADER_SIZE = 8
330 def __init__(self, frameType):
331 self.frameType = IntToC4(frameType)
332 self.frameData = ""
333
334 def addInt(self, intVal):
335 self.frameData = self.frameData + IntToC4(intVal)
336
337 def addChar(self, charVal):
338 self.frameData = self.frameData + charVal
339
340 def addStr(self, string):
341 #
342 # Add the trailing '\0'.
343 #
344 self.frameData = self.frameData + string + '\0'
345
346 def getSize(self):
347 return len(self.frameData) + PHPDBGFrame.FRAME_HEADER_SIZE
348
349 def writeFrame(self, conn):
350 header = self.frameType + IntToC4(len(self.frameData))
351 conn.sendall(header)
352 conn.sendall(self.frameData)
353
354
355 class PHPDBGPacket(object):
356 def __init__(self, packetType):
357 self.header = DBG_SYNC2_STR + packetType
358 self.frames = []
359 self.packetSize = 0
360
361 def addFrame(self, frame):
362 self.frames.append(frame)
363 self.packetSize += frame.getSize()
364
365 def sendPacket(self, conn, flags = 0):
366 self.header += IntToC4(flags)
367 self.header += IntToC4(self.packetSize)
368 conn.sendall(self.header)
369 for frame in self.frames:
370 frame.writeFrame(conn)
371
372
373 class PHPDBGException(Exception):
374 def __init__(self, msg = None, cause = None):
375 if (msg == None):
376 Exception.__init__(self)
377 elif (cause == None):
378 Exception.__init__(self, msg)
379 else:
380 Exception.__init__(self, "PHPDBGException: message:%s\n, cause:%s" % (msg, cause))
381
382
383 class PHPDBGConnException(PHPDBGException):
384 pass
385
386
387 class PHPDebuggerCallback(object):
388 ACTION_NONE = 0
389 ACTION_STOP = 1
390 ACTION_LISTEN = 2
391
392 def __init__(self, ui, service, lsnrHosti, lsnrPorti):
393 self.ui = ui
394 self.service = service
395 self.lsnrHost = lsnrHosti
396 self.lsnrPort = lsnrPorti
397 self.lsnrThr = None
398 self.lsnrAction = PHPDebuggerCallback.ACTION_NONE
399 self.clearInternals()
400 self.initLsnrThr()
401
402
403 ############################################################################
404 # Public callback functions begin
405 #
406 def Start(self):
407 self.lsnrThr.start()
408
409 def ShutdownServer(self, stopLsnr = True):
410 #
411 # First to tell php debugger to stop execution of the current PHP
412 # program. Disconnect with php dbg module too.
413 #
414 self.stopPhpDbg()
415
416 #
417 # Stop debug listener.
418 #
419 if stopLsnr:
420 self.stopLsnr()
421
422 def BreakExecution(self):
423 reqPacket = PHPDBGPacket(IntToC4(DBGC_PAUSE))
424
425 try:
426 reqPacket.sendPacket(self.lsnrThr)
427 self.awaitAndHandleResponse()
428 except PHPDBGConnException:
429 self.currConnFinished()
430 return
431
432 self.ui.LoadPHPFramesList(self.stackList)
433 return
434
435 def SingleStep(self):
436 reqPacket = PHPDBGPacket(DBGA_STEPINTO)
437
438 try:
439 reqPacket.sendPacket(self.lsnrThr)
440 self.lastCommand = DBGA_STEPINTO
441 self.awaitAndHandleResponse(blocking = True)
442 except PHPDBGConnException:
443 self.currConnFinished()
444 return
445
446 self.ui.LoadPHPFramesList(self.stackList)
447 return
448
449 def Next(self):
450 reqPacket = PHPDBGPacket(DBGA_STEPOVER)
451
452 try:
453 reqPacket.sendPacket(self.lsnrThr)
454 self.lastCommand = DBGA_STEPOVER
455 self.awaitAndHandleResponse(blocking = True)
456 except PHPDBGConnException:
457 self.currConnFinished()
458 return
459
460 self.ui.LoadPHPFramesList(self.stackList)
461 return
462
463 def Continue(self):
464 reqPacket = PHPDBGPacket(DBGA_CONTINUE)
465
466 try:
467 reqPacket.sendPacket(self.lsnrThr)
468 self.lastCommand = DBGA_CONTINUE
469 self.awaitAndHandleResponse(blocking = True)
470 except PHPDBGConnException:
471 self.currConnFinished()
472 return
473
474 self.ui.LoadPHPFramesList(self.stackList)
475 return
476
477 def Return(self):
478 reqPacket = PHPDBGPacket(DBGA_STEPOUT)
479
480 try:
481 reqPacket.sendPacket(self.lsnrThr)
482 self.lastCommand = DBGA_STEPOUT
483 self.awaitAndHandleResponse(blocking = True)
484 except PHPDBGConnException:
485 self.currConnFinished()
486 return
487
488 self.ui.LoadPHPFramesList(self.stackList)
489 return
490
491 def PushBreakpoints(self, noRemove = False):
492 tmpList = []
493 bps = self.service.GetMasterBreakpointDict()
494 for fileName in bps.keys():
495 if fileName.endswith('.php'):
496 lines = bps[fileName]
497 if lines:
498 for lineNo in lines:
499 if lineNo:
500 #
501 # A tuple (fileName, lineNo) is an item which is
502 # used as a key in self.bpDict.
503 #
504 tmpList.append(self.createBpKey(fileName, lineNo))
505 myprint("PushBreakpoints(): global breakpoint \'%s:%i\'", (fileName, lineNo))
506
507 #
508 # Check to see if we have any new breakpoints added.
509 #
510 for oneKey in tmpList:
511 if not self.bpDict.has_key(oneKey):
512 #
513 # A new breakpoint.
514 #
515 newBp = BreakPoint(self, oneKey[0], oneKey[1])
516 newBp.addSelf()
517 self.bpDict[oneKey] = newBp
518 myprint("PushBreakpoints(): newly added global breakpoint \'%s:%i\'", (oneKey[0], oneKey[1]))
519
520 if noRemove:
521 return
522
523 #
524 # Check to see if any bp that is in our list, but not in the latest
525 # global list. If so, it must have been removed recently in the
526 # global one. Remove it from our list and tell php debugger to do
527 # so as well.
528 #
529 toRemoveList = []
530 for oneKey in self.bpDict.keys():
531 if tmpList.count(oneKey) == 0:
532 toRemoveList.append((oneKey, self.bpDict[oneKey]))
533 myprint("PushBreakpoints(): recently removed global breakpoint \'%s:%i\'", (oneKey[0], oneKey[1]))
534
535 for bp in toRemoveList:
536 bp[1].removeSelf()
537 del self.bpDict[bp[0]]
538 myprint("PushBreakpoints(): successfully removed breakpoint \'%s:%i\' from both our local list and php debugger", (bp[0][0], bp[0][1]))
539
540 return
541 #
542 # Public callback functions end
543 ############################################################################
544
545
546 def newConnEventHandler(self):
547 #
548 # Ok, we've got a connection from the php debugger, and some initial
549 # frame data from it. Everything is ready and let's make some initial
550 # actions.
551 #
552 self.clearInternals()
553
554 try:
555 self.awaitAndHandleResponse(self.lsnrThr.getConnHeader())
556 except PHPDBGConnException:
557 self.currConnFinished()
558 return
559
560 self.PushBreakpoints(True)
561 self.ui.LoadPHPFramesList(self.stackList)
562
563 #
564 # This could be called when this object is constructed or when self is
565 # re-initialized after getting a new dbg module connection as a new
566 # session.
567 #
568 def clearInternals(self):
569 self.stackList = []
570 self.errStackList = []
571 self.stackFrameIndex = 0
572 self.isErrStack = False
573 self.errStr = ''
574 self.modList = []
575 self.stopOnError = True
576 self.lastCommand = None
577 self.evalRet = ''
578 self.modDict = {}
579 self.bpDict = {}
580 self.rawDataDict = {}
581 self.sessID = 0
582 self.sessType = 0
583 self.sessEnded = False
584 self.frameCounter = 1000
585 self.variableList = []
586 self.verMajor = 0
587 self.verMinor = 0
588 self.verDesc = None
589
590 def initLsnrThr(self):
591 self.actionEvent = threading.Event()
592 self.lsnrThr = PHPDBGLsnrThr(self, self.lsnrHost, self.lsnrPort, self.actionEvent, self.ui)
593
594 def awaitAndHandleResponse(self, header = None, blocking = False, disable = True, stopping = False):
595 if disable:
596 self.ui.DisableWhileDebuggerRunning()
597
598 while self.readResponse(header, blocking) != 0:
599 myprint("Waiting for response")
600
601 if stopping:
602 self.ui.DisableAfterStop()
603 else:
604 self.ui.EnableWhileDebuggerStopped()
605
606 def requestDBGVersion(self):
607 #TODO: necessary?
608 pass
609
610 def getSourceTree(self):
611 #TODO: necessary?
612 pass
613
614 def addDBGModName(self):
615 #TODO: necessary?
616 pass
617
618 def getNextFrameCounter(self):
619 self.frameCounter = self.frameCounter + 1
620 return self.frameCounter
621
622 def getVariables(self, stack):
623 self.variableList = []
624
625 reqPacket = PHPDBGPacket(DBGA_REQUEST)
626 reqFrame = PHPDBGFrame(FRAME_EVAL)
627
628 reqFrame.addInt(0)
629 reqFrame.addInt(stack.getFrameScopeId())
630 reqPacket.addFrame(reqFrame)
631 myprint("PHPDebuggerCallback::getVariables(): about to send eval request")
632
633 try:
634 reqPacket.sendPacket(self.lsnrThr)
635 self.awaitAndHandleResponse(disable = False)
636 except PHPDBGConnException:
637 self.currConnFinished()
638 return self.variableList
639
640 myprint("PHPDebuggerCallback::getVariables(): evalRet=%s", self.evalRet)
641 evalStr = PHPDBGEvalString(stack, self.evalRet)
642 if evalStr:
643 self.variableList = evalStr.getVars()
644 myprint("PHPDebuggerCallback::getVariables(): about to return")
645
646 return self.variableList
647
648 def evalBlock(self, stack, evalStr):
649 reqPacket = PHPDBGPacket(DBGA_REQUEST)
650 reqFrame1 = PHPDBGFrame(FRAME_EVAL)
651 reqFrame2 = PHPDBGFrame(FRAME_RAWDATA)
652
653 frameID = self.getNextFrameCounter()
654 reqFrame1.addInt(frameID)
655 reqFrame1.addInt(1)
656
657 reqFrame2.addInt(frameID)
658 reqFrame2.addInt(len(evalStr) + 1)
659 reqFrame2.addStr(evalString)
660
661 reqPacket.addFrame(reqFrame2)
662 reqPacket.addFrame(reqFrame1)
663
664 try:
665 reqPacket.sendPacket(self.lsnrThr)
666 self.awaitAndHandleResponse(disable = False)
667 except PHPDBGConnException:
668 self.currConnFinished()
669 return None
670
671 evalStr = PHPDBGEvalString(stack, self.evalRet)
672
673 return evalStr.getVars()
674
675 def getBPUnderHit(self):
676 for bp in self.bpDict.values():
677 if bp.isUnderHit():
678 return bp
679
680 return None
681
682 def getRawFrameData(self, frameNo):
683 if self.rawDataDict.has_key(frameNo):
684 #
685 # Once the frameData is consumed, remove it from rawDataDict.
686 #
687 return self.rawDataDict.pop(frameNo)
688 else:
689 #
690 # TODO: do we need to handle the case when the raw frame data hasn't
691 # been received before?
692 #
693 return None
694
695 def getModByNum(self, modNum):
696 if self.modDict.has_key(modNum):
697 return self.modDict[modNum]
698 else:
699 return None
700
701 def getModByFileName(self, fileName):
702 for mn, fn in self.modDict.iteritems():
703 if fn == fileName:
704 return mn
705
706 return 0
707
708 def setMod(self, modNum, fileName):
709 if modNum != 0 and fileName:
710 self.modDict[modNum] = fileName
711
712 return
713
714 def readResponse(self, headeri = None, blockingi = False):
715 inHeader = headeri
716 header = None
717 cmdReceived = 0
718 isFirstPacket = True
719 blocking = blockingi
720 self.isErrStack = False
721 self.rawDataDict.clear()
722
723 while True:
724 #
725 # If we have already received the first packet, we can't block any
726 # more.
727 #
728 if not isFirstPacket:
729 blocking = False
730
731 #
732 # If this is the first loop and we have a non-empty header passed in, use it. Otherwise,
733 # read in a new header. For subsequent loops, inHeader is None so we always read a new
734 # header from the wire.
735 #
736 if inHeader:
737 header = inHeader
738 inHeader = None
739 else:
740 header = ResponseHeader(self.lsnrThr, blocking)
741
742 if not header.isValid:
743 return 0
744
745 cmdReceived = header.command
746 frame = ResponsePacketFrame(self.lsnrThr, header.toRead, None, blocking)
747 if not frame.isValid:
748 return 0
749
750 isFirstPacket = False
751 isFirstStackFrame = True
752 while frame and frame.isValid:
753 frameName = frame.frameName
754 if frameName == FRAME_STACK:
755 if self.isErrStack:
756 self.errStackList = self.handleRespFrameStack(self.errStackList, frame, isFirstStackFrame)
757 else:
758 self.stackList = self.handleRespFrameStack(self.stackList, frame, isFirstStackFrame)
759
760 if isFirstStackFrame:
761 isFirstStackFrame = False
762 elif frameName == FRAME_SOURCE:
763 self.handleRespFrameSource(frame)
764 elif frameName == FRAME_SRC_TREE:
765 self.handleRespFrameSrcTree(frame)
766 elif frameName == FRAME_RAWDATA:
767 self.handleRespFrameRawdata(frame)
768 elif frameName == FRAME_ERROR:
769 self.handleRespFrameError(frame)
770 elif frameName == FRAME_EVAL:
771 self.handleRespFrameEval(frame)
772 elif frameName == FRAME_BPS:
773 self.handleRespFrameBps(frame)
774 elif frameName == FRAME_BPL:
775 self.handleRespFrameBpl(frame)
776 elif frameName == FRAME_VER:
777 self.handleRespFrameVer(frame)
778 elif frameName == FRAME_SID:
779 self.handleRespFrameSid(frame)
780 elif frameName == FRAME_SRCLINESINFO:
781 self.handleRespFrameSrclinesinfo(frame)
782 elif frameName == FRAME_SRCCTXINFO:
783 self.handleRespFrameSrcctxinfo(frame)
784 elif frameName == FRAME_LOG:
785 self.handleRespFrameLog(frame)
786 elif frameName == FRAME_PROF:
787 self.handleRespFrameProf(frame)
788 elif frameName == FRAME_PROF_C:
789 self.handleRespFrameProfC(frame)
790 elif frameName == FRAME_SET_OPT:
791 self.handleRespFrameSetOpt(frame)
792 else:
793 self.handleRespFrameUnknown(frame)
794 return 0
795
796 #
797 # After handling of this frame, force frame to point to the
798 # next one based on current frame's absolute size.
799 #
800 frame = frame.getNextFrame(True)
801
802 if cmdReceived == DBGC_REPLY:
803 self.handleRespCmdReply()
804 elif cmdReceived == DBGC_STARTUP:
805 self.handleRespCmdStartup()
806 elif cmdReceived == DBGC_END:
807 self.handleRespCmdEnd()
808 elif cmdReceived == DBGC_BREAKPOINT:
809 self.handleRespCmdBreakpoint()
810 cmdReceived = 0
811 elif cmdReceived == DBGC_STEPINTO_DONE:
812 self.handleRespCmdStepintoDone()
813 elif cmdReceived == DBGC_STEPOVER_DONE:
814 self.handleRespCmdStepoverDone()
815 elif cmdReceived == DBGC_STEPOUT_DONE:
816 self.handleRespCmdStepoutDone()
817 elif cmdReceived == DBGC_EMBEDDED_BREAK:
818 self.handleRespCmdEmbeddedBreak()
819 elif cmdReceived == DBGC_PAUSE:
820 self.handleRespCmdPause()
821 elif cmdReceived == DBGC_ERROR:
822 self.handleRespCmdError()
823 elif cmdReceived == DBGC_LOG:
824 self.handleRespCmdLog()
825 elif cmdReceived == DBGC_SID:
826 self.handleRespCmdSid()
827 else:
828 self.handleRespCmdUnknown()
829
830 return cmdReceived
831
832 def handleRespFrameStack(self, stackList, frame, isFirst):
833 if isFirst:
834 stackList = []
835 self.stackFrameIndex = 0
836
837 lineNo = frame.getNextInt()
838 modNo = frame.getNextInt()
839 scopeId = frame.getNextInt()
840 frameId = frame.getNextInt()
841 if modNo != 0:
842 newStackFrame = PHPStackFrame(self, self.getModByNum(modNo), lineNo, self.stackFrameIndex, scopeId, self.getRawFrameData(frameId), modNo)
843 stackList.append(newStackFrame)
844 self.stackFrameIndex = self.stackFrameIndex + 1
845
846 return stackList
847
848 def handleRespFrameSource(self, frame):
849 modNo = frame.getNextInt()
850 fromFilePos = frame.getNextInt()
851 error = frame.getNextInt()
852 fullSize = frame.getNextInt()
853 fileNameFrameId = frame.getNextInt()
854 textFrameId = frame.getNextInt()
855
856 fileName = self.getModByNum(modNo)
857 if not fileName:
858 self.setFileMod(modNo, fileNameFrameId)
859
860 #
861 # TODO: fullSize string and textFrameId are not handled here.
862 #
863 return
864
865 def handleRespFrameSrcTree(self, frame):
866 parentModNo = frame.getNextInt()
867 parentLineNo = frame.getNextInt()
868 modNo = frame.getNextInt()
869 fileNameFrameId = frame.getNextInt()
870
871 fileName = self.getModByNum(modNo)
872 if not fileName:
873 self.setFileMod(modNo, fileNameFrameId)
874
875 return
876
877 def handleRespFrameRawdata(self, frame):
878 frameNo = frame.getNextInt()
879 if frameNo > 0:
880 toRead = frame.getNextInt()
881 if toRead > 0:
882 str = frame.getNextString(toRead)
883 self.rawDataDict[frameNo] = str
884 myprint("handleRespFrameRawdata(): added \'%d\'=\'%s\' to rawDataDict.", (frameNo, str))
885
886 return
887
888 def handleRespFrameError(self, frame):
889 self.isErrStack = True
890
891 #
892 # Type of the error.
893 #
894 errInt0 = frame.getNextInt()
895 #
896 # ID of error message.
897 #
898 errInt1 = frame.getNextInt()
899
900 if errInt0 == E_ERROR:
901 errIdStr = "[Error]"
902 elif errInt0 == E_WARNING:
903 errIdStr = "[Warning]"
904 elif errInt0 == E_PARSE:
905 errIdStr = "[Parse Error]"
906 elif errInt0 == E_NOTICE:
907 errIdStr = "[Notice]"
908 elif errInt0 == E_CORE_ERROR:
909 errIdStr = "[Core Error]"
910 elif errInt0 == E_CORE_WARNING:
911 errIdStr = "[Core Warning]"
912 elif errInt0 == E_COMPILE_ERROR:
913 errIdStr = "[Compile Error]"
914 elif errInt0 == E_COMPILE_WARNING:
915 errIdStr = "[Compile Warning]"
916 elif errInt0 == E_USER_ERROR:
917 errIdStr = "[User Error]"
918 elif errInt0 == E_USER_WARNING:
919 errIdStr = "[User Warning]"
920 elif errInt0 == E_USER_NOTICE:
921 errIdStr = "[User Notice]"
922 else:
923 errIdStr = "[Unexpected Error]"
924
925 errMsg = self.getRawFrameData(errInt1)
926 if errMsg and len(errMsg) > 0:
927 self.errStr = errIdStr + ": " + errMsg + "\n"
928 else:
929 self.errStr = errIdStr + ": <Invalid Error Message>\n"
930
931 if not self.stopOnError:
932 if self.lastCommand == DBGA_CONTINUE:
933 self.Continue()
934 elif self.lastCommand == DBGA_STEPINTO:
935 self.SingleStep()
936 elif self.lastCommand == DBGA_STEPOUT:
937 self.Return()
938 elif self.lastCommand == DBGA_STEPOVER:
939 self.Next()
940
941 return
942
943 def handleRespFrameEval(self, frame):
944 evalInt0 = frame.getNextInt()
945 evalInt1 = frame.getNextInt()
946 evalInt2 = frame.getNextInt()
947 self.evalRet = self.getRawFrameData(evalInt1)
948 #TODO: is the following necessary?
949 evalStr = self.getRawFrameData(evalInt0)
950
951 return
952
953 def handleRespFrameBps(self, frame):
954 return
955
956 def handleRespFrameBpl(self, frame):
957 #
958 # Get this breakpoint.
959 #
960 dbgBp = []
961 for i in range(10):
962 dbgBp.append(frame.getNextInt())
963
964 if dbgBp[2] != 0:
965 #
966 # If filename is sent, get it from the rawDataDict.
967 #
968 fileName = self.getRawFrameData(dbgBp[2])
969 if not fileName:
970 return
971
972 #
973 # If this filename comes with a mod number, store this
974 # modNum/fileName into this session's modDict. Notice it might
975 # overwrite previous value.
976 #
977 if dbgBp[0] != 0:
978 self.setMod(dbgBp[0], fileName)
979 elif dbgBp[0] != 0:
980 #
981 # Use modNum to get the fileName.
982 #
983 fileName = self.getModByNum(dbgBp[0])
984 if not fileName:
985 return
986 else:
987 #
988 # Couldn't get the filename; nothing we can do with.
989 #
990 return
991
992 bpKey = self.createBpKey(fileName, dbgBp[1])
993 if not self.bpDict.has_key(bpKey):
994 #
995 # Not in our bp list? Anyway, create one for it.
996 #
997 ourBp = BreakPoint(self, fileName, dbgBp[1], dbgBp[0], dbgBp[3], dbgBp[4], dbgBp[5], dbgBp[6], dbgBp[7], dbgBp[8], dbgBp[9])
998 self.bpDict[bpKey] = ourBp
999 newlyCreated = True
1000 else:
1001 ourBp = self.bpDict[bpKey]
1002 newlyCreated = False
1003
1004 #
1005 # Update with the latest bp information.
1006 #
1007 if not newlyCreated:
1008 ourBp.update(dbgBp)
1009
1010 return
1011
1012 def handleRespFrameVer(self, frame):
1013 self.verMajor = frame.getNextInt()
1014 self.verMinor = frame.getNextInt()
1015 verFrameNo = frame.getNextInt()
1016 self.verDesc = self.getRawFrameData(verFrameNo)
1017 myprint("respFrameVer: verMajor=%s, verMinor=%s, verDesc=%s", (repr(self.verMajor), repr(self.verMinor), repr(self.verDesc)))
1018
1019 return
1020
1021 def handleRespFrameSid(self, frame):
1022 self.sessID = frame.getNextInt()
1023 self.sessType = frame.getNextInt()
1024 myprint("respFrameSid: sessID=%s, sessType=%s", (self.sessID, self.sessType))
1025
1026 return
1027
1028 def handleRespFrameSrclinesinfo(self, frame):
1029 return
1030
1031 def handleRespFrameSrcctxinfo(self, frame):
1032 return
1033
1034 def handleRespFrameLog(self, frame):
1035 #
1036 # TODO:
1037 # Now we don't do much here besides following the protocol to retrieve
1038 # the data.
1039 #
1040 logId = frame.getNextInt()
1041 logType = frame.getNextInt()
1042 modNo = frame.getNextInt()
1043 lineNo = frame.getNextInt()
1044 fileNameFrameId = frame.getNextInt()
1045 extInfo = frame.getNextInt()
1046
1047 fileName = self.getModByNum(modNo)
1048 if not fileName:
1049 self.setFileMod(modNo, fileNameFrameId)
1050
1051 return
1052
1053 def handleRespFrameProf(self, frame):
1054 return
1055
1056 def handleRespFrameProfC(self, frame):
1057 return
1058
1059 def handleRespFrameSetOpt(self, frame):
1060 return
1061
1062 def handleRespCmdReply(self):
1063 return
1064
1065 def handleRespCmdStartup(self):
1066 return
1067
1068 def handleRespCmdEnd(self):
1069 self.sessEnded = True
1070 return
1071
1072 def handleRespCmdBreakpoint(self):
1073 return
1074
1075 def handleRespCmdStepintoDone(self):
1076 return
1077
1078 def handleRespCmdStepoverDone(self):
1079 return
1080
1081 def handleRespCmdStepoutDone(self):
1082 return
1083
1084 def handleRespCmdEmbeddedBreak(self):
1085 return
1086
1087 def handleRespCmdPause(self):
1088 return
1089
1090 def handleRespCmdError(self):
1091 self.stackList = []
1092
1093 if len(self.errStackList) > 0:
1094 self.errStr = self.errStr + "Stack Trace:\n"
1095
1096 while len(self.errStackList) > 0:
1097 oneStack = self.errStackList.pop()
1098 self.errStr = self.errStr + "%s\n" % oneStack.getLongDisplayStr()
1099
1100 self.ui.showErrorDialog(self.errStr, "PHP Error")
1101 myprint("Got PHP Error:\n%s", self.errStr)
1102
1103 return
1104
1105 def handleRespCmdLog(self):
1106 return
1107
1108 def handleRespCmdSid(self):
1109 return
1110
1111 def setFileMod(self, modNo, fileNameFrameId):
1112 if fileNameFrameId != 0:
1113 fileName = self.getRawFrameData(fileNameFrameId)
1114 if fileName and modNo != 0:
1115 self.setMod(modNo, fileName)
1116
1117 return
1118
1119 def createBpKey(self, fileName, lineNo):
1120 #
1121 # This is to work around a bug in dbg module where it changes the path
1122 # names that we pass to it to lower cases.
1123 #
1124 if sysutils.isWindows():
1125 fileName = fileName.lower()
1126
1127 return (fileName, lineNo)
1128
1129 def setLsnrAction(self, actioni):
1130 self.lsnrAction = actioni
1131 return
1132
1133 def getLsnrAction(self):
1134 return self.lsnrAction
1135
1136 def currConnFinished(self):
1137 self.clearInternals()
1138 self.setLsnrAction(PHPDebuggerCallback.ACTION_LISTEN)
1139 self.actionEvent.set()
1140 self.ui.DisableAfterStop()
1141
1142 def stopPhpDbg(self):
1143 #
1144 # TODO: should send a request to stop the current running PHP program.
1145 # should handle network blocking issue correctly, otherwise, we
1146 # might hang here.
1147 #
1148 reqPacket = PHPDBGPacket(DBGA_STOP)
1149
1150 try:
1151 reqPacket.sendPacket(self.lsnrThr)
1152 self.awaitAndHandleResponse(stopping = True)
1153 except PHPDBGConnException:
1154 pass
1155
1156 self.currConnFinished()
1157 return
1158
1159 def stopLsnr(self):
1160 if not self.lsnrThr:
1161 return
1162
1163 #
1164 # Then we try to stop our listener thread.
1165 #
1166 if self.lsnrThr.hasBeenConnected():
1167 #
1168 # If the listener thread has already accepted a connection from a
1169 # php debug module/client, it is sleeping now and wait for this
1170 # condition object to be set so that it can exit.
1171 #
1172 self.setLsnrAction(PHPDebuggerCallback.ACTION_STOP)
1173 self.actionEvent.set()
1174 else:
1175 #
1176 # If the listener thread has never been connected from a php debug
1177 # module/client, it is still blocking on a accept() call. We
1178 # connect to it here and send a special shutdown command asking it
1179 # to exit.
1180 #
1181 shutdownMessage = IntToC4(DBG_SYNC) + IntToC4(DBGC_AG_SHUTDOWN_REQ) + IntToC4(0) + IntToC4(0)
1182 tempSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1183 try:
1184 tempSocket.connect((self.lsnrHost, self.lsnrPort))
1185 tempSocket.sendall(shutdownMessage)
1186 except:
1187 myprint("shutdown connection/send message got exception!")
1188
1189 tempSocket.close()
1190
1191 self.lsnrThr.join()
1192 self.lsnrThr = None
1193
1194
1195 class PHPDBGLsnrThr(threading.Thread):
1196 def __init__(self, interfacei, hosti, porti, actionEventi, uii):
1197 threading.Thread.__init__(self)
1198 self.interface = interfacei
1199 self.svrHost = hosti
1200 self.svrPort = porti
1201 self.actionEvent = actionEventi
1202 self.svrSocket = None
1203 self.clntConn = None
1204 self.clntAddr = None
1205 self.nonBlockingTimeout = 1
1206 self.connHeader = None
1207 self.ui = uii
1208
1209 def initSvrSocket(self):
1210 self.svrSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1211 self.svrSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1212 self.svrSocket.bind((self.svrHost, self.svrPort))
1213
1214 def waitForClntConn(self):
1215 self.svrSocket.listen(5)
1216 self.clntConn, self.clntAddr = self.svrSocket.accept()
1217 self.clntConn.settimeout(self.nonBlockingTimeout)
1218
1219 def run(self):
1220 #
1221 # Initialize this server socket.
1222 #
1223 self.initSvrSocket()
1224
1225 while True:
1226 #
1227 # Block until we get a new connection from a php debug client or our
1228 # debugger ui (with a special shutting down header/command).
1229 #
1230 self.waitForClntConn()
1231
1232 #
1233 # Ok, a new connection comes in ... Read the header to see where it
1234 # comes from.
1235 #
1236 self.connHeader = ResponseHeader(self)
1237 if self.connHeader.command == DBGC_AG_SHUTDOWN_REQ:
1238 #
1239 # This is a special command coming from our UI asking this
1240 # thread to exit. This only happens if after this thread has
1241 # been waiting for new connections from PHP debug module, no one
1242 # connects, and UI is ready to shutdown this thread.
1243 #
1244 self.shutdown()
1245 break
1246 else:
1247 #
1248 # Tell the main gui thread to handle this new connection.
1249 #
1250 wx.CallAfter(self.interface.newConnEventHandler)
1251
1252 #
1253 # From now on, PHPDebuggerCallback will communicate with the php
1254 # debug module using this thread's clntConn socket. This thread
1255 # itself will keep sleeping until get notified to make some
1256 # actions.
1257 #
1258 self.actionEvent.wait()
1259 self.actionEvent.clear()
1260
1261 action = self.interface.getLsnrAction()
1262 if action == PHPDebuggerCallback.ACTION_STOP:
1263 self.shutdown()
1264 break
1265 elif action == PHPDebuggerCallback.ACTION_LISTEN:
1266 if self.clntConn:
1267 self.clntConn.shutdown(socket.SHUT_RDWR)
1268 self.clntConn.close()
1269 self.clntConn = None
1270
1271 continue
1272 else:
1273 continue
1274
1275 def shutdown(self):
1276 #
1277 # Cleanup and ready to exit.
1278 #
1279 self.clntConn.shutdown(socket.SHUT_RDWR)
1280 self.clntConn.close()
1281 self.svrSocket.close()
1282
1283 def recv(self, size, blocking = False):
1284 if self.clntConn:
1285 myprint("recv: trying to receive %d bytes of data ...", size)
1286 if blocking:
1287 self.clntConn.settimeout(None)
1288 else:
1289 self.clntConn.settimeout(self.nonBlockingTimeout)
1290
1291 try:
1292 rData = self.clntConn.recv(size)
1293 except socket.timeout:
1294 myprint("recv: got timed out")
1295 rData = None
1296 except:
1297 myprint("recv: got an unexpected exception: %s", sys.exc_info()[0])
1298 raise PHPDBGConnException
1299
1300 return rData
1301 else:
1302 raise PHPDBGConnException
1303
1304 def sendall(self, message):
1305 if self.clntConn:
1306 try:
1307 self.clntConn.sendall(message)
1308 except:
1309 myprint("sendall: got an unexpected exception: %s", sys.exc_info()[0])
1310 raise PHPDBGConnException
1311 else:
1312 raise PHPDBGConnException
1313
1314 def hasBeenConnected(self):
1315 return self.clntConn != None
1316
1317 def getConnHeader(self):
1318 return self.connHeader
1319
1320
1321 class PHPValue(object):
1322 PEV_NAMES = ("undefined", "long", "double", "string", "array", "object", "boolean", "resource", "reference", "soft reference", "null")
1323 PEVT_UNKNOWN = 0
1324 PEVT_LONG = 1
1325 PEVT_DOUBLE = 2
1326 PEVT_STRING = 3
1327 PEVT_ARRAY = 4
1328 PEVT_OBJECT = 5
1329 PEVT_BOOLEAN = 6
1330 PEVT_RESOURCE = 7
1331 PEVT_REF = 8
1332 PEVT_SOFTREF = 9
1333 PEVT_NULL = 10
1334
1335 NULL_VALUE_STR = "NULL"
1336 TRUE_VALUE_STR = "True"
1337 FALSE_VALUE_STR = "False"
1338 OBJECT_VALUE_STR = "<%s> object"
1339 STRING_VALUE_STR = "\"%s\""
1340 REFERENCE_VALUE_STR = "<reference><%s>"
1341 RESOURCE_VALUE_STR = "<%s><%s>"
1342
1343 def __init__(self, frame, type, valueList):
1344 self.fStackFrame = frame
1345 self.fValueType = type
1346
1347 if type == self.PEVT_OBJECT:
1348 self.fValueString = self.OBJECT_VALUE_STR % valueList[0]
1349 self.fVariables = valueList[1:]
1350 elif type == self.PEVT_ARRAY:
1351 self.fValueString = ''
1352 self.fVariables = valueList
1353 else:
1354 self.fVariables = []
1355 if type == self.PEVT_STRING:
1356 self.fValueString = self.STRING_VALUE_STR % valueList[0]
1357 elif type == self.PEVT_NULL:
1358 self.fValueString = self.NULL_VALUE_STR
1359 elif type == self.PEVT_BOOLEAN:
1360 if valueList[0] == "0":
1361 self.fValueString = self.FALSE_VALUE_STR
1362 else:
1363 self.fValueString = self.TRUE_VALUE_STR
1364 elif type == self.PEVT_REF or type == self.PEVT_SOFTREF:
1365 self.fValueString = self.REFERENCE_VALUE_STR % valueList[0]
1366 elif type == self.PEVT_RESOURCE:
1367 self.fValueString = self.RESOURCE_VALUE_STR % (valueList[0], valueList[1])
1368 else:
1369 self.fValueString = valueList[0]
1370
1371 def addVariable(self, item):
1372 if item != None:
1373 self.fVariables.append(item)
1374
1375 return self.fVariables
1376
1377 def setParent(self, parent):
1378 if self.fVariables != None and len(self.fVariables) > 0:
1379 for item in self.fVariables:
1380 item.setParent(parent)
1381
1382 def getReferenceType(self):
1383 return self.fValueType
1384
1385 def getReferenceTypeName(self):
1386 return self.PEV_NAMES[self.fValueType]
1387
1388 def setReferenceType(self, type):
1389 self.fValueType = type
1390
1391 def getValueString(self):
1392 return self.fValueString
1393
1394 def getChildrenVariables(self):
1395 return self.fVariables
1396
1397 def hasVariables(self):
1398 return len(self.fVariables) > 0
1399
1400 def childrenIsSortable(self):
1401 #
1402 # TODO: if self.fValueType != self.PEVT_ARRAY:
1403 #
1404 return True
1405
1406
1407 class PHPVariable(object):
1408 def __init__(self, frame, parent, valueType, name, valueList):
1409 self.fStackFrame = frame
1410 self.fName = None
1411 self.fLongName = None
1412 self.fPureName = None
1413 self.fValue = PHPValue(frame, valueType, valueList)
1414 self.fParent = parent
1415 self.setName(name)
1416 self.setChildrensParent(valueList)
1417
1418 def setName(self, name):
1419 self.fPureName = name
1420
1421 type = self.getReferenceType()
1422 if type == PHPValue.PEVT_ARRAY:
1423 numItems = len(self.getChildrenVariables())
1424 self.fName = name + "[" + str(numItems) + "]"
1425 else:
1426 self.fName = name
1427
1428 if not self.fParent or self.fParent.getName() == None:
1429 self.fLongName = name
1430 else:
1431 self.setLongName()
1432
1433 return
1434
1435 def setLongName(self):
1436 parentType = self.fParent.getReferenceType()
1437 if parentType == PHPValue.PEVT_ARRAY:
1438 self.fLongName = self.fParent.getLongName() + "['" + self.fPureName + "']"
1439 elif parentType == PHPValue.PEVT_OBJECT:
1440 self.fLongName = self.fParent.getLongName() + "." + self.fName
1441 else:
1442 self.fLongName = self.fName
1443
1444 return
1445
1446 def getValue(self):
1447 return self.fValue
1448
1449 def getValueString(self):
1450 return self.fValue.getValueString()
1451
1452 def getChildrenVariables(self):
1453 return self.fValue.getChildrenVariables()
1454
1455 def getName(self):
1456 return self.fName
1457
1458 def getParent(self):
1459 return self.fParent
1460
1461 def setParent(self, parent):
1462 self.fParent = parent
1463 self.setLongName()
1464 return
1465
1466 def setChildrensParent(self, childrenList):
1467 if self.fValue.hasVariables():
1468 for child in self.fValue.getChildrenVariables():
1469 child.setParent(self)
1470
1471 def getLongName(self):
1472 return self.fLongName
1473
1474 def getReferenceTypeName(self):
1475 return self.fValue.getReferenceTypeName()
1476
1477 def getReferenceType(self):
1478 return self.fValue.getReferenceType()
1479
1480 def setReferenceType(self, type):
1481 tp = self.getValue.setReferenceType(type)
1482 return tp
1483
1484 def setValue(self, expression):
1485 if self.fValue.getReferenceType() == PHPValue.PEVT_STRING:
1486 evalString = self.fLongName + "=\"" + expression + "\""
1487 else:
1488 evalString = self.fLongName + "=" + expression
1489
1490 vars = self.fStackFrame.getPHPDBGInterface().evalBlock(self.fStackFrame, evalString)
1491 self.fValue = vars[0].fValue
1492
1493 def toString(self):
1494 rtype = self.getReferenceType()
1495 if rtype == PHPValue.PEVT_ARRAY:
1496 elements = len(self.fValue.getChildrenVariables())
1497 if elements == 0:
1498 tmpStr = self.getName() + " [no elements]"
1499 elif elements == 1:
1500 tmpStr = self.getName() + " [1 element]"
1501 else:
1502 tmpStr = self.getName() + " [" + str(elements) + " elements]"
1503 elif rtype == PHPValue.PEVT_OBJECT:
1504 tmpStr = self.getName() + " [ class: " + self.fValue.getValueString() + "]"
1505 elif rtype == PHPValue.PEVT_STRING:
1506 tmpStr = self.getName() + " = \"" + self.fValue.getValueString() + "\""
1507 else:
1508 tmpStr = self.getName() + " = " + self.fValue.getValueString()
1509
1510 return tmpStr
1511
1512 def hasChildren(self):
1513 return self.fValue.hasVariables()
1514
1515 def childrenIsSortable(self):
1516 return self.fValue.childrenIsSortable()
1517
1518
1519 class PHPStackFrame(object):
1520 def __init__(self, interface, file, line, frameIndex, scopeId, desc, modNum):
1521 self.interface = interface
1522 self.fileName = file
1523 self.lineNo = line
1524 self.frameIndex = frameIndex
1525 self.scopeId = scopeId
1526 self.desc = desc
1527 self.modNum = modNum
1528 self.variables = []
1529 self.shortFileName = None
1530 self.shortDesc = None
1531 self.displayStr = None
1532 self.longDisplayStr = None
1533
1534 self._getFileNamesAndShortDesc()
1535
1536 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)))
1537
1538 def _getFileNamesAndShortDesc(self):
1539 tmp = []
1540 if self.desc:
1541 tmp = self.desc.split("::")
1542
1543 if self.fileName:
1544 self.shortFileName = os.path.basename(self.fileName)
1545 if len(tmp) == 2:
1546 self.shortDesc = tmp[1]
1547 elif len(tmp) == 1:
1548 self.shortDesc = tmp[0]
1549 else:
1550 self.shortDesc = None
1551
1552 return
1553
1554 #
1555 # The fileName is None, we will try our best efforts to get it.
1556 #
1557 if len(tmp) == 2:
1558 #
1559 # We retrieved long finename from the description. If we haven't
1560 # stored the file mod before, set this one as the new one.
1561 # Otherwise, we prefer to keep the stored one.
1562 #
1563 if self.modNum != 0:
1564 storedFileName = self.interface.getModByNum(self.modNum)
1565 if not storedFileName:
1566 self.interface.setMod(self.modNum, tmp[0])
1567
1568 self.fileName = tmp[0]
1569 self.shortFileName = os.path.basename(tmp[0])
1570 self.shortDesc = tmp[1]
1571 elif len(tmp) == 1:
1572 self.fileName = None
1573 self.shortFileName = None
1574 self.shortDesc = tmp[0]
1575 else:
1576 self.shortFileName = None
1577 self.shortDesc = None
1578 myprint("PHPStackFrame::_getFileNamesAndShortDesc(): something wrong with desc: %s?", self.desc)
1579
1580 return
1581
1582 def getShortFileName(self):
1583 return self.shortFileName
1584
1585 def setShortFileName(self, shortFileName):
1586 self.shortFileName = shortFileName
1587
1588 def getShortDesc(self):
1589 return self.shortDesc
1590
1591 def getLineNo(self):
1592 return self.lineNo
1593
1594 def getInterface(self):
1595 return self.interface
1596
1597 def getVariables(self):
1598 if len(self.variables) == 0:
1599 self.variables = self.interface.getVariables(self)
1600
1601 return self.variables
1602
1603 def findVariables(self, s):
1604 if self.hasVariables():
1605 name = "$" + s
1606 for var in self.variables:
1607 if var.getName() == name:
1608 return var
1609
1610 return None
1611
1612 def hasVariables(self):
1613 if len(self.variables) == 0:
1614 return False
1615 else:
1616 return True
1617
1618 def getName(self):
1619 if self.getDesc():
1620 return self.getDesc() + " [line: " + str(self.getLineNo()) + "]"
1621 else:
1622 return self.getFileName() + " [line: " + str(self.getLineNo()) + "]"
1623
1624 def getFileName(self):
1625 return self.fileName
1626
1627 def setFileName(self, fileName):
1628 self.fileName = fileName
1629
1630 def setDesc(self, desc):
1631 self.desc = desc
1632
1633 def getDesc(self):
1634 return self.desc
1635
1636 def getFrameScopeId(self):
1637 return self.scopeId
1638
1639 def getFrameIndex(self):
1640 return self.frameIndex
1641
1642 def getDisplayStr(self, stackList = None):
1643 if self.displayStr:
1644 return self.displayStr
1645
1646 if not self.shortFileName:
1647 if stackList:
1648 i = stackList.index(self)
1649 for j in range(i + 1, len(stackList)):
1650 self.shortFileName = stackList[j].getShortFileName()
1651 if self.shortFileName:
1652 self.fileName = stackList[j].getFileName()
1653
1654 if self.shortFileName:
1655 if self.shortDesc:
1656 self.displayStr = "<%s> at %s:%d" % (self.shortDesc, self.shortFileName, self.lineNo)
1657 else:
1658 self.displayStr = "%s:%d" % (self.shortFileName, self.lineNo)
1659 else:
1660 if self.shortDesc:
1661 self.displayStr = "<%s>" % self.shortDesc
1662 else:
1663 self.displayStr = "<internal stack error>"
1664
1665 return self.displayStr
1666
1667 def getLongDisplayStr(self):
1668 if self.longDisplayStr:
1669 return self.longDisplayStr
1670
1671 if self.fileName:
1672 if self.shortDesc:
1673 self.longDisplayStr = "<%s> at %s:%d" % (self.shortDesc, self.fileName, self.lineNo)
1674 else:
1675 self.longDisplayStr = "%s:%d" % (self.fileName, self.lineNo)
1676 else:
1677 if self.shortDesc:
1678 self.longDisplayStr = "<%s>" % self.shortDesc
1679 else:
1680 self.longDisplayStr = "<internal stack error>"
1681
1682 return self.longDisplayStr
1683
1684 class BreakPoint(object):
1685 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):
1686 self.interface = interface
1687 self.fileName = fileName
1688 self.lineNo = lineNo
1689 self.bpID = bpId
1690 self.state = state
1691 self.isTemp = isTemp
1692 self.hitCount = hitCount
1693 self.skipHits = skipHits
1694 self.condition = condition
1695 self.isUnderHit = 0
1696 if modNum == 0:
1697 self.modNum = self.interface.getModByFileName(fileName)
1698 else:
1699 self.modNum = modNum
1700
1701 if self.modNum:
1702 self.fCounterOrZero = 0
1703 else:
1704 self.fCounterOrZero = interface.getNextFrameCounter()
1705
1706 def sendSelf(self):
1707 reqPacket = PHPDBGPacket(DBGA_REQUEST)
1708 reqFrame1 = PHPDBGFrame(FRAME_BPS)
1709
1710 if self.modNum:
1711 reqFrame1.addInt(self.modNum)
1712 else:
1713 #
1714 # 0 in modNum to tell to use fileName instead.
1715 #
1716 reqFrame1.addInt(0)
1717
1718 reqFrame1.addInt(self.lineNo) # lineNo
1719 reqFrame1.addInt(self.fCounterOrZero) # fileName frameCounter or 0
1720 reqFrame1.addInt(self.state) # state
1721 reqFrame1.addInt(self.isTemp) # isTemp
1722 reqFrame1.addInt(self.hitCount) # hitCount
1723 reqFrame1.addInt(self.skipHits) # skipHits
1724 reqFrame1.addInt(self.condition) # condition
1725 reqFrame1.addInt(self.bpID) # breakpoint sequence id
1726 reqFrame1.addInt(self.isUnderHit) # isUnderHit
1727
1728 if not self.modNum:
1729 reqFrame2 = PHPDBGFrame(FRAME_RAWDATA)
1730 reqFrame2.addInt(self.fCounterOrZero)
1731 reqFrame2.addInt(len(self.fileName) + 1)
1732 reqFrame2.addStr(self.fileName)
1733 reqPacket.addFrame(reqFrame2)
1734
1735 reqPacket.addFrame(reqFrame1)
1736
1737 try:
1738 reqPacket.sendPacket(self.interface.lsnrThr)
1739 self.interface.awaitAndHandleResponse()
1740 except PHPDBGConnException:
1741 self.interface.currConnFinished()
1742
1743 return
1744
1745 def addSelf(self):
1746 self.sendSelf()
1747
1748 def removeSelf(self):
1749 self.state = BPS_DISABLED
1750 self.sendSelf()
1751
1752 def isUnderHit(self):
1753 return self.isUnderHit == 1
1754
1755 def update(self, dbgBp):
1756 self.modNum = dbgBp[0]
1757 self.state = dbgBp[3]
1758 self.isTemp = dbgBp[4]
1759 self.hitCount = dbgBp[5]
1760 self.skipHits = dbgBp[6]
1761 self.condition = dbgBp[7]
1762 self.bpID = dbgBp[8]
1763 self.isUnderHit = dbgBp[9]
1764
1765
1766 class PHPDBGEvalString(object):
1767 def __init__(self, stackFrame, dataStr):
1768 self.stackFrame = stackFrame
1769 self.dataStr = dataStr
1770
1771 #
1772 # Get a list of variables under self.stackFrame.
1773 #
1774 def getVars(self):
1775 return self.parseAVariable(isRealVar = False)
1776
1777 #
1778 # if isRealVar:
1779 # returnList[0] = The Variable
1780 # else:
1781 # returnList = list of variables.
1782 #
1783 def parseAVariable(self, isRealVar = True):
1784 returnList = []
1785
1786 #
1787 # Get the variable name first. Notice we ignore this entity's data
1788 # type here.
1789 #
1790 if isRealVar:
1791 nameEntity = self.parseAnEntity()
1792 if not nameEntity or len(nameEntity) != 2 or type(nameEntity[1]) != str:
1793 myprint("PHPDBGEvalStr::parseAVariable() got a wrong name entity")
1794 return returnList
1795 else:
1796 varName = nameEntity[1]
1797
1798 #
1799 # Get the variable's value.
1800 #
1801 valueEntity = self.parseAnEntity()
1802 if not valueEntity or len(valueEntity) < 1:
1803 myprint("PHPDBGEvalStr::parseAVariable(): couldn't get a variable's value entity.")
1804 return returnList
1805
1806 #
1807 # This variable's data type.
1808 #
1809 varType = valueEntity[0]
1810
1811 if isRealVar:
1812 #
1813 # If this is a real variable, return a list which contains only
1814 # this variable item.
1815 #
1816 #valueEntity = valueEntity[1:]
1817 variable = PHPVariable(self.stackFrame, None, varType, varName, valueEntity[1:])
1818 #myprint("xxxxCreated variable varName=%s, valueEntity=%s", (repr(varName), repr(valueEntity[1])))
1819 myprint("xxxxCreated variable: %s", repr(variable.toString()))
1820 returnList.append(variable)
1821 else:
1822 #
1823 # If this is a root variable container, returns a list of
1824 # variables under the root. Do a sanity check here.
1825 #
1826 if valueEntity[0] != PHPValue.PEVT_ARRAY:
1827 myprint("PHPDBGEvalStr::parseAVariable(): failed to parse the root variable container.")
1828 else:
1829 returnList = valueEntity[1:]
1830
1831 return returnList
1832
1833 #
1834 # An entity could be a variable's name or its value.
1835 #
1836 # returnList[0] = variable data type
1837 # returnList[1:] = the real list
1838 #
1839 def parseAnEntity(self):
1840 if not self.dataStr or len(self.dataStr) < 2 or (self.dataStr[1] != ':' and self.dataStr[1] != ';'):
1841 myprint("PHPDBGEvalStr::parseAnEntity(): failed to parse %s.", repr(self.dataStr))
1842 return None
1843
1844 returnList = []
1845 typeChar = self.dataStr[0]
1846 self.dataStr = self.dataStr[2:]
1847 if typeChar == 'i':
1848 returnList.append(PHPValue.PEVT_LONG)
1849 self.parseInt(returnList)
1850 elif typeChar == 'a':
1851 returnList.append(PHPValue.PEVT_ARRAY)
1852 self.parseArray(returnList)
1853 elif typeChar == 's':
1854 returnList.append(PHPValue.PEVT_STRING)
1855 self.parseString(returnList)
1856 elif typeChar == 'O':
1857 returnList.append(PHPValue.PEVT_OBJECT)
1858 self.parseObject(returnList)
1859 elif typeChar == 'r':
1860 returnList.append(PHPValue.PEVT_SOFTREF)
1861 self.parseReference(returnList, isSoftRef = True)
1862 elif typeChar == 'R':
1863 returnList.append(PHPValue.PEVT_REF)
1864 self.parseReference(returnList, isSoftRef = False)
1865 elif typeChar == 'b':
1866 returnList.append(PHPValue.PEVT_BOOLEAN)
1867 self.parseBoolean(returnList)
1868 elif typeChar == 'd':
1869 returnList.append(PHPValue.PEVT_DOUBLE)
1870 self.parseDouble(returnList)
1871 elif typeChar == 'z':
1872 returnList.append(PHPValue.PEVT_RESOURCE)
1873 self.parseResource(returnList)
1874 elif typeChar == 'N':
1875 returnList.append(PHPValue.PEVT_NULL)
1876 self.parseNull(returnList)
1877 else:
1878 myprint("PHPDBGEvalStr::parseAnEntity(): unknown data type: %s", typeChar)
1879
1880 return returnList
1881
1882 def parseInt(self, returnList):
1883 myprint("enter parseInt().")
1884 returnList.append(self.getAnIntStr(';'))
1885
1886 return
1887
1888 def parseArray(self, returnList):
1889 myprint("enter parseArray().")
1890 #
1891 # The shortest array is 'a:0:{}'.
1892 #
1893 if len(self.dataStr) < 4:
1894 myprint("PHPDBGEvalStr::parseArray(): failed (1) to parse an array: %s.", repr(self.dataStr))
1895 return
1896
1897 expectedNumItems = self.getAnInt(':')
1898 if len(self.dataStr) < 2 or self.dataStr[0] != '{':
1899 myprint("PHPDBGEvalStr::parseArray(): failed (3) to parse an array: %s.", repr(self.dataStr))
1900 return
1901
1902 self.dataStr = self.dataStr[1:]
1903 varList = []
1904 while self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] != '}':
1905 tmpList = self.parseAVariable()
1906 if not tmpList or len(tmpList) != 1 or not tmpList[0]:
1907 myprint("PHPDBGEvalStr::parseArray(): failed (4) to parse an array. dataStr=%s.", repr(self.dataStr))
1908 break
1909 else:
1910 varList.append(tmpList[0])
1911
1912 if expectedNumItems != len(varList):
1913 myprint("PHPDBGEvalStr::parseArray(): failed (5) expected no. of items=%d, but got %d", (expectedNumItems, len(varList)))
1914
1915 #
1916 # An array should end with a '}'.
1917 #
1918 if self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] == '}':
1919 self.dataStr = self.dataStr[1:]
1920 returnList.extend(varList)
1921 else:
1922 myprint("PHPDBGEvalStr::parseArray(): failed (6) to parse an array. dataStr=%s.", repr(self.dataStr))
1923
1924 myprint("parseArray() ends.")
1925 return
1926
1927 def parseString(self, returnList, endChar = ';'):
1928 myprint("enter parseString().")
1929 #
1930 # The shortest string is 's:<str_len>:"<str>"<endChar>'.
1931 #
1932 if len(self.dataStr) < 5:
1933 myprint("PHPDBGEvalStr::parseString(): failed (1) to parse a string. dataStr=%s.", repr(self.dataStr))
1934 return
1935
1936 expectedStrLen = self.getAnInt(':')
1937 if len(self.dataStr) < expectedStrLen + 3 or self.dataStr[0] != '"':
1938 myprint("PHPDBGEvalStr::parseString(): failed (3) to parse a string. dataStr=%s.", repr(self.dataStr))
1939 return
1940
1941 strValue = self.dataStr[1:expectedStrLen + 1]
1942 if self.dataStr[expectedStrLen + 1:expectedStrLen + 3] != '"' + endChar:
1943 myprint("PHPDBGEvalStr::parseString(): failed (4) to parse a string. dataStr=%s.", repr(self.dataStr))
1944 return
1945
1946 #
1947 # Skip the starting double quote, real string, ending double quote, and ending semicolon.
1948 #
1949 self.dataStr = self.dataStr[expectedStrLen + 3:]
1950 returnList.append(strValue)
1951
1952 return
1953
1954 def parseObject(self, returnList):
1955 #
1956 # A simple sanity check. The shortest object is:
1957 # 'O:<class_name_len>:"<class_name>":<num_of_items>:{<list_of_items>}'
1958 #
1959 if not self.dataStr or len(self.dataStr) < 10:
1960 myprint("PHPDBGEvalStr::parseObject(): failed (1) to parse an object: %s.", repr(self.dataStr))
1961
1962 #
1963 # Get the class name in classNameList[0].
1964 #
1965 classNameList = []
1966 self.parseString(classNameList, ':')
1967
1968 expectedNumItems = self.getAnInt(':')
1969 if len(self.dataStr) < 2 or self.dataStr[0] != '{':
1970 myprint("PHPDBGEvalStr::parseObject(): failed (2) to parse an object: %s.", repr(self.dataStr))
1971 return
1972
1973 self.dataStr = self.dataStr[1:]
1974 varList = []
1975 while self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] != '}':
1976 tmpList = self.parseAVariable()
1977 if not tmpList or len(tmpList) != 1 or not tmpList[0]:
1978 myprint("PHPDBGEvalStr::parseObject(): failed (3) to parse an object. dataStr=%s.", repr(self.dataStr))
1979 break
1980 else:
1981 varList.append(tmpList[0])
1982
1983 if expectedNumItems != len(varList):
1984 myprint("PHPDBGEvalStr::parseObject(): failed (4) expected no. of items=%d, but got %d", (expectedNumItems, len(varList)))
1985
1986 #
1987 # An object should end with a '}'.
1988 #
1989 if self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] == '}':
1990 self.dataStr = self.dataStr[1:]
1991 returnList.append(classNameList[0])
1992 returnList.extend(varList)
1993 else:
1994 myprint("PHPDBGEvalStr::parseObject(): failed (6) to parse an object. dataStr=%s.", repr(self.dataStr))
1995
1996 myprint("parseObject() ends.")
1997 return
1998
1999 def parseReference(self, returnList, isSoftRef):
2000 myprint("enter parseReference().")
2001 intStr = self.getAnIntStr(';')
2002 if intStr:
2003 returnList.append(intStr)
2004
2005 return
2006
2007 def parseBoolean(self, returnList):
2008 tmpBooleanStr = self.getAnIntStr(';')
2009 returnList.append(tmpBooleanStr)
2010
2011 return
2012
2013 def parseDouble(self, returnList):
2014 tmpStr = self.getAStrTillEndChar(';')
2015 if tmpStr:
2016 returnList.append(tmpStr)
2017
2018 return
2019
2020 def parseResource(self, returnList):
2021 tmpList = []
2022 self.parseString(tmpList, ':')
2023
2024 if len(tmpList) == 1:
2025 returnList.extend(tmpList)
2026 else:
2027 return
2028
2029 resourceId = self.getAnIntStr(';')
2030 if resourceId:
2031 returnList.append(resourceId)
2032
2033 return
2034
2035 def parseNull(self, returnList):
2036 return
2037
2038 def getAStrTillEndChar(self, endChar):
2039 if len(self.dataStr) < 1:
2040 myprint("PHPDBGEvalStr::getAStrTillEndChar(): no more data string to work with.")
2041 return
2042
2043 i = self.findNextChar(self.dataStr, endChar)
2044 if i == -1:
2045 myprint("PHPDBGEvalStr::getAStrTillEndChar(): no double/float string supplied.")
2046 return
2047
2048 tmpStr = self.dataStr[:i]
2049 self.dataStr = self.dataStr[i + 1:]
2050
2051 if self.isFloat(tmpStr):
2052 return tmpStr
2053 else:
2054 myprint("PHPDBGEvalStr::getAStrTillEndChar(): parsing error. tried to get an float number, but get %s.", tmpStr)
2055 return None
2056
2057 def getAnInt(self, endChar):
2058 tmpStr = self.getAnIntStr(endChar)
2059 if tmpStr:
2060 return int(tmpStr)
2061 else:
2062 return 0
2063
2064 def getAnIntStr(self, endChar):
2065 if len(self.dataStr) == 0:
2066 myprint("PHPDBGEvalStr::getAnIntStr(): no more data string to work with.")
2067 return
2068
2069 i = self.findNextChar(self.dataStr, endChar)
2070 if i == -1:
2071 tmpStr = self.dataStr
2072 self.dataStr = ''
2073 else:
2074 tmpStr = self.dataStr[:i]
2075 self.dataStr = self.dataStr[i + 1:]
2076
2077 if self.isInt(tmpStr):
2078 return tmpStr
2079 else:
2080 myprint("PHPDBGEvalStr::getAnIntStr(): parsing error. tried to get an integer, but get %s.", tmpStr)
2081 return None
2082
2083 def isInt(self, aStr):
2084 try:
2085 int(aStr)
2086 except ValueError:
2087 return False
2088
2089 return True
2090
2091 def isFloat(self, aStr):
2092 try:
2093 float(aStr)
2094 except ValueError:
2095 return False
2096
2097 return True
2098
2099 def findNextChar(self, aStr, aChar):
2100 try:
2101 index = aStr.index(aChar)
2102 except ValueError:
2103 index = -1
2104
2105 return index