Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.ServiceModel.Internals / System / Runtime / Diagnostics / EtwDiagnosticTrace.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Runtime.Diagnostics
6 {
7     using System;
8     using System.Collections;
9     using System.Diagnostics;
10     using System.Globalization;
11     using System.IO;
12     using System.Security;
13     using System.Text;
14     using System.Xml;
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;
21
22     sealed class EtwDiagnosticTrace : DiagnosticTraceBase
23     {
24         //Diagnostics trace
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";
31
32         const int XmlBracketsLength = 5; // "<></>".Length;
33         const int XmlBracketsLengthForNullValue = 4; // "< />".Length; (Empty XML Element)
34
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")]
37         [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;
42
43         [Fx.Tag.SecurityNote(Critical = "Stores object created by a critical c'tor")]
44         [SecurityCritical]
45         EtwProvider etwProvider;
46         Guid etwProviderId;
47         [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
48         [SecurityCritical]
49         static EventDescriptor transferEventDescriptor = new EventDescriptor(499, 0, (byte)TraceChannel.Analytic, (byte)TraceEventLevel.LogAlways, (byte)TraceEventOpcode.Info, 0x0, 0x20000000001A0065);
50
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")]
53         [SecurityCritical]
54         [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.InitializeReferenceTypeStaticFieldsInline,
55                         Justification = "SecurityCriticial method")]
56         static EtwDiagnosticTrace()
57         {
58             // In Partial Trust, initialize to Guid.Empty to disable ETW Tracing.
59             if (!PartialTrustHelpers.HasEtwPermissions())
60             {
61                 defaultEtwProviderId = Guid.Empty;
62             }
63         }
64
65         [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider, eventSourceName field")]
66         [SecurityCritical]
67         public EtwDiagnosticTrace(string traceSourceName, Guid etwProviderId)
68             : base(traceSourceName)
69         {
70             try
71             {
72                 this.TraceSourceName = traceSourceName;
73                 this.EventSourceName = string.Concat(this.TraceSourceName, " ", EventSourceVersion);
74                 CreateTraceSource();
75             }
76             catch (Exception exception)
77             {
78                 if (Fx.IsFatal(exception))
79                 {
80                     throw;
81                 }
82
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
88             }
89
90             try
91             {
92                 CreateEtwProvider(etwProviderId);
93             }
94             catch (Exception exception)
95             {
96                 if (Fx.IsFatal(exception))
97                 {
98                     throw;
99                 }
100
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
107
108             }
109
110             if (this.TracingEnabled || this.EtwTracingEnabled)
111             {
112 #pragma warning disable 618
113                 this.AddDomainEventHandlersForCleanup();
114 #pragma warning restore 618
115             }
116         }
117
118         static public Guid DefaultEtwProviderId
119         {
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")]
124             get
125             {
126                 return EtwDiagnosticTrace.defaultEtwProviderId;
127             }
128             [Fx.Tag.SecurityNote(Critical = "setting critical field defaultEtwProviderId")]
129             [SecurityCritical]
130             [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
131                 Justification = "SecurityCriticial method")]
132             set
133             {
134                 EtwDiagnosticTrace.defaultEtwProviderId = value;
135             }
136         }
137
138         public EtwProvider EtwProvider
139         {
140             [Fx.Tag.SecurityNote(Critical = "Exposes the critical etwProvider field")]
141             [SecurityCritical]
142             get
143             {
144                 return this.etwProvider;
145             }
146         }
147
148         public bool IsEtwProviderEnabled
149         {
150             [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
151                 Safe = "Doesn't leak info\\resources")]
152             [SecuritySafeCritical]
153             get
154             {
155                 return (this.EtwTracingEnabled && this.etwProvider.IsEnabled());
156             }
157         }
158
159         public Action RefreshState
160         {
161             [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
162             Safe = "Doesn't leak resources or information")]
163             [SecuritySafeCritical]
164             get
165             {
166                 return this.EtwProvider.ControllerCallBack;
167             }
168
169             [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
170             Safe = "Doesn't leak resources or information")]
171             [SecuritySafeCritical]
172             set
173             {
174                 this.EtwProvider.ControllerCallBack = value;
175             }
176         }
177
178         public bool IsEnd2EndActivityTracingEnabled
179         {
180             [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
181             Safe = "Doesn't leak resources or information")]
182             [SecuritySafeCritical]
183             get
184             {
185                 return this.IsEtwProviderEnabled && this.EtwProvider.IsEnd2EndActivityTracingEnabled;
186             }
187         }
188
189         bool EtwTracingEnabled
190         {
191             [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
192                 Safe = "Doesn't leak info\\resources")]
193             [SecuritySafeCritical]
194             get
195             {
196                 return (this.etwProvider != null);
197             }
198         }
199
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)
203         {
204             this.EtwProvider.SetEnd2EndActivityTracingEnabled(isEnd2EndTracingEnabled);
205         }
206
207         public void SetAnnotation(Func<string> annotation)
208         {
209             EtwDiagnosticTrace.traceAnnotation = annotation;
210         }
211
212         public override bool ShouldTrace(TraceEventLevel level)
213         {
214             return base.ShouldTrace(level) || ShouldTraceToEtw(level);
215         }
216
217         [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
218             Safe = "Doesn't leak information\\resources")]
219         [SecuritySafeCritical]
220         public bool ShouldTraceToEtw(TraceEventLevel level)
221         {
222             return (this.EtwProvider != null && this.EtwProvider.IsEnabled((byte)level, 0));
223         }
224
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)
229         {
230             if (this.TracingEnabled)
231             {
232                 EventDescriptor eventDescriptor = EtwDiagnosticTrace.GetEventDescriptor(eventId, channel, traceEventLevel);
233                 this.Event(ref eventDescriptor, description);
234             }
235         }
236
237         [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
238         [SecurityCritical]
239         public void Event(ref EventDescriptor eventDescriptor, string description)
240         {
241             if (this.TracingEnabled)
242             {
243                 TracePayload tracePayload = this.GetSerializedPayload(null, null, null);
244                 this.WriteTraceSource(ref eventDescriptor, description, tracePayload);
245             }
246         }
247
248         public void SetAndTraceTransfer(Guid newId, bool emitTransfer)
249         {
250             if (emitTransfer)
251             {
252                 TraceTransfer(newId);
253             }
254             EtwDiagnosticTrace.ActivityId = newId;
255         }
256
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)
261         {
262             Guid oldId = EtwDiagnosticTrace.ActivityId;
263             if (newId != oldId)
264             {
265                 try
266                 {
267                     if (this.HaveListeners)
268                     {
269                         this.TraceSource.TraceTransfer(0, null, newId);
270                     }
271                     //also emit to ETW
272                     if (this.IsEtwEventEnabled(ref EtwDiagnosticTrace.transferEventDescriptor, false))
273                     {
274                         this.etwProvider.WriteTransferEvent(ref EtwDiagnosticTrace.transferEventDescriptor, new EventTraceActivity(oldId), newId,
275                             EtwDiagnosticTrace.traceAnnotation == null ? string.Empty : EtwDiagnosticTrace.traceAnnotation(),
276                             DiagnosticTraceBase.AppDomainFriendlyName);
277                     }
278                 }
279                 catch (Exception e)
280                 {
281                     if (Fx.IsFatal(e))
282                     {
283                         throw;
284                     }
285
286                     LogTraceFailure(null, e);
287                 }
288             }
289         }
290
291         [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
292         [SecurityCritical]
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)
295         {
296             if (this.TracingEnabled)
297             {
298                 XPathNavigator navigator = null;
299                 try
300                 {
301                     string msdnTraceCode;
302                     int legacyEventId;
303                     EtwDiagnosticTrace.GenerateLegacyTraceCode(ref eventDescriptor, out msdnTraceCode, out legacyEventId);
304
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);
310
311                     if (this.CalledShutdown)
312                     {
313                         this.TraceSource.Flush();
314                     }
315                 }
316                 catch (Exception exception)
317                 {
318                     if (Fx.IsFatal(exception))
319                     {
320                         throw;
321                     }
322
323                     LogTraceFailure(navigator == null ? string.Empty : navigator.ToString(), exception);
324                 }
325             }
326         }
327
328         [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
329         [SecurityCritical]
330         static string BuildTrace(ref EventDescriptor eventDescriptor, string description, TracePayload payload, string msdnTraceCode)
331         {
332             StringBuilder sb = StringBuilderPool.Take();
333             try
334             {
335                 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
336                 {
337                     using (XmlTextWriter writer = new XmlTextWriter(stringWriter))
338                     {
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));
344
345                         writer.WriteElementString(DiagnosticStrings.TraceCodeTag, msdnTraceCode);
346                         writer.WriteElementString(DiagnosticStrings.DescriptionTag, description);
347                         writer.WriteElementString(DiagnosticStrings.AppDomain, payload.AppDomainFriendlyName);
348
349                         if (!string.IsNullOrEmpty(payload.EventSource))
350                         {
351                             writer.WriteElementString(DiagnosticStrings.SourceTag, payload.EventSource);
352                         }
353
354                         if (!string.IsNullOrEmpty(payload.ExtendedData))
355                         {
356                             writer.WriteRaw(payload.ExtendedData);
357                         }
358
359                         if (!string.IsNullOrEmpty(payload.SerializedException))
360                         {
361                             writer.WriteRaw(payload.SerializedException);
362                         }
363
364                         writer.WriteEndElement();
365                         writer.Flush();
366                         stringWriter.Flush();
367
368                         return sb.ToString();
369                     }
370                 }
371             }
372             finally
373             {
374                 StringBuilderPool.Return(sb);
375             }
376         }
377
378         [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
379         [SecurityCritical]
380         static void GenerateLegacyTraceCode(ref EventDescriptor eventDescriptor, out string msdnTraceCode, out int legacyEventId)
381         {
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.
386
387             switch (eventDescriptor.EventId)
388             {
389                 case EventIdsWithMsdnTraceCode.AppDomainUnload:
390                     msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.AppDomainUnload);
391                     legacyEventId = LegacyTraceEventIds.AppDomainUnload;
392                     break;
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;
399                     break;    
400                 case EventIdsWithMsdnTraceCode.ThrowingExceptionVerbose:
401                 case EventIdsWithMsdnTraceCode.ThrowingExceptionWarning:
402                     msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.ThrowingException);
403                     legacyEventId = LegacyTraceEventIds.ThrowingException;
404                     break; 
405                 case EventIdsWithMsdnTraceCode.UnhandledException:
406                     msdnTraceCode = GenerateMsdnTraceCode(DiagnosticTraceSource, TraceCodes.UnhandledException);
407                     legacyEventId = LegacyTraceEventIds.UnhandledException;
408                     break; 
409                 default:
410                     msdnTraceCode = eventDescriptor.EventId.ToString(CultureInfo.InvariantCulture);
411                     legacyEventId = eventDescriptor.EventId;
412                     break;
413             }
414         }
415
416         // helper for standardized trace code generation
417         static string GenerateMsdnTraceCode(string traceSource, string traceCodeString)
418         {
419             return string.Format(CultureInfo.InvariantCulture,
420                 "http://msdn.microsoft.com/{0}/library/{1}.{2}.aspx",
421                 CultureInfo.CurrentCulture.Name,
422                 traceSource, traceCodeString);
423         }
424
425         static string LookupChannel(TraceChannel traceChannel)
426         {
427             string channelName;
428             switch (traceChannel)
429             {
430                 case TraceChannel.Admin:
431                     channelName = "Admin";
432                     break;
433                 case TraceChannel.Analytic:
434                     channelName = "Analytic";
435                     break;
436                 case TraceChannel.Application:
437                     channelName = "Application";
438                     break;
439                 case TraceChannel.Debug:
440                     channelName = "Debug";
441                     break;
442                 case TraceChannel.Operational:
443                     channelName = "Operational";
444                     break;
445                 case TraceChannel.Perf:
446                     channelName = "Perf";
447                     break;
448                 default:
449                     channelName = traceChannel.ToString();
450                     break;
451             }
452
453             return channelName;
454         }
455
456         public TracePayload GetSerializedPayload(object source, TraceRecord traceRecord, Exception exception)
457         {
458             return this.GetSerializedPayload(source, traceRecord, exception, false);
459         }
460
461         public TracePayload GetSerializedPayload(object source, TraceRecord traceRecord, Exception exception, bool getServiceReference)
462         {
463             string eventSource = null;
464             string extendedData = null;
465             string serializedException = null;
466
467             if (source != null)
468             {
469                 eventSource = CreateSourceString(source);
470             }
471
472             if (traceRecord != null)
473             {
474                 StringBuilder sb = StringBuilderPool.Take();
475                 try
476                 {
477                     using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
478                     {
479                         using (XmlTextWriter writer = new XmlTextWriter(stringWriter))
480                         {
481                             writer.WriteStartElement(DiagnosticStrings.ExtendedDataTag);
482                             traceRecord.WriteTo(writer);
483                             writer.WriteEndElement();
484                             writer.Flush();
485                             stringWriter.Flush();
486
487                             extendedData = sb.ToString();
488                         }
489                     }
490                 }
491                 finally
492                 {
493                     StringBuilderPool.Return(sb);
494                 }
495             }
496
497             if (exception != null)
498             {
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);
501             }
502
503             if (getServiceReference && (EtwDiagnosticTrace.traceAnnotation != null))
504             {
505                 return new TracePayload(serializedException, eventSource, DiagnosticTraceBase.AppDomainFriendlyName, extendedData, EtwDiagnosticTrace.traceAnnotation());
506             }
507
508             return new TracePayload(serializedException, eventSource, DiagnosticTraceBase.AppDomainFriendlyName, extendedData, string.Empty);
509         }
510
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)
515         {
516             return IsEtwEventEnabled(ref eventDescriptor, true);
517         }
518
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)
523         {
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.
530             if (fullCheck)
531             {
532                 return (this.EtwTracingEnabled && this.etwProvider.IsEventEnabled(ref eventDescriptor));
533             }
534
535             return (this.EtwTracingEnabled && this.etwProvider.IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords));
536         }
537
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()
544         {
545             if (!string.IsNullOrEmpty(this.TraceSourceName))
546             {
547                 SetTraceSource(new DiagnosticTraceSource(this.TraceSourceName));
548             }
549         }
550
551         [Fx.Tag.SecurityNote(Critical = "Sets this.etwProvider and calls EtwProvider constructor, which are Security Critical")]
552         [SecurityCritical]
553         void CreateEtwProvider(Guid etwProviderId)
554         {
555             if (etwProviderId != Guid.Empty && EtwDiagnosticTrace.isVistaOrGreater)
556             {
557                 //Pick EtwProvider from cache, add to cache if not found
558                 this.etwProvider = (EtwProvider)etwProviderCache[etwProviderId];
559                 if (this.etwProvider == null)
560                 {
561                     lock (etwProviderCache)
562                     {
563                         this.etwProvider = (EtwProvider)etwProviderCache[etwProviderId];
564                         if (this.etwProvider == null)
565                         {
566                             this.etwProvider = new EtwProvider(etwProviderId);
567                             etwProviderCache.Add(etwProviderId, this.etwProvider);
568                         }
569                     }
570                 }
571
572                 this.etwProviderId = etwProviderId;
573             }
574         }
575
576         [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")]
577         [SecurityCritical]
578         static EventDescriptor GetEventDescriptor(int eventId, TraceChannel channel, TraceEventLevel traceEventLevel)
579         {
580             unchecked
581             {
582                 //map channel to keywords
583                 long keyword = (long)0x0;
584                 if (channel == TraceChannel.Admin)
585                 {
586                     keyword = keyword | (long)0x8000000000000000;
587                 }
588                 else if (channel == TraceChannel.Operational)
589                 {
590                     keyword = keyword | 0x4000000000000000;
591                 }
592                 else if (channel == TraceChannel.Analytic)
593                 {
594                     keyword = keyword | 0x2000000000000000;
595                 }
596                 else if (channel == TraceChannel.Debug)
597                 {
598                     keyword = keyword | 0x100000000000000;
599                 }
600                 else if (channel == TraceChannel.Perf)
601                 {
602                     keyword = keyword | 0x0800000000000000;
603                 }
604                 return new EventDescriptor(eventId, 0x0, (byte)channel, (byte)traceEventLevel, 0x0, 0x0, (long)keyword);
605             }
606         }
607
608         protected override void OnShutdownTracing()
609         {
610             ShutdownTraceSource();
611             ShutdownEtwProvider();
612         }
613
614         void ShutdownTraceSource()
615         {
616             try
617             {
618                 if (TraceCore.AppDomainUnloadIsEnabled(this))
619                 {
620                     TraceCore.AppDomainUnload(this, AppDomain.CurrentDomain.FriendlyName,
621                         DiagnosticTraceBase.ProcessName, DiagnosticTraceBase.ProcessId.ToString(CultureInfo.CurrentCulture));
622                 }
623                 this.TraceSource.Flush();
624             }
625             catch (Exception exception)
626             {
627                 if (Fx.IsFatal(exception))
628                 {
629                     throw;
630                 }
631
632                 //log failure
633                 LogTraceFailure(null, exception);
634             }
635         }
636
637         [Fx.Tag.SecurityNote(Critical = "Access critical etwProvider field",
638             Safe = "Doesn't leak info\\resources")]
639         [SecuritySafeCritical]
640         void ShutdownEtwProvider()
641         {
642             try
643             {
644                 if (this.etwProvider != null)
645                 {
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
649                 }
650             }
651             catch (Exception exception)
652             {
653                 if (Fx.IsFatal(exception))
654                 {
655                     throw;
656                 }
657
658                 //log failure
659                 LogTraceFailure(null, exception);
660             }
661         }
662
663         public override bool IsEnabled()
664         {
665             return TraceCore.TraceCodeEventLogCriticalIsEnabled(this)
666                 || TraceCore.TraceCodeEventLogVerboseIsEnabled(this)
667                 || TraceCore.TraceCodeEventLogInfoIsEnabled(this)
668                 || TraceCore.TraceCodeEventLogWarningIsEnabled(this)
669                 || TraceCore.TraceCodeEventLogErrorIsEnabled(this);
670         }
671
672         public override void TraceEventLogEvent(TraceEventType type, TraceRecord traceRecord)
673         {
674             switch (type)
675             {
676                 case TraceEventType.Critical:
677                     if (TraceCore.TraceCodeEventLogCriticalIsEnabled(this))
678                     {
679                         TraceCore.TraceCodeEventLogCritical(this, traceRecord);
680                     }
681                     break;
682
683                 case TraceEventType.Verbose:
684                     if (TraceCore.TraceCodeEventLogVerboseIsEnabled(this))
685                     {
686                         TraceCore.TraceCodeEventLogVerbose(this, traceRecord);
687                     }
688                     break;
689
690                 case TraceEventType.Information:
691                     if (TraceCore.TraceCodeEventLogInfoIsEnabled(this))
692                     {
693                         TraceCore.TraceCodeEventLogInfo(this, traceRecord);
694                     }
695                     break;
696
697                 case TraceEventType.Warning:
698                     if (TraceCore.TraceCodeEventLogWarningIsEnabled(this))
699                     {
700                         TraceCore.TraceCodeEventLogWarning(this, traceRecord);
701                     }
702                     break;
703
704                 case TraceEventType.Error:
705                     if (TraceCore.TraceCodeEventLogErrorIsEnabled(this))
706                     {
707                         TraceCore.TraceCodeEventLogError(this, traceRecord);
708                     }
709                     break;
710             }
711         }
712
713         protected override void OnUnhandledException(Exception exception)
714         {
715             if (TraceCore.UnhandledExceptionIsEnabled(this))
716             {
717                 TraceCore.UnhandledException(this, exception != null ? exception.ToString() : string.Empty, exception);
718             }
719         }
720
721         internal static string ExceptionToTraceString(Exception exception, int maxTraceStringLength)
722         {
723             StringBuilder sb = StringBuilderPool.Take();
724             try
725             {
726                 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
727                 {
728                     using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
729                     {
730                         WriteExceptionToTraceString(xml, exception, maxTraceStringLength, MaxExceptionDepth);
731                         xml.Flush();
732                         stringWriter.Flush();
733
734                         return sb.ToString();
735                     }
736                 }
737             }
738             finally
739             {
740                 StringBuilderPool.Return(sb);
741             }
742         }
743
744         static void WriteExceptionToTraceString(XmlTextWriter xml, Exception exception, int remainingLength, int remainingAllowedRecursionDepth)
745         {
746             if (remainingAllowedRecursionDepth < 1)
747             {
748                 return;
749             }
750
751             if (!WriteStartElement(xml, DiagnosticStrings.ExceptionTag, ref remainingLength))
752             {
753                 return;
754             }
755
756             try
757             {
758                 IList<Tuple<string, string>> exceptionInfo = new List<Tuple<string, string>>() 
759                 {
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())),
764                 };
765
766                 System.ComponentModel.Win32Exception win32Exception = exception as System.ComponentModel.Win32Exception;
767                 if (win32Exception != null)
768                 {
769                     exceptionInfo.Add(
770                         new Tuple<string, string>(
771                             DiagnosticStrings.NativeErrorCodeTag,
772                             win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture)));
773                 }
774
775                 foreach (Tuple<string, string> item in exceptionInfo)
776                 {
777                     if (!WriteXmlElementString(xml, item.Item1, item.Item2, ref remainingLength))
778                     {
779                         return;
780                     }
781                 }
782
783                 if (exception.Data != null && exception.Data.Count > 0)
784                 {
785                     string exceptionData = GetExceptionData(exception);
786                     if (exceptionData.Length < remainingLength)
787                     {
788                         xml.WriteRaw(exceptionData);
789                         remainingLength -= exceptionData.Length;
790                     }
791                 }
792
793                 if (exception.InnerException != null)
794                 {
795                     string innerException = GetInnerException(exception, remainingLength, remainingAllowedRecursionDepth - 1);
796                     if (!string.IsNullOrEmpty(innerException) && innerException.Length < remainingLength)
797                     {
798                         xml.WriteRaw(innerException);
799                     }
800                 }
801             }
802             finally
803             {
804                 xml.WriteEndElement();
805             }
806         }
807
808         static string GetInnerException(Exception exception, int remainingLength, int remainingAllowedRecursionDepth)
809         {
810             if (remainingAllowedRecursionDepth < 1)
811             {
812                 return null;
813             }
814
815             StringBuilder sb = StringBuilderPool.Take();
816             try
817             {
818                 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
819                 {
820                     using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
821                     {
822                         if (!WriteStartElement(xml, DiagnosticStrings.InnerExceptionTag, ref remainingLength))
823                         {
824                             return null;
825                         }
826
827                         WriteExceptionToTraceString(xml, exception.InnerException, remainingLength, remainingAllowedRecursionDepth);
828                         xml.WriteEndElement();
829                         xml.Flush();
830                         stringWriter.Flush();
831
832                         return sb.ToString();
833                     }
834                 }
835             }
836             finally
837             {
838                 StringBuilderPool.Return(sb);
839             }
840         }
841
842         static string GetExceptionData(Exception exception)
843         {
844             StringBuilder sb = StringBuilderPool.Take();
845             try
846             {
847                 using (StringWriter stringWriter = new StringWriter(sb, CultureInfo.CurrentCulture))
848                 {
849                     using (XmlTextWriter xml = new XmlTextWriter(stringWriter))
850                     {
851
852                         xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
853                         foreach (object dataItem in exception.Data.Keys)
854                         {
855                             xml.WriteStartElement(DiagnosticStrings.DataTag);
856                             xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
857                             if (exception.Data[dataItem] == null)
858                             {
859                                 xml.WriteElementString(DiagnosticStrings.ValueTag, string.Empty);
860                             }
861                             else
862                             {
863                                 xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
864                             }
865                             
866                             xml.WriteEndElement();
867                         }
868                         xml.WriteEndElement();
869                         xml.Flush();
870                         stringWriter.Flush();
871
872                         return sb.ToString();
873                     }
874                 }
875             }
876             finally
877             {
878                 StringBuilderPool.Return(sb);
879             }
880         }
881
882         static bool WriteStartElement(XmlTextWriter xml, string localName, ref int remainingLength)
883         {
884             int minXmlLength = (localName.Length * 2) + EtwDiagnosticTrace.XmlBracketsLength;
885             if (minXmlLength <= remainingLength)
886             {
887                 xml.WriteStartElement(localName);
888                 remainingLength -= minXmlLength;
889                 return true;
890             }
891             return false;            
892         }
893
894         static bool WriteXmlElementString(XmlTextWriter xml, string localName, string value, ref int remainingLength)
895         {
896             int xmlElementLength;
897
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)
900             {
901                 xmlElementLength = localName.Length + EtwDiagnosticTrace.XmlBracketsLengthForNullValue;
902             }
903
904             else
905             {
906                 xmlElementLength = (localName.Length * 2) + EtwDiagnosticTrace.XmlBracketsLength + value.Length;
907             }
908                 
909             if (xmlElementLength <= remainingLength)
910             {
911                 xml.WriteElementString(localName, value);
912                 remainingLength -= xmlElementLength;
913                 return true;
914             }
915             return false;
916         }
917
918         static class TraceCodes
919         {
920             public const string AppDomainUnload = "AppDomainUnload";
921             public const string TraceHandledException = "TraceHandledException";
922             public const string ThrowingException = "ThrowingException";
923             public const string UnhandledException = "UnhandledException";
924         }
925
926         static class EventIdsWithMsdnTraceCode
927         {
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;
938         }
939
940         static class LegacyTraceEventIds
941         {
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;
949         }
950
951         static class StringBuilderPool
952         {
953             const int maxPooledStringBuilders = 64;
954             static readonly ConcurrentQueue<StringBuilder> freeStringBuilders = new ConcurrentQueue<StringBuilder>();
955
956             public static StringBuilder Take()
957             {
958                 StringBuilder sb = null;
959                 if (freeStringBuilders.TryDequeue(out sb))
960                 {
961                     return sb;
962                 }
963
964                 return new StringBuilder();
965             }
966
967             public static void Return(StringBuilder sb)
968             {
969                 Fx.Assert(sb != null, "'sb' MUST NOT be NULL.");
970                 if (freeStringBuilders.Count <= maxPooledStringBuilders)
971                 {
972                     // There is a race condition here so the count could be off a little bit (but insignificantly)
973                     sb.Clear();
974                     freeStringBuilders.Enqueue(sb);
975                 }
976             }
977         }
978     }
979 }