]> git.saurik.com Git - wxWidgets.git/blame - wxPython/samples/ide/activegrid/tool/DebuggerHarness.py
Patch from Davide Salomoni that adds an optional point
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / DebuggerHarness.py
CommitLineData
1f780e48
RD
1#----------------------------------------------------------------------------
2# Name: DebuggerHarness.py
3# Purpose:
4#
5# Author: Matt Fryer
6#
7# Created: 7/28/04
8# CVS-ID: $Id$
9# Copyright: (c) 2005 ActiveGrid, Inc.
10# License: wxWindows License
11#----------------------------------------------------------------------------
12import bdb
13import sys
14import SimpleXMLRPCServer
15import threading
16import xmlrpclib
17import os
18import types
19import Queue
20import traceback
21import inspect
22from xml.dom.minidom import getDOMImplementation
23import atexit
24import pickle
25
26if sys.platform.startswith("win"):
27 import win32api
28 _WINDOWS = True
29else:
30 _WINDOWS = False
31
32_VERBOSE = False
33_DEBUG_DEBUGGER = False
34
35class Adb(bdb.Bdb):
36
37 def __init__(self, harness, queue):
38 bdb.Bdb.__init__(self)
39 self._harness = harness
40 self._userBreak = False
41 self._queue = queue
42 self._knownCantExpandFiles = {}
43 self._knownExpandedFiles = {}
44
45 def getLongName(self, filename):
46 if not _WINDOWS:
47 return filename
48 if self._knownCantExpandFiles.get(filename):
49 return filename
50 if self._knownExpandedFiles.get(filename):
51 return self._knownExpandedFiles.get(filename)
52 try:
53 newname = win32api.GetLongPathName(filename)
54 self._knownExpandedFiles[filename] = newname
55 return newname
56 except:
57 self._knownCantExpandFiles[filename] = filename
58 return filename
59
60 def canonic(self, orig_filename):
61 if orig_filename == "<" + orig_filename[1:-1] + ">":
62 return orig_filename
63 filename = self.getLongName(orig_filename)
64
65 canonic = self.fncache.get(filename)
66 if not canonic:
67 canonic = os.path.abspath(filename)
68 canonic = os.path.normcase(canonic)
69 self.fncache[filename] = canonic
70 return canonic
71
72
73 # Overriding this so that we continue to trace even if no breakpoints are set.
74 def set_continue(self):
75 self.stopframe = self.botframe
76 self.returnframe = None
77 self.quitting = 0
78
79 def do_clear(self, arg):
80 bdb.Breakpoint.bpbynumber[int(arg)].deleteMe()
81
82 def user_line(self, frame):
83 if self.in_debugger_code(frame):
84 self.set_step()
85 return
86 message = self.__frame2message(frame)
87 self._harness.interaction(message, frame, "")
88
89 def user_call(self, frame, argument_list):
90 if self.in_debugger_code(frame):
91 self.set_step()
92 return
93 if self.stop_here(frame):
94 message = self.__frame2message(frame)
95 self._harness.interaction(message, frame, "")
96
97 def user_return(self, frame, return_value):
98 if self.in_debugger_code(frame):
99 self.set_step()
100 return
101 message = self.__frame2message(frame)
102 self._harness.interaction(message, frame, "")
103
104 def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
105 frame.f_locals['__exception__'] = exc_type, exc_value
106 if type(exc_type) == type(''):
107 exc_type_name = exc_type
108 else:
109 exc_type_name = exc_type.__name__
110 message = "Exception occured: " + repr(exc_type_name) + " See locals.__exception__ for details."
111 traceback.print_exception(exc_type, exc_value, exc_traceback)
112 self._harness.interaction(message, frame, message)
113
114 def in_debugger_code(self, frame):
115 if _DEBUG_DEBUGGER: return False
116 message = self.__frame2message(frame)
117 return message.count('DebuggerHarness') > 0
118
119 def frame2message(self, frame):
120 return self.__frame2message(frame)
121
122 def __frame2message(self, frame):
123 code = frame.f_code
124 filename = code.co_filename
125 lineno = frame.f_lineno
126 basename = os.path.basename(filename)
127 message = "%s:%s" % (basename, lineno)
128 if code.co_name != "?":
129 message = "%s: %s()" % (message, code.co_name)
130 return message
131
132 def runFile(self, fileName):
133 self.reset()
134 #global_dict = {}
135 #global_dict['__name__'] = '__main__'
136 try:
137 fileToRun = open(fileName, mode='r')
138 if _VERBOSE: print "Running file ", fileName
139 sys.settrace(self.trace_dispatch)
140 import __main__
141 exec fileToRun in __main__.__dict__,__main__.__dict__
142 except SystemExit:
143 pass
144 except:
145 tp, val, tb = sys.exc_info()
146 traceback.print_exception(tp, val, tb)
147
148 sys.settrace(None)
149 self.quitting = 1
150 #global_dict.clear()
151
152 def trace_dispatch(self, frame, event, arg):
153 if self.quitting:
154 return # None
155 # Check for ui events
156 self.readQueue()
157 if event == 'line':
158 return self.dispatch_line(frame)
159 if event == 'call':
160 return self.dispatch_call(frame, arg)
161 if event == 'return':
162 return self.dispatch_return(frame, arg)
163 if event == 'exception':
164 return self.dispatch_exception(frame, arg)
165 print 'Adb.dispatch: unknown debugging event:', `event`
166 return self.trace_dispatch
167
168 def readQueue(self):
169 while self._queue.qsize():
170 try:
171 item = self._queue.get_nowait()
172 if item.kill():
173 self._harness.do_exit(kill=True)
174 elif item.breakHere():
175 self._userBreak = True
176 elif item.hasBreakpoints():
177 self.set_all_breakpoints(item.getBreakpoints())
178 except Queue.Empty:
179 pass
180
181 def set_all_breakpoints(self, dict):
182 self.clear_all_breaks()
183 for fileName in dict.keys():
184 lineList = dict[fileName]
185 for lineNumber in lineList:
186
187 if _VERBOSE: print "Setting break at line ", str(lineNumber), " in file ", self.canonic(fileName)
188 self.set_break(fileName, int(lineNumber))
189 return ""
190
191 def stop_here(self, frame):
192 if( self._userBreak ):
193 return True
194
195
196 # (CT) stopframe may now also be None, see dispatch_call.
197 # (CT) the former test for None is therefore removed from here.
198 if frame is self.stopframe:
199 return True
200 while frame is not None and frame is not self.stopframe:
201 if frame is self.botframe:
202 return True
203 frame = frame.f_back
204 return False
205
206class BreakNotify(object):
207 def __init__(self, bps=None, break_here=False, kill=False):
208 self._bps = bps
209 self._break_here = break_here
210 self._kill = kill
211
212 def breakHere(self):
213 return self._break_here
214
215 def kill(self):
216 return self._kill
217
218 def getBreakpoints(self):
219 return self._bps
220
221 def hasBreakpoints(self):
222 return (self._bps != None)
223
224class AGXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
225 def __init__(self, address, logRequests=0):
226 SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, address, logRequests=logRequests)
227
228class BreakListenerThread(threading.Thread):
229 def __init__(self, host, port, queue):
230 threading.Thread.__init__(self)
231 self._host = host
232 self._port = int(port)
233 self._keepGoing = True
234 self._queue = queue
235 self._server = AGXMLRPCServer((self._host, self._port), logRequests=0)
236 self._server.register_function(self.update_breakpoints)
237 self._server.register_function(self.break_requested)
238 self._server.register_function(self.die)
239
240 def break_requested(self):
241 bn = BreakNotify(break_here=True)
242 self._queue.put(bn)
243 return ""
244
245 def update_breakpoints(self, pickled_Binary_bpts):
246 dict = pickle.loads(pickled_Binary_bpts.data)
247 bn = BreakNotify(bps=dict)
248 self._queue.put(bn)
249 return ""
250
251 def die(self):
252 bn = BreakNotify(kill=True)
253 self._queue.put(bn)
254 return ""
255
256 def run(self):
257 while self._keepGoing:
258 try:
259 self._server.handle_request()
260 except:
261 if _VERBOSE:
262 tp, val, tb = sys.exc_info()
263 print "Exception in BreakListenerThread.run():", str(tp), str(val)
264 self._keepGoing = False
265
266 def AskToStop(self):
267 self._keepGoing = False
268 if type(self._server) is not types.NoneType:
269 if _VERBOSE: print "Before calling server close on breakpoint server"
270 self._server.server_close()
271 if _VERBOSE: print "Calling server close on breakpoint server"
272
273
274class DebuggerHarness(object):
275
276 def __init__(self):
277 # Host and port for debugger-side RPC server
278 self._hostname = sys.argv[1]
279 self._portNumber = int(sys.argv[2])
280 # Name the gui proxy object is registered under
281 self._breakPortNumber = int(sys.argv[3])
282 # Host and port where the gui proxy can be found.
283 self._guiHost = sys.argv[4]
284 self._guiPort = int(sys.argv[5])
285 # Command to debug.
286 self._command = sys.argv[6]
287 # Strip out the harness' arguments so that the process we run will see argv as if
288 # it was called directly.
289 sys.argv = sys.argv[6:]
290 self._currentFrame = None
291 self._wait = False
292 # Connect to the gui-side RPC server.
293 self._guiServerUrl = 'http://' + self._guiHost + ':' + str(self._guiPort) + '/'
294 if _VERBOSE: print "Connecting to gui server at ", self._guiServerUrl
295 self._guiServer = xmlrpclib.ServerProxy(self._guiServerUrl,allow_none=1)
296
297 # Start the break listener
298 self._breakQueue = Queue.Queue(50)
299 self._breakListener = BreakListenerThread(self._hostname, self._breakPortNumber, self._breakQueue)
300 self._breakListener.start()
301 # Create the debugger.
302 self._adb = Adb(self, self._breakQueue)
303
304 # Create the debugger-side RPC Server and register functions for remote calls.
305 self._server = AGXMLRPCServer((self._hostname, self._portNumber), logRequests=0)
306 self._server.register_function(self.set_step)
307 self._server.register_function(self.set_continue)
308 self._server.register_function(self.set_next)
309 self._server.register_function(self.set_return)
310 self._server.register_function(self.set_breakpoint)
311 self._server.register_function(self.clear_breakpoint)
312 self._server.register_function(self.set_all_breakpoints)
313 self._server.register_function(self.attempt_introspection)
314 self._server.register_function(self.add_watch)
315
316 self.message_frame_dict = {}
317 self.introspection_list = []
318 atexit.register(self.do_exit)
319
320 def run(self):
321 self._adb.runFile(self._command)
322 self.do_exit(kill=True)
323
324
325 def do_exit(self, kill=False):
326 self._adb.set_quit()
327 self._breakListener.AskToStop()
328 self._server.server_close()
329 try:
330 self._guiServer.quit()
331 except:
332 pass
333 if kill:
334 try:
335 sys.exit()
336 except:
337 pass
338
339 def set_breakpoint(self, fileName, lineNo):
340 self._adb.set_break(fileName, lineNo)
341 return ""
342
343 def set_all_breakpoints(self, dict):
344 self._adb.clear_all_breaks()
345 for fileName in dict.keys():
346 lineList = dict[fileName]
347 for lineNumber in lineList:
348 self._adb.set_break(fileName, int(lineNumber))
349 if _VERBOSE: print "Setting break at ", str(lineNumber), " in file ", fileName
350 return ""
351
352 def clear_breakpoint(self, fileName, lineNo):
353 self._adb.clear_break(fileName, lineNo)
354 return ""
355
356 def add_watch(self, name, text, frame_message, run_once):
357 if len(frame_message) > 0:
358 frame = self.message_frame_dict[frame_message]
359 try:
360 item = eval(text, frame.f_globals, frame.f_locals)
361 return self.get_watch_document(item, name)
362 except:
363 tp, val, tb = sys.exc_info()
364 return self.get_exception_document(tp, val, tb)
365 return ""
366
367 def attempt_introspection(self, frame_message, chain):
368 try:
369 frame = self.message_frame_dict[frame_message]
370 if frame:
371 name = chain.pop(0)
372 if name == 'globals':
373 item = frame.f_globals
374 elif name == 'locals':
375 item = frame.f_locals
376
377 for name in chain:
378 item = self.getNextItem(item, name)
379 return self.get_introspection_document(item, name)
380 except:
381 tp, val, tb = sys.exc_info()
382 traceback.print_exception(tp, val, tb)
383 return self.get_empty_introspection_document()
384
385 def getNextItem(self, link, identifier):
386 tp = type(link)
387 if self.isTupleized(identifier):
388 return self.deTupleize(link, identifier)
389 else:
390 if tp == types.DictType or tp == types.DictProxyType:
391 return link[identifier]
392 else:
393 if hasattr(link, identifier):
394 return getattr(link, identifier)
395 if _VERBOSE or True: print "Failed to find link ", identifier, " on thing: ", self.saferepr(link), " of type ", repr(type(link))
396 return None
397
398 def isPrimitive(self, item):
399 tp = type(item)
400 return tp is types.IntType or tp is types.LongType or tp is types.FloatType \
401 or tp is types.BooleanType or tp is types.ComplexType \
402 or tp is types.StringType
403
404 def isTupleized(self, value):
405 return value.count('[')
406
407 def deTupleize(self, link, string1):
408 try:
409 start = string1.find('[')
410 end = string1.find(']')
411 num = int(string1[start+1:end])
412 return link[num]
413 except:
414 tp,val,tb = sys.exc_info()
415 if _VERBOSE: print "Got exception in deTupleize: ", val
416 return None
417
418 def wrapAndCompress(self, stringDoc):
419 import bz2
420 return xmlrpclib.Binary(bz2.compress(stringDoc))
421
422 def get_empty_introspection_document(self):
423 doc = getDOMImplementation().createDocument(None, "replacement", None)
424 return self.wrapAndCompress(doc.toxml())
425
426 def get_watch_document(self, item, identifier):
427 doc = getDOMImplementation().createDocument(None, "watch", None)
428 top_element = doc.documentElement
429 self.addAny(top_element, identifier, item, doc, 2)
430 return self.wrapAndCompress(doc.toxml())
431
432 def get_introspection_document(self, item, identifier):
433 doc = getDOMImplementation().createDocument(None, "replacement", None)
434 top_element = doc.documentElement
435 self.addAny(top_element, identifier, item, doc, 2)
436 return self.wrapAndCompress(doc.toxml())
437
438 def get_exception_document(self, name, tp, val, tb):
439 stack = traceback.format_exception(tp, val, tb)
440 wholeStack = ""
441 for line in stack:
442 wholeStack += line
443 doc = getDOMImplementation().createDocument(None, "watch", None)
444 top_element = doc.documentElement
445 item_node = doc.createElement("dict_nv_element")
446 item_node.setAttribute('value', wholeStack)
447 item_node.setAttribute('name', str(name))
448 top_element.appendChild(item_node)
449
450 def addAny(self, top_element, name, item, doc, ply):
451 tp = type(item)
452 if ply < 1:
453 self.addNode(top_element,name, self.saferepr(item), doc)
454 elif tp is types.TupleType or tp is types.ListType:
455 self.addTupleOrList(top_element, name, item, doc, ply - 1)
456 elif tp is types.DictType or tp is types.DictProxyType:
457 self.addDict(top_element, name, item, doc, ply -1)
458 elif inspect.ismodule(item):
459 self.addModule(top_element, name, item, doc, ply -1)
460 elif inspect.isclass(item) or tp is types.InstanceType:
461 self.addClass(top_element, name, item, doc, ply -1)
462 #elif hasattr(item, '__dict__'):
463 # self.addDictAttr(top_element, name, item, doc, ply -1)
464 elif hasattr(item, '__dict__'):
465 self.addDict(top_element, name, item.__dict__, doc, ply -1)
466 else:
467 self.addNode(top_element,name, self.saferepr(item), doc)
468
469 def addTupleOrList(self, top_node, name, tupple, doc, ply):
470 tupleNode = doc.createElement('tuple')
471 tupleNode.setAttribute('name', str(name))
472 tupleNode.setAttribute('value', str(type(tupple)))
473 top_node.appendChild(tupleNode)
474 count = 0
475 for item in tupple:
476 self.addAny(tupleNode, name +'[' + str(count) + ']',item, doc, ply -1)
477 count += 1
478
479
480 def getFrameXML(self, base_frame):
481 doc = getDOMImplementation().createDocument(None, "stack", None)
482 top_element = doc.documentElement
483
484 stack = []
485 frame = base_frame
486 while frame is not None:
487 if((frame.f_code.co_filename.count('DebuggerHarness.py') == 0) or _DEBUG_DEBUGGER):
488 stack.append(frame)
489 frame = frame.f_back
490 stack.reverse()
491 self.message_frame_dict = {}
492 for f in stack:
493 self.addFrame(f,top_element, doc)
494 return doc.toxml()
495
496 def addFrame(self, frame, root_element, document):
497 frameNode = document.createElement('frame')
498 root_element.appendChild(frameNode)
499
500 code = frame.f_code
501 filename = code.co_filename
502 frameNode.setAttribute('file', str(filename))
503 frameNode.setAttribute('line', str(frame.f_lineno))
504 message = self._adb.frame2message(frame)
505 frameNode.setAttribute('message', message)
506 #print "Frame: %s %s %s" %(message, frame.f_lineno, filename)
507 self.message_frame_dict[message] = frame
508 self.addDict(frameNode, "locals", frame.f_locals, document, 2)
509 self.addNode(frameNode, "globals", "", document)
510
511 def getRepr(self, varName, globals, locals):
512 try:
513 return repr(eval(varName, globals, locals))
514 except:
515 return 'Error: Could not recover value.'
516
517 def addNode(self, parent_node, name, value, document):
518 item_node = document.createElement("dict_nv_element")
519 item_node.setAttribute('value', self.saferepr(value))
520 item_node.setAttribute('name', str(name))
521 parent_node.appendChild(item_node)
522
523 def addDictAttr(self, root_node, name, thing, document, ply):
524 dict_node = document.createElement('thing')
525 root_node.setAttribute('name', name)
526 root_node.setAttribute('value', str(type(dict)) + " add attr")
527 self.addDict(root_node, name, thing.__dict__, document, ply) # Not decreminting ply
528
529 def saferepr(self, thing):
530 try:
531 return repr(thing)
532 except:
533 tp, val, tb = sys.exc_info()
534 return repr(val)
535
536 def addDict(self, root_node, name, dict, document, ply):
537 dict_node = document.createElement('dict')
538 dict_node.setAttribute('name', name)
539 dict_node.setAttribute('value', str(type(dict)) + " add dict")
540 root_node.appendChild(dict_node)
541 for key in dict.keys():
542 strkey = str(key)
543 try:
544 self.addAny(dict_node, strkey, dict[key], document, ply-1)
545 except:
546 tp,val,tb=sys.exc_info()
547 if _VERBOSE:
548 print "Error recovering key: ", str(key), " from node ", str(name), " Val = ", str(val)
549 traceback.print_exception(tp, val, tb)
550 self.addAny(dict_node, strkey, "Exception getting " + str(name) + "[" + strkey + "]: " + str(val), document, ply -1)
551
552 def addClass(self, root_node, name, class_item, document, ply):
553 item_node = document.createElement('class')
554 item_node.setAttribute('name', str(name))
555 root_node.appendChild(item_node)
556 try:
557 if hasattr(class_item, '__dict__'):
558 self.addAny(item_node, '__dict__', class_item.__dict__, document, ply -1)
559 except:
560 tp,val,tb=sys.exc_info()
561 if _VERBOSE:
562 traceback.print_exception(tp, val, tb)
563 self.addAny(item_node, '__dict__', "Exception getting __dict__: " + str(val), document, ply -1)
564 try:
565 if hasattr(class_item, '__name__'):
566 self.addAny(item_node,'__name__',class_item.__name__, document, ply -1)
567 except:
568 tp,val,tb=sys.exc_info()
569 if _VERBOSE:
570 traceback.print_exception(tp, val, tb)
571 self.addAny(item_node,'__name__',"Exception getting class.__name__: " + val, document, ply -1)
572 try:
573 if hasattr(class_item, '__module__'):
574 self.addAny(item_node, '__module__', class_item.__module__, document, ply -1)
575 except:
576 tp,val,tb=sys.exc_info()
577 if _VERBOSE:
578 traceback.print_exception(tp, val, tb)
579 self.addAny(item_node, '__module__', "Exception getting class.__module__: " + val, document, ply -1)
580 try:
581 if hasattr(class_item, '__doc__'):
582 self.addAny(item_node, '__doc__', class_item.__doc__, document, ply -1)
583 except:
584 tp,val,tb=sys.exc_info()
585 if _VERBOSE:
586 traceback.print_exception(tp, val, tb)
587 self.addAny(item_node, '__doc__', "Exception getting class.__doc__: " + val, document, ply -1)
588 try:
589 if hasattr(class_item, '__bases__'):
590 self.addAny(item_node, '__bases__', class_item.__bases__, document, ply -1)
591 except:
592 tp,val,tb=sys.exc_info()
593 if _VERBOSE:
594 traceback.print_exception(tp, val, tb)
595 self.addAny(item_node, '__bases__', "Exception getting class.__bases__: " + val, document, ply -1)
596
597 def addModule(self, root_node, name, module_item, document, ply):
598 item_node = document.createElement('module')
599 item_node.setAttribute('name', str(name))
600 root_node.appendChild(item_node)
601 try:
602 if hasattr(module_item, '__file__'):
603 self.addAny(item_node, '__file__', module_item.__file__, document, ply -1)
604 except:
605 pass
606 try:
607 if hasattr(module_item, '__doc__'):
608 self.addAny(item_node,'__doc__', module_item.__doc__, document, ply -1)
609 except:
610 pass
611
612 # The debugger calls this method when it reaches a breakpoint.
613 def interaction(self, message, frame, info):
614 if _VERBOSE:
615 print 'hit debug side interaction'
616 self._userBreak = False
617
618 self._currentFrame = frame
619 done = False
620 while not done:
621 try:
622 import bz2
623 xml = self.getFrameXML(frame)
624 arg = xmlrpclib.Binary(bz2.compress(xml))
625 if _VERBOSE:
626 print '============== calling gui side interaction============'
627 self._guiServer.interaction(xmlrpclib.Binary(message), arg, info)
628 if _VERBOSE:
629 print 'after interaction'
630 done = True
631 except:
632 tp, val, tb = sys.exc_info()
633 if True or _VERBOSE:
634 print 'Error contacting GUI server!: '
635 try:
636 traceback.print_exception(tp, val, tb)
637 except:
638 print "Exception printing traceback",
639 tp, val, tb = sys.exc_info()
640 traceback.print_exception(tp, val, tb)
641 done = False
642 # Block while waiting to be called back from the GUI. Eventually, self._wait will
643 # be set false by a function on this side. Seems pretty lame--I'm surprised it works.
644 self.waitForRPC()
645
646
647 def waitForRPC(self):
648 self._wait = True
649 while self._wait :
650 try:
651 if _VERBOSE:
652 print "+++ in harness wait for rpc, before handle_request"
653 self._server.handle_request()
654 if _VERBOSE:
655 print "+++ in harness wait for rpc, after handle_request"
656 except:
657 if _VERBOSE:
658 tp, val, tb = sys.exc_info()
659 print "Got waitForRpc exception : ", repr(tp), ": ", val
660 #time.sleep(0.1)
661
662 def set_step(self):
663 self._adb.set_step()
664 self._wait = False
665 return ""
666
667 def set_continue(self):
668 self._adb.set_continue()
669 self._wait = False
670 return ""
671
672 def set_next(self):
673 self._adb.set_next(self._currentFrame)
674 self._wait = False
675 return ""
676
677 def set_return(self):
678 self._adb.set_return(self._currentFrame)
679 self._wait = False
680 return ""
681
682if __name__ == '__main__':
683 try:
684 harness = DebuggerHarness()
685 harness.run()
686 except SystemExit:
687 print "Exiting..."
688 except:
689 tp, val, tb = sys.exc_info()
690 traceback.print_exception(tp, val, tb)