Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

adb.py 18KB

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