home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activepython / ActivePython-2.1.1.msi / Python21_win32comext_axdebug_adb.py < prev    next >
Encoding:
Python Source  |  2001-07-26  |  14.3 KB  |  396 lines

  1. """The glue between the Python debugger interface and the Active Debugger interface
  2. """
  3. from win32com.axdebug.util import trace, _wrap, _wrap_remove
  4. from win32com.server.util import unwrap
  5. import win32com.client.connect
  6. import gateways
  7. import sys, bdb, traceback
  8. import axdebug, stackframe
  9. import win32api, pythoncom
  10. import thread, os
  11.  
  12. def fnull(*args):
  13.     pass
  14.  
  15. try:
  16.     os.environ["DEBUG_AXDEBUG"]
  17.     debugging = 1
  18. except KeyError:
  19.     debugging = 0
  20.  
  21. traceenter = fnull # trace enter of functions
  22. tracev = fnull # verbose trace
  23.  
  24. if debugging:
  25.     traceenter = trace # trace enter of functions
  26.     tracev = trace # verbose trace
  27.  
  28. def _dumpf(frame):
  29.     if frame is None:
  30.         return "<None>"
  31.     else:
  32.         addn = "(with trace!)"
  33.         if frame.f_trace is None:
  34.             addn = " **No Trace Set **"
  35.         return "Frame at %d, file %s, line: %d%s" % (id(frame), frame.f_code.co_filename, frame.f_lineno, addn)
  36.  
  37. g_adb = None
  38.  
  39. def OnSetBreakPoint(codeContext, breakPointState, lineNo):
  40.     try:
  41.         fileName = codeContext.codeContainer.GetFileName()
  42.         # inject the code into linecache.
  43.         import linecache
  44.         linecache.cache[fileName] = 0, 0, codeContext.codeContainer.GetText(), fileName
  45.         g_adb._OnSetBreakPoint(fileName, codeContext, breakPointState, lineNo+1)
  46.     except:
  47.         traceback.print_exc()
  48.  
  49. class Adb(bdb.Bdb,gateways.RemoteDebugApplicationEvents):
  50.     def __init__(self):
  51.         self.debugApplication = None
  52.         self.debuggingThread = None
  53.         self.debuggingThreadStateHandle = None
  54.         self.stackSnifferCookie = self.stackSniffer = None
  55.         self.codeContainerProvider = None
  56.         self.debuggingThread = None
  57.         self.breakFlags = None
  58.         self.breakReason = None
  59.         self.appDebugger = None
  60.         self.appEventConnection = None
  61.         self.logicalbotframe = None # Anything at this level or below does not exist!
  62.         self.currentframe = None # The frame we are currently in.
  63.         self.recursiveData = [] # Data saved for each reentery on this thread.
  64.         bdb.Bdb.__init__(self)
  65.         self._threadprotectlock = thread.allocate_lock()
  66.         self.reset()
  67.  
  68.     def canonic(self, fname):
  69.         if fname[0]=='<':
  70.             return fname
  71.         return bdb.Bdb.canonic(self, fname)
  72.  
  73.     def reset(self):
  74.         traceenter("adb.reset")
  75.         bdb.Bdb.reset(self)
  76.         
  77.     def __xxxxx__set_break(self, filename, lineno, cond = None):
  78.         # As per standard one, except no linecache checking!
  79.         if not self.breaks.has_key(filename):
  80.             self.breaks[filename] = []
  81.         list = self.breaks[filename]
  82.         if lineno in list:
  83.             return 'There is already a breakpoint there!'
  84.         list.append(lineno)
  85.         if cond is not None: self.cbreaks[filename, lineno]=cond
  86.         
  87.     def stop_here(self, frame):
  88.         traceenter("stop_here", _dumpf(frame), _dumpf(self.stopframe))
  89.         # As per bdb.stop_here, except for logicalbotframe
  90.         if self.stopframe is None:
  91.             return 1
  92.         if frame is self.stopframe:
  93.             return 1
  94.         
  95.         tracev("stop_here said 'No'!")
  96.         return 0
  97.         
  98.     def break_here(self, frame):
  99.         traceenter("break_here", self.breakFlags, _dumpf(frame))
  100.         self.breakReason = None
  101.         if self.breakFlags==axdebug.APPBREAKFLAG_DEBUGGER_HALT:
  102.             self.breakReason = axdebug.BREAKREASON_DEBUGGER_HALT
  103.         elif self.breakFlags==axdebug.APPBREAKFLAG_DEBUGGER_BLOCK:
  104.             self.breakReason = axdebug.BREAKREASON_DEBUGGER_BLOCK
  105.         elif self.breakFlags==axdebug.APPBREAKFLAG_STEP:
  106.             self.breakReason = axdebug.BREAKREASON_STEP
  107.         else:
  108.             print "Calling base 'break_here' with", self.breaks
  109.             if bdb.Bdb.break_here(self, frame):
  110.                 self.breakReason = axdebug.BREAKREASON_BREAKPOINT
  111.         return self.breakReason is not None
  112.     
  113.     def break_anywhere(self, frame):
  114.         traceenter("break_anywhere", _dumpf(frame))
  115.         if self.breakFlags==axdebug.APPBREAKFLAG_DEBUGGER_HALT:
  116.             self.breakReason = axdebug.BREAKREASON_DEBUGGER_HALT
  117.             return 1
  118.         rc = bdb.Bdb.break_anywhere(self, frame)
  119.         tracev("break_anywhere",_dumpf(frame),"returning",rc)
  120.         return rc
  121.  
  122.     def dispatch_return(self, frame, arg):
  123.         traceenter("dispatch_return", _dumpf(frame), arg)
  124.         if self.logicalbotframe is frame:
  125.             # We dont want to debug parent frames.
  126.             tracev("dispatch_return resetting sys.trace")
  127.             sys.settrace(None)
  128.             return
  129. #            self.bSetTrace = 0
  130.         self.currentframe = frame.f_back
  131.         return bdb.Bdb.dispatch_return(self, frame, arg)
  132.  
  133.     def dispatch_line(self, frame):
  134.         traceenter("dispatch_line", _dumpf(frame), _dumpf(self.botframe))
  135. #        trace("logbotframe is", _dumpf(self.logicalbotframe), "botframe is", self.botframe)
  136.         if frame is self.logicalbotframe:
  137.             trace("dispatch_line", _dumpf(frame), "for bottom frame returing tracer")
  138.             # The next code executed in the frame above may be a builtin (eg, apply())
  139.             # in which sys.trace needs to be set.
  140.             sys.settrace(self.trace_dispatch)
  141.             # And return the tracer incase we are about to execute Python code,
  142.             # in which case sys tracer is ignored!
  143.             return self.trace_dispatch
  144.     
  145.         if self.codeContainerProvider.FromFileName(frame.f_code.co_filename) is None:
  146.             trace("dispatch_line has no document for", _dumpf(frame), "- skipping trace!")
  147.             return None
  148.         self.currentframe = frame # So the stack sniffer knows our most recent, debuggable code.
  149.         return bdb.Bdb.dispatch_line(self, frame)
  150.     
  151.     def dispatch_call(self, frame, arg):
  152.         traceenter("dispatch_call",_dumpf(frame))
  153.         frame.f_locals['__axstack_address__'] = axdebug.GetStackAddress()
  154.         if frame is self.botframe:
  155.             trace("dispatch_call is self.botframe - returning tracer")
  156.             return self.trace_dispatch
  157.         # Not our bottom frame.  If we have a document for it,
  158.         # then trace it, otherwise run at full speed.
  159.         if self.codeContainerProvider.FromFileName(frame.f_code.co_filename) is None:
  160.             trace("dispatch_call has no document for", _dumpf(frame), "- skipping trace!")
  161. ##            sys.settrace(None)
  162.             return None
  163.         return self.trace_dispatch
  164.         
  165. #        rc =  bdb.Bdb.dispatch_call(self, frame, arg)
  166. #        trace("dispatch_call", _dumpf(frame),"returned",rc)
  167. #        return rc
  168.  
  169.     def trace_dispatch(self, frame, event, arg):
  170.         traceenter("trace_dispatch", _dumpf(frame), event, arg)
  171.         if self.debugApplication is None:
  172.             trace("trace_dispatch has no application!")
  173.             return # None
  174.         return bdb.Bdb.trace_dispatch(self, frame, event, arg)
  175.  
  176.  
  177.     #
  178.     # The user functions do bugger all!
  179.     #
  180. #    def user_call(self, frame, argument_list):
  181. #        traceenter("user_call",_dumpf(frame))
  182.  
  183.     def user_line(self, frame):
  184.         traceenter("user_line",_dumpf(frame))
  185.         # Traces at line zero
  186.         if frame.f_lineno!=0:
  187.             breakReason = self.breakReason
  188.             if breakReason is None:
  189.                 breakReason = axdebug.BREAKREASON_STEP
  190.             self._HandleBreakPoint(frame, None, breakReason)
  191.  
  192.     def user_return(self, frame, return_value):
  193. #        traceenter("user_return",_dumpf(frame),return_value)
  194.         bdb.Bdb.user_return(self, frame, return_value)
  195.     
  196.     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
  197. #        traceenter("user_exception")
  198.         bdb.Bdb.user_exception(self, frame, (exc_type, exc_value, exc_traceback))
  199.     
  200.  
  201.     def _HandleBreakPoint(self, frame, tb, reason):
  202.         traceenter("Calling HandleBreakPoint with reason", reason,"at frame", _dumpf(frame))
  203.         traceenter(" Current frame is", _dumpf(self.currentframe))
  204.         try:
  205.             resumeAction = self.debugApplication.HandleBreakPoint(reason)
  206.             tracev("HandleBreakPoint returned with ", resumeAction)
  207.         except pythoncom.com_error, details:
  208.             # Eeek - the debugger is dead, or something serious is happening.
  209.             # Assume we should continue
  210.             resumeAction = axdebug.BREAKRESUMEACTION_CONTINUE
  211.             trace("HandleBreakPoint FAILED with", details)
  212.         
  213.         self.stack = []
  214.         self.curindex = 0
  215.         if resumeAction == axdebug.BREAKRESUMEACTION_ABORT:
  216.             self.set_quit()
  217.         elif resumeAction == axdebug.BREAKRESUMEACTION_CONTINUE:
  218.             tracev("resume action is continue")
  219.             self.set_continue()
  220.         elif resumeAction == axdebug.BREAKRESUMEACTION_STEP_INTO:
  221.             tracev("resume action is step")
  222.             self.set_step()
  223.         elif resumeAction == axdebug.BREAKRESUMEACTION_STEP_OVER:
  224.             tracev("resume action is next")
  225.             self.set_next(frame)
  226.         elif resumeAction == axdebug.BREAKRESUMEACTION_STEP_OUT:
  227.             tracev("resume action is stop out")
  228.             self.set_return(frame)
  229.         else:
  230.             assert(0, "unknown resume action flags")
  231.         self.breakReason = None
  232.  
  233.     def set_trace(self):
  234.         self.breakReason = axdebug.BREAKREASON_LANGUAGE_INITIATED
  235.         bdb.Bdb.set_trace(self)
  236.  
  237.     def CloseApp(self):
  238.         traceenter("ClosingApp")
  239.         self.reset()
  240.         self.logicalbotframe = None
  241.         if self.stackSnifferCookie is not None:
  242.             try:
  243.                 self.debugApplication.RemoveStackFrameSniffer(self.stackSnifferCookie)
  244.  
  245.             except pythoncom.com_error:
  246.                 trace("*** Could not RemoveStackFrameSniffer %d" % (self.stackSnifferCookie))
  247.         if self.stackSniffer:
  248.             _wrap_remove(self.stackSniffer)
  249.         self.stackSnifferCookie = self.stackSniffer = None
  250.  
  251.         if self.appEventConnection is not None:
  252.             self.appEventConnection.Disconnect()
  253.             self.appEventConnection = None
  254.         self.debugApplication = None
  255.         self.appDebugger = None
  256.         if self.codeContainerProvider is not None:
  257.             self.codeContainerProvider.Close()
  258.             self.codeContainerProvider = None
  259.         
  260.     def AttachApp(self, debugApplication, codeContainerProvider):
  261. #        traceenter("AttachApp", debugApplication, codeContainerProvider)
  262.         self.codeContainerProvider = codeContainerProvider
  263.         self.debugApplication = debugApplication
  264.         self.stackSniffer = _wrap(stackframe.DebugStackFrameSniffer(self), axdebug.IID_IDebugStackFrameSniffer)
  265.         self.stackSnifferCookie = debugApplication.AddStackFrameSniffer(self.stackSniffer)
  266. #        trace("StackFrameSniffer added (%d)" % self.stackSnifferCookie)
  267.         
  268.         # Connect to the application events.
  269.         self.appEventConnection = win32com.client.connect.SimpleConnection(self.debugApplication, self, axdebug.IID_IRemoteDebugApplicationEvents)
  270.         
  271.     def ResetAXDebugging(self):
  272.         traceenter("ResetAXDebugging", self, "with refcount", len(self.recursiveData))
  273.         if win32api.GetCurrentThreadId()!=self.debuggingThread:
  274.             trace("ResetAXDebugging called on other thread")
  275.             return
  276.  
  277.         if len(self.recursiveData)==0:
  278. #            print "ResetAXDebugging called for final time."
  279.             self.logicalbotframe = None
  280.             self.debuggingThread = None
  281.             self.currentframe = None
  282.             self.debuggingThreadStateHandle = None
  283.             return
  284.  
  285.         self.logbotframe, self.stopframe, self.currentframe, self.debuggingThreadStateHandle = self.recursiveData[0]
  286.         self.recursiveData = self.recursiveData[1:]
  287.         
  288.     def SetupAXDebugging(self, baseFrame = None, userFrame = None):
  289.         """Get ready for potential debugging.  Must be called on the thread
  290.         that is being debugged.
  291.         """
  292.         # userFrame is for non AXScript debugging.  This is the first frame of the
  293.         # users code.
  294.         if userFrame is None: 
  295.             userFrame = baseFrame
  296.         else:
  297.             # We have missed the "dispatch_call" function, so set this up now!
  298.             userFrame.f_locals['__axstack_address__'] = axdebug.GetStackAddress()
  299.  
  300.         traceenter("SetupAXDebugging", self)
  301.         self._threadprotectlock.acquire()
  302.         try:
  303.             thisThread = win32api.GetCurrentThreadId()
  304.             if self.debuggingThread is None:
  305.                 self.debuggingThread = thisThread
  306.             else:
  307.                 if self.debuggingThread!=thisThread:
  308.                     trace("SetupAXDebugging called on other thread - ignored!")
  309.                     return
  310.                 # push our context.
  311.                 self.recursiveData.insert(0, (self.logicalbotframe,self.stopframe, self.currentframe,self.debuggingThreadStateHandle))
  312.         finally:
  313.             self._threadprotectlock.release()
  314.  
  315.         trace("SetupAXDebugging has base frame as", _dumpf(baseFrame))
  316.         self.botframe = baseFrame
  317.         self.stopframe = userFrame
  318.         self.logicalbotframe = baseFrame
  319.         self.currentframe = None
  320.         self.debuggingThreadStateHandle = axdebug.GetThreadStateHandle()
  321.  
  322.         self._BreakFlagsChanged()
  323.         
  324.     # RemoteDebugApplicationEvents
  325.     def OnConnectDebugger(self, appDebugger):
  326.         traceenter("OnConnectDebugger", appDebugger)
  327.         self.appDebugger = appDebugger
  328.         
  329.     def OnDisconnectDebugger(self):
  330.         traceenter("OnDisconnectDebugger")
  331.         self.set_quit()
  332.  
  333.     def OnSetName(self, name):
  334.         traceenter("OnSetName", name)
  335.     def OnDebugOutput(self, string):
  336.         traceenter("OnDebugOutput", string)
  337.     def OnClose(self):
  338.         traceenter("OnClose")
  339.     def OnEnterBreakPoint(self, rdat):
  340.         traceenter("OnEnterBreakPoint", rdat)
  341.     def OnLeaveBreakPoint(self, rdat):
  342.         traceenter("OnLeaveBreakPoint", rdat)
  343.     def OnCreateThread(self, rdat):
  344.         traceenter("OnCreateThread", rdat)
  345.     def OnDestroyThread(self, rdat):
  346.         traceenter("OnDestroyThread", rdat)
  347.     def OnBreakFlagChange(self, abf, rdat):
  348.         traceenter("Debugger OnBreakFlagChange", abf, rdat)
  349.         self.breakFlags = abf
  350.         self._BreakFlagsChanged()
  351.         
  352.     def _BreakFlagsChanged(self):
  353.         traceenter("_BreakFlagsChanged to %s with our thread = %s, and debugging thread = %s" % (self.breakFlags, self.debuggingThread, win32api.GetCurrentThreadId()))
  354.         trace("_BreakFlagsChanged has breaks", self.breaks)
  355.         # If a request comes on our debugging thread, then do it now!
  356. #        if self.debuggingThread!=win32api.GetCurrentThreadId():
  357. #            return
  358.  
  359.         if len(self.breaks) or self.breakFlags:
  360.  
  361.             if self.logicalbotframe:
  362.                 trace("BreakFlagsChange with bot frame", _dumpf(self.logicalbotframe))
  363.                 # We have frames not to be debugged (eg, Scripting engine frames
  364.                 # (sys.settrace will be set when out logicalbotframe is hit - 
  365.                 #  this may not be the right thing to do, as it may not cause the
  366.                 #  immediate break we desire.)
  367.                 self.logicalbotframe.f_trace = self.trace_dispatch
  368.             else:
  369.                 trace("BreakFlagsChanged, but no bottom frame")
  370.                 if self.stopframe is not None:
  371.                     self.stopframe.f_trace = self.trace_dispatch
  372.             # If we have the thread-state for the thread being debugged, then
  373.             # we dynamically set its trace function - it is possible that the thread
  374.             # being debugged is in a blocked call (eg, a message box) and we
  375.             # want to hit the debugger the instant we return
  376.         if self.debuggingThreadStateHandle is not None and \
  377.            self.breakFlags and \
  378.            self.debuggingThread != win32api.GetCurrentThreadId():
  379.             axdebug.SetThreadStateTrace(self.debuggingThreadStateHandle, self.trace_dispatch)
  380.     def _OnSetBreakPoint(self, key, codeContext, bps, lineNo):
  381.         traceenter("_OnSetBreakPoint", self, key, codeContext, bps, lineNo)
  382.         if bps==axdebug.BREAKPOINT_ENABLED:
  383.             problem = self.set_break(key, lineNo)
  384.             if problem:
  385.                 print "*** set_break failed -", problem
  386.             trace("_OnSetBreakPoint just set BP and has breaks", self.breaks)
  387.         else:
  388.             self.clear_break(key, lineNo)
  389.         self._BreakFlagsChanged()
  390.         trace("_OnSetBreakPoint leaving with breaks", self.breaks)
  391.  
  392. def Debugger():
  393.     global g_adb
  394.     if g_adb is None:
  395.         g_adb = Adb()
  396.     return g_adb