diff options
author | Marek Safar <marek.safar@gmail.com> | 2019-01-07 17:07:37 +0100 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2019-01-07 08:07:37 -0800 |
commit | d5c10c97f827354a83ef2915d31ac4e1c212662f (patch) | |
tree | 21580b03880d7fe4d250b8d31aa6ec07ebd40752 /src/System.Private.CoreLib/shared | |
parent | c0d778417449b2e44ba2fedacd3046f1af755744 (diff) | |
download | coreclr-d5c10c97f827354a83ef2915d31ac4e1c212662f.tar.gz coreclr-d5c10c97f827354a83ef2915d31ac4e1c212662f.tar.bz2 coreclr-d5c10c97f827354a83ef2915d31ac4e1c212662f.zip |
Moves StackTrace to shared partition (#21757)
* Moves StackTrace to shared partition
* Remove obsolete comment
* StackFrame::GetFileName behaves like property, remove SecurityException
* Adjust CoreRT ifdefs, fix names for consistency
Diffstat (limited to 'src/System.Private.CoreLib/shared')
-rw-r--r-- | src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems | 1 | ||||
-rw-r--r-- | src/System.Private.CoreLib/shared/System/Diagnostics/StackTrace.cs | 396 |
2 files changed, 397 insertions, 0 deletions
diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 5df43dce88..0504497f62 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -197,6 +197,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebuggerVisualizerAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebugProvider.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\StackFrame.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\StackTrace.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\StackTraceHiddenAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\DivideByZeroException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\DllNotFoundException.cs" /> diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/StackTrace.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/StackTrace.cs new file mode 100644 index 0000000000..bec0e24c18 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Diagnostics/StackTrace.cs @@ -0,0 +1,396 @@ +// 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; + } + } +} |