2016-08-03 10:59:49 +00:00
//-----------------------------------------------------------------------------
// 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 ;
2017-08-21 15:34:15 +00:00
using System.ServiceModel.Internals ;
2016-08-03 10:59:49 +00:00
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;
2017-08-21 15:34:15 +00:00
const int XmlBracketsLengthForNullValue = 4 ; // "< />".Length; (Empty XML Element)
2016-08-03 10:59:49 +00:00
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]
2016-11-10 13:04:39 +00:00
[SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml", Justification = "It is internal code. No security concern.")]
2016-08-03 10:59:49 +00:00
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 ,
"http://msdn.microsoft.com/{0}/library/{1}.{2}.aspx" ,
CultureInfo . CurrentCulture . Name ,
traceSource , 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 )
{
2017-08-21 15:34:15 +00:00
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 ;
}
2016-08-03 10:59:49 +00:00
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 ) ;
}
}
}
}
}