/Debugger/Debugger.Core/StackFrame.cs

http://github.com/icsharpcode/ILSpy · C# · 403 lines · 296 code · 49 blank · 58 comment · 45 complexity · 08c17614553a68392ee1450595fff5bf MD5 · raw file

  1. // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
  2. // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Runtime.InteropServices;
  6. using Debugger.MetaData;
  7. using Debugger.Interop.CorDebug;
  8. using Debugger.Interop.MetaData;
  9. namespace Debugger
  10. {
  11. /// <summary>
  12. /// A stack frame which is being executed on some thread.
  13. /// Use to obtain arguments or local variables.
  14. /// </summary>
  15. public class StackFrame: DebuggerObject
  16. {
  17. Thread thread;
  18. AppDomain appDomain;
  19. Process process;
  20. ICorDebugILFrame corILFrame;
  21. object corILFramePauseSession;
  22. ICorDebugFunction corFunction;
  23. DebugMethodInfo methodInfo;
  24. uint chainIndex;
  25. uint frameIndex;
  26. /// <summary> The process in which this stack frame is executed </summary>
  27. public AppDomain AppDomain {
  28. get { return appDomain; }
  29. }
  30. public Process Process {
  31. get { return process; }
  32. }
  33. /// <summary> Get the method which this stack frame is executing </summary>
  34. public DebugMethodInfo MethodInfo {
  35. get { return methodInfo; }
  36. }
  37. /// <summary> A thread in which the stack frame is executed </summary>
  38. public Thread Thread {
  39. get { return thread; }
  40. }
  41. /// <summary> Internal index of the stack chain. The value is increasing with age. </summary>
  42. public uint ChainIndex {
  43. get { return chainIndex; }
  44. }
  45. /// <summary> Internal index of the stack frame. The value is increasing with age. </summary>
  46. public uint FrameIndex {
  47. get { return frameIndex; }
  48. }
  49. /// <summary> True if the stack frame has symbols defined.
  50. /// (That is has accesss to the .pdb file) </summary>
  51. public bool HasSymbols {
  52. get {
  53. return GetSegmentForOffet(0) != null;
  54. }
  55. }
  56. /// <summary> Returns true is this incance can not be used any more. </summary>
  57. public bool IsInvalid {
  58. get {
  59. try {
  60. object frame = this.CorILFrame;
  61. return false;
  62. } catch (DebuggerException) {
  63. return true;
  64. }
  65. }
  66. }
  67. internal StackFrame(Thread thread, ICorDebugILFrame corILFrame, uint chainIndex, uint frameIndex)
  68. {
  69. this.process = thread.Process;
  70. this.thread = thread;
  71. this.appDomain = process.AppDomains[corILFrame.GetFunction().GetClass().GetModule().GetAssembly().GetAppDomain()];
  72. this.corILFrame = corILFrame;
  73. this.corILFramePauseSession = process.PauseSession;
  74. this.corFunction = corILFrame.GetFunction();
  75. this.chainIndex = chainIndex;
  76. this.frameIndex = frameIndex;
  77. MetaDataImport metaData = thread.Process.Modules[corFunction.GetClass().GetModule()].MetaData;
  78. int methodGenArgs = metaData.EnumGenericParams(corFunction.GetToken()).Length;
  79. // Class parameters are first, then the method ones
  80. List<ICorDebugType> corGenArgs = ((ICorDebugILFrame2)corILFrame).EnumerateTypeParameters().ToList();
  81. // Remove method parametrs at the end
  82. corGenArgs.RemoveRange(corGenArgs.Count - methodGenArgs, methodGenArgs);
  83. List<DebugType> genArgs = new List<DebugType>(corGenArgs.Count);
  84. foreach(ICorDebugType corGenArg in corGenArgs) {
  85. genArgs.Add(DebugType.CreateFromCorType(this.AppDomain, corGenArg));
  86. }
  87. DebugType debugType = DebugType.CreateFromCorClass(
  88. this.AppDomain,
  89. null,
  90. corFunction.GetClass(),
  91. genArgs.ToArray()
  92. );
  93. this.methodInfo = (DebugMethodInfo)debugType.GetMember(corFunction.GetToken());
  94. }
  95. /// <summary> Returns diagnostic description of the frame </summary>
  96. public override string ToString()
  97. {
  98. return this.MethodInfo.ToString();
  99. }
  100. internal ICorDebugILFrame CorILFrame {
  101. get {
  102. if (corILFramePauseSession != process.PauseSession) {
  103. // Reobtain the stackframe
  104. StackFrame stackFrame = this.Thread.GetStackFrameAt(chainIndex, frameIndex);
  105. if (stackFrame.MethodInfo != this.MethodInfo) throw new DebuggerException("The stack frame on the thread does not represent the same method anymore");
  106. corILFrame = stackFrame.corILFrame;
  107. corILFramePauseSession = stackFrame.corILFramePauseSession;
  108. }
  109. return corILFrame;
  110. }
  111. }
  112. [Debugger.Tests.Ignore]
  113. public int IP {
  114. get {
  115. uint corInstructionPtr;
  116. CorDebugMappingResult mappingResult;
  117. CorILFrame.GetIP(out corInstructionPtr, out mappingResult);
  118. return (int)corInstructionPtr;
  119. }
  120. }
  121. public int[] ILRanges { get; set; }
  122. public int SourceCodeLine { get; set; }
  123. SourcecodeSegment GetSegmentForOffet(int offset)
  124. {
  125. return SourcecodeSegment.ResolveForIL(this.MethodInfo.DebugModule, corFunction, SourceCodeLine, offset, ILRanges);
  126. }
  127. /// <summary> Step into next instruction </summary>
  128. public void StepInto()
  129. {
  130. AsyncStepInto();
  131. process.WaitForPause();
  132. }
  133. /// <summary> Step over next instruction </summary>
  134. public void StepOver()
  135. {
  136. AsyncStepOver();
  137. process.WaitForPause();
  138. }
  139. /// <summary> Step out of the stack frame </summary>
  140. public void StepOut()
  141. {
  142. AsyncStepOut();
  143. process.WaitForPause();
  144. }
  145. /// <summary> Step into next instruction </summary>
  146. public void AsyncStepInto()
  147. {
  148. AsyncStep(true);
  149. }
  150. /// <summary> Step over next instruction </summary>
  151. public void AsyncStepOver()
  152. {
  153. AsyncStep(false);
  154. }
  155. /// <summary> Step out of the stack frame </summary>
  156. public void AsyncStepOut()
  157. {
  158. Stepper.StepOut(this, "normal");
  159. AsyncContinue();
  160. }
  161. void AsyncStep(bool stepIn)
  162. {
  163. if (stepIn) {
  164. Stepper stepInStepper = Stepper.StepIn(this, ILRanges, "normal");
  165. this.Thread.CurrentStepIn = stepInStepper;
  166. Stepper clearCurrentStepIn = Stepper.StepOut(this, "clear current step in");
  167. clearCurrentStepIn.StepComplete += delegate {
  168. if (this.Thread.CurrentStepIn == stepInStepper) {
  169. this.Thread.CurrentStepIn = null;
  170. }
  171. };
  172. clearCurrentStepIn.Ignore = true;
  173. } else {
  174. Stepper.StepOver(this, ILRanges, "normal");
  175. }
  176. AsyncContinue();
  177. }
  178. void AsyncContinue()
  179. {
  180. if (process.Options.SuspendOtherThreads) {
  181. process.AsyncContinue(DebuggeeStateAction.Clear, new Thread[] { this.Thread }, CorDebugThreadState.THREAD_SUSPEND);
  182. } else {
  183. process.AsyncContinue(DebuggeeStateAction.Clear, this.Process.UnsuspendedThreads, CorDebugThreadState.THREAD_RUN);
  184. }
  185. }
  186. /// <summary>
  187. /// Get the information about the next statement to be executed.
  188. ///
  189. /// Returns null on error.
  190. /// </summary>
  191. public SourcecodeSegment NextStatement {
  192. get {
  193. return GetSegmentForOffet(IP);
  194. }
  195. }
  196. /// <summary>
  197. /// Determine whether the instrustion pointer can be set to given location
  198. /// </summary>
  199. /// <returns> Best possible location. Null is not possible. </returns>
  200. public SourcecodeSegment CanSetIP(string filename, int line, int column)
  201. {
  202. return SetIP(true, filename, line, column);
  203. }
  204. /// <summary>
  205. /// Set the instrustion pointer to given location
  206. /// </summary>
  207. /// <returns> Best possible location. Null is not possible. </returns>
  208. public SourcecodeSegment SetIP(string filename, int line, int column)
  209. {
  210. return SetIP(false, filename, line, column);
  211. }
  212. SourcecodeSegment SetIP(bool simulate, string filename, int line, int column)
  213. {
  214. process.AssertPaused();
  215. SourcecodeSegment segment = SourcecodeSegment.Resolve(this.MethodInfo.DebugModule, filename, null, line, column);
  216. if (segment != null && segment.CorFunction.GetToken() == this.MethodInfo.MetadataToken) {
  217. try {
  218. if (simulate) {
  219. CorILFrame.CanSetIP((uint)segment.ILStart);
  220. } else {
  221. // Invalidates all frames and chains for the current thread
  222. CorILFrame.SetIP((uint)segment.ILStart);
  223. process.NotifyResumed(DebuggeeStateAction.Keep);
  224. process.NotifyPaused(PausedReason.SetIP);
  225. process.RaisePausedEvents();
  226. }
  227. } catch {
  228. return null;
  229. }
  230. return segment;
  231. }
  232. return null;
  233. }
  234. /// <summary>
  235. /// Gets the instance of the class asociated with the current frame.
  236. /// That is, 'this' in C#.
  237. /// Note that for delegates and enumerators this returns the instance of the display class.
  238. /// The get the captured this, use GetLocalVariableThis.
  239. /// </summary>
  240. [Debugger.Tests.Ignore]
  241. public Value GetThisValue()
  242. {
  243. return new Value(appDomain, GetThisCorValue());
  244. }
  245. ICorDebugValue GetThisCorValue()
  246. {
  247. if (this.MethodInfo.IsStatic) throw new GetValueException("Static method does not have 'this'.");
  248. ICorDebugValue corValue;
  249. try {
  250. corValue = CorILFrame.GetArgument(0);
  251. } catch (COMException e) {
  252. // System.Runtime.InteropServices.COMException (0x80131304): An IL variable is not available at the current native IP. (See Forum-8640)
  253. if ((uint)e.ErrorCode == 0x80131304) throw new GetValueException("Not available in the current state");
  254. throw;
  255. }
  256. // This can be 'by ref' for value types
  257. if (corValue.GetTheType() == (uint)CorElementType.BYREF) {
  258. corValue = ((ICorDebugReferenceValue)corValue).Dereference();
  259. }
  260. return corValue;
  261. }
  262. /// <summary> Total number of arguments (excluding implicit 'this' argument) </summary>
  263. public int ArgumentCount {
  264. get {
  265. ICorDebugValueEnum argumentEnum = CorILFrame.EnumerateArguments();
  266. uint argCount = argumentEnum.GetCount();
  267. if (!this.MethodInfo.IsStatic) {
  268. argCount--; // Remove 'this' from count
  269. }
  270. return (int)argCount;
  271. }
  272. }
  273. /// <summary> Gets argument with a given name </summary>
  274. /// <returns> Null if not found </returns>
  275. public Value GetArgumentValue(string name)
  276. {
  277. DebugParameterInfo par = this.MethodInfo.GetParameter(name);
  278. if (par == null)
  279. return null;
  280. return GetArgumentValue(par.Position);
  281. }
  282. /// <summary> Gets argument with a given index </summary>
  283. /// <param name="index"> Zero-based index </param>
  284. public Value GetArgumentValue(int index)
  285. {
  286. return new Value(appDomain, GetArgumentCorValue(index));
  287. }
  288. ICorDebugValue GetArgumentCorValue(int index)
  289. {
  290. ICorDebugValue corValue;
  291. try {
  292. // Non-static methods include 'this' as first argument
  293. corValue = CorILFrame.GetArgument((uint)(this.MethodInfo.IsStatic? index : (index + 1)));
  294. } catch (COMException e) {
  295. if ((uint)e.ErrorCode == 0x80131304) throw new GetValueException("Unavailable in optimized code");
  296. throw;
  297. }
  298. // Method arguments can be passed 'by ref'
  299. if (corValue.GetTheType() == (uint)CorElementType.BYREF) {
  300. try {
  301. corValue = ((ICorDebugReferenceValue)corValue).Dereference();
  302. } catch (COMException e) {
  303. if ((uint)e.ErrorCode == 0x80131305) {
  304. // A reference value was found to be bad during dereferencing.
  305. // This can sometimes happen after a stack overflow
  306. throw new GetValueException("Bad reference");
  307. } else {
  308. throw;
  309. }
  310. }
  311. }
  312. return corValue;
  313. }
  314. /// <summary> Get local variable with given name </summary>
  315. /// <returns> Null if not found </returns>
  316. public Value GetLocalVariableValue(string name)
  317. {
  318. DebugLocalVariableInfo loc = this.MethodInfo.GetLocalVariable(this.IP, name);
  319. if (loc == null)
  320. return null;
  321. return loc.GetValue(this);
  322. }
  323. /// <summary> Get instance of 'this'. It works well with delegates and enumerators. </summary>
  324. [Debugger.Tests.Ignore]
  325. public Value GetLocalVariableThis()
  326. {
  327. DebugLocalVariableInfo thisVar = this.MethodInfo.GetLocalVariableThis();
  328. if (thisVar != null)
  329. return thisVar.GetValue(this);
  330. return null;
  331. }
  332. public override bool Equals(object obj)
  333. {
  334. StackFrame other = obj as StackFrame;
  335. return
  336. other != null &&
  337. other.Thread == this.Thread &&
  338. other.ChainIndex == this.ChainIndex &&
  339. other.FrameIndex == this.FrameIndex &&
  340. other.MethodInfo == this.methodInfo;
  341. }
  342. public override int GetHashCode()
  343. {
  344. int hashCode = 0;
  345. unchecked {
  346. if (thread != null) hashCode += 1000000009 * thread.GetHashCode();
  347. if (methodInfo != null) hashCode += 1000000093 * methodInfo.GetHashCode();
  348. hashCode += 1000000097 * chainIndex.GetHashCode();
  349. hashCode += 1000000103 * frameIndex.GetHashCode();
  350. }
  351. return hashCode;
  352. }
  353. }
  354. }