|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security;
using System.Text;
using Microsoft.Xml;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.ServiceModel;
namespace System.Runtime.Diagnostics
{
internal sealed class EtwDiagnosticTrace : DiagnosticTraceBase
{
private const int XmlBracketsLength = 5; // "<></>".Length;
private const int MaxExceptionStringLength = 28 * 1024;
private const int MaxExceptionDepth = 64;
private static Guid s_defaultEtwProviderId = Guid.Empty;
//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.
s_defaultEtwProviderId = Guid.Empty;
}
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider, eventSourceName field")]
[SecurityCritical]
public EtwDiagnosticTrace(string traceSourceName, Guid etwProviderId)
: base(traceSourceName)
{
}
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.s_defaultEtwProviderId;
}
[Fx.Tag.SecurityNote(Critical = "setting critical field defaultEtwProviderId")]
[SecurityCritical]
[SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
Justification = "SecurityCriticial method")]
set
{
EtwDiagnosticTrace.s_defaultEtwProviderId = value;
}
}
public bool IsEtwProviderEnabled
{
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak info\\resources")]
[SecuritySafeCritical]
get
{
return false;
}
}
public bool IsEnd2EndActivityTracingEnabled
{
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak resources or information")]
[SecuritySafeCritical]
get
{
return false;
}
}
private bool EtwTracingEnabled
{
[Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
Safe = "Doesn't leak info\\resources")]
[SecuritySafeCritical]
get
{
return false;
}
}
[Fx.Tag.SecurityNote(Critical = "Accesses the security critical etwProvider field", Safe = "Doesn't leak info\\resources")]
[SecuritySafeCritical]
public void SetEnd2EndActivityTracingEnabled(bool isEnd2EndTracingEnabled)
{
}
[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)
{
}
[Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
[SecurityCritical]
public void WriteTraceSource(ref EventDescriptor eventDescriptor, string description, TracePayload payload)
{
}
private 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 (XmlWriter writer = XmlWriter.Create(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);
}
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)
{
return false;
}
public override bool IsEnabled()
{
return false;
}
internal static string ExceptionToTraceString(Exception exception, int maxTraceStringLength)
{
StringBuilder sb = StringBuilderPool.Take();
try
{
using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
{
using (XmlWriter xml = XmlWriter.Create(stringWriter))
{
WriteExceptionToTraceString(xml, exception, maxTraceStringLength, MaxExceptionDepth);
xml.Flush();
stringWriter.Flush();
return sb.ToString();
}
}
}
finally
{
StringBuilderPool.Return(sb);
}
}
private static void WriteExceptionToTraceString(XmlWriter 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))), // Stack trace is sometimes null
new Tuple<string, string> (DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString())),
};
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();
}
}
private 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 (XmlWriter xml = XmlWriter.Create(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);
}
}
private static string GetExceptionData(Exception exception)
{
StringBuilder sb = StringBuilderPool.Take();
try
{
using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
{
using (XmlWriter xml = XmlWriter.Create(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);
}
}
private static bool WriteStartElement(XmlWriter 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;
}
private static bool WriteXmlElementString(XmlWriter xml, string localName, string value, ref int remainingLength)
{
int xmlElementLength = (localName.Length * 2) + EtwDiagnosticTrace.XmlBracketsLength + (value != null ? value.Length : 0);
if (xmlElementLength <= remainingLength)
{
xml.WriteElementString(localName, value);
remainingLength -= xmlElementLength;
return true;
}
return false;
}
private static class TraceCodes
{
public const string AppDomainUnload = "AppDomainUnload";
public const string TraceHandledException = "TraceHandledException";
public const string ThrowingException = "ThrowingException";
public const string UnhandledException = "UnhandledException";
}
private 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;
}
private 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;
}
internal static class StringBuilderPool
{
private const int maxPooledStringBuilders = 64;
private static readonly ConcurrentQueue<StringBuilder> s_freeStringBuilders = new ConcurrentQueue<StringBuilder>();
public static StringBuilder Take()
{
StringBuilder sb = null;
if (s_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 (s_freeStringBuilders.Count <= maxPooledStringBuilders)
{
// There is a race condition here so the count could be off a little bit (but insignificantly)
sb.Clear();
s_freeStringBuilders.Enqueue(sb);
}
}
}
}
}
|