| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- using System.Collections;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Reflection;
- using System.Runtime.CompilerServices;
- using System.Text;
- namespace System.Diagnostics
- {
- /// <summary>
- /// Class which represents a description of a stack trace
- /// There is no good reason for the methods of this class to be virtual.
- /// </summary>
- public partial class StackTrace
- {
- public const int METHODS_TO_SKIP = 0;
- private int _numOfFrames;
- private int _methodsToSkip;
-
- /// <summary>
- /// Stack frames comprising this stack trace.
- /// </summary>
- private StackFrame[] _stackFrames;
- /// <summary>
- /// Constructs a stack trace from the current location.
- /// </summary>
- public StackTrace()
- {
- InitializeForCurrentThread(METHODS_TO_SKIP, false);
- }
- /// <summary>
- /// Constructs a stack trace from the current location.
- /// </summary>
- public StackTrace(bool fNeedFileInfo)
- {
- InitializeForCurrentThread(METHODS_TO_SKIP, fNeedFileInfo);
- }
- /// <summary>
- /// Constructs a stack trace from the current location, in a caller's
- /// frame
- /// </summary>
- public StackTrace(int skipFrames)
- {
- if (skipFrames < 0)
- throw new ArgumentOutOfRangeException(nameof(skipFrames),
- SR.ArgumentOutOfRange_NeedNonNegNum);
- InitializeForCurrentThread(skipFrames + METHODS_TO_SKIP, false);
- }
- /// <summary>
- /// Constructs a stack trace from the current location, in a caller's
- /// frame
- /// </summary>
- public StackTrace(int skipFrames, bool fNeedFileInfo)
- {
- if (skipFrames < 0)
- throw new ArgumentOutOfRangeException(nameof(skipFrames),
- SR.ArgumentOutOfRange_NeedNonNegNum);
- InitializeForCurrentThread(skipFrames + METHODS_TO_SKIP, fNeedFileInfo);
- }
- /// <summary>
- /// Constructs a stack trace from the current location.
- /// </summary>
- public StackTrace(Exception e)
- {
- if (e == null)
- throw new ArgumentNullException(nameof(e));
- InitializeForException(e, METHODS_TO_SKIP, false);
- }
- /// <summary>
- /// Constructs a stack trace from the current location.
- /// </summary>
- public StackTrace(Exception e, bool fNeedFileInfo)
- {
- if (e == null)
- throw new ArgumentNullException(nameof(e));
- InitializeForException(e, METHODS_TO_SKIP, fNeedFileInfo);
- }
- /// <summary>
- /// Constructs a stack trace from the current location, in a caller's
- /// frame
- /// </summary>
- public StackTrace(Exception e, int skipFrames)
- {
- if (e == null)
- throw new ArgumentNullException(nameof(e));
- if (skipFrames < 0)
- throw new ArgumentOutOfRangeException(nameof(skipFrames),
- SR.ArgumentOutOfRange_NeedNonNegNum);
- InitializeForException(e, skipFrames + METHODS_TO_SKIP, false);
- }
- /// <summary>
- /// Constructs a stack trace from the current location, in a caller's
- /// frame
- /// </summary>
- public StackTrace(Exception e, int skipFrames, bool fNeedFileInfo)
- {
- if (e == null)
- throw new ArgumentNullException(nameof(e));
- if (skipFrames < 0)
- throw new ArgumentOutOfRangeException(nameof(skipFrames),
- SR.ArgumentOutOfRange_NeedNonNegNum);
- InitializeForException(e, skipFrames + METHODS_TO_SKIP, fNeedFileInfo);
- }
- /// <summary>
- /// Constructs a "fake" stack trace, just containing a single frame.
- /// Does not have the overhead of a full stack trace.
- /// </summary>
- public StackTrace(StackFrame frame)
- {
- _stackFrames = new StackFrame[] { frame };
- _numOfFrames = 1;
- }
- /// <summary>
- /// Property to get the number of frames in the stack trace
- /// </summary>
- public virtual int FrameCount
- {
- get { return _numOfFrames; }
- }
- /// <summary>
- /// Returns a given stack frame. Stack frames are numbered starting at
- /// zero, which is the last stack frame pushed.
- /// </summary>
- public virtual StackFrame GetFrame(int index)
- {
- if (_stackFrames != null && index < _numOfFrames && index >= 0)
- return _stackFrames[index + _methodsToSkip];
- return null;
- }
- /// <summary>
- /// Returns an array of all stack frames for this stacktrace.
- /// The array is ordered and sized such that GetFrames()[i] == GetFrame(i)
- /// The nth element of this array is the same as GetFrame(n).
- /// The length of the array is the same as FrameCount.
- /// </summary>
- public virtual StackFrame[] GetFrames()
- {
- if (_stackFrames == null || _numOfFrames <= 0)
- return null;
- // We have to return a subset of the array. Unfortunately this
- // means we have to allocate a new array and copy over.
- StackFrame[] array = new StackFrame[_numOfFrames];
- Array.Copy(_stackFrames, _methodsToSkip, array, 0, _numOfFrames);
- return array;
- }
- /// <summary>
- /// Builds a readable representation of the stack trace
- /// </summary>
- public override string ToString()
- {
- // Include a trailing newline for backwards compatibility
- return ToString(TraceFormat.TrailingNewLine);
- }
- /// <summary>
- /// TraceFormat is used to specify options for how the
- /// string-representation of a StackTrace should be generated.
- /// </summary>
- internal enum TraceFormat
- {
- Normal,
- TrailingNewLine, // include a trailing new line character
- }
- #if !CORERT
- /// <summary>
- /// Builds a readable representation of the stack trace, specifying
- /// the format for backwards compatibility.
- /// </summary>
- internal string ToString(TraceFormat traceFormat)
- {
- string word_At = SR.Word_At;
- string inFileLineNum = SR.StackTrace_InFileLineNumber;
- bool fFirstFrame = true;
- StringBuilder sb = new StringBuilder(255);
- for (int iFrameIndex = 0; iFrameIndex < _numOfFrames; iFrameIndex++)
- {
- StackFrame sf = GetFrame(iFrameIndex);
- MethodBase mb = sf.GetMethod();
- if (mb != null && (ShowInStackTrace(mb) ||
- (iFrameIndex == _numOfFrames - 1))) // Don't filter last frame
- {
- // We want a newline at the end of every line except for the last
- if (fFirstFrame)
- fFirstFrame = false;
- else
- sb.Append(Environment.NewLine);
- sb.AppendFormat(CultureInfo.InvariantCulture, " {0} ", word_At);
- bool isAsync = false;
- Type declaringType = mb.DeclaringType;
- string methodName = mb.Name;
- bool methodChanged = false;
- if (declaringType != null && declaringType.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false))
- {
- isAsync = typeof(IAsyncStateMachine).IsAssignableFrom(declaringType);
- if (isAsync || typeof(IEnumerator).IsAssignableFrom(declaringType))
- {
- methodChanged = TryResolveStateMachineMethod(ref mb, out declaringType);
- }
- }
- // if there is a type (non global method) print it
- // ResolveStateMachineMethod may have set declaringType to null
- if (declaringType != null)
- {
- // Append t.FullName, replacing '+' with '.'
- string fullName = declaringType.FullName;
- for (int i = 0; i < fullName.Length; i++)
- {
- char ch = fullName[i];
- sb.Append(ch == '+' ? '.' : ch);
- }
- sb.Append('.');
- }
- sb.Append(mb.Name);
- // deal with the generic portion of the method
- if (mb is MethodInfo mi && mi.IsGenericMethod)
- {
- Type[] typars = mi.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(']');
- }
- ParameterInfo[] pi = null;
- try
- {
- pi = mb.GetParameters();
- }
- catch
- {
- // The parameter info cannot be loaded, so we don't
- // append the parameter list.
- }
- if (pi != null)
- {
- // arguments printing
- sb.Append('(');
- bool fFirstParam = true;
- for (int j = 0; j < pi.Length; j++)
- {
- if (fFirstParam == false)
- sb.Append(", ");
- else
- fFirstParam = false;
- string typeName = "<UnknownType>";
- if (pi[j].ParameterType != null)
- typeName = pi[j].ParameterType.Name;
- sb.Append(typeName);
- sb.Append(' ');
- sb.Append(pi[j].Name);
- }
- sb.Append(')');
- }
- if (methodChanged)
- {
- // Append original method name e.g. +MoveNext()
- sb.Append('+');
- sb.Append(methodName);
- sb.Append('(').Append(')');
- }
- // source location printing
- if (sf.GetILOffset() != -1)
- {
- // If we don't have a PDB or PDB-reading is disabled for the module,
- // then the file name will be null.
- string fileName = sf.GetFileName();
- if (fileName != null)
- {
- // tack on " in c:\tmp\MyFile.cs:line 5"
- sb.Append(' ');
- sb.AppendFormat(CultureInfo.InvariantCulture, inFileLineNum, fileName, sf.GetFileLineNumber());
- }
- }
- if (sf.GetIsLastFrameFromForeignExceptionStackTrace() &&
- !isAsync) // Skip EDI boundary for async
- {
- sb.Append(Environment.NewLine);
- sb.Append(SR.Exception_EndStackTraceFromPreviousThrow);
- }
- }
- }
- if (traceFormat == TraceFormat.TrailingNewLine)
- sb.Append(Environment.NewLine);
- return sb.ToString();
- }
- #endif // !CORERT
- private static bool ShowInStackTrace(MethodBase mb)
- {
- Debug.Assert(mb != null);
- return !(mb.IsDefined(typeof(StackTraceHiddenAttribute)) || (mb.DeclaringType?.IsDefined(typeof(StackTraceHiddenAttribute)) ?? false));
- }
- private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
- {
- Debug.Assert(method != null);
- Debug.Assert(method.DeclaringType != null);
- declaringType = method.DeclaringType;
- Type parentType = declaringType.DeclaringType;
- if (parentType == null)
- {
- return false;
- }
- MethodInfo[] methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
- if (methods == null)
- {
- return false;
- }
- foreach (MethodInfo candidateMethod in methods)
- {
- IEnumerable<StateMachineAttribute> attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(inherit: false);
- if (attributes == null)
- {
- continue;
- }
- bool foundAttribute = false, foundIteratorAttribute = false;
- foreach (StateMachineAttribute asma in attributes)
- {
- if (asma.StateMachineType == declaringType)
- {
- foundAttribute = true;
- foundIteratorAttribute |= asma is IteratorStateMachineAttribute || asma is AsyncIteratorStateMachineAttribute;
- }
- }
- if (foundAttribute)
- {
- // If this is an iterator (sync or async), mark the iterator as changed, so it gets the + annotation
- // of the original method. Non-iterator async state machines resolve directly to their builder methods
- // so aren't marked as changed.
- method = candidateMethod;
- declaringType = candidateMethod.DeclaringType;
- return foundIteratorAttribute;
- }
- }
- return false;
- }
- }
- }
|