StackFrame.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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.Text;
  5. using System;
  6. using System.IO;
  7. using System.Reflection;
  8. namespace System.Diagnostics
  9. {
  10. /// <summary>
  11. /// There is no good reason for the methods of this class to be virtual.
  12. /// </summary>
  13. public partial class StackFrame
  14. {
  15. /// <summary>
  16. /// Reflection information for the method if available, null otherwise.
  17. /// </summary>
  18. private MethodBase _method;
  19. /// <summary>
  20. /// Native offset of the current instruction within the current method if available,
  21. /// OFFSET_UNKNOWN otherwise.
  22. /// </summary>
  23. private int _nativeOffset;
  24. /// <summary>
  25. /// IL offset of the current instruction within the current method if available,
  26. /// OFFSET_UNKNOWN otherwise.
  27. /// </summary>
  28. private int _ilOffset;
  29. /// <summary>
  30. /// Source file name representing the current code location if available, null otherwise.
  31. /// </summary>
  32. private string _fileName;
  33. /// <summary>
  34. /// Line number representing the current code location if available, 0 otherwise.
  35. /// </summary>
  36. private int _lineNumber;
  37. /// <summary>
  38. /// Column number representing the current code location if available, 0 otherwise.
  39. /// </summary>
  40. private int _columnNumber;
  41. /// <summary>
  42. /// This flag is set to true when the frame represents a rethrow marker.
  43. /// </summary>
  44. private bool _isLastFrameFromForeignExceptionStackTrace;
  45. private void InitMembers()
  46. {
  47. _method = null;
  48. _nativeOffset = OFFSET_UNKNOWN;
  49. _ilOffset = OFFSET_UNKNOWN;
  50. _fileName = null;
  51. _lineNumber = 0;
  52. _columnNumber = 0;
  53. _isLastFrameFromForeignExceptionStackTrace = false;
  54. }
  55. /// <summary>
  56. /// Constructs a StackFrame corresponding to the active stack frame.
  57. /// </summary>
  58. public StackFrame()
  59. {
  60. InitMembers();
  61. BuildStackFrame(0 + StackTrace.METHODS_TO_SKIP, false);
  62. }
  63. /// <summary>
  64. /// Constructs a StackFrame corresponding to the active stack frame.
  65. /// </summary>
  66. public StackFrame(bool needFileInfo)
  67. {
  68. InitMembers();
  69. BuildStackFrame(0 + StackTrace.METHODS_TO_SKIP, needFileInfo);
  70. }
  71. /// <summary>
  72. /// Constructs a StackFrame corresponding to a calling stack frame.
  73. /// </summary>
  74. public StackFrame(int skipFrames)
  75. {
  76. InitMembers();
  77. BuildStackFrame(skipFrames + StackTrace.METHODS_TO_SKIP, false);
  78. }
  79. /// <summary>
  80. /// Constructs a StackFrame corresponding to a calling stack frame.
  81. /// </summary>
  82. public StackFrame(int skipFrames, bool needFileInfo)
  83. {
  84. InitMembers();
  85. BuildStackFrame(skipFrames + StackTrace.METHODS_TO_SKIP, needFileInfo);
  86. }
  87. /// <summary>
  88. /// Constructs a "fake" stack frame, just containing the given file
  89. /// name and line number. Use when you don't want to use the
  90. /// debugger's line mapping logic.
  91. /// </summary>
  92. public StackFrame(string fileName, int lineNumber)
  93. {
  94. InitMembers();
  95. BuildStackFrame(StackTrace.METHODS_TO_SKIP, false);
  96. _fileName = fileName;
  97. _lineNumber = lineNumber;
  98. _columnNumber = 0;
  99. }
  100. /// <summary>
  101. /// Constructs a "fake" stack frame, just containing the given file
  102. /// name, line number and column number. Use when you don't want to
  103. /// use the debugger's line mapping logic.
  104. /// </summary>
  105. public StackFrame(string fileName, int lineNumber, int colNumber)
  106. {
  107. InitMembers();
  108. BuildStackFrame(StackTrace.METHODS_TO_SKIP, false);
  109. _fileName = fileName;
  110. _lineNumber = lineNumber;
  111. _columnNumber = colNumber;
  112. }
  113. /// <summary>
  114. /// Constant returned when the native or IL offset is unknown
  115. /// </summary>
  116. public const int OFFSET_UNKNOWN = -1;
  117. internal virtual void SetMethodBase(MethodBase mb)
  118. {
  119. _method = mb;
  120. }
  121. internal virtual void SetOffset(int iOffset)
  122. {
  123. _nativeOffset = iOffset;
  124. }
  125. internal virtual void SetILOffset(int iOffset)
  126. {
  127. _ilOffset = iOffset;
  128. }
  129. internal virtual void SetFileName(string strFName)
  130. {
  131. _fileName = strFName;
  132. }
  133. internal virtual void SetLineNumber(int iLine)
  134. {
  135. _lineNumber = iLine;
  136. }
  137. internal virtual void SetColumnNumber(int iCol)
  138. {
  139. _columnNumber = iCol;
  140. }
  141. internal virtual void SetIsLastFrameFromForeignExceptionStackTrace(bool fIsLastFrame)
  142. {
  143. _isLastFrameFromForeignExceptionStackTrace = fIsLastFrame;
  144. }
  145. internal virtual bool GetIsLastFrameFromForeignExceptionStackTrace()
  146. {
  147. return _isLastFrameFromForeignExceptionStackTrace;
  148. }
  149. /// <summary>
  150. /// Returns the method the frame is executing
  151. /// </summary>
  152. public virtual MethodBase GetMethod()
  153. {
  154. return _method;
  155. }
  156. /// <summary>
  157. /// Returns the offset from the start of the native (jitted) code for the
  158. /// method being executed
  159. /// </summary>
  160. public virtual int GetNativeOffset()
  161. {
  162. return _nativeOffset;
  163. }
  164. /// <summary>
  165. /// Returns the offset from the start of the IL code for the
  166. /// method being executed. This offset may be approximate depending
  167. /// on whether the jitter is generating debuggable code or not.
  168. /// </summary>
  169. public virtual int GetILOffset()
  170. {
  171. return _ilOffset;
  172. }
  173. /// <summary>
  174. /// Returns the file name containing the code being executed. This
  175. /// information is normally extracted from the debugging symbols
  176. /// for the executable.
  177. /// </summary>
  178. public virtual string GetFileName()
  179. {
  180. return _fileName;
  181. }
  182. /// <summary>
  183. /// Returns the line number in the file containing the code being executed.
  184. /// This information is normally extracted from the debugging symbols
  185. /// for the executable.
  186. /// </summary>
  187. public virtual int GetFileLineNumber()
  188. {
  189. return _lineNumber;
  190. }
  191. /// <summary>
  192. /// Returns the column number in the line containing the code being executed.
  193. /// This information is normally extracted from the debugging symbols
  194. /// for the executable.
  195. /// </summary>
  196. public virtual int GetFileColumnNumber()
  197. {
  198. return _columnNumber;
  199. }
  200. /// <summary>
  201. /// Builds a readable representation of the stack frame
  202. /// </summary>
  203. public override string ToString()
  204. {
  205. StringBuilder sb = new StringBuilder(255);
  206. bool includeFileInfoIfAvailable;
  207. if (_method != null)
  208. {
  209. sb.Append(_method.Name);
  210. // deal with the generic portion of the method
  211. if (_method is MethodInfo methodInfo && methodInfo.IsGenericMethod)
  212. {
  213. Type[] typars = methodInfo.GetGenericArguments();
  214. sb.Append('<');
  215. int k = 0;
  216. bool fFirstTyParam = true;
  217. while (k < typars.Length)
  218. {
  219. if (fFirstTyParam == false)
  220. sb.Append(',');
  221. else
  222. fFirstTyParam = false;
  223. sb.Append(typars[k].Name);
  224. k++;
  225. }
  226. sb.Append('>');
  227. }
  228. includeFileInfoIfAvailable = true;
  229. }
  230. else
  231. {
  232. includeFileInfoIfAvailable = AppendStackFrameWithoutMethodBase(sb);
  233. }
  234. if (includeFileInfoIfAvailable)
  235. {
  236. sb.Append(" at offset ");
  237. if (_nativeOffset == OFFSET_UNKNOWN)
  238. sb.Append("<offset unknown>");
  239. else
  240. sb.Append(_nativeOffset);
  241. sb.Append(" in file:line:column ");
  242. bool useFileName = (_fileName != null);
  243. if (!useFileName)
  244. sb.Append("<filename unknown>");
  245. else
  246. sb.Append(_fileName);
  247. sb.Append(':');
  248. sb.Append(_lineNumber);
  249. sb.Append(':');
  250. sb.Append(_columnNumber);
  251. }
  252. else
  253. {
  254. sb.Append("<null>");
  255. }
  256. sb.Append(Environment.NewLine);
  257. return sb.ToString();
  258. }
  259. }
  260. }