// // System.Diagnostics.StackFrame.cs // // Author: // Alexander Klyubin (klyubin@aqris.com) // Dietmar Maurer (dietmar@ximian.com) // // (C) 2001 // using System; using System.Reflection; using System.Runtime.CompilerServices; namespace System.Diagnostics { /// /// Stack frame. /// [Serializable] public class StackFrame { /// /// Constant returned when the native or IL offset is unknown. /// public const int OFFSET_UNKNOWN = -1; /// /// Offset from the start of the IL code for the method /// being executed. /// private int ilOffset = OFFSET_UNKNOWN; /// /// Offset from the start of the native code for the method /// being executed. /// private int nativeOffset = OFFSET_UNKNOWN; /// /// Method associated with this stack frame. /// private MethodBase methodBase; /// /// File name. /// private string fileName; /// /// Line number. /// private int lineNumber; /// /// Column number. /// private int columnNumber; #if TARGET_JVM static bool get_frame_info (int skip, bool needFileInfo, out MethodBase method, out int iloffset, out int native_offset, out string file, out int line, out int column) { native_offset = 0; line = 0; column = 0; file = ""; iloffset = 0; method = null; return false; } #else [MethodImplAttribute(MethodImplOptions.InternalCall)] extern static bool get_frame_info (int skip, bool needFileInfo, out MethodBase method, out int iloffset, out int native_offset, out string file, out int line, out int column); #endif /// /// Initializes a new StackFrame object corresponding to the /// active stack frame. /// public StackFrame() { get_frame_info (2, false, out methodBase, out ilOffset, out nativeOffset, out fileName, out lineNumber, out columnNumber); } /// /// Initializes a new StackFrame object corresponding to the /// active stack frame. /// /// /// TODO: /// public StackFrame(bool needFileInfo) : this() { get_frame_info (2, needFileInfo, out methodBase, out ilOffset, out nativeOffset, out fileName, out lineNumber, out columnNumber); } /// /// Initializes a new StackFrame object corresponding to the /// active stack frame. /// /// /// The number of frames up the stack to skip. /// public StackFrame(int skipFrames) { get_frame_info (skipFrames + 2, false, out methodBase, out ilOffset, out nativeOffset, out fileName, out lineNumber, out columnNumber); } /// /// Initializes a new StackFrame object corresponding to the /// active stack frame. /// /// /// The number of frames up the stack to skip. /// /// /// TODO: /// public StackFrame(int skipFrames, bool needFileInfo) { get_frame_info (skipFrames + 2, needFileInfo, out methodBase, out ilOffset, out nativeOffset, out fileName, out lineNumber, out columnNumber); } /// /// Constructs a fake stack frame that just contains the /// given file name and line number. Use this constructor /// when you do not want to use the debugger's line mapping /// logic. /// /// /// The given file name. /// /// /// The line number in the specified file. /// // LAMESPEC: According to the MSDN docs, this creates a // fake stack frame. But MS fills out the frame info as well public StackFrame(string fileName, int lineNumber) { get_frame_info (2, false, out methodBase, out ilOffset, out nativeOffset, out fileName, out lineNumber, out columnNumber); this.fileName = fileName; this.lineNumber = lineNumber; this.columnNumber = 0; } /// /// Constructs a fake stack frame that just contains the /// given file name and line number. Use this constructor /// when you do not want to use the debugger's line mapping /// logic. /// /// /// The given file name. /// /// /// The line number in the specified file. /// /// /// The column number in the specified file. /// // LAMESPEC: According to the MSDN docs, this creates a // fake stack frame. But MS fills out the frame info as well public StackFrame(string fileName, int lineNumber, int colNumber) { get_frame_info (2, false, out methodBase, out ilOffset, out nativeOffset, out fileName, out lineNumber, out columnNumber); this.fileName = fileName; this.lineNumber = lineNumber; this.columnNumber = colNumber; } /// /// Gets the line number in the file containing the code /// being executed. This information is typically extracted /// from the debugging symbols for the executable. /// /// /// The file line number or zero if it cannot be determined. /// public virtual int GetFileLineNumber() { return lineNumber; } /// /// Gets the column number in the file containing the code /// being executed. This information is typically extracted /// from the debugging symbols for the executable. /// /// /// The file column number or zero if it cannot be determined. /// public virtual int GetFileColumnNumber() { return columnNumber; } /// /// Gets the file name containing the code being executed. /// This information is typically extracted from the /// debugging symbols for the executable. /// /// /// The file name or null if it cannot be determined. /// public virtual string GetFileName() { return fileName; } /// /// Gets the offset from the start of the IL code for the /// method being executed. This offset may be approximate /// depending on whether the JIT compiler is generating /// debugging code or not. /// /// /// The offset from the start of the IL code for the method /// being executed. /// public virtual int GetILOffset() { return ilOffset; } /// /// Gets the method in which the frame is executing. /// /// /// The method the frame is executing in. /// public virtual MethodBase GetMethod() { return methodBase; } /// /// Gets the offset from the start of the native /// (JIT-compiled) code for the method being executed. /// /// /// The offset from the start of the native (JIT-compiled) /// code or the method being executed. /// public virtual int GetNativeOffset() { return nativeOffset; } /// /// Builds a readable representation of the stack frame. /// /// /// A readable representation of the stack frame. /// public override string ToString() { string methodNameString = (GetMethod() == null) ? "" : GetMethod().Name; string offsetString = (GetILOffset() == OFFSET_UNKNOWN) ? "" : "offset " + GetILOffset(); string fileNameString = (GetFileName() == null) ? "" : GetFileName(); return methodNameString + " at " + offsetString + " in file:line:column " + fileNameString + ":" + GetFileLineNumber() + ":" + GetFileColumnNumber(); } public override bool Equals(Object obj) { if ((obj == null) || (!(obj is StackFrame))) { return false; } StackFrame rhs = (StackFrame) obj; if (!ObjectsEqual(GetMethod(), rhs.GetMethod())) { return false; } if (!ObjectsEqual(GetFileName(), rhs.GetFileName())) { return false; } if (GetFileLineNumber() != rhs.GetFileLineNumber()) { return false; } if (GetFileColumnNumber() != rhs.GetFileColumnNumber()) { return false; } if (GetILOffset() != rhs.GetILOffset()) { return false; } if (GetNativeOffset() != rhs.GetNativeOffset()) { return false; } return true; } public override int GetHashCode() { return GetFileLineNumber(); } /// /// Checks whether two objects are equal. /// The objects are assumed equal if and only if either /// both of the references are null or they /// equal via Equals method. /// /// /// First object. /// /// /// Second object. /// /// /// true if the two objects are equal, /// false otherwise. /// private static bool ObjectsEqual(Object obj1, Object obj2) { if (obj1 == null) { return (obj2 == null); } else { return obj1.Equals(obj2); } } } }