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