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