1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Runtime.Diagnostics
8 using System.Collections;
9 using System.Diagnostics;
10 using System.Globalization;
12 using System.Security;
15 using System.Xml.XPath;
16 using System.Diagnostics.CodeAnalysis;
17 using System.Security.Permissions;
18 using System.ServiceModel.Internals;
19 using System.Collections.Generic;
20 using System.Collections.Concurrent;
22 sealed class EtwDiagnosticTrace : DiagnosticTraceBase
25 const int WindowsVistaMajorNumber = 6;
26 const string EventSourceVersion = "4.0.0.0";
27 const ushort TracingEventLogCategory = 4;
28 const int MaxExceptionStringLength = 28 * 1024;
29 const int MaxExceptionDepth = 64;
30 const string DiagnosticTraceSource = "System.ServiceModel.Diagnostics";
32 const int XmlBracketsLength = 5; // "<></>".Length;
33 const int XmlBracketsLengthForNullValue = 4; // "< />".Length; (Empty XML Element)
35 static readonly public Guid ImmutableDefaultEtwProviderId = new Guid("{c651f5f6-1c0d-492e-8ae1-b4efd7c9d503}");
36 [Fx.Tag.SecurityNote(Critical = "provider Id to create EtwProvider, which is SecurityCritical")]
38 static Guid defaultEtwProviderId = ImmutableDefaultEtwProviderId;
39 static Hashtable etwProviderCache = new Hashtable();
40 static bool isVistaOrGreater = Environment.OSVersion.Version.Major >= WindowsVistaMajorNumber;
41 static Func<string> traceAnnotation;
43 [Fx.Tag.SecurityNote(Critical = "Stores object created by a critical c'tor")]
45 EtwProvider etwProvider;
47 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
49 static EventDescriptor transferEventDescriptor = new EventDescriptor(499, 0, (byte)TraceChannel.Analytic, (byte)TraceEventLevel.LogAlways, (byte)TraceEventOpcode.Info, 0x0, 0x20000000001A0065);
51 //Compiler will add all static initializers into the static constructor. Adding an explicit one to mark SecurityCritical.
52 [Fx.Tag.SecurityNote(Critical = "setting critical field defaultEtwProviderId")]
54 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.InitializeReferenceTypeStaticFieldsInline,
55 Justification = "SecurityCriticial method")]
56 static EtwDiagnosticTrace()
58 // In Partial Trust, initialize to Guid.Empty to disable ETW Tracing.
59 if (!PartialTrustHelpers.HasEtwPermissions())
61 defaultEtwProviderId = Guid.Empty;
65 [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider, eventSourceName field")]
67 public EtwDiagnosticTrace(string traceSourceName, Guid etwProviderId)
68 : base(traceSourceName)
72 this.TraceSourceName = traceSourceName;
73 this.EventSourceName = string.Concat(this.TraceSourceName, " ", EventSourceVersion);
76 catch (Exception exception)
78 if (Fx.IsFatal(exception))
83 #pragma warning disable 618
84 EventLogger logger = new EventLogger(this.EventSourceName, null);
85 logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false,
86 exception.ToString());
87 #pragma warning restore 618
92 CreateEtwProvider(etwProviderId);
94 catch (Exception exception)
96 if (Fx.IsFatal(exception))
101 this.etwProvider = null;
102 #pragma warning disable 618
103 EventLogger logger = new EventLogger(this.EventSourceName, null);
104 logger.LogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToSetupTracing, false,
105 exception.ToString());
106 #pragma warning restore 618
110 if (this.TracingEnabled || this.EtwTracingEnabled)
112 #pragma warning disable 618
113 this.AddDomainEventHandlersForCleanup();
114 #pragma warning restore 618
118 static public Guid DefaultEtwProviderId
120 [Fx.Tag.SecurityNote(Critical = "reading critical field defaultEtwProviderId", Safe = "Doesn't leak info\\resources")]
121 [SecuritySafeCritical]
122 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
123 Justification = "SecuritySafeCriticial method")]
126 return EtwDiagnosticTrace.defaultEtwProviderId;
128 [Fx.Tag.SecurityNote(Critical = "setting critical field defaultEtwProviderId")]
130 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
131 Justification = "SecurityCriticial method")]
134 EtwDiagnosticTrace.defaultEtwProviderId = value;
138 public EtwProvider EtwProvider
140 [Fx.Tag.SecurityNote(Critical = "Exposes the critical etwProvider field")]
144 return this.etwProvider;
148 public bool IsEtwProviderEnabled
150 [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
151 Safe = "Doesn't leak info\\resources")]
152 [SecuritySafeCritical]
155 return (this.EtwTracingEnabled && this.etwProvider.IsEnabled());
159 public Action RefreshState
161 [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
162 Safe = "Doesn't leak resources or information")]
163 [SecuritySafeCritical]
166 return this.EtwProvider.ControllerCallBack;
169 [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
170 Safe = "Doesn't leak resources or information")]
171 [SecuritySafeCritical]
174 this.EtwProvider.ControllerCallBack = value;
178 public bool IsEnd2EndActivityTracingEnabled
180 [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
181 Safe = "Doesn't leak resources or information")]
182 [SecuritySafeCritical]
185 return this.IsEtwProviderEnabled && this.EtwProvider.IsEnd2EndActivityTracingEnabled;
189 bool EtwTracingEnabled
191 [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
192 Safe = "Doesn't leak info\\resources")]
193 [SecuritySafeCritical]
196 return (this.etwProvider != null);
200 [Fx.Tag.SecurityNote(Critical = "Accesses the security critical etwProvider field", Safe = "Doesn't leak info\\resources")]
201 [SecuritySafeCritical]
202 public void SetEnd2EndActivityTracingEnabled(bool isEnd2EndTracingEnabled)
204 this.EtwProvider.SetEnd2EndActivityTracingEnabled(isEnd2EndTracingEnabled);
207 public void SetAnnotation(Func<string> annotation)
209 EtwDiagnosticTrace.traceAnnotation = annotation;
212 public override bool ShouldTrace(TraceEventLevel level)
214 return base.ShouldTrace(level) || ShouldTraceToEtw(level);
217 [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
218 Safe = "Doesn't leak information\\resources")]
219 [SecuritySafeCritical]
220 public bool ShouldTraceToEtw(TraceEventLevel level)
222 return (this.EtwProvider != null && this.EtwProvider.IsEnabled((byte)level, 0));
225 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand",
226 Safe = "Doesn't leak information\\resources")]
227 [SecuritySafeCritical]
228 public void Event(int eventId, TraceEventLevel traceEventLevel, TraceChannel channel, string description)
230 if (this.TracingEnabled)
232 EventDescriptor eventDescriptor = EtwDiagnosticTrace.GetEventDescriptor(eventId, channel, traceEventLevel);
233 this.Event(ref eventDescriptor, description);
237 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
239 public void Event(ref EventDescriptor eventDescriptor, string description)
241 if (this.TracingEnabled)
243 TracePayload tracePayload = this.GetSerializedPayload(null, null, null);
244 this.WriteTraceSource(ref eventDescriptor, description, tracePayload);
248 public void SetAndTraceTransfer(Guid newId, bool emitTransfer)
252 TraceTransfer(newId);
254 EtwDiagnosticTrace.ActivityId = newId;
257 [Fx.Tag.SecurityNote(Critical = "Access critical transferEventDescriptor field, as well as other critical methods",
258 Safe = "Doesn't leak information or resources")]
259 [SecuritySafeCritical]
260 public void TraceTransfer(Guid newId)
262 Guid oldId = EtwDiagnosticTrace.ActivityId;
267 if (this.HaveListeners)
269 this.TraceSource.TraceTransfer(0, null, newId);
272 if (this.IsEtwEventEnabled(ref EtwDiagnosticTrace.transferEventDescriptor, false))
274 this.etwProvider.WriteTransferEvent(ref EtwDiagnosticTrace.transferEventDescriptor, new EventTraceActivity(oldId), newId,
275 EtwDiagnosticTrace.traceAnnotation == null ? string.Empty : EtwDiagnosticTrace.traceAnnotation(),
276 DiagnosticTraceBase.AppDomainFriendlyName);
286 LogTraceFailure(null, e);
291 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
293 [SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml", Justification = "It is internal code. No security concern.")]
294 public void WriteTraceSource(ref EventDescriptor eventDescriptor, string description, TracePayload payload)
296 if (this.TracingEnabled)
298 XPathNavigator navigator = null;
301 string msdnTraceCode;
303 EtwDiagnosticTrace.GenerateLegacyTraceCode(ref eventDescriptor, out msdnTraceCode, out legacyEventId);
305 string traceString = BuildTrace(ref eventDescriptor, description, payload, msdnTraceCode);
306 XmlDocument traceDocument = new XmlDocument();
307 traceDocument.LoadXml(traceString);
308 navigator = traceDocument.CreateNavigator();
309 this.TraceSource.TraceData(TraceLevelHelper.GetTraceEventType(eventDescriptor.Level, eventDescriptor.Opcode), legacyEventId, navigator);
311 if (this.CalledShutdown)
313 this.TraceSource.Flush();
316 catch (Exception exception)
318 if (Fx.IsFatal(exception))
323 LogTraceFailure(navigator == null ? string.Empty : navigator.ToString(), exception);
328 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
330 static string BuildTrace(ref EventDescriptor eventDescriptor, string description, TracePayload payload, string msdnTraceCode)
332 StringBuilder sb = StringBuilderPool.Take();
335 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
337 using (XmlTextWriter writer = new XmlTextWriter(stringWriter))
339 writer.WriteStartElement(DiagnosticStrings.TraceRecordTag);
340 writer.WriteAttributeString(DiagnosticStrings.NamespaceTag, EtwDiagnosticTrace.TraceRecordVersion);
341 writer.WriteAttributeString(DiagnosticStrings.SeverityTag,
342 TraceLevelHelper.LookupSeverity((TraceEventLevel)eventDescriptor.Level, (TraceEventOpcode)eventDescriptor.Opcode));
343 writer.WriteAttributeString(DiagnosticStrings.ChannelTag, EtwDiagnosticTrace.LookupChannel((TraceChannel)eventDescriptor.Channel));
345 writer.WriteElementString(DiagnosticStrings.TraceCodeTag, msdnTraceCode);
346 writer.WriteElementString(DiagnosticStrings.DescriptionTag, description);
347 writer.WriteElementString(DiagnosticStrings.AppDomain, payload.AppDomainFriendlyName);
349 if (!string.IsNullOrEmpty(payload.EventSource))
351 writer.WriteElementString(DiagnosticStrings.SourceTag, payload.EventSource);
354 if (!string.IsNullOrEmpty(payload.ExtendedData))
356 writer.WriteRaw(payload.ExtendedData);
359 if (!string.IsNullOrEmpty(payload.SerializedException))
361 writer.WriteRaw(payload.SerializedException);
364 writer.WriteEndElement();
366 stringWriter.Flush();
368 return sb.ToString();
374 StringBuilderPool.Return(sb);
378 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
380 static void GenerateLegacyTraceCode(ref EventDescriptor eventDescriptor, out string msdnTraceCode, out int legacyEventId)
382 // 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
383 // The mapping between legacy trace code and the new ETW event ids has to be done manually - for example
384 // because there was only one event for HandledException in system.diagnostics. For ETW there are multiple events
385 // because you have to specify the verbosity level per event in the manifest.
387 switch (eventDescriptor.EventId)
389 case EventIdsWithMsdnTraceCode.AppDomainUnload:
390 msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.AppDomainUnload);
391 legacyEventId = LegacyTraceEventIds.AppDomainUnload;
393 case EventIdsWithMsdnTraceCode.HandledExceptionError:
394 case EventIdsWithMsdnTraceCode.HandledExceptionWarning:
395 case EventIdsWithMsdnTraceCode.HandledExceptionInfo:
396 case EventIdsWithMsdnTraceCode.HandledExceptionVerbose:
397 msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.TraceHandledException);
398 legacyEventId = LegacyTraceEventIds.TraceHandledException;
400 case EventIdsWithMsdnTraceCode.ThrowingExceptionVerbose:
401 case EventIdsWithMsdnTraceCode.ThrowingExceptionWarning:
402 msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.ThrowingException);
403 legacyEventId = LegacyTraceEventIds.ThrowingException;
405 case EventIdsWithMsdnTraceCode.UnhandledException:
406 msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.UnhandledException);
407 legacyEventId = LegacyTraceEventIds.UnhandledException;
410 msdnTraceCode = eventDescriptor.EventId.ToString(CultureInfo.InvariantCulture);
411 legacyEventId = eventDescriptor.EventId;
416 // helper for standardized trace code generation
417 static string GenerateMsdnTraceCode(string traceSource, string traceCodeString)
419 return string.Format(CultureInfo.InvariantCulture,
420 "http://msdn.microsoft.com/{0}/library/{1}.{2}.aspx",
421 CultureInfo.CurrentCulture.Name,
422 traceSource, traceCodeString);
425 static string LookupChannel(TraceChannel traceChannel)
428 switch (traceChannel)
430 case TraceChannel.Admin:
431 channelName = "Admin";
433 case TraceChannel.Analytic:
434 channelName = "Analytic";
436 case TraceChannel.Application:
437 channelName = "Application";
439 case TraceChannel.Debug:
440 channelName = "Debug";
442 case TraceChannel.Operational:
443 channelName = "Operational";
445 case TraceChannel.Perf:
446 channelName = "Perf";
449 channelName = traceChannel.ToString();
456 public TracePayload GetSerializedPayload(object source, TraceRecord traceRecord, Exception exception)
458 return this.GetSerializedPayload(source, traceRecord, exception, false);
461 public TracePayload GetSerializedPayload(object source, TraceRecord traceRecord, Exception exception, bool getServiceReference)
463 string eventSource = null;
464 string extendedData = null;
465 string serializedException = null;
469 eventSource = CreateSourceString(source);
472 if (traceRecord != null)
474 StringBuilder sb = StringBuilderPool.Take();
477 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
479 using (XmlTextWriter writer = new XmlTextWriter(stringWriter))
481 writer.WriteStartElement(DiagnosticStrings.ExtendedDataTag);
482 traceRecord.WriteTo(writer);
483 writer.WriteEndElement();
485 stringWriter.Flush();
487 extendedData = sb.ToString();
493 StringBuilderPool.Return(sb);
497 if (exception != null)
499 // We want to keep the ETW trace message to under 32k. So we keep the serialized exception to under 28k bytes.
500 serializedException = ExceptionToTraceString(exception, MaxExceptionStringLength);
503 if (getServiceReference && (EtwDiagnosticTrace.traceAnnotation != null))
505 return new TracePayload(serializedException, eventSource, DiagnosticTraceBase.AppDomainFriendlyName, extendedData, EtwDiagnosticTrace.traceAnnotation());
508 return new TracePayload(serializedException, eventSource, DiagnosticTraceBase.AppDomainFriendlyName, extendedData, string.Empty);
511 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand",
512 Safe = "Only queries the status of the provider - does not modify the state")]
513 [SecuritySafeCritical]
514 public bool IsEtwEventEnabled(ref EventDescriptor eventDescriptor)
516 return IsEtwEventEnabled(ref eventDescriptor, true);
519 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand",
520 Safe = "Only queries the status of the provider - does not modify the state")]
521 [SecuritySafeCritical]
522 public bool IsEtwEventEnabled(ref EventDescriptor eventDescriptor, bool fullCheck)
524 // A full check queries ETW via a p/invoke call to see if the event is really enabled.
525 // Checking against the level and keywords passed in the ETW callback can provide false positives,
526 // but never a false negative.
527 // The only code which specifies false is two generated classes, System.Runtime.TraceCore and
528 // System.Activities.EtwTrackingParticipantTrackRecords, and the method EtwDiagnosticTrace.TraceTransfer().
529 // FxTrace uses IsEtwEventEnabled without the boolean, which then calls this method specifying true.
532 return (this.EtwTracingEnabled && this.etwProvider.IsEventEnabled(ref eventDescriptor));
535 return (this.EtwTracingEnabled && this.etwProvider.IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords));
538 [Fx.Tag.SecurityNote(Critical = "Access the critical Listeners property",
539 Safe = "Only Removes the default listener of the local source")]
540 [SecuritySafeCritical]
541 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
542 Justification = "SecuritySafeCriticial method")]
543 void CreateTraceSource()
545 if (!string.IsNullOrEmpty(this.TraceSourceName))
547 SetTraceSource(new DiagnosticTraceSource(this.TraceSourceName));
551 [Fx.Tag.SecurityNote(Critical = "Sets this.etwProvider and calls EtwProvider constructor, which are Security Critical")]
553 void CreateEtwProvider(Guid etwProviderId)
555 if (etwProviderId != Guid.Empty && EtwDiagnosticTrace.isVistaOrGreater)
557 //Pick EtwProvider from cache, add to cache if not found
558 this.etwProvider = (EtwProvider)etwProviderCache[etwProviderId];
559 if (this.etwProvider == null)
561 lock (etwProviderCache)
563 this.etwProvider = (EtwProvider)etwProviderCache[etwProviderId];
564 if (this.etwProvider == null)
566 this.etwProvider = new EtwProvider(etwProviderId);
567 etwProviderCache.Add(etwProviderId, this.etwProvider);
572 this.etwProviderId = etwProviderId;
576 [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
578 static EventDescriptor GetEventDescriptor(int eventId, TraceChannel channel, TraceEventLevel traceEventLevel)
582 //map channel to keywords
583 long keyword = (long)0x0;
584 if (channel == TraceChannel.Admin)
586 keyword = keyword | (long)0x8000000000000000;
588 else if (channel == TraceChannel.Operational)
590 keyword = keyword | 0x4000000000000000;
592 else if (channel == TraceChannel.Analytic)
594 keyword = keyword | 0x2000000000000000;
596 else if (channel == TraceChannel.Debug)
598 keyword = keyword | 0x100000000000000;
600 else if (channel == TraceChannel.Perf)
602 keyword = keyword | 0x0800000000000000;
604 return new EventDescriptor(eventId, 0x0, (byte)channel, (byte)traceEventLevel, 0x0, 0x0, (long)keyword);
608 protected override void OnShutdownTracing()
610 ShutdownTraceSource();
611 ShutdownEtwProvider();
614 void ShutdownTraceSource()
618 if (TraceCore.AppDomainUnloadIsEnabled(this))
620 TraceCore.AppDomainUnload(this, AppDomain.CurrentDomain.FriendlyName,
621 DiagnosticTraceBase.ProcessName, DiagnosticTraceBase.ProcessId.ToString(CultureInfo.CurrentCulture));
623 this.TraceSource.Flush();
625 catch (Exception exception)
627 if (Fx.IsFatal(exception))
633 LogTraceFailure(null, exception);
637 [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
638 Safe = "Doesn't leak info\\resources")]
639 [SecuritySafeCritical]
640 void ShutdownEtwProvider()
644 if (this.etwProvider != null)
646 this.etwProvider.Dispose();
647 //no need to set this.etwProvider as null as Dispose() provides the necessary guard
648 //leaving it non-null protects trace calls from NullReferenceEx, CSDMain Bug 136228
651 catch (Exception exception)
653 if (Fx.IsFatal(exception))
659 LogTraceFailure(null, exception);
663 public override bool IsEnabled()
665 return TraceCore.TraceCodeEventLogCriticalIsEnabled(this)
666 || TraceCore.TraceCodeEventLogVerboseIsEnabled(this)
667 || TraceCore.TraceCodeEventLogInfoIsEnabled(this)
668 || TraceCore.TraceCodeEventLogWarningIsEnabled(this)
669 || TraceCore.TraceCodeEventLogErrorIsEnabled(this);
672 public override void TraceEventLogEvent(TraceEventType type, TraceRecord traceRecord)
676 case TraceEventType.Critical:
677 if (TraceCore.TraceCodeEventLogCriticalIsEnabled(this))
679 TraceCore.TraceCodeEventLogCritical(this, traceRecord);
683 case TraceEventType.Verbose:
684 if (TraceCore.TraceCodeEventLogVerboseIsEnabled(this))
686 TraceCore.TraceCodeEventLogVerbose(this, traceRecord);
690 case TraceEventType.Information:
691 if (TraceCore.TraceCodeEventLogInfoIsEnabled(this))
693 TraceCore.TraceCodeEventLogInfo(this, traceRecord);
697 case TraceEventType.Warning:
698 if (TraceCore.TraceCodeEventLogWarningIsEnabled(this))
700 TraceCore.TraceCodeEventLogWarning(this, traceRecord);
704 case TraceEventType.Error:
705 if (TraceCore.TraceCodeEventLogErrorIsEnabled(this))
707 TraceCore.TraceCodeEventLogError(this, traceRecord);
713 protected override void OnUnhandledException(Exception exception)
715 if (TraceCore.UnhandledExceptionIsEnabled(this))
717 TraceCore.UnhandledException(this, exception != null ? exception.ToString() : string.Empty, exception);
721 internal static string ExceptionToTraceString(Exception exception, int maxTraceStringLength)
723 StringBuilder sb = StringBuilderPool.Take();
726 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
728 using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
730 WriteExceptionToTraceString(xml, exception, maxTraceStringLength, MaxExceptionDepth);
732 stringWriter.Flush();
734 return sb.ToString();
740 StringBuilderPool.Return(sb);
744 static void WriteExceptionToTraceString(XmlTextWriter xml, Exception exception, int remainingLength, int remainingAllowedRecursionDepth)
746 if (remainingAllowedRecursionDepth < 1)
751 if (!WriteStartElement(xml, DiagnosticStrings.ExceptionTag, ref remainingLength))
758 IList<Tuple<string, string>> exceptionInfo = new List<Tuple<string, string>>()
760 new Tuple<string, string> (DiagnosticStrings.ExceptionTypeTag, XmlEncode(exception.GetType().AssemblyQualifiedName)),
761 new Tuple<string, string> (DiagnosticStrings.MessageTag, XmlEncode(exception.Message)),
762 new Tuple<string, string> (DiagnosticStrings.StackTraceTag, XmlEncode(StackTraceString(exception))),
763 new Tuple<string, string> (DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString())),
766 System.ComponentModel.Win32Exception win32Exception = exception as System.ComponentModel.Win32Exception;
767 if (win32Exception != null)
770 new Tuple<string, string>(
771 DiagnosticStrings.NativeErrorCodeTag,
772 win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture)));
775 foreach (Tuple<string, string> item in exceptionInfo)
777 if (!WriteXmlElementString(xml, item.Item1, item.Item2, ref remainingLength))
783 if (exception.Data != null && exception.Data.Count > 0)
785 string exceptionData = GetExceptionData(exception);
786 if (exceptionData.Length < remainingLength)
788 xml.WriteRaw(exceptionData);
789 remainingLength -= exceptionData.Length;
793 if (exception.InnerException != null)
795 string innerException = GetInnerException(exception, remainingLength, remainingAllowedRecursionDepth - 1);
796 if (!string.IsNullOrEmpty(innerException) && innerException.Length < remainingLength)
798 xml.WriteRaw(innerException);
804 xml.WriteEndElement();
808 static string GetInnerException(Exception exception, int remainingLength, int remainingAllowedRecursionDepth)
810 if (remainingAllowedRecursionDepth < 1)
815 StringBuilder sb = StringBuilderPool.Take();
818 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
820 using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
822 if (!WriteStartElement(xml, DiagnosticStrings.InnerExceptionTag, ref remainingLength))
827 WriteExceptionToTraceString(xml, exception.InnerException, remainingLength, remainingAllowedRecursionDepth);
828 xml.WriteEndElement();
830 stringWriter.Flush();
832 return sb.ToString();
838 StringBuilderPool.Return(sb);
842 static string GetExceptionData(Exception exception)
844 StringBuilder sb = StringBuilderPool.Take();
847 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
849 using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
852 xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
853 foreach (object dataItem in exception.Data.Keys)
855 xml.WriteStartElement(DiagnosticStrings.DataTag);
856 xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
857 if (exception.Data[dataItem] == null)
859 xml.WriteElementString(DiagnosticStrings.ValueTag, string.Empty);
863 xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
866 xml.WriteEndElement();
868 xml.WriteEndElement();
870 stringWriter.Flush();
872 return sb.ToString();
878 StringBuilderPool.Return(sb);
882 static bool WriteStartElement(XmlTextWriter xml, string localName, ref int remainingLength)
884 int minXmlLength = (localName.Length * 2) + EtwDiagnosticTrace.XmlBracketsLength;
885 if (minXmlLength <= remainingLength)
887 xml.WriteStartElement(localName);
888 remainingLength -= minXmlLength;
894 static bool WriteXmlElementString(XmlTextWriter xml, string localName, string value, ref int remainingLength)
896 int xmlElementLength;
898 // 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)
899 if (string.IsNullOrEmpty(value) && !LocalAppContextSwitches.IncludeNullExceptionMessageInETWTrace)
901 xmlElementLength = localName.Length + EtwDiagnosticTrace.XmlBracketsLengthForNullValue;
906 xmlElementLength = (localName.Length * 2) + EtwDiagnosticTrace.XmlBracketsLength + value.Length;
909 if (xmlElementLength <= remainingLength)
911 xml.WriteElementString(localName, value);
912 remainingLength -= xmlElementLength;
918 static class TraceCodes
920 public const string AppDomainUnload = "AppDomainUnload";
921 public const string TraceHandledException = "TraceHandledException";
922 public const string ThrowingException = "ThrowingException";
923 public const string UnhandledException = "UnhandledException";
926 static class EventIdsWithMsdnTraceCode
928 // EventIds for which we need to translate the traceCode and the eventId
929 // when system.diagnostics tracing is enabled.
930 public const int AppDomainUnload = 57393;
931 public const int ThrowingExceptionWarning = 57396;
932 public const int ThrowingExceptionVerbose = 57407;
933 public const int HandledExceptionInfo = 57394;
934 public const int HandledExceptionWarning = 57404;
935 public const int HandledExceptionError = 57405;
936 public const int HandledExceptionVerbose = 57406;
937 public const int UnhandledException = 57397;
940 static class LegacyTraceEventIds
942 // Diagnostic trace codes
943 public const int Diagnostics = 0X20000;
944 public const int AppDomainUnload = LegacyTraceEventIds.Diagnostics | 0X0001;
945 public const int EventLog = LegacyTraceEventIds.Diagnostics | 0X0002;
946 public const int ThrowingException = LegacyTraceEventIds.Diagnostics | 0X0003;
947 public const int TraceHandledException = LegacyTraceEventIds.Diagnostics | 0X0004;
948 public const int UnhandledException = LegacyTraceEventIds.Diagnostics | 0X0005;
951 static class StringBuilderPool
953 const int maxPooledStringBuilders = 64;
954 static readonly ConcurrentQueue<StringBuilder> freeStringBuilders = new ConcurrentQueue<StringBuilder>();
956 public static StringBuilder Take()
958 StringBuilder sb = null;
959 if (freeStringBuilders.TryDequeue(out sb))
964 return new StringBuilder();
967 public static void Return(StringBuilder sb)
969 Fx.Assert(sb != null, "'sb' MUST NOT be NULL.");
970 if (freeStringBuilders.Count <= maxPooledStringBuilders)
972 // There is a race condition here so the count could be off a little bit (but insignificantly)
974 freeStringBuilders.Enqueue(sb);