Processor_Debugger.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using MoonSharp.Interpreter.DataStructs;
  6. using MoonSharp.Interpreter.Debugging;
  7. namespace MoonSharp.Interpreter.Execution.VM
  8. {
  9. // This part is practically written procedural style - it looks more like C than C#.
  10. // This is intentional so to avoid this-calls and virtual-calls as much as possible.
  11. // Same reason for the "sealed" declaration.
  12. sealed partial class Processor
  13. {
  14. internal void AttachDebugger(IDebugger debugger)
  15. {
  16. m_Debug.DebuggerAttached = debugger;
  17. }
  18. private void ListenDebugger(Instruction instr, int instructionPtr)
  19. {
  20. if (m_Debug.DebuggerAttached.IsPauseRequested() ||
  21. (instr.SourceCodeRef != null && instr.SourceCodeRef.Breakpoint && instr.SourceCodeRef != m_Debug.LastHlRef))
  22. {
  23. m_Debug.DebuggerCurrentAction = DebuggerAction.ActionType.None;
  24. m_Debug.DebuggerCurrentActionTarget = -1;
  25. }
  26. switch (m_Debug.DebuggerCurrentAction)
  27. {
  28. case DebuggerAction.ActionType.Run:
  29. return;
  30. case DebuggerAction.ActionType.ByteCodeStepOver:
  31. if (m_Debug.DebuggerCurrentActionTarget != instructionPtr) return;
  32. break;
  33. case DebuggerAction.ActionType.ByteCodeStepOut:
  34. case DebuggerAction.ActionType.StepOut:
  35. if (m_ExecutionStack.Count >= m_Debug.ExStackDepthAtStep) return;
  36. break;
  37. case DebuggerAction.ActionType.StepIn:
  38. if ((m_ExecutionStack.Count >= m_Debug.ExStackDepthAtStep) && (instr.SourceCodeRef == null || instr.SourceCodeRef == m_Debug.LastHlRef)) return;
  39. break;
  40. case DebuggerAction.ActionType.StepOver:
  41. if (instr.SourceCodeRef == null || instr.SourceCodeRef == m_Debug.LastHlRef || m_ExecutionStack.Count > m_Debug.ExStackDepthAtStep) return;
  42. break;
  43. }
  44. RefreshDebugger(false, instructionPtr);
  45. while (true)
  46. {
  47. var action = m_Debug.DebuggerAttached.GetAction(instructionPtr, instr.SourceCodeRef);
  48. switch (action.Action)
  49. {
  50. case DebuggerAction.ActionType.StepIn:
  51. case DebuggerAction.ActionType.StepOver:
  52. case DebuggerAction.ActionType.StepOut:
  53. case DebuggerAction.ActionType.ByteCodeStepOut:
  54. m_Debug.DebuggerCurrentAction = action.Action;
  55. m_Debug.LastHlRef = instr.SourceCodeRef;
  56. m_Debug.ExStackDepthAtStep = m_ExecutionStack.Count;
  57. return;
  58. case DebuggerAction.ActionType.ByteCodeStepIn:
  59. m_Debug.DebuggerCurrentAction = DebuggerAction.ActionType.ByteCodeStepIn;
  60. m_Debug.DebuggerCurrentActionTarget = -1;
  61. return;
  62. case DebuggerAction.ActionType.ByteCodeStepOver:
  63. m_Debug.DebuggerCurrentAction = DebuggerAction.ActionType.ByteCodeStepOver;
  64. m_Debug.DebuggerCurrentActionTarget = instructionPtr + 1;
  65. return;
  66. case DebuggerAction.ActionType.Run:
  67. m_Debug.DebuggerCurrentAction = DebuggerAction.ActionType.Run;
  68. m_Debug.DebuggerCurrentActionTarget = -1;
  69. return;
  70. case DebuggerAction.ActionType.ToggleBreakpoint:
  71. ToggleBreakPoint(action, null);
  72. RefreshDebugger(true, instructionPtr);
  73. break;
  74. case DebuggerAction.ActionType.SetBreakpoint:
  75. ToggleBreakPoint(action, true);
  76. RefreshDebugger(true, instructionPtr);
  77. break;
  78. case DebuggerAction.ActionType.ClearBreakpoint:
  79. ToggleBreakPoint(action, false);
  80. RefreshDebugger(true, instructionPtr);
  81. break;
  82. case DebuggerAction.ActionType.Refresh:
  83. RefreshDebugger(false, instructionPtr);
  84. break;
  85. case DebuggerAction.ActionType.HardRefresh:
  86. RefreshDebugger(true, instructionPtr);
  87. break;
  88. case DebuggerAction.ActionType.None:
  89. default:
  90. break;
  91. }
  92. }
  93. }
  94. private bool ToggleBreakPoint(DebuggerAction action, bool? state)
  95. {
  96. SourceCode src = m_Script.GetSourceCode(action.SourceID);
  97. bool found = false;
  98. foreach (SourceRef srf in src.Refs)
  99. {
  100. if (srf.CannotBreakpoint)
  101. continue;
  102. if (srf.IncludesLocation(action.SourceID, action.SourceLine, action.SourceCol))
  103. {
  104. found = true;
  105. //System.Diagnostics.Debug.WriteLine(string.Format("BRK: found {0} for {1} on contains", srf, srf.Type));
  106. if (state == null)
  107. srf.Breakpoint = !srf.Breakpoint;
  108. else
  109. srf.Breakpoint = state.Value;
  110. if (srf.Breakpoint)
  111. {
  112. m_Debug.BreakPoints.Add(srf);
  113. }
  114. else
  115. {
  116. m_Debug.BreakPoints.Remove(srf);
  117. }
  118. }
  119. }
  120. if (!found)
  121. {
  122. int minDistance = int.MaxValue;
  123. SourceRef nearest = null;
  124. foreach (SourceRef srf in src.Refs)
  125. {
  126. if (srf.CannotBreakpoint)
  127. continue;
  128. int dist = srf.GetLocationDistance(action.SourceID, action.SourceLine, action.SourceCol);
  129. if (dist < minDistance)
  130. {
  131. minDistance = dist;
  132. nearest = srf;
  133. }
  134. }
  135. if (nearest != null)
  136. {
  137. //System.Diagnostics.Debug.WriteLine(string.Format("BRK: found {0} for {1} on distance {2}", nearest, nearest.Type, minDistance));
  138. if (state == null)
  139. nearest.Breakpoint = !nearest.Breakpoint;
  140. else
  141. nearest.Breakpoint = state.Value;
  142. if (nearest.Breakpoint)
  143. {
  144. m_Debug.BreakPoints.Add(nearest);
  145. }
  146. else
  147. {
  148. m_Debug.BreakPoints.Remove(nearest);
  149. }
  150. return true;
  151. }
  152. else
  153. return false;
  154. }
  155. else
  156. return true;
  157. }
  158. private void RefreshDebugger(bool hard, int instructionPtr)
  159. {
  160. SourceRef sref = GetCurrentSourceRef(instructionPtr);
  161. ScriptExecutionContext context = new ScriptExecutionContext(this, null, sref);
  162. List<DynamicExpression> watchList = m_Debug.DebuggerAttached.GetWatchItems();
  163. List<WatchItem> callStack = Debugger_GetCallStack(sref);
  164. List<WatchItem> watches = Debugger_RefreshWatches(context, watchList);
  165. List<WatchItem> vstack = Debugger_RefreshVStack();
  166. m_Debug.DebuggerAttached.Update(WatchType.CallStack, callStack);
  167. m_Debug.DebuggerAttached.Update(WatchType.Watches, watches);
  168. m_Debug.DebuggerAttached.Update(WatchType.VStack, vstack);
  169. if (hard)
  170. m_Debug.DebuggerAttached.RefreshBreakpoints(m_Debug.BreakPoints);
  171. }
  172. private List<WatchItem> Debugger_RefreshVStack()
  173. {
  174. List<WatchItem> lwi = new List<WatchItem>();
  175. for (int i = 0; i < Math.Min(32, m_ValueStack.Count); i++)
  176. {
  177. lwi.Add(new WatchItem()
  178. {
  179. Address = i,
  180. Value = m_ValueStack.Peek(i)
  181. });
  182. }
  183. return lwi;
  184. }
  185. private List<WatchItem> Debugger_RefreshWatches(ScriptExecutionContext context, List<DynamicExpression> watchList)
  186. {
  187. return watchList.Select(w => Debugger_RefreshWatch(context, w)).ToList();
  188. }
  189. private WatchItem Debugger_RefreshWatch(ScriptExecutionContext context, DynamicExpression dynExpr)
  190. {
  191. try
  192. {
  193. SymbolRef L = dynExpr.FindSymbol(context);
  194. DynValue v = dynExpr.Evaluate(context);
  195. return new WatchItem()
  196. {
  197. IsError = dynExpr.IsConstant(),
  198. LValue = L,
  199. Value = v,
  200. Name = dynExpr.ExpressionCode
  201. };
  202. }
  203. catch (Exception ex)
  204. {
  205. return new WatchItem()
  206. {
  207. IsError = true,
  208. Value = DynValue.NewString(ex.Message),
  209. Name = dynExpr.ExpressionCode
  210. };
  211. }
  212. }
  213. internal List<WatchItem> Debugger_GetCallStack(SourceRef startingRef)
  214. {
  215. List<WatchItem> wis = new List<WatchItem>();
  216. for (int i = 0; i < m_ExecutionStack.Count; i++)
  217. {
  218. var c = m_ExecutionStack.Peek(i);
  219. var I = m_RootChunk.Code[c.Debug_EntryPoint];
  220. string callname = I.OpCode == OpCode.FuncMeta ? I.Name : null;
  221. if (c.ClrFunction != null)
  222. {
  223. wis.Add(new WatchItem()
  224. {
  225. Address = -1,
  226. BasePtr = -1,
  227. RetAddress = c.ReturnAddress,
  228. Location = startingRef,
  229. Name = c.ClrFunction.Name
  230. });
  231. }
  232. else
  233. {
  234. wis.Add(new WatchItem()
  235. {
  236. Address = c.Debug_EntryPoint,
  237. BasePtr = c.BasePointer,
  238. RetAddress = c.ReturnAddress,
  239. Name = callname,
  240. Location = startingRef,
  241. });
  242. }
  243. startingRef = c.CallingSourceRef;
  244. if (c.Continuation != null)
  245. {
  246. wis.Add(new WatchItem()
  247. {
  248. Name = c.Continuation.Name,
  249. Location = SourceRef.GetClrLocation()
  250. });
  251. }
  252. }
  253. return wis;
  254. }
  255. }
  256. }