StackTrace.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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
  118. {
  119. get { return _numOfFrames; }
  120. }
  121. /// <summary>
  122. /// Returns a given stack frame. Stack frames are numbered starting at
  123. /// zero, which is the last stack frame pushed.
  124. /// </summary>
  125. public virtual StackFrame GetFrame(int index)
  126. {
  127. if (_stackFrames != null && index < _numOfFrames && index >= 0)
  128. return _stackFrames[index + _methodsToSkip];
  129. return null;
  130. }
  131. /// <summary>
  132. /// Returns an array of all stack frames for this stacktrace.
  133. /// The array is ordered and sized such that GetFrames()[i] == GetFrame(i)
  134. /// The nth element of this array is the same as GetFrame(n).
  135. /// The length of the array is the same as FrameCount.
  136. /// </summary>
  137. public virtual StackFrame[] GetFrames()
  138. {
  139. if (_stackFrames == null || _numOfFrames <= 0)
  140. return null;
  141. // We have to return a subset of the array. Unfortunately this
  142. // means we have to allocate a new array and copy over.
  143. StackFrame[] array = new StackFrame[_numOfFrames];
  144. Array.Copy(_stackFrames, _methodsToSkip, array, 0, _numOfFrames);
  145. return array;
  146. }
  147. /// <summary>
  148. /// Builds a readable representation of the stack trace
  149. /// </summary>
  150. public override string ToString()
  151. {
  152. // Include a trailing newline for backwards compatibility
  153. return ToString(TraceFormat.TrailingNewLine);
  154. }
  155. /// <summary>
  156. /// TraceFormat is used to specify options for how the
  157. /// string-representation of a StackTrace should be generated.
  158. /// </summary>
  159. internal enum TraceFormat
  160. {
  161. Normal,
  162. TrailingNewLine, // include a trailing new line character
  163. }
  164. #if !CORERT
  165. /// <summary>
  166. /// Builds a readable representation of the stack trace, specifying
  167. /// the format for backwards compatibility.
  168. /// </summary>
  169. internal string ToString(TraceFormat traceFormat)
  170. {
  171. string word_At = SR.Word_At;
  172. string inFileLineNum = SR.StackTrace_InFileLineNumber;
  173. bool fFirstFrame = true;
  174. StringBuilder sb = new StringBuilder(255);
  175. for (int iFrameIndex = 0; iFrameIndex < _numOfFrames; iFrameIndex++)
  176. {
  177. StackFrame sf = GetFrame(iFrameIndex);
  178. MethodBase mb = sf.GetMethod();
  179. if (mb != null && (ShowInStackTrace(mb) ||
  180. (iFrameIndex == _numOfFrames - 1))) // Don't filter last frame
  181. {
  182. // We want a newline at the end of every line except for the last
  183. if (fFirstFrame)
  184. fFirstFrame = false;
  185. else
  186. sb.Append(Environment.NewLine);
  187. sb.AppendFormat(CultureInfo.InvariantCulture, " {0} ", word_At);
  188. bool isAsync = false;
  189. Type declaringType = mb.DeclaringType;
  190. string methodName = mb.Name;
  191. bool methodChanged = false;
  192. if (declaringType != null && declaringType.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false))
  193. {
  194. isAsync = typeof(IAsyncStateMachine).IsAssignableFrom(declaringType);
  195. if (isAsync || typeof(IEnumerator).IsAssignableFrom(declaringType))
  196. {
  197. methodChanged = TryResolveStateMachineMethod(ref mb, out declaringType);
  198. }
  199. }
  200. // if there is a type (non global method) print it
  201. // ResolveStateMachineMethod may have set declaringType to null
  202. if (declaringType != null)
  203. {
  204. // Append t.FullName, replacing '+' with '.'
  205. string fullName = declaringType.FullName;
  206. for (int i = 0; i < fullName.Length; i++)
  207. {
  208. char ch = fullName[i];
  209. sb.Append(ch == '+' ? '.' : ch);
  210. }
  211. sb.Append('.');
  212. }
  213. sb.Append(mb.Name);
  214. // deal with the generic portion of the method
  215. if (mb is MethodInfo mi && mi.IsGenericMethod)
  216. {
  217. Type[] typars = mi.GetGenericArguments();
  218. sb.Append('[');
  219. int k = 0;
  220. bool fFirstTyParam = true;
  221. while (k < typars.Length)
  222. {
  223. if (fFirstTyParam == false)
  224. sb.Append(',');
  225. else
  226. fFirstTyParam = false;
  227. sb.Append(typars[k].Name);
  228. k++;
  229. }
  230. sb.Append(']');
  231. }
  232. ParameterInfo[] pi = null;
  233. try
  234. {
  235. pi = mb.GetParameters();
  236. }
  237. catch
  238. {
  239. // The parameter info cannot be loaded, so we don't
  240. // append the parameter list.
  241. }
  242. if (pi != null)
  243. {
  244. // arguments printing
  245. sb.Append('(');
  246. bool fFirstParam = true;
  247. for (int j = 0; j < pi.Length; j++)
  248. {
  249. if (fFirstParam == false)
  250. sb.Append(", ");
  251. else
  252. fFirstParam = false;
  253. string typeName = "<UnknownType>";
  254. if (pi[j].ParameterType != null)
  255. typeName = pi[j].ParameterType.Name;
  256. sb.Append(typeName);
  257. sb.Append(' ');
  258. sb.Append(pi[j].Name);
  259. }
  260. sb.Append(')');
  261. }
  262. if (methodChanged)
  263. {
  264. // Append original method name e.g. +MoveNext()
  265. sb.Append('+');
  266. sb.Append(methodName);
  267. sb.Append('(').Append(')');
  268. }
  269. // source location printing
  270. if (sf.GetILOffset() != -1)
  271. {
  272. // If we don't have a PDB or PDB-reading is disabled for the module,
  273. // then the file name will be null.
  274. string fileName = sf.GetFileName();
  275. if (fileName != null)
  276. {
  277. // tack on " in c:\tmp\MyFile.cs:line 5"
  278. sb.Append(' ');
  279. sb.AppendFormat(CultureInfo.InvariantCulture, inFileLineNum, fileName, sf.GetFileLineNumber());
  280. }
  281. }
  282. if (sf.GetIsLastFrameFromForeignExceptionStackTrace() &&
  283. !isAsync) // Skip EDI boundary for async
  284. {
  285. sb.Append(Environment.NewLine);
  286. sb.Append(SR.Exception_EndStackTraceFromPreviousThrow);
  287. }
  288. }
  289. }
  290. if (traceFormat == TraceFormat.TrailingNewLine)
  291. sb.Append(Environment.NewLine);
  292. return sb.ToString();
  293. }
  294. #endif // !CORERT
  295. private static bool ShowInStackTrace(MethodBase mb)
  296. {
  297. Debug.Assert(mb != null);
  298. return !(mb.IsDefined(typeof(StackTraceHiddenAttribute)) || (mb.DeclaringType?.IsDefined(typeof(StackTraceHiddenAttribute)) ?? false));
  299. }
  300. private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
  301. {
  302. Debug.Assert(method != null);
  303. Debug.Assert(method.DeclaringType != null);
  304. declaringType = method.DeclaringType;
  305. Type parentType = declaringType.DeclaringType;
  306. if (parentType == null)
  307. {
  308. return false;
  309. }
  310. MethodInfo[] methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  311. if (methods == null)
  312. {
  313. return false;
  314. }
  315. foreach (MethodInfo candidateMethod in methods)
  316. {
  317. IEnumerable<StateMachineAttribute> attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(inherit: false);
  318. if (attributes == null)
  319. {
  320. continue;
  321. }
  322. bool foundAttribute = false, foundIteratorAttribute = false;
  323. foreach (StateMachineAttribute asma in attributes)
  324. {
  325. if (asma.StateMachineType == declaringType)
  326. {
  327. foundAttribute = true;
  328. foundIteratorAttribute |= asma is IteratorStateMachineAttribute || asma is AsyncIteratorStateMachineAttribute;
  329. }
  330. }
  331. if (foundAttribute)
  332. {
  333. // If this is an iterator (sync or async), mark the iterator as changed, so it gets the + annotation
  334. // of the original method. Non-iterator async state machines resolve directly to their builder methods
  335. // so aren't marked as changed.
  336. method = candidateMethod;
  337. declaringType = candidateMethod.DeclaringType;
  338. return foundIteratorAttribute;
  339. }
  340. }
  341. return false;
  342. }
  343. }
  344. }