PageRenderTime 65ms CodeModel.GetById 19ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 0ms

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