|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Runtime.Diagnostics
{
using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Diagnostics.CodeAnalysis;
using System.Security.Permissions;
using System.ServiceModel.Internals;
using System.Collections.Generic;
using System.Collections.Concurrent;
sealed class EtwDiagnosticTrace : DiagnosticTraceBase
{
//Diagnostics trace
const int WindowsVistaMajorNumber = 6;
const string EventSourceVersion = "4.0.0.0";
const ushort TracingEventLogCategory = 4;
const int MaxExceptionStringLength = 28 * 1024;
const int MaxExceptionDepth = 64;
const string DiagnosticTraceSource = "System.ServiceModel.Diagnostics";
const int XmlBracketsLength = 5; // "<></>".Length;
const int XmlBracketsLengthForNullValue = 4; // "< />".Length; (Empty XML Element)
static readonly public Guid ImmutableDefaultEtwProviderId = new Guid("{c651f5f6-1c0d-492e-8ae1-b4efd7c9d503}");
[Fx.Tag.SecurityNote(Critical = "provider Id to create EtwProvider, which is SecurityCritical")]
[SecurityCritical]
static Guid defaultEtwProviderId = ImmutableDefaultEtwProviderId;
static Hashtable etwProviderCache = new Hashtable();
static bool isVistaOrGreater = Environment.OSVersion.Version.Major >= WindowsVistaMajorNumber;
static Func<string> traceAnnotation;
[Fx.Tag.SecurityNote(Critical = "Stores object created by a critical c'tor")]
[SecurityCritical]
EtwProvider etwProvider;
Guid etwProviderId;
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
[SecurityCritical]
static EventDescriptor transferEventDescriptor = new EventDescriptor(499, 0, (byte)TraceChannel.Analytic, (byte)TraceEventLevel.LogAlways, (byte)TraceEventOpcode.Info, 0x0, 0x20000000001A0065);
//Compiler will add all static initializers into the static constructor. Adding an explicit one to mark SecurityCritical.
[Fx.Tag.SecurityNote(Critical = "setting critical field defaultEtwProviderId")]
[SecurityCritical]
[SuppressMessage(FxCop.Category.Performance, FxCop.Rule.InitializeReferenceTypeStaticFieldsInline,
Justification = "SecurityCriticial method")]
static EtwDiagnosticTrace()
{
// In Partial Trust, initialize to Guid.Empty to disable ETW Tracing.
if (!PartialTrustHelpers.HasEtwPermissions())
{
defaultEtwProviderId = Guid.Empty;
}
}
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider, eventSourceName field")]
[SecurityCritical]
public EtwDiagnosticTrace(string traceSourceName, Guid etwProviderId)
: base(traceSourceName)
{
try
{
this.TraceSourceName = traceSourceName;
this.EventSourceName = string.Concat(this.TraceSourceName, " ", EventSourceVersion);
CreateTraceSource();
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
#pragma warning disable 618
EventLogger logger = new EventLogger(this.EventSourceName, null);
logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false,
exception.ToString());
#pragma warning restore 618
}
try
{
CreateEtwProvider(etwProviderId);
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
this.etwProvider = null;
#pragma warning disable 618
EventLogger logger = new EventLogger(this.EventSourceName, null);
logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false,
exception.ToString());
#pragma warning restore 618
}
if (this.TracingEnabled || this.EtwTracingEnabled)
{
#pragma warning disable 618
this.AddDomainEventHandlersForCleanup();
#pragma warning restore 618
}
}
static public Guid DefaultEtwProviderId
{
[Fx.Tag.SecurityNote(Critical = "reading critical field defaultEtwProviderId", Safe = "Doesn't leak info\\resources")]
[SecuritySafeCritical]
[SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
Justification = "SecuritySafeCriticial method")]
get
{
return EtwDiagnosticTrace.defaultEtwProviderId;
}
[Fx.Tag.SecurityNote(Critical = "setting critical field defaultEtwProviderId")]
[SecurityCritical]
[SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
Justification = "SecurityCriticial method")]
set
{
EtwDiagnosticTrace.defaultEtwProviderId = value;
}
}
public EtwProvider EtwProvider
{
[Fx.Tag.SecurityNote(Critical = "Exposes the critical etwProvider field")]
[SecurityCritical]
get
{
return this.etwProvider;
}
}
public bool IsEtwProviderEnabled
{
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak info\\resources")]
[SecuritySafeCritical]
get
{
return (this.EtwTracingEnabled && this.etwProvider.IsEnabled());
}
}
public Action RefreshState
{
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak resources or information")]
[SecuritySafeCritical]
get
{
return this.EtwProvider.ControllerCallBack;
}
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak resources or information")]
[SecuritySafeCritical]
set
{
this.EtwProvider.ControllerCallBack = value;
}
}
public bool IsEnd2EndActivityTracingEnabled
{
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak resources or information")]
[SecuritySafeCritical]
get
{
return this.IsEtwProviderEnabled && this.EtwProvider.IsEnd2EndActivityTracingEnabled;
}
}
bool EtwTracingEnabled
{
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak info\\resources")]
[SecuritySafeCritical]
get
{
return (this.etwProvider != null);
}
}
[Fx.Tag.SecurityNote(Critical = "Accesses the security critical etwProvider field", Safe = "Doesn't leak info\\resources")]
[SecuritySafeCritical]
public void SetEnd2EndActivityTracingEnabled(bool isEnd2EndTracingEnabled)
{
this.EtwProvider.SetEnd2EndActivityTracingEnabled(isEnd2EndTracingEnabled);
}
public void SetAnnotation(Func<string> annotation)
{
EtwDiagnosticTrace.traceAnnotation = annotation;
}
public override bool ShouldTrace(TraceEventLevel level)
{
return base.ShouldTrace(level) || ShouldTraceToEtw(level);
}
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak information\\resources")]
[SecuritySafeCritical]
public bool ShouldTraceToEtw(TraceEventLevel level)
{
return (this.EtwProvider != null && this.EtwProvider.IsEnabled((byte)level, 0));
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand",
Safe = "Doesn't leak information\\resources")]
[SecuritySafeCritical]
public void Event(int eventId, TraceEventLevel traceEventLevel, TraceChannel channel, string description)
{
if (this.TracingEnabled)
{
EventDescriptor eventDescriptor = EtwDiagnosticTrace.GetEventDescriptor(eventId, channel, traceEventLevel);
this.Event(ref eventDescriptor, description);
}
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
[SecurityCritical]
public void Event(ref EventDescriptor eventDescriptor, string description)
{
if (this.TracingEnabled)
{
TracePayload tracePayload = this.GetSerializedPayload(null, null, null);
this.WriteTraceSource(ref eventDescriptor, description, tracePayload);
}
}
public void SetAndTraceTransfer(Guid newId, bool emitTransfer)
{
if (emitTransfer)
{
TraceTransfer(newId);
}
EtwDiagnosticTrace.ActivityId = newId;
}
[Fx.Tag.SecurityNote(Critical = "Access critical transferEventDescriptor field, as well as other critical methods",
Safe = "Doesn't leak information or resources")]
[SecuritySafeCritical]
public void TraceTransfer(Guid newId)
{
Guid oldId = EtwDiagnosticTrace.ActivityId;
if (newId != oldId)
{
try
{
if (this.HaveListeners)
{
this.TraceSource.TraceTransfer(0, null, newId);
}
//also emit to ETW
if (this.IsEtwEventEnabled(ref EtwDiagnosticTrace.transferEventDescriptor, false))
{
this.etwProvider.WriteTransferEvent(ref EtwDiagnosticTrace.transferEventDescriptor, new EventTraceActivity(oldId), newId,
EtwDiagnosticTrace.traceAnnotation == null ? string.Empty : EtwDiagnosticTrace.traceAnnotation(),
DiagnosticTraceBase.AppDomainFriendlyName);
}
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
LogTraceFailure(null, e);
}
}
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
[SecurityCritical]
[SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml", Justification = "It is internal code. No security concern.")]
public void WriteTraceSource(ref EventDescriptor eventDescriptor, string description, TracePayload payload)
{
if (this.TracingEnabled)
{
XPathNavigator navigator = null;
try
{
string msdnTraceCode;
int legacyEventId;
EtwDiagnosticTrace.GenerateLegacyTraceCode(ref eventDescriptor, out msdnTraceCode, out legacyEventId);
string traceString = BuildTrace(ref eventDescriptor, description, payload, msdnTraceCode);
XmlDocument traceDocument = new XmlDocument();
traceDocument.LoadXml(traceString);
navigator = traceDocument.CreateNavigator();
this.TraceSource.TraceData(TraceLevelHelper.GetTraceEventType(eventDescriptor.Level, eventDescriptor.Opcode), legacyEventId, navigator);
if (this.CalledShutdown)
{
this.TraceSource.Flush();
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
LogTraceFailure(navigator == null ? string.Empty : navigator.ToString(), exception);
}
}
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
[SecurityCritical]
static string BuildTrace(ref EventDescriptor eventDescriptor, string description, TracePayload payload, string msdnTraceCode)
{
StringBuilder sb = StringBuilderPool.Take();
try
{
using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
{
using (XmlTextWriter writer = new XmlTextWriter(stringWriter))
{
writer.WriteStartElement(DiagnosticStrings.TraceRecordTag);
writer.WriteAttributeString(DiagnosticStrings.NamespaceTag, EtwDiagnosticTrace.TraceRecordVersion);
writer.WriteAttributeString(DiagnosticStrings.SeverityTag,
TraceLevelHelper.LookupSeverity((TraceEventLevel)eventDescriptor.Level, (TraceEventOpcode)eventDescriptor.Opcode));
writer.WriteAttributeString(DiagnosticStrings.ChannelTag, EtwDiagnosticTrace.LookupChannel((TraceChannel)eventDescriptor.Channel));
writer.WriteElementString(DiagnosticStrings.TraceCodeTag, msdnTraceCode);
writer.WriteElementString(DiagnosticStrings.DescriptionTag, description);
writer.WriteElementString(DiagnosticStrings.AppDomain, payload.AppDomainFriendlyName);
if (!string.IsNullOrEmpty(payload.EventSource))
{
writer.WriteElementString(DiagnosticStrings.SourceTag, payload.EventSource);
}
if (!string.IsNullOrEmpty(payload.ExtendedData))
{
writer.WriteRaw(payload.ExtendedData);
}
if (!string.IsNullOrEmpty(payload.SerializedException))
{
writer.WriteRaw(payload.SerializedException);
}
writer.WriteEndElement();
writer.Flush();
stringWriter.Flush();
return sb.ToString();
}
}
}
finally
{
StringBuilderPool.Return(sb);
}
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
[SecurityCritical]
static void GenerateLegacyTraceCode(ref EventDescriptor eventDescriptor, out string msdnTraceCode, out int legacyEventId)
{
// To avoid breaking changes between 4.0 and 4.5 we have to use the same values for EventID and TraceCode like in 4.0
// The mapping between legacy trace code and the new ETW event ids has to be done manually - for example
// because there was only one event for HandledException in system.diagnostics. For ETW there are multiple events
// because you have to specify the verbosity level per event in the manifest.
switch (eventDescriptor.EventId)
{
case EventIdsWithMsdnTraceCode.AppDomainUnload:
msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.AppDomainUnload);
legacyEventId = LegacyTraceEventIds.AppDomainUnload;
break;
case EventIdsWithMsdnTraceCode.HandledExceptionError:
case EventIdsWithMsdnTraceCode.HandledExceptionWarning:
case EventIdsWithMsdnTraceCode.HandledExceptionInfo:
case EventIdsWithMsdnTraceCode.HandledExceptionVerbose:
msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.TraceHandledException);
legacyEventId = LegacyTraceEventIds.TraceHandledException;
break;
case EventIdsWithMsdnTraceCode.ThrowingExceptionVerbose:
case EventIdsWithMsdnTraceCode.ThrowingExceptionWarning:
msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.ThrowingException);
legacyEventId = LegacyTraceEventIds.ThrowingException;
break;
case EventIdsWithMsdnTraceCode.UnhandledException:
msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.UnhandledException);
legacyEventId = LegacyTraceEventIds.UnhandledException;
break;
default:
msdnTraceCode = eventDescriptor.EventId.ToString(CultureInfo.InvariantCulture);
legacyEventId = eventDescriptor.EventId;
break;
}
}
// helper for standardized trace code generation
static string GenerateMsdnTraceCode(string traceSource, string traceCodeString)
{
return string.Format(CultureInfo.InvariantCulture,
"https://docs.microsoft.com/dotnet/framework/wcf/diagnostics/tracing/{0}-{1}",
traceSource.Replace('.', '-'), traceCodeString);
}
static string LookupChannel(TraceChannel traceChannel)
{
string channelName;
switch (traceChannel)
{
case TraceChannel.Admin:
channelName = "Admin";
break;
case TraceChannel.Analytic:
channelName = "Analytic";
break;
case TraceChannel.Application:
channelName = "Application";
break;
case TraceChannel.Debug:
channelName = "Debug";
break;
case TraceChannel.Operational:
channelName = "Operational";
break;
case TraceChannel.Perf:
channelName = "Perf";
break;
default:
channelName = traceChannel.ToString();
break;
}
return channelName;
}
public TracePayload GetSerializedPayload(object source, TraceRecord traceRecord, Exception exception)
{
return this.GetSerializedPayload(source, traceRecord, exception, false);
}
public TracePayload GetSerializedPayload(object source, TraceRecord traceRecord, Exception exception, bool getServiceReference)
{
string eventSource = null;
string extendedData = null;
string serializedException = null;
if (source != null)
{
eventSource = CreateSourceString(source);
}
if (traceRecord != null)
{
StringBuilder sb = StringBuilderPool.Take();
try
{
using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
{
using (XmlTextWriter writer = new XmlTextWriter(stringWriter))
{
writer.WriteStartElement(DiagnosticStrings.ExtendedDataTag);
traceRecord.WriteTo(writer);
writer.WriteEndElement();
writer.Flush();
stringWriter.Flush();
extendedData = sb.ToString();
}
}
}
finally
{
StringBuilderPool.Return(sb);
}
}
if (exception != null)
{
// We want to keep the ETW trace message to under 32k. So we keep the serialized exception to under 28k bytes.
serializedException = ExceptionToTraceString(exception, MaxExceptionStringLength);
}
if (getServiceReference && (EtwDiagnosticTrace.traceAnnotation != null))
{
return new TracePayload(serializedException, eventSource, DiagnosticTraceBase.AppDomainFriendlyName, extendedData, EtwDiagnosticTrace.traceAnnotation());
}
return new TracePayload(serializedException, eventSource, DiagnosticTraceBase.AppDomainFriendlyName, extendedData, string.Empty);
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand",
Safe = "Only queries the status of the provider - does not modify the state")]
[SecuritySafeCritical]
public bool IsEtwEventEnabled(ref EventDescriptor eventDescriptor)
{
return IsEtwEventEnabled(ref eventDescriptor, true);
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand",
Safe = "Only queries the status of the provider - does not modify the state")]
[SecuritySafeCritical]
public bool IsEtwEventEnabled(ref EventDescriptor eventDescriptor, bool fullCheck)
{
// A full check queries ETW via a p/invoke call to see if the event is really enabled.
// Checking against the level and keywords passed in the ETW callback can provide false positives,
// but never a false negative.
// The only code which specifies false is two generated classes, System.Runtime.TraceCore and
// System.Activities.EtwTrackingParticipantTrackRecords, and the method EtwDiagnosticTrace.TraceTransfer().
// FxTrace uses IsEtwEventEnabled without the boolean, which then calls this method specifying true.
if (fullCheck)
{
return (this.EtwTracingEnabled && this.etwProvider.IsEventEnabled(ref eventDescriptor));
}
return (this.EtwTracingEnabled && this.etwProvider.IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords));
}
[Fx.Tag.SecurityNote(Critical = "Access the critical Listeners property",
Safe = "Only Removes the default listener of the local source")]
[SecuritySafeCritical]
[SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
Justification = "SecuritySafeCriticial method")]
void CreateTraceSource()
{
if (!string.IsNullOrEmpty(this.TraceSourceName))
{
SetTraceSource(new DiagnosticTraceSource(this.TraceSourceName));
}
}
[Fx.Tag.SecurityNote(Critical = "Sets this.etwProvider and calls EtwProvider constructor, which are Security Critical")]
[SecurityCritical]
void CreateEtwProvider(Guid etwProviderId)
{
if (etwProviderId != Guid.Empty && EtwDiagnosticTrace.isVistaOrGreater)
{
//Pick EtwProvider from cache, add to cache if not found
this.etwProvider = (EtwProvider)etwProviderCache[etwProviderId];
if (this.etwProvider == null)
{
lock (etwProviderCache)
{
this.etwProvider = (EtwProvider)etwProviderCache[etwProviderId];
if (this.etwProvider == null)
{
this.etwProvider = new EtwProvider(etwProviderId);
etwProviderCache.Add(etwProviderId, this.etwProvider);
}
}
}
this.etwProviderId = etwProviderId;
}
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
[SecurityCritical]
static EventDescriptor GetEventDescriptor(int eventId, TraceChannel channel, TraceEventLevel traceEventLevel)
{
unchecked
{
//map channel to keywords
long keyword = (long)0x0;
if (channel == TraceChannel.Admin)
{
keyword = keyword | (long)0x8000000000000000;
}
else if (channel == TraceChannel.Operational)
{
keyword = keyword | 0x4000000000000000;
}
else if (channel == TraceChannel.Analytic)
{
keyword = keyword | 0x2000000000000000;
}
else if (channel == TraceChannel.Debug)
{
keyword = keyword | 0x100000000000000;
}
else if (channel == TraceChannel.Perf)
{
keyword = keyword | 0x0800000000000000;
}
return new EventDescriptor(eventId, 0x0, (byte)channel, (byte)traceEventLevel, 0x0, 0x0, (long)keyword);
}
}
protected override void OnShutdownTracing()
{
ShutdownTraceSource();
ShutdownEtwProvider();
}
void ShutdownTraceSource()
{
try
{
if (TraceCore.AppDomainUnloadIsEnabled(this))
{
TraceCore.AppDomainUnload(this, AppDomain.CurrentDomain.FriendlyName,
DiagnosticTraceBase.ProcessName, DiagnosticTraceBase.ProcessId.ToString(CultureInfo.CurrentCulture));
}
this.TraceSource.Flush();
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
//log failure
LogTraceFailure(null, exception);
}
}
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak info\\resources")]
[SecuritySafeCritical]
void ShutdownEtwProvider()
{
try
{
if (this.etwProvider != null)
{
this.etwProvider.Dispose();
//no need to set this.etwProvider as null as Dispose() provides the necessary guard
//leaving it non-null protects trace calls from NullReferenceEx, CSDMain Bug 136228
}
}
catch (Exception exception)
{
if (Fx.IsFatal(exception))
{
throw;
}
//log failure
LogTraceFailure(null, exception);
}
}
public override bool IsEnabled()
{
return TraceCore.TraceCodeEventLogCriticalIsEnabled(this)
|| TraceCore.TraceCodeEventLogVerboseIsEnabled(this)
|| TraceCore.TraceCodeEventLogInfoIsEnabled(this)
|| TraceCore.TraceCodeEventLogWarningIsEnabled(this)
|| TraceCore.TraceCodeEventLogErrorIsEnabled(this);
}
public override void TraceEventLogEvent(TraceEventType type, TraceRecord traceRecord)
{
switch (type)
{
case TraceEventType.Critical:
if (TraceCore.TraceCodeEventLogCriticalIsEnabled(this))
{
TraceCore.TraceCodeEventLogCritical(this, traceRecord);
}
break;
case TraceEventType.Verbose:
if (TraceCore.TraceCodeEventLogVerboseIsEnabled(this))
{
TraceCore.TraceCodeEventLogVerbose(this, traceRecord);
}
break;
case TraceEventType.Information:
if (TraceCore.TraceCodeEventLogInfoIsEnabled(this))
{
TraceCore.TraceCodeEventLogInfo(this, traceRecord);
}
break;
case TraceEventType.Warning:
if (TraceCore.TraceCodeEventLogWarningIsEnabled(this))
{
TraceCore.TraceCodeEventLogWarning(this, traceRecord);
}
break;
case TraceEventType.Error:
if (TraceCore.TraceCodeEventLogErrorIsEnabled(this))
{
TraceCore.TraceCodeEventLogError(this, traceRecord);
}
break;
}
}
protected override void OnUnhandledException(Exception exception)
{
if (TraceCore.UnhandledExceptionIsEnabled(this))
{
TraceCore.UnhandledException(this, exception != null ? exception.ToString() : string.Empty, exception);
}
}
internal static string ExceptionToTraceString(Exception exception, int maxTraceStringLength)
{
StringBuilder sb = StringBuilderPool.Take();
try
{
using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
{
using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
{
WriteExceptionToTraceString(xml, exception, maxTraceStringLength, MaxExceptionDepth);
xml.Flush();
stringWriter.Flush();
return sb.ToString();
}
}
}
finally
{
StringBuilderPool.Return(sb);
}
}
static void WriteExceptionToTraceString(XmlTextWriter xml, Exception exception, int remainingLength, int remainingAllowedRecursionDepth)
{
if (remainingAllowedRecursionDepth < 1)
{
return;
}
if (!WriteStartElement(xml, DiagnosticStrings.ExceptionTag, ref remainingLength))
{
return;
}
try
{
IList<Tuple<string, string>> exceptionInfo = new List<Tuple<string, string>>()
{
new Tuple<string, string> (DiagnosticStrings.ExceptionTypeTag, XmlEncode(exception.GetType().AssemblyQualifiedName)),
new Tuple<string, string> (DiagnosticStrings.MessageTag, XmlEncode(exception.Message)),
new Tuple<string, string> (DiagnosticStrings.StackTraceTag, XmlEncode(StackTraceString(exception))),
new Tuple<string, string> (DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString())),
};
System.ComponentModel.Win32Exception win32Exception = exception as System.ComponentModel.Win32Exception;
if (win32Exception != null)
{
exceptionInfo.Add(
new Tuple<string, string>(
DiagnosticStrings.NativeErrorCodeTag,
win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture)));
}
foreach (Tuple<string, string> item in exceptionInfo)
{
if (!WriteXmlElementString(xml, item.Item1, item.Item2, ref remainingLength))
{
return;
}
}
if (exception.Data != null && exception.Data.Count > 0)
{
string exceptionData = GetExceptionData(exception);
if (exceptionData.Length < remainingLength)
{
xml.WriteRaw(exceptionData);
remainingLength -= exceptionData.Length;
}
}
if (exception.InnerException != null)
{
string innerException = GetInnerException(exception, remainingLength, remainingAllowedRecursionDepth - 1);
if (!string.IsNullOrEmpty(innerException) && innerException.Length < remainingLength)
{
xml.WriteRaw(innerException);
}
}
}
finally
{
xml.WriteEndElement();
}
}
static string GetInnerException(Exception exception, int remainingLength, int remainingAllowedRecursionDepth)
{
if (remainingAllowedRecursionDepth < 1)
{
return null;
}
StringBuilder sb = StringBuilderPool.Take();
try
{
using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
{
using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
{
if (!WriteStartElement(xml, DiagnosticStrings.InnerExceptionTag, ref remainingLength))
{
return null;
}
WriteExceptionToTraceString(xml, exception.InnerException, remainingLength, remainingAllowedRecursionDepth);
xml.WriteEndElement();
xml.Flush();
stringWriter.Flush();
return sb.ToString();
}
}
}
finally
{
StringBuilderPool.Return(sb);
}
}
static string GetExceptionData(Exception exception)
{
StringBuilder sb = StringBuilderPool.Take();
try
{
using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
{
using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
{
xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
foreach (object dataItem in exception.Data.Keys)
{
xml.WriteStartElement(DiagnosticStrings.DataTag);
xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
if (exception.Data[dataItem] == null)
{
xml.WriteElementString(DiagnosticStrings.ValueTag, string.Empty);
}
else
{
xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
}
xml.WriteEndElement();
}
xml.WriteEndElement();
xml.Flush();
stringWriter.Flush();
return sb.ToString();
}
}
}
finally
{
StringBuilderPool.Return(sb);
}
}
static bool WriteStartElement(XmlTextWriter xml, string localName, ref int remainingLength)
{
int minXmlLength = (localName.Length * 2) + EtwDiagnosticTrace.XmlBracketsLength;
if (minXmlLength <= remainingLength)
{
xml.WriteStartElement(localName);
remainingLength -= minXmlLength;
return true;
}
return false;
}
static bool WriteXmlElementString(XmlTextWriter xml, string localName, string value, ref int remainingLength)
{
int xmlElementLength;
// Quirk to fix DevDiv 155469: All previous versions of that platform (up-to 4.6.2) will get the old behavior (throw null ref when Exception Message property is null)
if (string.IsNullOrEmpty(value) && !LocalAppContextSwitches.IncludeNullExceptionMessageInETWTrace)
{
xmlElementLength = localName.Length + EtwDiagnosticTrace.XmlBracketsLengthForNullValue;
}
else
{
xmlElementLength = (localName.Length * 2) + EtwDiagnosticTrace.XmlBracketsLength + value.Length;
}
if (xmlElementLength <= remainingLength)
{
xml.WriteElementString(localName, value);
remainingLength -= xmlElementLength;
return true;
}
return false;
}
static class TraceCodes
{
public const string AppDomainUnload = "AppDomainUnload";
public const string TraceHandledException = "TraceHandledException";
public const string ThrowingException = "ThrowingException";
public const string UnhandledException = "UnhandledException";
}
static class EventIdsWithMsdnTraceCode
{
// EventIds for which we need to translate the traceCode and the eventId
// when system.diagnostics tracing is enabled.
public const int AppDomainUnload = 57393;
public const int ThrowingExceptionWarning = 57396;
public const int ThrowingExceptionVerbose = 57407;
public const int HandledExceptionInfo = 57394;
public const int HandledExceptionWarning = 57404;
public const int HandledExceptionError = 57405;
public const int HandledExceptionVerbose = 57406;
public const int UnhandledException = 57397;
}
static class LegacyTraceEventIds
{
// Diagnostic trace codes
public const int Diagnostics = 0X20000;
public const int AppDomainUnload = LegacyTraceEventIds.Diagnostics | 0X0001;
public const int EventLog = LegacyTraceEventIds.Diagnostics | 0X0002;
public const int ThrowingException = LegacyTraceEventIds.Diagnostics | 0X0003;
public const int TraceHandledException = LegacyTraceEventIds.Diagnostics | 0X0004;
public const int UnhandledException = LegacyTraceEventIds.Diagnostics | 0X0005;
}
static class StringBuilderPool
{
const int maxPooledStringBuilders = 64;
static readonly ConcurrentQueue<StringBuilder> freeStringBuilders = new ConcurrentQueue<StringBuilder>();
public static StringBuilder Take()
{
StringBuilder sb = null;
if (freeStringBuilders.TryDequeue(out sb))
{
return sb;
}
return new StringBuilder();
}
public static void Return(StringBuilder sb)
{
Fx.Assert(sb != null, "'sb' MUST NOT be NULL.");
if (freeStringBuilders.Count <= maxPooledStringBuilders)
{
// There is a race condition here so the count could be off a little bit (but insignificantly)
sb.Clear();
freeStringBuilders.Enqueue(sb);
}
}
}
}
}
|