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.ComponentModel ;
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 ;
abstract class DiagnosticTraceBase
{
//Diagnostics trace
protected const string DefaultTraceListenerName = "Default" ;
protected const string TraceRecordVersion = "http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" ;
protected static string AppDomainFriendlyName = AppDomain . CurrentDomain . FriendlyName ;
const ushort TracingEventLogCategory = 4 ;
object thisLock ;
bool tracingEnabled = true ;
bool calledShutdown ;
bool haveListeners ;
SourceLevels level ;
protected string TraceSourceName ;
TraceSource traceSource ;
[Fx.Tag.SecurityNote(Critical = "This determines the event source name.")]
[SecurityCritical]
string eventSourceName ;
public DiagnosticTraceBase ( string traceSourceName )
{
this . thisLock = new object ( ) ;
this . TraceSourceName = traceSourceName ;
this . LastFailure = DateTime . MinValue ;
}
protected DateTime LastFailure { get ; set ; }
[ SuppressMessage ( FxCop . Category . Security , FxCop . Rule . DoNotIndirectlyExposeMethodsWithLinkDemands ,
Justification = "SecurityCritical method. Does not expose critical resources returned by methods with Link Demands" ) ]
[ Fx . Tag . SecurityNote ( Critical = "Critical because we are invoking TraceSource.Listeners which has a Link Demand for UnmanagedCode permission." ,
Miscellaneous = "Asserting Unmanaged Code causes traceSource.Listeners to be successfully initiated and cached. But the Listeners property has a LinkDemand for UnmanagedCode, so it can't be read by partially trusted assemblies in heterogeneous appdomains" ) ]
[SecurityCritical]
[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
static void UnsafeRemoveDefaultTraceListener ( TraceSource traceSource )
{
traceSource . Listeners . Remove ( DiagnosticTraceBase . DefaultTraceListenerName ) ;
}
public TraceSource TraceSource
{
get
{
return this . traceSource ;
}
set
{
SetTraceSource ( value ) ;
}
}
[ SuppressMessage ( FxCop . Category . Security , FxCop . Rule . DoNotIndirectlyExposeMethodsWithLinkDemands ,
Justification = "Does not expose critical resources returned by methods with Link Demands" ) ]
[ Fx . Tag . SecurityNote ( Critical = "Critical because we are invoking TraceSource.Listeners which has a Link Demand for UnmanagedCode permission." ,
Safe = "Safe because are only retrieving the count of listeners and removing the default trace listener - we aren't leaking any critical resources." ) ]
[SecuritySafeCritical]
protected void SetTraceSource ( TraceSource traceSource )
{
if ( traceSource ! = null )
{
UnsafeRemoveDefaultTraceListener ( traceSource ) ;
this . traceSource = traceSource ;
this . haveListeners = this . traceSource . Listeners . Count > 0 ;
}
}
public bool HaveListeners
{
get
{
return this . haveListeners ;
}
}
SourceLevels FixLevel ( SourceLevels level )
{
//the bit fixing below is meant to keep the trace level legal even if somebody uses numbers in config
if ( ( ( level & ~ SourceLevels . Information ) & SourceLevels . Verbose ) ! = 0 )
{
level | = SourceLevels . Verbose ;
}
else if ( ( ( level & ~ SourceLevels . Warning ) & SourceLevels . Information ) ! = 0 )
{
level | = SourceLevels . Information ;
}
else if ( ( ( level & ~ SourceLevels . Error ) & SourceLevels . Warning ) ! = 0 )
{
level | = SourceLevels . Warning ;
}
if ( ( ( level & ~ SourceLevels . Critical ) & SourceLevels . Error ) ! = 0 )
{
level | = SourceLevels . Error ;
}
if ( ( level & SourceLevels . Critical ) ! = 0 )
{
level | = SourceLevels . Critical ;
}
// If only the ActivityTracing flag is set, then
// we really have Off. Do not do ActivityTracing then.
if ( level = = SourceLevels . ActivityTracing )
{
level = SourceLevels . Off ;
}
return level ;
}
protected virtual void OnSetLevel ( SourceLevels level )
{
}
[ SuppressMessage ( FxCop . Category . Security , FxCop . Rule . DoNotIndirectlyExposeMethodsWithLinkDemands ,
Justification = "Does not expose critical resources returned by methods with Link Demands" ) ]
[Fx.Tag.SecurityNote(Critical = "Critical because we are invoking TraceSource.Listeners and SourceSwitch.Level which have Link Demands for UnmanagedCode permission.")]
[SecurityCritical]
void SetLevel ( SourceLevels level )
{
SourceLevels fixedLevel = FixLevel ( level ) ;
this . level = fixedLevel ;
if ( this . TraceSource ! = null )
{
// Need this for setup from places like TransactionBridge.
this . haveListeners = this . TraceSource . Listeners . Count > 0 ;
OnSetLevel ( level ) ;
#pragma warning disable 618
this . tracingEnabled = this . HaveListeners & & ( level ! = SourceLevels . Off ) ;
#pragma warning restore 618
this . TraceSource . Switch . Level = level ;
}
}
[Fx.Tag.SecurityNote(Critical = "Critical because we are invoking SetLevel.")]
[SecurityCritical]
void SetLevelThreadSafe ( SourceLevels level )
{
lock ( this . thisLock )
{
SetLevel ( level ) ;
}
}
public SourceLevels Level
{
get
{
if ( this . TraceSource ! = null & & ( this . TraceSource . Switch . Level ! = this . level ) )
{
this . level = this . TraceSource . Switch . Level ;
}
return this . level ;
}
[Fx.Tag.SecurityNote(Critical = "Critical because we are invoking SetLevelTheadSafe.")]
[SecurityCritical]
set
{
SetLevelThreadSafe ( value ) ;
}
}
protected string EventSourceName
{
[ Fx . Tag . SecurityNote ( Critical = "Access critical eventSourceName field" ,
Safe = "Doesn't leak info\\resources" ) ]
[SecuritySafeCritical]
get
{
return this . eventSourceName ;
}
[Fx.Tag.SecurityNote(Critical = "This determines the event source name.")]
[SecurityCritical]
set
{
this . eventSourceName = value ;
}
}
public bool TracingEnabled
{
get
{
return this . tracingEnabled & & this . traceSource ! = null ;
}
}
protected static string ProcessName
{
[ Fx . Tag . SecurityNote ( Critical = "Satisfies a LinkDemand for 'PermissionSetAttribute' on type 'Process' when calling method GetCurrentProcess" ,
Safe = "Does not leak any resource and has been reviewed" ) ]
[SecuritySafeCritical]
get
{
string retval = null ;
using ( Process process = Process . GetCurrentProcess ( ) )
{
retval = process . ProcessName ;
}
return retval ;
}
}
protected static int ProcessId
{
[ Fx . Tag . SecurityNote ( Critical = "Satisfies a LinkDemand for 'PermissionSetAttribute' on type 'Process' when calling method GetCurrentProcess" ,
Safe = "Does not leak any resource and has been reviewed" ) ]
[SecuritySafeCritical]
get
{
int retval = - 1 ;
using ( Process process = Process . GetCurrentProcess ( ) )
{
retval = process . Id ;
}
return retval ;
}
}
public virtual bool ShouldTrace ( TraceEventLevel level )
{
return ShouldTraceToTraceSource ( level ) ;
}
public bool ShouldTrace ( TraceEventType type )
{
return this . TracingEnabled & & this . HaveListeners & &
( this . TraceSource ! = null ) & &
0 ! = ( ( int ) type & ( int ) this . Level ) ;
}
public bool ShouldTraceToTraceSource ( TraceEventLevel level )
{
return ShouldTrace ( TraceLevelHelper . GetTraceEventType ( level ) ) ;
}
//only used for exceptions, perf is not important
public static string XmlEncode ( string text )
{
if ( string . IsNullOrEmpty ( text ) )
{
return text ;
}
int len = text . Length ;
StringBuilder encodedText = new StringBuilder ( len + 8 ) ; //perf optimization, expecting no more than 2 > characters
for ( int i = 0 ; i < len ; + + i )
{
char ch = text [ i ] ;
switch ( ch )
{
case '<' :
encodedText . Append ( "<" ) ;
break ;
case '>' :
encodedText . Append ( ">" ) ;
break ;
case '&' :
encodedText . Append ( "&" ) ;
break ;
default :
encodedText . Append ( ch ) ;
break ;
}
}
return encodedText . ToString ( ) ;
}
[ Fx . Tag . SecurityNote ( Critical = "Sets global event handlers for the AppDomain" ,
Safe = "Doesn't leak resources\\Information" ) ]
[SecuritySafeCritical]
[ SuppressMessage ( FxCop . Category . Security , FxCop . Rule . DoNotIndirectlyExposeMethodsWithLinkDemands ,
Justification = "SecuritySafeCritical method, Does not expose critical resources returned by methods with Link Demands" ) ]
protected void AddDomainEventHandlersForCleanup ( )
{
AppDomain currentDomain = AppDomain . CurrentDomain ;
if ( this . TraceSource ! = null )
{
this . haveListeners = this . TraceSource . Listeners . Count > 0 ;
}
this . tracingEnabled = this . haveListeners ;
if ( this . TracingEnabled )
{
currentDomain . UnhandledException + = new UnhandledExceptionEventHandler ( UnhandledExceptionHandler ) ;
this . SetLevel ( this . TraceSource . Switch . Level ) ;
#if MONO_FEATURE_MULTIPLE_APPDOMAINS
currentDomain . DomainUnload + = new EventHandler ( ExitOrUnloadEventHandler ) ;
#endif
currentDomain . ProcessExit + = new EventHandler ( ExitOrUnloadEventHandler ) ;
}
}
void ExitOrUnloadEventHandler ( object sender , EventArgs e )
{
ShutdownTracing ( ) ;
}
protected abstract void OnUnhandledException ( Exception exception ) ;
protected void UnhandledExceptionHandler ( object sender , UnhandledExceptionEventArgs args )
{
Exception e = ( Exception ) args . ExceptionObject ;
OnUnhandledException ( e ) ;
ShutdownTracing ( ) ;
}
protected static string CreateSourceString ( object source )
{
var traceSourceStringProvider = source as ITraceSourceStringProvider ;
if ( traceSourceStringProvider ! = null )
{
return traceSourceStringProvider . GetSourceString ( ) ;
}
return CreateDefaultSourceString ( source ) ;
}
internal static string CreateDefaultSourceString ( object source )
{
if ( source = = null )
{
throw new ArgumentNullException ( "source" ) ;
}
return String . Format ( CultureInfo . CurrentCulture , "{0}/{1}" , source . GetType ( ) . ToString ( ) , source . GetHashCode ( ) ) ;
}
protected static void AddExceptionToTraceString ( XmlWriter xml , Exception exception )
{
xml . WriteElementString ( DiagnosticStrings . ExceptionTypeTag , XmlEncode ( exception . GetType ( ) . AssemblyQualifiedName ) ) ;
xml . WriteElementString ( DiagnosticStrings . MessageTag , XmlEncode ( exception . Message ) ) ;
xml . WriteElementString ( DiagnosticStrings . StackTraceTag , XmlEncode ( StackTraceString ( exception ) ) ) ;
xml . WriteElementString ( DiagnosticStrings . ExceptionStringTag , XmlEncode ( exception . ToString ( ) ) ) ;
Win32Exception win32Exception = exception as Win32Exception ;
if ( win32Exception ! = null )
{
xml . WriteElementString ( DiagnosticStrings . NativeErrorCodeTag , win32Exception . NativeErrorCode . ToString ( "X" , CultureInfo . InvariantCulture ) ) ;
}
if ( exception . Data ! = null & & exception . Data . Count > 0 )
{
xml . WriteStartElement ( DiagnosticStrings . DataItemsTag ) ;
foreach ( object dataItem in exception . Data . Keys )
{
xml . WriteStartElement ( DiagnosticStrings . DataTag ) ;
xml . WriteElementString ( DiagnosticStrings . KeyTag , XmlEncode ( dataItem . ToString ( ) ) ) ;
xml . WriteElementString ( DiagnosticStrings . ValueTag , XmlEncode ( exception . Data [ dataItem ] . ToString ( ) ) ) ;
xml . WriteEndElement ( ) ;
}
xml . WriteEndElement ( ) ;
}
if ( exception . InnerException ! = null )
{
xml . WriteStartElement ( DiagnosticStrings . InnerExceptionTag ) ;
AddExceptionToTraceString ( xml , exception . InnerException ) ;
xml . WriteEndElement ( ) ;
}
}
protected static string StackTraceString ( Exception exception )
{
string retval = exception . StackTrace ;
if ( string . IsNullOrEmpty ( retval ) )
{
// This means that the exception hasn't been thrown yet. We need to manufacture the stack then.
StackTrace stackTrace = new StackTrace ( false ) ;
// Figure out how many frames should be throw away
System . Diagnostics . StackFrame [ ] stackFrames = stackTrace . GetFrames ( ) ;
int frameCount = 0 ;
bool breakLoop = false ;
foreach ( StackFrame frame in stackFrames )
{
string methodName = frame . GetMethod ( ) . Name ;
switch ( methodName )
{
case "StackTraceString" :
case "AddExceptionToTraceString" :
case "BuildTrace" :
case "TraceEvent" :
case "TraceException" :
case "GetAdditionalPayload" :
+ + frameCount ;
break ;
default :
if ( methodName . StartsWith ( "ThrowHelper" , StringComparison . Ordinal ) )
{
+ + frameCount ;
}
else
{
breakLoop = true ;
}
break ;
}
if ( breakLoop )
{
break ;
}
}
stackTrace = new StackTrace ( frameCount , false ) ;
retval = stackTrace . ToString ( ) ;
}
return retval ;
}
//CSDMain:109153, Duplicate code from System.ServiceModel.Diagnostics
[ Fx . Tag . SecurityNote ( Critical = "Calls unsafe methods, UnsafeCreateEventLogger and UnsafeLogEvent." ,
Safe = "Event identities cannot be spoofed as they are constants determined inside the method, Demands the same permission that is asserted by the unsafe method." ) ]
[SecuritySafeCritical]
[ SuppressMessage ( FxCop . Category . Security , FxCop . Rule . SecureAsserts ,
Justification = "Should not demand permission that is asserted by the EtwProvider ctor." ) ]
protected void LogTraceFailure ( string traceString , Exception exception )
{
const int FailureBlackoutDuration = 10 ;
TimeSpan FailureBlackout = TimeSpan . FromMinutes ( FailureBlackoutDuration ) ;
try
{
lock ( this . thisLock )
{
if ( DateTime . UtcNow . Subtract ( this . LastFailure ) > = FailureBlackout )
{
this . LastFailure = DateTime . UtcNow ;
#pragma warning disable 618
EventLogger logger = EventLogger . UnsafeCreateEventLogger ( this . eventSourceName , this ) ;
#pragma warning restore 618
if ( exception = = null )
{
logger . UnsafeLogEvent ( TraceEventType . Error , TracingEventLogCategory , ( uint ) System . Runtime . Diagnostics . EventLogEventId . FailedToTraceEvent , false ,
traceString ) ;
}
else
{
logger . UnsafeLogEvent ( TraceEventType . Error , TracingEventLogCategory , ( uint ) System . Runtime . Diagnostics . EventLogEventId . FailedToTraceEventWithException , false ,
traceString , exception . ToString ( ) ) ;
}
}
}
}
catch ( Exception eventLoggerException )
{
if ( Fx . IsFatal ( eventLoggerException ) )
{
throw ;
}
}
}
protected abstract void OnShutdownTracing ( ) ;
void ShutdownTracing ( )
{
if ( ! this . calledShutdown )
{
this . calledShutdown = true ;
try
{
OnShutdownTracing ( ) ;
}
2017-08-21 15:34:15 +00:00
#pragma warning suppress 56500 //Microsoft; Taken care of by FxCop
2016-08-03 10:59:49 +00:00
catch ( Exception exception )
{
if ( Fx . IsFatal ( exception ) )
{
throw ;
}
//log failure
LogTraceFailure ( null , exception ) ;
}
}
}
protected bool CalledShutdown
{
get
{
return this . calledShutdown ;
}
}
public static Guid ActivityId
{
[ Fx . Tag . SecurityNote ( Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode" ,
Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM" ) ]
[SecuritySafeCritical]
[ SuppressMessage ( FxCop . Category . Security , FxCop . Rule . DoNotIndirectlyExposeMethodsWithLinkDemands ,
Justification = "SecuritySafeCriticial method" ) ]
get
{
object id = Trace . CorrelationManager . ActivityId ;
return id = = null ? Guid . Empty : ( Guid ) id ;
}
[ Fx . Tag . SecurityNote ( Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode" ,
Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM" ) ]
[SecuritySafeCritical]
set
{
Trace . CorrelationManager . ActivityId = value ;
}
}
#pragma warning restore 56500
protected static string LookupSeverity ( TraceEventType type )
{
string s ;
switch ( type )
{
case TraceEventType . Critical :
s = "Critical" ;
break ;
case TraceEventType . Error :
s = "Error" ;
break ;
case TraceEventType . Warning :
s = "Warning" ;
break ;
case TraceEventType . Information :
s = "Information" ;
break ;
case TraceEventType . Verbose :
s = "Verbose" ;
break ;
case TraceEventType . Start :
s = "Start" ;
break ;
case TraceEventType . Stop :
s = "Stop" ;
break ;
case TraceEventType . Suspend :
s = "Suspend" ;
break ;
case TraceEventType . Transfer :
s = "Transfer" ;
break ;
default :
s = type . ToString ( ) ;
break ;
}
#pragma warning disable 618
Fx . Assert ( s = = type . ToString ( ) , "Return value should equal the name of the enum" ) ;
#pragma warning restore 618
return s ;
}
public abstract bool IsEnabled ( ) ;
public abstract void TraceEventLogEvent ( TraceEventType type , TraceRecord traceRecord ) ;
}
}