ExceptionTrace.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.Runtime
  5. {
  6. using System;
  7. using System.Collections.ObjectModel;
  8. using System.Diagnostics;
  9. using System.Globalization;
  10. using System.Reflection;
  11. using System.Runtime.CompilerServices;
  12. using System.Runtime.Diagnostics;
  13. using System.Runtime.Interop;
  14. using System.Runtime.Versioning;
  15. using System.Security;
  16. class ExceptionTrace
  17. {
  18. const ushort FailFastEventLogCategory = 6;
  19. string eventSourceName;
  20. readonly EtwDiagnosticTrace diagnosticTrace;
  21. public ExceptionTrace(string eventSourceName, EtwDiagnosticTrace diagnosticTrace)
  22. {
  23. Fx.Assert(diagnosticTrace != null, "'diagnosticTrace' MUST NOT be NULL.");
  24. this.eventSourceName = eventSourceName;
  25. this.diagnosticTrace = diagnosticTrace;
  26. }
  27. public void AsInformation(Exception exception)
  28. {
  29. //Traces an informational trace message
  30. TraceCore.HandledException(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception);
  31. }
  32. public void AsWarning(Exception exception)
  33. {
  34. //Traces a warning trace message
  35. TraceCore.HandledExceptionWarning(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception);
  36. }
  37. public Exception AsError(Exception exception)
  38. {
  39. // AggregateExceptions are automatically unwrapped.
  40. AggregateException aggregateException = exception as AggregateException;
  41. if (aggregateException != null)
  42. {
  43. return AsError<Exception>(aggregateException);
  44. }
  45. // TargetInvocationExceptions are automatically unwrapped.
  46. TargetInvocationException targetInvocationException = exception as TargetInvocationException;
  47. if (targetInvocationException != null && targetInvocationException.InnerException != null)
  48. {
  49. return AsError(targetInvocationException.InnerException);
  50. }
  51. return TraceException<Exception>(exception);
  52. }
  53. public Exception AsError(Exception exception, string eventSource)
  54. {
  55. // AggregateExceptions are automatically unwrapped.
  56. AggregateException aggregateException = exception as AggregateException;
  57. if (aggregateException != null)
  58. {
  59. return AsError<Exception>(aggregateException, eventSource);
  60. }
  61. // TargetInvocationExceptions are automatically unwrapped.
  62. TargetInvocationException targetInvocationException = exception as TargetInvocationException;
  63. if (targetInvocationException != null && targetInvocationException.InnerException != null)
  64. {
  65. return AsError(targetInvocationException.InnerException, eventSource);
  66. }
  67. return TraceException<Exception>(exception, eventSource);
  68. }
  69. public Exception AsError(TargetInvocationException targetInvocationException, string eventSource)
  70. {
  71. Fx.Assert(targetInvocationException != null, "targetInvocationException cannot be null.");
  72. // If targetInvocationException contains any fatal exceptions, return it directly
  73. // without tracing it or any inner exceptions.
  74. if (Fx.IsFatal(targetInvocationException))
  75. {
  76. return targetInvocationException;
  77. }
  78. // A non-null inner exception could require further unwrapping in AsError.
  79. Exception innerException = targetInvocationException.InnerException;
  80. if (innerException != null)
  81. {
  82. return AsError(innerException, eventSource);
  83. }
  84. // A null inner exception is unlikely but possible.
  85. // In this case, trace and return the targetInvocationException itself.
  86. return TraceException<Exception>(targetInvocationException, eventSource);
  87. }
  88. public Exception AsError<TPreferredException>(AggregateException aggregateException)
  89. {
  90. return AsError<TPreferredException>(aggregateException, this.eventSourceName);
  91. }
  92. /// <summary>
  93. /// Extracts the first inner exception of type <typeparamref name="TPreferredException"/>
  94. /// from the <see cref="AggregateException"/> if one is present.
  95. /// </summary>
  96. /// <remarks>
  97. /// If no <typeparamref name="TPreferredException"/> inner exception is present, this
  98. /// method returns the first inner exception. All inner exceptions will be traced,
  99. /// including the one returned. The containing <paramref name="aggregateException"/>
  100. /// will not be traced unless there are no inner exceptions.
  101. /// </remarks>
  102. /// <typeparam name="TPreferredException">The preferred type of inner exception to extract.
  103. /// Use <c>typeof(Exception)</c> to extract the first exception regardless of type.</typeparam>
  104. /// <param name="aggregateException">The <see cref="AggregateException"/> to examine.</param>
  105. /// <param name="eventSource">The event source to trace.</param>
  106. /// <returns>The extracted exception. It will not be <c>null</c>
  107. /// but it may not be of type <typeparamref name="TPreferredException"/>.</returns>
  108. public Exception AsError<TPreferredException>(AggregateException aggregateException, string eventSource)
  109. {
  110. Fx.Assert(aggregateException != null, "aggregateException cannot be null.");
  111. // If aggregateException contains any fatal exceptions, return it directly
  112. // without tracing it or any inner exceptions.
  113. if (Fx.IsFatal(aggregateException))
  114. {
  115. return aggregateException;
  116. }
  117. // Collapse possibly nested graph into a flat list.
  118. // Empty inner exception list is unlikely but possible via public api.
  119. ReadOnlyCollection<Exception> innerExceptions = aggregateException.Flatten().InnerExceptions;
  120. if (innerExceptions.Count == 0)
  121. {
  122. return TraceException(aggregateException, eventSource);
  123. }
  124. // Find the first inner exception, giving precedence to TPreferredException
  125. Exception favoredException = null;
  126. foreach (Exception nextInnerException in innerExceptions)
  127. {
  128. // AggregateException may wrap TargetInvocationException, so unwrap those as well
  129. TargetInvocationException targetInvocationException = nextInnerException as TargetInvocationException;
  130. Exception innerException = (targetInvocationException != null && targetInvocationException.InnerException != null)
  131. ? targetInvocationException.InnerException
  132. : nextInnerException;
  133. if (innerException is TPreferredException && favoredException == null)
  134. {
  135. favoredException = innerException;
  136. }
  137. // All inner exceptions are traced
  138. TraceException<Exception>(innerException, eventSource);
  139. }
  140. if (favoredException == null)
  141. {
  142. Fx.Assert(innerExceptions.Count > 0, "InnerException.Count is known to be > 0 here.");
  143. favoredException = innerExceptions[0];
  144. }
  145. return favoredException;
  146. }
  147. public ArgumentException Argument(string paramName, string message)
  148. {
  149. return TraceException<ArgumentException>(new ArgumentException(message, paramName));
  150. }
  151. public ArgumentNullException ArgumentNull(string paramName)
  152. {
  153. return TraceException<ArgumentNullException>(new ArgumentNullException(paramName));
  154. }
  155. public ArgumentNullException ArgumentNull(string paramName, string message)
  156. {
  157. return TraceException<ArgumentNullException>(new ArgumentNullException(paramName, message));
  158. }
  159. public ArgumentException ArgumentNullOrEmpty(string paramName)
  160. {
  161. return this.Argument(paramName, InternalSR.ArgumentNullOrEmpty(paramName));
  162. }
  163. public ArgumentOutOfRangeException ArgumentOutOfRange(string paramName, object actualValue, string message)
  164. {
  165. return TraceException<ArgumentOutOfRangeException>(new ArgumentOutOfRangeException(paramName, actualValue, message));
  166. }
  167. // When throwing ObjectDisposedException, it is highly recommended that you use this ctor
  168. // [C#]
  169. // public ObjectDisposedException(string objectName, string message);
  170. // And provide null for objectName but meaningful and relevant message for message.
  171. // It is recommended because end user really does not care or can do anything on the disposed object, commonly an internal or private object.
  172. public ObjectDisposedException ObjectDisposed(string message)
  173. {
  174. // pass in null, not disposedObject.GetType().FullName as per the above guideline
  175. return TraceException<ObjectDisposedException>(new ObjectDisposedException(null, message));
  176. }
  177. public void TraceUnhandledException(Exception exception)
  178. {
  179. TraceCore.UnhandledException(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception);
  180. }
  181. public void TraceHandledException(Exception exception, TraceEventType traceEventType)
  182. {
  183. switch (traceEventType)
  184. {
  185. case TraceEventType.Error:
  186. if (TraceCore.HandledExceptionErrorIsEnabled(this.diagnosticTrace))
  187. {
  188. TraceCore.HandledExceptionError(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception);
  189. }
  190. break;
  191. case TraceEventType.Warning:
  192. if (TraceCore.HandledExceptionWarningIsEnabled(this.diagnosticTrace))
  193. {
  194. TraceCore.HandledExceptionWarning(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception);
  195. }
  196. break;
  197. case TraceEventType.Verbose:
  198. if (TraceCore.HandledExceptionVerboseIsEnabled(this.diagnosticTrace))
  199. {
  200. TraceCore.HandledExceptionVerbose(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception);
  201. }
  202. break;
  203. default:
  204. if (TraceCore.HandledExceptionIsEnabled(this.diagnosticTrace))
  205. {
  206. TraceCore.HandledException(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception);
  207. }
  208. break;
  209. }
  210. }
  211. public void TraceEtwException(Exception exception, TraceEventType eventType)
  212. {
  213. switch (eventType)
  214. {
  215. case TraceEventType.Error:
  216. case TraceEventType.Warning:
  217. if (TraceCore.ThrowingExceptionIsEnabled(this.diagnosticTrace))
  218. {
  219. TraceCore.ThrowingEtwException(this.diagnosticTrace, this.eventSourceName, exception != null ? exception.ToString() : string.Empty, exception);
  220. }
  221. break;
  222. case TraceEventType.Critical:
  223. if (TraceCore.UnhandledExceptionIsEnabled(this.diagnosticTrace))
  224. {
  225. TraceCore.EtwUnhandledException(this.diagnosticTrace, exception != null ? exception.ToString() : string.Empty, exception);
  226. }
  227. break;
  228. default:
  229. if (TraceCore.ThrowingExceptionVerboseIsEnabled(this.diagnosticTrace))
  230. {
  231. TraceCore.ThrowingEtwExceptionVerbose(this.diagnosticTrace, this.eventSourceName, exception != null ? exception.ToString() : string.Empty, exception);
  232. }
  233. break;
  234. }
  235. }
  236. TException TraceException<TException>(TException exception)
  237. where TException : Exception
  238. {
  239. return TraceException<TException>(exception, this.eventSourceName);
  240. }
  241. [ResourceConsumption(ResourceScope.Process)]
  242. [Fx.Tag.SecurityNote(Critical = "Calls 'System.Runtime.Interop.UnsafeNativeMethods.IsDebuggerPresent()' which is a P/Invoke method",
  243. Safe = "Does not leak any resource, needed for debugging")]
  244. [SecuritySafeCritical]
  245. TException TraceException<TException>(TException exception, string eventSource)
  246. where TException : Exception
  247. {
  248. if (TraceCore.ThrowingExceptionIsEnabled(this.diagnosticTrace))
  249. {
  250. TraceCore.ThrowingException(this.diagnosticTrace, eventSource, exception != null ? exception.ToString() : string.Empty, exception);
  251. }
  252. BreakOnException(exception);
  253. return exception;
  254. }
  255. [Fx.Tag.SecurityNote(Critical = "Calls into critical method UnsafeNativeMethods.IsDebuggerPresent and UnsafeNativeMethods.DebugBreak",
  256. Safe = "Safe because it's a no-op in retail builds.")]
  257. [SecuritySafeCritical]
  258. void BreakOnException(Exception exception)
  259. {
  260. #if DEBUG
  261. if (Fx.BreakOnExceptionTypes != null)
  262. {
  263. foreach (Type breakType in Fx.BreakOnExceptionTypes)
  264. {
  265. if (breakType.IsAssignableFrom(exception.GetType()))
  266. {
  267. // This is intended to "crash" the process so that a debugger can be attached. If a managed
  268. // debugger is already attached, it will already be able to hook these exceptions. We don't
  269. // want to simulate an unmanaged crash (DebugBreak) in that case.
  270. if (!Debugger.IsAttached && !UnsafeNativeMethods.IsDebuggerPresent())
  271. {
  272. UnsafeNativeMethods.DebugBreak();
  273. }
  274. }
  275. }
  276. }
  277. #endif
  278. }
  279. [MethodImpl(MethodImplOptions.NoInlining)]
  280. internal void TraceFailFast(string message)
  281. {
  282. EventLogger logger = null;
  283. #pragma warning disable 618
  284. logger = new EventLogger(this.eventSourceName, this.diagnosticTrace);
  285. #pragma warning restore 618
  286. TraceFailFast(message, logger);
  287. }
  288. // Generate an event Log entry for failfast purposes
  289. // To force a Watson on a dev machine, do the following:
  290. // 1. Set \HKLM\SOFTWARE\Microsoft\PCHealth\ErrorReporting ForceQueueMode = 0
  291. // 2. In the command environment, set COMPLUS_DbgJitDebugLaunchSetting=0
  292. [MethodImpl(MethodImplOptions.NoInlining)]
  293. internal void TraceFailFast(string message, EventLogger logger)
  294. {
  295. if (logger != null)
  296. {
  297. try
  298. {
  299. string stackTrace = null;
  300. try
  301. {
  302. stackTrace = new StackTrace().ToString();
  303. }
  304. catch (Exception exception)
  305. {
  306. stackTrace = exception.Message;
  307. if (Fx.IsFatal(exception))
  308. {
  309. throw;
  310. }
  311. }
  312. finally
  313. {
  314. logger.LogEvent(TraceEventType.Critical,
  315. FailFastEventLogCategory,
  316. (uint)System.Runtime.Diagnostics.EventLogEventId.FailFast,
  317. message,
  318. stackTrace);
  319. }
  320. }
  321. catch (Exception ex)
  322. {
  323. logger.LogEvent(TraceEventType.Critical,
  324. FailFastEventLogCategory,
  325. (uint)System.Runtime.Diagnostics.EventLogEventId.FailFastException,
  326. ex.ToString());
  327. if (Fx.IsFatal(ex))
  328. {
  329. throw;
  330. }
  331. }
  332. }
  333. }
  334. }
  335. }