summaryrefslogtreecommitdiff
path: root/src/System.Private.CoreLib/shared
diff options
context:
space:
mode:
authorMarek Safar <marek.safar@gmail.com>2019-01-07 17:07:37 +0100
committerJan Kotas <jkotas@microsoft.com>2019-01-07 08:07:37 -0800
commitd5c10c97f827354a83ef2915d31ac4e1c212662f (patch)
tree21580b03880d7fe4d250b8d31aa6ec07ebd40752 /src/System.Private.CoreLib/shared
parentc0d778417449b2e44ba2fedacd3046f1af755744 (diff)
downloadcoreclr-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.projitems1
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/StackTrace.cs396
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;
+ }
+ }
+}