349 lines
11 KiB
C#
349 lines
11 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
namespace System.Diagnostics {
|
|
|
|
using System.Text;
|
|
using System;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Security.Permissions;
|
|
using System.Diagnostics.Contracts;
|
|
|
|
// There is no good reason for the methods of this class to be virtual.
|
|
// In order to ensure trusted code can trust the data it gets from a
|
|
// StackTrace, we use an InheritanceDemand to prevent partially-trusted
|
|
// subclasses.
|
|
#if !FEATURE_CORECLR
|
|
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode=true)]
|
|
#endif
|
|
[Serializable]
|
|
[System.Runtime.InteropServices.ComVisible(true)]
|
|
public class StackFrame
|
|
{
|
|
private MethodBase method;
|
|
private int offset;
|
|
private int ILOffset;
|
|
private String strFileName;
|
|
private int iLineNumber;
|
|
private int iColumnNumber;
|
|
|
|
#if FEATURE_EXCEPTIONDISPATCHINFO
|
|
[System.Runtime.Serialization.OptionalField]
|
|
private bool fIsLastFrameFromForeignExceptionStackTrace;
|
|
#endif // FEATURE_EXCEPTIONDISPATCHINFO
|
|
|
|
internal void InitMembers()
|
|
{
|
|
method = null;
|
|
offset = OFFSET_UNKNOWN;
|
|
ILOffset = OFFSET_UNKNOWN;
|
|
strFileName = null;
|
|
iLineNumber = 0;
|
|
iColumnNumber = 0;
|
|
#if FEATURE_EXCEPTIONDISPATCHINFO
|
|
fIsLastFrameFromForeignExceptionStackTrace = false;
|
|
#endif // FEATURE_EXCEPTIONDISPATCHINFO
|
|
|
|
}
|
|
|
|
// Constructs a StackFrame corresponding to the active stack frame.
|
|
#if FEATURE_CORECLR
|
|
[System.Security.SecuritySafeCritical]
|
|
#endif
|
|
public StackFrame()
|
|
{
|
|
InitMembers();
|
|
BuildStackFrame (0 + StackTrace.METHODS_TO_SKIP, false);// iSkipFrames=0
|
|
}
|
|
|
|
// Constructs a StackFrame corresponding to the active stack frame.
|
|
#if FEATURE_CORECLR
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
#endif
|
|
public StackFrame(bool fNeedFileInfo)
|
|
{
|
|
InitMembers();
|
|
BuildStackFrame (0 + StackTrace.METHODS_TO_SKIP, fNeedFileInfo);// iSkipFrames=0
|
|
}
|
|
|
|
// Constructs a StackFrame corresponding to a calling stack frame.
|
|
//
|
|
public StackFrame(int skipFrames)
|
|
{
|
|
InitMembers();
|
|
BuildStackFrame (skipFrames + StackTrace.METHODS_TO_SKIP, false);
|
|
}
|
|
|
|
// Constructs a StackFrame corresponding to a calling stack frame.
|
|
//
|
|
#if FEATURE_CORECLR
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
#endif
|
|
public StackFrame(int skipFrames, bool fNeedFileInfo)
|
|
{
|
|
InitMembers();
|
|
BuildStackFrame (skipFrames + StackTrace.METHODS_TO_SKIP, fNeedFileInfo);
|
|
}
|
|
|
|
|
|
// Called from the class "StackTrace"
|
|
//
|
|
internal StackFrame(bool DummyFlag1, bool DummyFlag2)
|
|
{
|
|
InitMembers();
|
|
}
|
|
|
|
// Constructs a "fake" stack frame, just containing the given file
|
|
// name and line number. Use when you don't want to use the
|
|
// debugger's line mapping logic.
|
|
//
|
|
public StackFrame(String fileName, int lineNumber)
|
|
{
|
|
InitMembers();
|
|
BuildStackFrame (StackTrace.METHODS_TO_SKIP, false);
|
|
strFileName = fileName;
|
|
iLineNumber = lineNumber;
|
|
iColumnNumber = 0;
|
|
}
|
|
|
|
|
|
// Constructs a "fake" stack frame, just containing the given file
|
|
// name, line number and column number. Use when you don't want to
|
|
// use the debugger's line mapping logic.
|
|
//
|
|
public StackFrame(String fileName, int lineNumber, int colNumber)
|
|
{
|
|
InitMembers();
|
|
BuildStackFrame (StackTrace.METHODS_TO_SKIP, false);
|
|
strFileName = fileName;
|
|
iLineNumber = lineNumber;
|
|
iColumnNumber = colNumber;
|
|
}
|
|
|
|
|
|
// Constant returned when the native or IL offset is unknown
|
|
public const int OFFSET_UNKNOWN = -1;
|
|
|
|
|
|
internal virtual void SetMethodBase (MethodBase mb)
|
|
{
|
|
method = mb;
|
|
}
|
|
|
|
internal virtual void SetOffset (int iOffset)
|
|
{
|
|
offset = iOffset;
|
|
}
|
|
|
|
internal virtual void SetILOffset (int iOffset)
|
|
{
|
|
ILOffset = iOffset;
|
|
}
|
|
|
|
internal virtual void SetFileName (String strFName)
|
|
{
|
|
strFileName = strFName;
|
|
}
|
|
|
|
internal virtual void SetLineNumber (int iLine)
|
|
{
|
|
iLineNumber = iLine;
|
|
}
|
|
|
|
internal virtual void SetColumnNumber (int iCol)
|
|
{
|
|
iColumnNumber = iCol;
|
|
}
|
|
|
|
#if FEATURE_EXCEPTIONDISPATCHINFO
|
|
internal virtual void SetIsLastFrameFromForeignExceptionStackTrace (bool fIsLastFrame)
|
|
{
|
|
fIsLastFrameFromForeignExceptionStackTrace = fIsLastFrame;
|
|
}
|
|
|
|
internal virtual bool GetIsLastFrameFromForeignExceptionStackTrace()
|
|
{
|
|
return fIsLastFrameFromForeignExceptionStackTrace;
|
|
}
|
|
#endif // FEATURE_EXCEPTIONDISPATCHINFO
|
|
|
|
// Returns the method the frame is executing
|
|
//
|
|
public virtual MethodBase GetMethod ()
|
|
{
|
|
Contract.Ensures(Contract.Result<MethodBase>() != null);
|
|
|
|
return method;
|
|
}
|
|
|
|
// Returns the offset from the start of the native (jitted) code for the
|
|
// method being executed
|
|
//
|
|
public virtual int GetNativeOffset ()
|
|
{
|
|
return offset;
|
|
}
|
|
|
|
|
|
// Returns the offset from the start of the IL code for the
|
|
// method being executed. This offset may be approximate depending
|
|
// on whether the jitter is generating debuggable code or not.
|
|
//
|
|
public virtual int GetILOffset()
|
|
{
|
|
return ILOffset;
|
|
}
|
|
|
|
// Returns the file name containing the code being executed. This
|
|
// information is normally extracted from the debugging symbols
|
|
// for the executable.
|
|
//
|
|
#if FEATURE_CORECLR
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
#else
|
|
[System.Security.SecuritySafeCritical]
|
|
#endif
|
|
public virtual String GetFileName()
|
|
{
|
|
if (strFileName != null)
|
|
{
|
|
// This isn't really correct, but we don't have
|
|
// a permission that protects discovery of potentially
|
|
// local urls so we'll use this.
|
|
|
|
FileIOPermission perm = new FileIOPermission( PermissionState.None );
|
|
perm.AllFiles = FileIOPermissionAccess.PathDiscovery;
|
|
perm.Demand();
|
|
}
|
|
|
|
return strFileName;
|
|
}
|
|
|
|
// Returns the line number in the file containing the code being executed.
|
|
// This information is normally extracted from the debugging symbols
|
|
// for the executable.
|
|
//
|
|
public virtual int GetFileLineNumber()
|
|
{
|
|
return iLineNumber;
|
|
}
|
|
|
|
// Returns the column number in the line containing the code being executed.
|
|
// This information is normally extracted from the debugging symbols
|
|
// for the executable.
|
|
//
|
|
public virtual int GetFileColumnNumber()
|
|
{
|
|
return iColumnNumber;
|
|
}
|
|
|
|
|
|
// Builds a readable representation of the stack frame
|
|
//
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
public override String ToString()
|
|
{
|
|
StringBuilder sb = new StringBuilder(255);
|
|
|
|
if (method != null)
|
|
{
|
|
sb.Append(method.Name);
|
|
|
|
// deal with the generic portion of the method
|
|
if (method is MethodInfo && ((MethodInfo)method).IsGenericMethod)
|
|
{
|
|
Type[] typars = ((MethodInfo)method).GetGenericArguments();
|
|
|
|
sb.Append("<");
|
|
int k = 0;
|
|
bool fFirstTyParam = true;
|
|
while (k < typars.Length)
|
|
{
|
|
if (fFirstTyParam == false)
|
|
sb.Append(",");
|
|
else
|
|
fFirstTyParam = false;
|
|
|
|
sb.Append(typars[k].Name);
|
|
k++;
|
|
}
|
|
|
|
sb.Append(">");
|
|
}
|
|
|
|
sb.Append(" at offset ");
|
|
if (offset == OFFSET_UNKNOWN)
|
|
sb.Append("<offset unknown>");
|
|
else
|
|
sb.Append(offset);
|
|
|
|
sb.Append(" in file:line:column ");
|
|
|
|
bool useFileName = (strFileName != null);
|
|
|
|
if (useFileName)
|
|
{
|
|
try
|
|
{
|
|
// This isn't really correct, but we don't have
|
|
// a permission that protects discovery of potentially
|
|
// local urls so we'll use this.
|
|
|
|
FileIOPermission perm = new FileIOPermission(PermissionState.None);
|
|
perm.AllFiles = FileIOPermissionAccess.PathDiscovery;
|
|
perm.Demand();
|
|
}
|
|
catch (System.Security.SecurityException)
|
|
{
|
|
useFileName = false;
|
|
}
|
|
}
|
|
|
|
if (!useFileName)
|
|
sb.Append("<filename unknown>");
|
|
else
|
|
sb.Append(strFileName);
|
|
sb.Append(":");
|
|
sb.Append(iLineNumber);
|
|
sb.Append(":");
|
|
sb.Append(iColumnNumber);
|
|
}
|
|
else
|
|
{
|
|
sb.Append("<null>");
|
|
}
|
|
sb.Append(Environment.NewLine);
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
|
|
private void BuildStackFrame(int skipFrames, bool fNeedFileInfo)
|
|
{
|
|
StackFrameHelper StackF = new StackFrameHelper(fNeedFileInfo, null);
|
|
|
|
StackTrace.GetStackFramesInternal (StackF, 0, null);
|
|
|
|
int iNumOfFrames = StackF.GetNumberOfFrames();
|
|
|
|
skipFrames += StackTrace.CalculateFramesToSkip (StackF, iNumOfFrames);
|
|
|
|
if ((iNumOfFrames - skipFrames) > 0)
|
|
{
|
|
method = StackF.GetMethodBase (skipFrames);
|
|
offset = StackF.GetOffset (skipFrames);
|
|
ILOffset = StackF.GetILOffset (skipFrames);
|
|
if (fNeedFileInfo)
|
|
{
|
|
strFileName = StackF.GetFilename (skipFrames);
|
|
iLineNumber = StackF.GetLineNumber (skipFrames);
|
|
iColumnNumber = StackF.GetColumnNumber (skipFrames);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|