StackTrace.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Globalization;
  7. using System.Reflection;
  8. using System.Runtime.CompilerServices;
  9. using System.Text;
  10. namespace System.Diagnostics
  11. {
  12. /// <summary>
  13. /// Class which represents a description of a stack trace
  14. /// There is no good reason for the methods of this class to be virtual.
  15. /// </summary>
  16. public partial class StackTrace
  17. {
  18. public const int METHODS_TO_SKIP = 0;
  19. private int _numOfFrames;
  20. private int _methodsToSkip;
  21. /// <summary>
  22. /// Stack frames comprising this stack trace.
  23. /// </summary>
  24. private StackFrame[]? _stackFrames;
  25. /// <summary>
  26. /// Constructs a stack trace from the current location.
  27. /// </summary>
  28. public StackTrace()
  29. {
  30. InitializeForCurrentThread(METHODS_TO_SKIP, false);
  31. }
  32. /// <summary>
  33. /// Constructs a stack trace from the current location.
  34. /// </summary>
  35. public StackTrace(bool fNeedFileInfo)
  36. {
  37. InitializeForCurrentThread(METHODS_TO_SKIP, fNeedFileInfo);
  38. }
  39. /// <summary>
  40. /// Constructs a stack trace from the current location, in a caller's
  41. /// frame
  42. /// </summary>
  43. public StackTrace(int skipFrames)
  44. {
  45. if (skipFrames < 0)
  46. throw new ArgumentOutOfRangeException(nameof(skipFrames),
  47. SR.ArgumentOutOfRange_NeedNonNegNum);
  48. InitializeForCurrentThread(skipFrames + METHODS_TO_SKIP, false);
  49. }
  50. /// <summary>
  51. /// Constructs a stack trace from the current location, in a caller's
  52. /// frame
  53. /// </summary>
  54. public StackTrace(int skipFrames, bool fNeedFileInfo)
  55. {
  56. if (skipFrames < 0)
  57. throw new ArgumentOutOfRangeException(nameof(skipFrames),
  58. SR.ArgumentOutOfRange_NeedNonNegNum);
  59. InitializeForCurrentThread(skipFrames + METHODS_TO_SKIP, fNeedFileInfo);
  60. }
  61. /// <summary>
  62. /// Constructs a stack trace from the current location.
  63. /// </summary>
  64. public StackTrace(Exception e)
  65. {
  66. if (e == null)
  67. throw new ArgumentNullException(nameof(e));
  68. InitializeForException(e, METHODS_TO_SKIP, false);
  69. }
  70. /// <summary>
  71. /// Constructs a stack trace from the current location.
  72. /// </summary>
  73. public StackTrace(Exception e, bool fNeedFileInfo)
  74. {
  75. if (e == null)
  76. throw new ArgumentNullException(nameof(e));
  77. InitializeForException(e, METHODS_TO_SKIP, fNeedFileInfo);
  78. }
  79. /// <summary>
  80. /// Constructs a stack trace from the current location, in a caller's
  81. /// frame
  82. /// </summary>
  83. public StackTrace(Exception e, int skipFrames)
  84. {
  85. if (e == null)
  86. throw new ArgumentNullException(nameof(e));
  87. if (skipFrames < 0)
  88. throw new ArgumentOutOfRangeException(nameof(skipFrames),
  89. SR.ArgumentOutOfRange_NeedNonNegNum);
  90. InitializeForException(e, skipFrames + METHODS_TO_SKIP, false);
  91. }
  92. /// <summary>
  93. /// Constructs a stack trace from the current location, in a caller's
  94. /// frame
  95. /// </summary>
  96. public StackTrace(Exception e, int skipFrames, bool fNeedFileInfo)
  97. {
  98. if (e == null)
  99. throw new ArgumentNullException(nameof(e));
  100. if (skipFrames < 0)
  101. throw new ArgumentOutOfRangeException(nameof(skipFrames),
  102. SR.ArgumentOutOfRange_NeedNonNegNum);
  103. InitializeForException(e, skipFrames + METHODS_TO_SKIP, fNeedFileInfo);
  104. }
  105. /// <summary>
  106. /// Constructs a "fake" stack trace, just containing a single frame.
  107. /// Does not have the overhead of a full stack trace.
  108. /// </summary>
  109. public StackTrace(StackFrame frame)
  110. {
  111. _stackFrames = new StackFrame[] { frame };
  112. _numOfFrames = 1;
  113. }
  114. /// <summary>
  115. /// Property to get the number of frames in the stack trace
  116. /// </summary>
  117. public virtual int FrameCount => _numOfFrames;
  118. /// <summary>
  119. /// Returns a given stack frame. Stack frames are numbered starting at
  120. /// zero, which is the last stack frame pushed.
  121. /// </summary>
  122. public virtual StackFrame? GetFrame(int index)
  123. {
  124. if (_stackFrames != null && index < _numOfFrames && index >= 0)
  125. return _stackFrames[index + _methodsToSkip];
  126. return null;
  127. }
  128. /// <summary>
  129. /// Returns an array of all stack frames for this stacktrace.
  130. /// The array is ordered and sized such that GetFrames()[i] == GetFrame(i)
  131. /// The nth element of this array is the same as GetFrame(n).
  132. /// The length of the array is the same as FrameCount.
  133. /// </summary>
  134. public virtual StackFrame[] GetFrames()
  135. {
  136. if (_stackFrames == null || _numOfFrames <= 0)
  137. return Array.Empty<StackFrame>();
  138. // We have to return a subset of the array. Unfortunately this
  139. // means we have to allocate a new array and copy over.
  140. StackFrame[] array = new StackFrame[_numOfFrames];
  141. Array.Copy(_stackFrames, _methodsToSkip, array, 0, _numOfFrames);
  142. return array;
  143. }
  144. /// <summary>
  145. /// Builds a readable representation of the stack trace
  146. /// </summary>
  147. public override string ToString()
  148. {
  149. // Include a trailing newline for backwards compatibility
  150. return ToString(TraceFormat.TrailingNewLine);
  151. }
  152. /// <summary>
  153. /// TraceFormat is used to specify options for how the
  154. /// string-representation of a StackTrace should be generated.
  155. /// </summary>
  156. internal enum TraceFormat
  157. {
  158. Normal,
  159. TrailingNewLine, // include a trailing new line character
  160. }
  161. #if !CORERT
  162. /// <summary>
  163. /// Builds a readable representation of the stack trace, specifying
  164. /// the format for backwards compatibility.
  165. /// </summary>
  166. internal string ToString(TraceFormat traceFormat)
  167. {
  168. var sb = new StringBuilder(256);
  169. ToString(traceFormat, sb);
  170. return sb.ToString();
  171. }
  172. internal void ToString(TraceFormat traceFormat, StringBuilder sb)
  173. {
  174. string word_At = SR.Word_At;
  175. string inFileLineNum = SR.StackTrace_InFileLineNumber;
  176. bool fFirstFrame = true;
  177. for (int iFrameIndex = 0; iFrameIndex < _numOfFrames; iFrameIndex++)
  178. {
  179. StackFrame? sf = GetFrame(iFrameIndex);
  180. MethodBase? mb = sf?.GetMethod();
  181. if (mb != null && (ShowInStackTrace(mb) ||
  182. (iFrameIndex == _numOfFrames - 1))) // Don't filter last frame
  183. {
  184. // We want a newline at the end of every line except for the last
  185. if (fFirstFrame)
  186. fFirstFrame = false;
  187. else
  188. sb.AppendLine();
  189. sb.AppendFormat(CultureInfo.InvariantCulture, " {0} ", word_At);
  190. bool isAsync = false;
  191. Type? declaringType = mb.DeclaringType;
  192. string methodName = mb.Name;
  193. bool methodChanged = false;
  194. if (declaringType != null && declaringType.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false))
  195. {
  196. isAsync = typeof(IAsyncStateMachine).IsAssignableFrom(declaringType);
  197. if (isAsync || typeof(IEnumerator).IsAssignableFrom(declaringType))
  198. {
  199. methodChanged = TryResolveStateMachineMethod(ref mb, out declaringType);
  200. }
  201. }
  202. // if there is a type (non global method) print it
  203. // ResolveStateMachineMethod may have set declaringType to null
  204. if (declaringType != null)
  205. {
  206. // Append t.FullName, replacing '+' with '.'
  207. string fullName = declaringType.FullName!;
  208. for (int i = 0; i < fullName.Length; i++)
  209. {
  210. char ch = fullName[i];
  211. sb.Append(ch == '+' ? '.' : ch);
  212. }
  213. sb.Append('.');
  214. }
  215. sb.Append(mb.Name);
  216. // deal with the generic portion of the method
  217. if (mb is MethodInfo mi && mi.IsGenericMethod)
  218. {
  219. Type[] typars = mi.GetGenericArguments();
  220. sb.Append('[');
  221. int k = 0;
  222. bool fFirstTyParam = true;
  223. while (k < typars.Length)
  224. {
  225. if (!fFirstTyParam)
  226. sb.Append(',');
  227. else
  228. fFirstTyParam = false;
  229. sb.Append(typars[k].Name);
  230. k++;
  231. }
  232. sb.Append(']');
  233. }
  234. ParameterInfo[]? pi = null;
  235. try
  236. {
  237. pi = mb.GetParameters();
  238. }
  239. catch
  240. {
  241. // The parameter info cannot be loaded, so we don't
  242. // append the parameter list.
  243. }
  244. if (pi != null)
  245. {
  246. // arguments printing
  247. sb.Append('(');
  248. bool fFirstParam = true;
  249. for (int j = 0; j < pi.Length; j++)
  250. {
  251. if (!fFirstParam)
  252. sb.Append(", ");
  253. else
  254. fFirstParam = false;
  255. string typeName = "<UnknownType>";
  256. if (pi[j].ParameterType != null)
  257. typeName = pi[j].ParameterType.Name;
  258. sb.Append(typeName);
  259. sb.Append(' ');
  260. sb.Append(pi[j].Name);
  261. }
  262. sb.Append(')');
  263. }
  264. if (methodChanged)
  265. {
  266. // Append original method name e.g. +MoveNext()
  267. sb.Append('+');
  268. sb.Append(methodName);
  269. sb.Append('(').Append(')');
  270. }
  271. // source location printing
  272. if (sf!.GetILOffset() != -1)
  273. {
  274. // If we don't have a PDB or PDB-reading is disabled for the module,
  275. // then the file name will be null.
  276. string? fileName = sf.GetFileName();
  277. if (fileName != null)
  278. {
  279. // tack on " in c:\tmp\MyFile.cs:line 5"
  280. sb.Append(' ');
  281. sb.AppendFormat(CultureInfo.InvariantCulture, inFileLineNum, fileName, sf.GetFileLineNumber());
  282. }
  283. }
  284. // Skip EDI boundary for async
  285. if (sf.IsLastFrameFromForeignExceptionStackTrace && !isAsync)
  286. {
  287. sb.AppendLine();
  288. sb.Append(SR.Exception_EndStackTraceFromPreviousThrow);
  289. }
  290. }
  291. }
  292. if (traceFormat == TraceFormat.TrailingNewLine)
  293. sb.AppendLine();
  294. }
  295. #endif // !CORERT
  296. private static bool ShowInStackTrace(MethodBase mb)
  297. {
  298. Debug.Assert(mb != null);
  299. if ((mb.MethodImplementationFlags & MethodImplAttributes.AggressiveInlining) != 0)
  300. {
  301. // Aggressive Inlines won't normally show in the StackTrace; however for Tier0 Jit and
  302. // cross-assembly AoT/R2R these inlines will be blocked until Tier1 Jit re-Jits
  303. // them when they will inline. We don't show them in the StackTrace to bring consistency
  304. // between this first-pass asm and fully optimized asm.
  305. return false;
  306. }
  307. if (mb.IsDefined(typeof(StackTraceHiddenAttribute), inherit: false))
  308. {
  309. // Don't show where StackTraceHidden is applied to the method.
  310. return false;
  311. }
  312. Type? declaringType = mb.DeclaringType;
  313. // Methods don't always have containing types, for example dynamic RefEmit generated methods.
  314. if (declaringType != null &&
  315. declaringType.IsDefined(typeof(StackTraceHiddenAttribute), inherit: false))
  316. {
  317. // Don't show where StackTraceHidden is applied to the containing Type of the method.
  318. return false;
  319. }
  320. return true;
  321. }
  322. private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
  323. {
  324. Debug.Assert(method != null);
  325. Debug.Assert(method.DeclaringType != null);
  326. declaringType = method.DeclaringType;
  327. Type? parentType = declaringType.DeclaringType;
  328. if (parentType == null)
  329. {
  330. return false;
  331. }
  332. MethodInfo[]? methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  333. if (methods == null)
  334. {
  335. return false;
  336. }
  337. foreach (MethodInfo candidateMethod in methods)
  338. {
  339. IEnumerable<StateMachineAttribute>? attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(inherit: false);
  340. if (attributes == null)
  341. {
  342. continue;
  343. }
  344. bool foundAttribute = false, foundIteratorAttribute = false;
  345. foreach (StateMachineAttribute asma in attributes)
  346. {
  347. if (asma.StateMachineType == declaringType)
  348. {
  349. foundAttribute = true;
  350. foundIteratorAttribute |= asma is IteratorStateMachineAttribute || asma is AsyncIteratorStateMachineAttribute;
  351. }
  352. }
  353. if (foundAttribute)
  354. {
  355. // If this is an iterator (sync or async), mark the iterator as changed, so it gets the + annotation
  356. // of the original method. Non-iterator async state machines resolve directly to their builder methods
  357. // so aren't marked as changed.
  358. method = candidateMethod;
  359. declaringType = candidateMethod.DeclaringType!;
  360. return foundIteratorAttribute;
  361. }
  362. }
  363. return false;
  364. }
  365. }
  366. }