Re-Factor EventSource to Support Writing to EventPipe (#11435)
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Diagnostics / Tracing / EventProvider.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 using Microsoft.Win32;
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization;
9 using System.Runtime.InteropServices;
10 using System.Security;
11 #if !CORECLR && !ES_BUILD_PN
12 using System.Security.Permissions;
13 #endif // !CORECLR && !ES_BUILD_PN
14 using System.Threading;
15 using System;
16
17 #if !ES_BUILD_AGAINST_DOTNET_V35
18 using Contract = System.Diagnostics.Contracts.Contract;
19 #else
20 using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
21 #endif
22
23 #if ES_BUILD_AGAINST_DOTNET_V35
24 using Microsoft.Internal;       // for Tuple (can't define alias for open generic types so we "use" the whole namespace)
25 #endif
26
27 #if ES_BUILD_STANDALONE
28 namespace Microsoft.Diagnostics.Tracing
29 #else
30 namespace System.Diagnostics.Tracing
31 #endif
32 {
33     // New in CLR4.0
34     internal enum ControllerCommand
35     {
36         // Strictly Positive numbers are for provider-specific commands, negative number are for 'shared' commands. 256
37         // The first 256 negative numbers are reserved for the framework.  
38         Update = 0,                 // Not used by EventPrividerBase.  
39         SendManifest = -1,
40         Enable = -2,
41         Disable = -3,
42     };
43
44     /// <summary>
45     /// Only here because System.Diagnostics.EventProvider needs one more extensibility hook (when it gets a 
46     /// controller callback)
47     /// </summary>
48 #if !CORECLR && !ES_BUILD_PN
49     [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
50 #endif // !CORECLR && !ES_BUILD_PN
51     internal partial class EventProvider : IDisposable
52     {
53         // This is the windows EVENT_DATA_DESCRIPTOR structure.  We expose it because this is what
54         // subclasses of EventProvider use when creating efficient (but unsafe) version of
55         // EventWrite.   We do make it a nested type because we really don't expect anyone to use 
56         // it except subclasses (and then only rarely).  
57         public struct EventData
58         {
59             internal unsafe ulong Ptr;
60             internal uint Size;
61             internal uint Reserved;
62         }
63
64         /// <summary>
65         /// A struct characterizing ETW sessions (identified by the etwSessionId) as
66         /// activity-tracing-aware or legacy. A session that's activity-tracing-aware
67         /// has specified one non-zero bit in the reserved range 44-47 in the 
68         /// 'allKeywords' value it passed in for a specific EventProvider.
69         /// </summary>
70         public struct SessionInfo
71         {
72             internal int sessionIdBit;      // the index of the bit used for tracing in the "reserved" field of AllKeywords
73             internal int etwSessionId;      // the machine-wide ETW session ID
74
75             internal SessionInfo(int sessionIdBit_, int etwSessionId_)
76             { sessionIdBit = sessionIdBit_; etwSessionId = etwSessionId_; }
77         }
78
79         private static bool m_setInformationMissing;
80
81         private IEventProvider m_eventProvider;          // The interface that implements the specific logging mechanism functions.
82         UnsafeNativeMethods.ManifestEtw.EtwEnableCallback m_etwCallback;     // Trace Callback function
83         private long m_regHandle;                        // Trace Registration Handle
84         private byte m_level;                            // Tracing Level
85         private long m_anyKeywordMask;                   // Trace Enable Flags
86         private long m_allKeywordMask;                   // Match all keyword
87         private List<SessionInfo> m_liveSessions;        // current live sessions (Tuple<sessionIdBit, etwSessionId>)
88         private bool m_enabled;                          // Enabled flag from Trace callback
89         private Guid m_providerId;                       // Control Guid 
90         internal bool m_disposed;                        // when true provider has unregistered
91
92         [ThreadStatic]
93         private static WriteEventErrorCode s_returnCode; // The last return code 
94
95         private const int s_basicTypeAllocationBufferSize = 16;
96         private const int s_etwMaxNumberArguments = 128;
97         private const int s_etwAPIMaxRefObjCount = 8;
98         private const int s_maxEventDataDescriptors = 128;
99         private const int s_traceEventMaximumSize = 65482;
100         private const int s_traceEventMaximumStringSize = 32724;
101
102         [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
103         public enum WriteEventErrorCode : int
104         {
105             //check mapping to runtime codes
106             NoError = 0,
107             NoFreeBuffers = 1,
108             EventTooBig = 2,
109             NullInput = 3,
110             TooManyArgs = 4,
111             Other = 5,
112         };
113
114         // Because callbacks happen on registration, and we need the callbacks for those setup
115         // we can't call Register in the constructor.   
116         //
117         // Note that EventProvider should ONLY be used by EventSource.  In particular because
118         // it registers a callback from native code you MUST dispose it BEFORE shutdown, otherwise
119         // you may get native callbacks during shutdown when we have destroyed the delegate.  
120         // EventSource has special logic to do this, no one else should be calling EventProvider.  
121         internal EventProvider()
122         {
123 #if PLATFORM_WINDOWS
124             m_eventProvider = new EtwEventProvider();
125 #elif FEATURE_PERFTRACING
126             m_eventProvider = new EventPipeEventProvider();
127 #endif
128         }
129
130         /// <summary>
131         /// This method registers the controlGuid of this class with ETW. We need to be running on
132         /// Vista or above. If not a PlatformNotSupported exception will be thrown. If for some 
133         /// reason the ETW Register call failed a NotSupported exception will be thrown. 
134         /// </summary>
135         // <SecurityKernel Critical="True" Ring="0">
136         // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventRegister(System.Guid&,Microsoft.Win32.UnsafeNativeMethods.ManifestEtw+EtwEnableCallback,System.Void*,System.Int64&):System.UInt32" />
137         // <SatisfiesLinkDemand Name="Win32Exception..ctor(System.Int32)" />
138         // <ReferencesCritical Name="Method: EtwEnableCallBack(Guid&, Int32, Byte, Int64, Int64, Void*, Void*):Void" Ring="1" />
139         // </SecurityKernel>
140         internal unsafe void Register(Guid providerGuid)
141         {
142             m_providerId = providerGuid;
143             uint status;
144             m_etwCallback = new UnsafeNativeMethods.ManifestEtw.EtwEnableCallback(EtwEnableCallBack);
145
146             status = EventRegister(ref m_providerId, m_etwCallback);
147             if (status != 0)
148             {
149                 throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status)));
150             }
151         }
152
153         //
154         // implement Dispose Pattern to early deregister from ETW insted of waiting for 
155         // the finalizer to call deregistration.
156         // Once the user is done with the provider it needs to call Close() or Dispose()
157         // If neither are called the finalizer will unregister the provider anyway
158         //
159         public void Dispose()
160         {
161             Dispose(true);
162             GC.SuppressFinalize(this);
163         }
164
165         // <SecurityKernel Critical="True" TreatAsSafe="Does not expose critical resource" Ring="1">
166         // <ReferencesCritical Name="Method: Deregister():Void" Ring="1" />
167         // </SecurityKernel>
168         protected virtual void Dispose(bool disposing)
169         {
170             //
171             // explicit cleanup is done by calling Dispose with true from 
172             // Dispose() or Close(). The disposing arguement is ignored because there
173             // are no unmanaged resources.
174             // The finalizer calls Dispose with false.
175             //
176
177             //
178             // check if the object has been allready disposed
179             //
180             if (m_disposed) return;
181
182             // Disable the provider.  
183             m_enabled = false;
184
185             // Do most of the work under a lock to avoid shutdown race.
186
187             long registrationHandle = 0;  
188             lock (EventListener.EventListenersLock)
189             {
190                 // Double check
191                 if (m_disposed)
192                     return;
193
194                 registrationHandle = m_regHandle;
195                 m_regHandle = 0;
196                 m_disposed = true;
197             }
198
199             // We do the Unregistration outside the EventListenerLock because there is a lock
200             // inside the ETW routines.   This lock is taken before ETW issues commands
201             // Thus the ETW lock gets taken first and then our EventListenersLock gets taken
202             // in SendCommand(), and also here.  If we called EventUnregister after taking
203             // the EventListenersLock then the take-lock order is reversed and we can have
204             // deadlocks in race conditions (dispose racing with an ETW command).   
205             // 
206             // We solve by Unregistering after releasing the EventListenerLock.     
207             if (registrationHandle != 0)
208                 EventUnregister(registrationHandle);
209
210         }
211
212         /// <summary>
213         /// This method deregisters the controlGuid of this class with ETW.
214         /// 
215         /// </summary>
216         public virtual void Close()
217         {
218             Dispose();
219         }
220
221         ~EventProvider()
222         {
223             Dispose(false);
224         }
225
226         // <SecurityKernel Critical="True" Ring="0">
227         // <UsesUnsafeCode Name="Parameter filterData of type: Void*" />
228         // <UsesUnsafeCode Name="Parameter callbackContext of type: Void*" />
229         // </SecurityKernel>
230         unsafe void EtwEnableCallBack(
231                         [In] ref System.Guid sourceId,
232                         [In] int controlCode,
233                         [In] byte setLevel,
234                         [In] long anyKeyword,
235                         [In] long allKeyword,
236                         [In] UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData,
237                         [In] void* callbackContext
238                         )
239         {
240             // This is an optional callback API. We will therefore ignore any failures that happen as a 
241             // result of turning on this provider as to not crash the app.
242             // EventSource has code to validate whether initialization it expected to occur actually occurred
243             try
244             {
245                 ControllerCommand command = ControllerCommand.Update;
246                 IDictionary<string, string> args = null;
247                 bool skipFinalOnControllerCommand = false;
248                 if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_ENABLE_PROVIDER)
249                 {
250                     m_enabled = true;
251                     m_level = setLevel;
252                     m_anyKeywordMask = anyKeyword;
253                     m_allKeywordMask = allKeyword;
254
255                     // ES_SESSION_INFO is a marker for additional places we #ifdeffed out to remove
256                     // references to EnumerateTraceGuidsEx.  This symbol is actually not used because
257                     // today we use FEATURE_ACTIVITYSAMPLING to determine if this code is there or not.
258                     // However we put it in the #if so that we don't lose the fact that this feature
259                     // switch is at least partially independent of FEATURE_ACTIVITYSAMPLING
260
261                     List<Tuple<SessionInfo, bool>> sessionsChanged = GetSessions();
262                     foreach (var session in sessionsChanged)
263                     {
264                         int sessionChanged = session.Item1.sessionIdBit;
265                         int etwSessionId = session.Item1.etwSessionId;
266                         bool bEnabling = session.Item2;
267
268                         skipFinalOnControllerCommand = true;
269                         args = null;                                // reinitialize args for every session...
270
271                         // if we get more than one session changed we have no way
272                         // of knowing which one "filterData" belongs to
273                         if (sessionsChanged.Count > 1)
274                             filterData = null;
275
276                         // read filter data only when a session is being *added*
277                         byte[] data;
278                         int keyIndex;
279                         if (bEnabling &&
280                             GetDataFromController(etwSessionId, filterData, out command, out data, out keyIndex))
281                         {
282                             args = new Dictionary<string, string>(4);
283                             while (keyIndex < data.Length)
284                             {
285                                 int keyEnd = FindNull(data, keyIndex);
286                                 int valueIdx = keyEnd + 1;
287                                 int valueEnd = FindNull(data, valueIdx);
288                                 if (valueEnd < data.Length)
289                                 {
290                                     string key = System.Text.Encoding.UTF8.GetString(data, keyIndex, keyEnd - keyIndex);
291                                     string value = System.Text.Encoding.UTF8.GetString(data, valueIdx, valueEnd - valueIdx);
292                                     args[key] = value;
293                                 }
294                                 keyIndex = valueEnd + 1;
295                             }
296                         }
297
298                         // execute OnControllerCommand once for every session that has changed.
299                         OnControllerCommand(command, args, (bEnabling ? sessionChanged : -sessionChanged), etwSessionId);
300                     }
301                 }
302                 else if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_DISABLE_PROVIDER)
303                 {
304                     m_enabled = false;
305                     m_level = 0;
306                     m_anyKeywordMask = 0;
307                     m_allKeywordMask = 0;
308                     m_liveSessions = null;
309                 }
310                 else if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_CAPTURE_STATE)
311                 {
312                     command = ControllerCommand.SendManifest;
313                 }
314                 else
315                     return;     // per spec you ignore commands you don't recognize.  
316
317                 if (!skipFinalOnControllerCommand)
318                     OnControllerCommand(command, args, 0, 0);
319             }
320             catch (Exception)
321             {
322                 // We want to ignore any failures that happen as a result of turning on this provider as to
323                 // not crash the app.
324             }
325         }
326
327         // New in CLR4.0
328         protected virtual void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments, int sessionId, int etwSessionId) { }
329         protected EventLevel Level { get { return (EventLevel)m_level; } set { m_level = (byte)value; } }
330         protected EventKeywords MatchAnyKeyword { get { return (EventKeywords)m_anyKeywordMask; } set { m_anyKeywordMask = unchecked((long)value); } }
331         protected EventKeywords MatchAllKeyword { get { return (EventKeywords)m_allKeywordMask; } set { m_allKeywordMask = unchecked((long)value); } }
332
333         static private int FindNull(byte[] buffer, int idx)
334         {
335             while (idx < buffer.Length && buffer[idx] != 0)
336                 idx++;
337             return idx;
338         }
339
340         /// <summary>
341         /// Determines the ETW sessions that have been added and/or removed to the set of
342         /// sessions interested in the current provider. It does so by (1) enumerating over all
343         /// ETW sessions that enabled 'this.m_Guid' for the current process ID, and (2)
344         /// comparing the current list with a list it cached on the previous invocation.
345         ///
346         /// The return value is a list of tuples, where the SessionInfo specifies the
347         /// ETW session that was added or remove, and the bool specifies whether the
348         /// session was added or whether it was removed from the set.
349         /// </summary>
350         private List<Tuple<SessionInfo, bool>> GetSessions()
351         {
352             List<SessionInfo> liveSessionList = null;
353
354             GetSessionInfo(
355                 (int etwSessionId, long matchAllKeywords, ref List<SessionInfo> sessionList) =>
356                     GetSessionInfoCallback(etwSessionId, matchAllKeywords, ref sessionList),
357                 ref liveSessionList);
358
359             List<Tuple<SessionInfo, bool>> changedSessionList = new List<Tuple<SessionInfo, bool>>();
360
361             // first look for sessions that have gone away (or have changed)
362             // (present in the m_liveSessions but not in the new liveSessionList)
363             if (m_liveSessions != null)
364             {
365                 foreach (SessionInfo s in m_liveSessions)
366                 {
367                     int idx;
368                     if ((idx = IndexOfSessionInList(liveSessionList, s.etwSessionId)) < 0 ||
369                         (liveSessionList[idx].sessionIdBit != s.sessionIdBit))
370                         changedSessionList.Add(Tuple.Create(s, false));
371
372                 }
373             }
374             // next look for sessions that were created since the last callback  (or have changed)
375             // (present in the new liveSessionList but not in m_liveSessions)
376             if (liveSessionList != null)
377             {
378                 foreach (SessionInfo s in liveSessionList)
379                 {
380                     int idx;
381                     if ((idx = IndexOfSessionInList(m_liveSessions, s.etwSessionId)) < 0 ||
382                         (m_liveSessions[idx].sessionIdBit != s.sessionIdBit))
383                         changedSessionList.Add(Tuple.Create(s, true));
384                 }
385             }
386
387             m_liveSessions = liveSessionList;
388             return changedSessionList;
389         }
390
391
392         /// <summary>
393         /// This method is the callback used by GetSessions() when it calls into GetSessionInfo(). 
394         /// It updates a List{SessionInfo} based on the etwSessionId and matchAllKeywords that 
395         /// GetSessionInfo() passes in.
396         /// </summary>
397         private static void GetSessionInfoCallback(int etwSessionId, long matchAllKeywords,
398                                 ref List<SessionInfo> sessionList)
399         {
400             uint sessionIdBitMask = (uint)SessionMask.FromEventKeywords(unchecked((ulong)matchAllKeywords));
401             // an ETW controller that specifies more than the mandated bit for our EventSource
402             // will be ignored...
403             if (bitcount(sessionIdBitMask) > 1)
404                 return;
405
406             if (sessionList == null)
407                 sessionList = new List<SessionInfo>(8);
408
409             if (bitcount(sessionIdBitMask) == 1)
410             {
411                 // activity-tracing-aware etw session
412                 sessionList.Add(new SessionInfo(bitindex(sessionIdBitMask) + 1, etwSessionId));
413             }
414             else
415             {
416                 // legacy etw session
417                 sessionList.Add(new SessionInfo(bitcount((uint)SessionMask.All) + 1, etwSessionId));
418             }
419         }
420
421         private delegate void SessionInfoCallback(int etwSessionId, long matchAllKeywords, ref List<SessionInfo> sessionList);
422         
423         /// <summary>
424         /// This method enumerates over all active ETW sessions that have enabled 'this.m_Guid' 
425         /// for the current process ID, calling 'action' for each session, and passing it the
426         /// ETW session and the 'AllKeywords' the session enabled for the current provider.
427         /// </summary>
428         private unsafe void GetSessionInfo(SessionInfoCallback action, ref List<SessionInfo> sessionList)
429         {
430             // We wish the EventSource package to be legal for Windows Store applications.   
431             // Currently EnumerateTraceGuidsEx is not an allowed API, so we avoid its use here
432             // and use the information in the registry instead.  This means that ETW controllers
433             // that do not publish their intent to the registry (basically all controllers EXCEPT 
434             // TraceEventSesion) will not work properly 
435
436             // However the framework version of EventSource DOES have ES_SESSION_INFO defined and thus
437             // does not have this issue.  
438 #if (PLATFORM_WINDOWS && (ES_SESSION_INFO || !ES_BUILD_STANDALONE))
439             int buffSize = 256;     // An initial guess that probably works most of the time.  
440             byte* buffer;
441             for (; ; )
442             {
443                 var space = stackalloc byte[buffSize];
444                 buffer = space;
445                 var hr = 0;
446
447                 fixed (Guid* provider = &m_providerId)
448                 {
449                     hr = UnsafeNativeMethods.ManifestEtw.EnumerateTraceGuidsEx(UnsafeNativeMethods.ManifestEtw.TRACE_QUERY_INFO_CLASS.TraceGuidQueryInfo,
450                         provider, sizeof(Guid), buffer, buffSize, ref buffSize);
451                 }
452                 if (hr == 0)
453                     break;
454                 if (hr != 122 /* ERROR_INSUFFICIENT_BUFFER */)
455                     return;
456             }
457
458             var providerInfos = (UnsafeNativeMethods.ManifestEtw.TRACE_GUID_INFO*)buffer;
459             var providerInstance = (UnsafeNativeMethods.ManifestEtw.TRACE_PROVIDER_INSTANCE_INFO*)&providerInfos[1];
460             int processId = unchecked((int)Win32Native.GetCurrentProcessId());
461             // iterate over the instances of the EventProvider in all processes
462             for (int i = 0; i < providerInfos->InstanceCount; i++)
463             {
464                 if (providerInstance->Pid == processId)
465                 {
466                     var enabledInfos = (UnsafeNativeMethods.ManifestEtw.TRACE_ENABLE_INFO*)&providerInstance[1];
467                     // iterate over the list of active ETW sessions "listening" to the current provider
468                     for (int j = 0; j < providerInstance->EnableCount; j++)
469                         action(enabledInfos[j].LoggerId, enabledInfos[j].MatchAllKeyword, ref sessionList);
470                 }
471                 if (providerInstance->NextOffset == 0)
472                     break;
473                 Debug.Assert(0 <= providerInstance->NextOffset && providerInstance->NextOffset < buffSize);
474                 var structBase = (byte*)providerInstance;
475                 providerInstance = (UnsafeNativeMethods.ManifestEtw.TRACE_PROVIDER_INSTANCE_INFO*)&structBase[providerInstance->NextOffset];
476             }
477 #else 
478 #if !ES_BUILD_PCL && !FEATURE_PAL  // TODO command arguments don't work on PCL builds...
479             // This code is only used in the Nuget Package Version of EventSource.  because
480             // the code above is using APIs baned from UWP apps.     
481             // 
482             // TODO: In addition to only working when TraceEventSession enables the provider, this code
483             // also has a problem because TraceEvent does not clean up if the registry is stale 
484             // It is unclear if it is worth keeping, but for now we leave it as it does work
485             // at least some of the time.  
486
487             // Determine our session from what is in the registry.  
488             string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
489             if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8)
490                 regKey = @"Software" + @"\Wow6432Node" + regKey;
491             else
492                 regKey = @"Software" + regKey;
493
494             var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(regKey);
495             if (key != null)
496             {
497                 foreach (string valueName in key.GetValueNames())
498                 {
499                     if (valueName.StartsWith("ControllerData_Session_", StringComparison.Ordinal))
500                     {
501                         string strId = valueName.Substring(23);      // strip of the ControllerData_Session_
502                         int etwSessionId;
503                         if (int.TryParse(strId, out etwSessionId))
504                         {
505                             // we need to assert this permission for partial trust scenarios
506                             (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert();
507                             var data = key.GetValue(valueName) as byte[];
508                             if (data != null)
509                             {
510                                 var dataAsString = System.Text.Encoding.UTF8.GetString(data);
511                                 int keywordIdx = dataAsString.IndexOf("EtwSessionKeyword", StringComparison.Ordinal);
512                                 if (0 <= keywordIdx)
513                                 {
514                                     int startIdx = keywordIdx + 18;
515                                     int endIdx = dataAsString.IndexOf('\0', startIdx);
516                                     string keywordBitString = dataAsString.Substring(startIdx, endIdx-startIdx);
517                                     int keywordBit;
518                                     if (0 < endIdx && int.TryParse(keywordBitString, out keywordBit))
519                                         action(etwSessionId, 1L << keywordBit, ref sessionList);
520                                 }
521                             }
522                         }
523                     }
524                 }
525             }
526 #endif
527 #endif
528         }
529
530         /// <summary>
531         /// Returns the index of the SesisonInfo from 'sessions' that has the specified 'etwSessionId'
532         /// or -1 if the value is not present.
533         /// </summary>
534         private static int IndexOfSessionInList(List<SessionInfo> sessions, int etwSessionId)
535         {
536             if (sessions == null)
537                 return -1;
538             // for non-coreclr code we could use List<T>.FindIndex(Predicate<T>), but we need this to compile
539             // on coreclr as well
540             for (int i = 0; i < sessions.Count; ++i)
541                 if (sessions[i].etwSessionId == etwSessionId)
542                     return i;
543
544             return -1;
545         }
546
547         /// <summary>
548         /// Gets any data to be passed from the controller to the provider.  It starts with what is passed
549         /// into the callback, but unfortunately this data is only present for when the provider is active
550         /// at the time the controller issues the command.  To allow for providers to activate after the
551         /// controller issued a command, we also check the registry and use that to get the data.  The function
552         /// returns an array of bytes representing the data, the index into that byte array where the data
553         /// starts, and the command being issued associated with that data.  
554         /// </summary>
555         private unsafe bool GetDataFromController(int etwSessionId,
556                 UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData, out ControllerCommand command, out byte[] data, out int dataStart)
557         {
558             data = null;
559             dataStart = 0;
560             if (filterData == null)
561             {
562 #if (!ES_BUILD_PCL && !ES_BUILD_PN && !FEATURE_PAL)
563                 string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
564                 if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8)
565                     regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey;
566                 else
567                     regKey = @"HKEY_LOCAL_MACHINE\Software" + regKey;
568
569                 string valueName = "ControllerData_Session_" + etwSessionId.ToString(CultureInfo.InvariantCulture);
570
571                 // we need to assert this permission for partial trust scenarios
572 #if !CORECLR
573                 (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert();
574 #endif
575                 data = Microsoft.Win32.Registry.GetValue(regKey, valueName, null) as byte[];
576                 if (data != null)
577                 {
578                     // We only used the persisted data from the registry for updates.   
579                     command = ControllerCommand.Update;
580                     return true;
581                 }
582 #endif
583             }
584             else
585             {
586                 if (filterData->Ptr != 0 && 0 < filterData->Size && filterData->Size <= 1024)
587                 {
588                     data = new byte[filterData->Size];
589                     Marshal.Copy((IntPtr)filterData->Ptr, data, 0, data.Length);
590                 }
591                 command = (ControllerCommand)filterData->Type;
592                 return true;
593             }
594
595             command = ControllerCommand.Update;
596             return false;
597         }
598
599         /// <summary>
600         /// IsEnabled, method used to test if provider is enabled
601         /// </summary>
602         public bool IsEnabled()
603         {
604             return m_enabled;
605         }
606
607         /// <summary>
608         /// IsEnabled, method used to test if event is enabled
609         /// </summary>
610         /// <param name="level">
611         /// Level  to test
612         /// </param>
613         /// <param name="keywords">
614         /// Keyword  to test
615         /// </param>
616         public bool IsEnabled(byte level, long keywords)
617         {
618             //
619             // If not enabled at all, return false.
620             //
621             if (!m_enabled)
622             {
623                 return false;
624             }
625
626             // This also covers the case of Level == 0.
627             if ((level <= m_level) ||
628                 (m_level == 0))
629             {
630
631                 //
632                 // Check if Keyword is enabled
633                 //
634
635                 if ((keywords == 0) ||
636                     (((keywords & m_anyKeywordMask) != 0) &&
637                      ((keywords & m_allKeywordMask) == m_allKeywordMask)))
638                 {
639                     return true;
640                 }
641             }
642
643             return false;
644         }
645
646         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
647         public static WriteEventErrorCode GetLastWriteEventError()
648         {
649             return s_returnCode;
650         }
651
652         //
653         // Helper function to set the last error on the thread
654         //
655         private static void SetLastError(int error)
656         {
657             switch (error)
658             {
659                 case UnsafeNativeMethods.ManifestEtw.ERROR_ARITHMETIC_OVERFLOW:
660                 case UnsafeNativeMethods.ManifestEtw.ERROR_MORE_DATA:
661                     s_returnCode = WriteEventErrorCode.EventTooBig;
662                     break;
663                 case UnsafeNativeMethods.ManifestEtw.ERROR_NOT_ENOUGH_MEMORY:
664                     s_returnCode = WriteEventErrorCode.NoFreeBuffers;
665                     break;
666             }
667         }
668
669         // <SecurityKernel Critical="True" Ring="0">
670         // <UsesUnsafeCode Name="Local intptrPtr of type: IntPtr*" />
671         // <UsesUnsafeCode Name="Local intptrPtr of type: Int32*" />
672         // <UsesUnsafeCode Name="Local longptr of type: Int64*" />
673         // <UsesUnsafeCode Name="Local uintptr of type: UInt32*" />
674         // <UsesUnsafeCode Name="Local ulongptr of type: UInt64*" />
675         // <UsesUnsafeCode Name="Local charptr of type: Char*" />
676         // <UsesUnsafeCode Name="Local byteptr of type: Byte*" />
677         // <UsesUnsafeCode Name="Local shortptr of type: Int16*" />
678         // <UsesUnsafeCode Name="Local sbyteptr of type: SByte*" />
679         // <UsesUnsafeCode Name="Local ushortptr of type: UInt16*" />
680         // <UsesUnsafeCode Name="Local floatptr of type: Single*" />
681         // <UsesUnsafeCode Name="Local doubleptr of type: Double*" />
682         // <UsesUnsafeCode Name="Local boolptr of type: Boolean*" />
683         // <UsesUnsafeCode Name="Local guidptr of type: Guid*" />
684         // <UsesUnsafeCode Name="Local decimalptr of type: Decimal*" />
685         // <UsesUnsafeCode Name="Local booleanptr of type: Boolean*" />
686         // <UsesUnsafeCode Name="Parameter dataDescriptor of type: EventData*" />
687         // <UsesUnsafeCode Name="Parameter dataBuffer of type: Byte*" />
688         // </SecurityKernel>
689         private static unsafe object EncodeObject(ref object data, ref EventData* dataDescriptor, ref byte* dataBuffer, ref uint totalEventSize)
690         /*++
691
692         Routine Description:
693
694            This routine is used by WriteEvent to unbox the object type and
695            to fill the passed in ETW data descriptor. 
696
697         Arguments:
698
699            data - argument to be decoded
700
701            dataDescriptor - pointer to the descriptor to be filled (updated to point to the next empty entry)
702
703            dataBuffer - storage buffer for storing user data, needed because cant get the address of the object
704                         (updated to point to the next empty entry)
705
706         Return Value:
707
708            null if the object is a basic type other than string or byte[]. String otherwise
709
710         --*/
711         {
712         Again:
713             dataDescriptor->Reserved = 0;
714
715             string sRet = data as string;
716             byte[] blobRet = null;
717
718             if (sRet != null)
719             {
720                 dataDescriptor->Size = ((uint)sRet.Length + 1) * 2;
721             }
722             else if ((blobRet = data as byte[]) != null)
723             {
724                 // first store array length
725                 *(int*)dataBuffer = blobRet.Length;
726                 dataDescriptor->Ptr = (ulong)dataBuffer;
727                 dataDescriptor->Size = 4;
728                 totalEventSize += dataDescriptor->Size;
729
730                 // then the array parameters
731                 dataDescriptor++;
732                 dataBuffer += s_basicTypeAllocationBufferSize;
733                 dataDescriptor->Size = (uint)blobRet.Length;
734             }
735             else if (data is IntPtr)
736             {
737                 dataDescriptor->Size = (uint)sizeof(IntPtr);
738                 IntPtr* intptrPtr = (IntPtr*)dataBuffer;
739                 *intptrPtr = (IntPtr)data;
740                 dataDescriptor->Ptr = (ulong)intptrPtr;
741             }
742             else if (data is int)
743             {
744                 dataDescriptor->Size = (uint)sizeof(int);
745                 int* intptr = (int*)dataBuffer;
746                 *intptr = (int)data;
747                 dataDescriptor->Ptr = (ulong)intptr;
748             }
749             else if (data is long)
750             {
751                 dataDescriptor->Size = (uint)sizeof(long);
752                 long* longptr = (long*)dataBuffer;
753                 *longptr = (long)data;
754                 dataDescriptor->Ptr = (ulong)longptr;
755             }
756             else if (data is uint)
757             {
758                 dataDescriptor->Size = (uint)sizeof(uint);
759                 uint* uintptr = (uint*)dataBuffer;
760                 *uintptr = (uint)data;
761                 dataDescriptor->Ptr = (ulong)uintptr;
762             }
763             else if (data is UInt64)
764             {
765                 dataDescriptor->Size = (uint)sizeof(ulong);
766                 UInt64* ulongptr = (ulong*)dataBuffer;
767                 *ulongptr = (ulong)data;
768                 dataDescriptor->Ptr = (ulong)ulongptr;
769             }
770             else if (data is char)
771             {
772                 dataDescriptor->Size = (uint)sizeof(char);
773                 char* charptr = (char*)dataBuffer;
774                 *charptr = (char)data;
775                 dataDescriptor->Ptr = (ulong)charptr;
776             }
777             else if (data is byte)
778             {
779                 dataDescriptor->Size = (uint)sizeof(byte);
780                 byte* byteptr = (byte*)dataBuffer;
781                 *byteptr = (byte)data;
782                 dataDescriptor->Ptr = (ulong)byteptr;
783             }
784             else if (data is short)
785             {
786                 dataDescriptor->Size = (uint)sizeof(short);
787                 short* shortptr = (short*)dataBuffer;
788                 *shortptr = (short)data;
789                 dataDescriptor->Ptr = (ulong)shortptr;
790             }
791             else if (data is sbyte)
792             {
793                 dataDescriptor->Size = (uint)sizeof(sbyte);
794                 sbyte* sbyteptr = (sbyte*)dataBuffer;
795                 *sbyteptr = (sbyte)data;
796                 dataDescriptor->Ptr = (ulong)sbyteptr;
797             }
798             else if (data is ushort)
799             {
800                 dataDescriptor->Size = (uint)sizeof(ushort);
801                 ushort* ushortptr = (ushort*)dataBuffer;
802                 *ushortptr = (ushort)data;
803                 dataDescriptor->Ptr = (ulong)ushortptr;
804             }
805             else if (data is float)
806             {
807                 dataDescriptor->Size = (uint)sizeof(float);
808                 float* floatptr = (float*)dataBuffer;
809                 *floatptr = (float)data;
810                 dataDescriptor->Ptr = (ulong)floatptr;
811             }
812             else if (data is double)
813             {
814                 dataDescriptor->Size = (uint)sizeof(double);
815                 double* doubleptr = (double*)dataBuffer;
816                 *doubleptr = (double)data;
817                 dataDescriptor->Ptr = (ulong)doubleptr;
818             }
819             else if (data is bool)
820             {
821                 // WIN32 Bool is 4 bytes
822                 dataDescriptor->Size = 4;
823                 int* intptr = (int*)dataBuffer;
824                 if (((bool)data))
825                 {
826                     *intptr = 1;
827                 }
828                 else
829                 {
830                     *intptr = 0;
831                 }
832                 dataDescriptor->Ptr = (ulong)intptr;
833             }
834             else if (data is Guid)
835             {
836                 dataDescriptor->Size = (uint)sizeof(Guid);
837                 Guid* guidptr = (Guid*)dataBuffer;
838                 *guidptr = (Guid)data;
839                 dataDescriptor->Ptr = (ulong)guidptr;
840             }
841             else if (data is decimal)
842             {
843                 dataDescriptor->Size = (uint)sizeof(decimal);
844                 decimal* decimalptr = (decimal*)dataBuffer;
845                 *decimalptr = (decimal)data;
846                 dataDescriptor->Ptr = (ulong)decimalptr;
847             }
848             else if (data is DateTime)
849             {
850                 const long UTCMinTicks = 504911232000000000;
851                 long dateTimeTicks = 0;
852                 // We cannot translate dates sooner than 1/1/1601 in UTC. 
853                 // To avoid getting an ArgumentOutOfRangeException we compare with 1/1/1601 DateTime ticks
854                 if (((DateTime)data).Ticks > UTCMinTicks)
855                     dateTimeTicks = ((DateTime)data).ToFileTimeUtc();
856                 dataDescriptor->Size = (uint)sizeof(long);
857                 long* longptr = (long*)dataBuffer;
858                 *longptr = dateTimeTicks;
859                 dataDescriptor->Ptr = (ulong)longptr;
860             }
861             else
862             {
863                 if (data is System.Enum)
864                 {
865                     Type underlyingType = Enum.GetUnderlyingType(data.GetType());
866                     if (underlyingType == typeof(int))
867                     {
868 #if !ES_BUILD_PCL
869                         data = ((IConvertible)data).ToInt32(null);
870 #else
871                         data = (int)data;
872 #endif
873                         goto Again;
874                     }
875                     else if (underlyingType == typeof(long))
876                     {
877 #if !ES_BUILD_PCL
878                         data = ((IConvertible)data).ToInt64(null);
879 #else
880                         data = (long)data;
881 #endif
882                         goto Again;
883                     }
884                 }
885
886                 // To our eyes, everything else is a just a string
887                 if (data == null)
888                     sRet = "";
889                 else
890                     sRet = data.ToString();
891                 dataDescriptor->Size = ((uint)sRet.Length + 1) * 2;
892             }
893
894             totalEventSize += dataDescriptor->Size;
895
896             // advance buffers
897             dataDescriptor++;
898             dataBuffer += s_basicTypeAllocationBufferSize;
899
900             return (object)sRet ?? (object)blobRet;
901         }
902
903         /// <summary>
904         /// WriteEvent, method to write a parameters with event schema properties
905         /// </summary>
906         /// <param name="eventDescriptor">
907         /// Event Descriptor for this event. 
908         /// </param>
909         /// <param name="activityID">
910         /// A pointer to the activity ID GUID to log 
911         /// </param>
912         /// <param name="childActivityID">
913         /// childActivityID is marked as 'related' to the current activity ID. 
914         /// </param>
915         /// <param name="eventPayload">
916         /// Payload for the ETW event. 
917         /// </param>
918         // <SecurityKernel Critical="True" Ring="0">
919         // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" />
920         // <UsesUnsafeCode Name="Local dataBuffer of type: Byte*" />
921         // <UsesUnsafeCode Name="Local pdata of type: Char*" />
922         // <UsesUnsafeCode Name="Local userData of type: EventData*" />
923         // <UsesUnsafeCode Name="Local userDataPtr of type: EventData*" />
924         // <UsesUnsafeCode Name="Local currentBuffer of type: Byte*" />
925         // <UsesUnsafeCode Name="Local v0 of type: Char*" />
926         // <UsesUnsafeCode Name="Local v1 of type: Char*" />
927         // <UsesUnsafeCode Name="Local v2 of type: Char*" />
928         // <UsesUnsafeCode Name="Local v3 of type: Char*" />
929         // <UsesUnsafeCode Name="Local v4 of type: Char*" />
930         // <UsesUnsafeCode Name="Local v5 of type: Char*" />
931         // <UsesUnsafeCode Name="Local v6 of type: Char*" />
932         // <UsesUnsafeCode Name="Local v7 of type: Char*" />
933         // <ReferencesCritical Name="Method: EncodeObject(Object&, EventData*, Byte*):String" Ring="1" />
934         // </SecurityKernel>
935         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Performance-critical code")]
936         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
937         internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, Guid* activityID, Guid* childActivityID, params object[] eventPayload)
938         {
939             int status = 0;
940
941             if (IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords))
942             {
943                 int argCount = 0;
944                 unsafe
945                 {
946                     argCount = eventPayload.Length;
947
948                     if (argCount > s_etwMaxNumberArguments)
949                     {
950                         s_returnCode = WriteEventErrorCode.TooManyArgs;
951                         return false;
952                     }
953
954                     uint totalEventSize = 0;
955                     int index;
956                     int refObjIndex = 0;
957                     List<int> refObjPosition = new List<int>(s_etwAPIMaxRefObjCount);
958                     List<object> dataRefObj = new List<object>(s_etwAPIMaxRefObjCount);
959                     EventData* userData = stackalloc EventData[2 * argCount];
960                     EventData* userDataPtr = (EventData*)userData;
961                     byte* dataBuffer = stackalloc byte[s_basicTypeAllocationBufferSize * 2 * argCount]; // Assume 16 chars for non-string argument
962                     byte* currentBuffer = dataBuffer;
963
964                     //
965                     // The loop below goes through all the arguments and fills in the data 
966                     // descriptors. For strings save the location in the dataString array.
967                     // Calculates the total size of the event by adding the data descriptor
968                     // size value set in EncodeObject method.
969                     //
970                     bool hasNonStringRefArgs = false;
971                     for (index = 0; index < eventPayload.Length; index++)
972                     {
973                         if (eventPayload[index] != null)
974                         {
975                             object supportedRefObj;
976                             supportedRefObj = EncodeObject(ref eventPayload[index], ref userDataPtr, ref currentBuffer, ref totalEventSize);
977
978                             if (supportedRefObj != null)
979                             {
980                                 // EncodeObject advanced userDataPtr to the next empty slot
981                                 int idx = (int)(userDataPtr - userData - 1);
982                                 if (!(supportedRefObj is string))
983                                 {
984                                     if (eventPayload.Length + idx + 1 - index > s_etwMaxNumberArguments)
985                                     {
986                                         s_returnCode = WriteEventErrorCode.TooManyArgs;
987                                         return false;
988                                     }
989                                     hasNonStringRefArgs = true;
990                                 }
991                                 dataRefObj.Add(supportedRefObj);
992                                 refObjPosition.Add(idx);
993                                 refObjIndex++;
994                             }
995                         }
996                         else
997                         {
998                             s_returnCode = WriteEventErrorCode.NullInput;
999                             return false;
1000                         }
1001                     }
1002
1003                     // update argCount based on actual number of arguments written to 'userData'
1004                     argCount = (int)(userDataPtr - userData);
1005
1006                     if (totalEventSize > s_traceEventMaximumSize)
1007                     {
1008                         s_returnCode = WriteEventErrorCode.EventTooBig;
1009                         return false;
1010                     }
1011
1012                     // the optimized path (using "fixed" instead of allocating pinned GCHandles
1013                     if (!hasNonStringRefArgs && (refObjIndex < s_etwAPIMaxRefObjCount))
1014                     {
1015                         // Fast path: at most 8 string arguments
1016
1017                         // ensure we have at least s_etwAPIMaxStringCount in dataString, so that
1018                         // the "fixed" statement below works
1019                         while (refObjIndex < s_etwAPIMaxRefObjCount)
1020                         {
1021                             dataRefObj.Add(null);
1022                             ++refObjIndex;
1023                         }
1024
1025                         //
1026                         // now fix any string arguments and set the pointer on the data descriptor 
1027                         //
1028                         fixed (char* v0 = (string)dataRefObj[0], v1 = (string)dataRefObj[1], v2 = (string)dataRefObj[2], v3 = (string)dataRefObj[3],
1029                                 v4 = (string)dataRefObj[4], v5 = (string)dataRefObj[5], v6 = (string)dataRefObj[6], v7 = (string)dataRefObj[7])
1030                         {
1031                             userDataPtr = (EventData*)userData;
1032                             if (dataRefObj[0] != null)
1033                             {
1034                                 userDataPtr[refObjPosition[0]].Ptr = (ulong)v0;
1035                             }
1036                             if (dataRefObj[1] != null)
1037                             {
1038                                 userDataPtr[refObjPosition[1]].Ptr = (ulong)v1;
1039                             }
1040                             if (dataRefObj[2] != null)
1041                             {
1042                                 userDataPtr[refObjPosition[2]].Ptr = (ulong)v2;
1043                             }
1044                             if (dataRefObj[3] != null)
1045                             {
1046                                 userDataPtr[refObjPosition[3]].Ptr = (ulong)v3;
1047                             }
1048                             if (dataRefObj[4] != null)
1049                             {
1050                                 userDataPtr[refObjPosition[4]].Ptr = (ulong)v4;
1051                             }
1052                             if (dataRefObj[5] != null)
1053                             {
1054                                 userDataPtr[refObjPosition[5]].Ptr = (ulong)v5;
1055                             }
1056                             if (dataRefObj[6] != null)
1057                             {
1058                                 userDataPtr[refObjPosition[6]].Ptr = (ulong)v6;
1059                             }
1060                             if (dataRefObj[7] != null)
1061                             {
1062                                 userDataPtr[refObjPosition[7]].Ptr = (ulong)v7;
1063                             }
1064
1065                             status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, argCount, userData);
1066                         }
1067                     }
1068                     else
1069                     {
1070                         // Slow path: use pinned handles
1071                         userDataPtr = (EventData*)userData;
1072
1073                         GCHandle[] rgGCHandle = new GCHandle[refObjIndex];
1074                         for (int i = 0; i < refObjIndex; ++i)
1075                         {
1076                             // below we still use "fixed" to avoid taking dependency on the offset of the first field
1077                             // in the object (the way we would need to if we used GCHandle.AddrOfPinnedObject)
1078                             rgGCHandle[i] = GCHandle.Alloc(dataRefObj[i], GCHandleType.Pinned);
1079                             if (dataRefObj[i] is string)
1080                             {
1081                                 fixed (char* p = (string)dataRefObj[i])
1082                                     userDataPtr[refObjPosition[i]].Ptr = (ulong)p;
1083                             }
1084                             else
1085                             {
1086                                 fixed (byte* p = (byte[])dataRefObj[i])
1087                                     userDataPtr[refObjPosition[i]].Ptr = (ulong)p;
1088                             }
1089                         }
1090
1091                         status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, argCount, userData);
1092
1093                         for (int i = 0; i < refObjIndex; ++i)
1094                         {
1095                             rgGCHandle[i].Free();
1096                         }
1097                     }
1098                 }
1099             }
1100
1101             if (status != 0)
1102             {
1103                 SetLastError((int)status);
1104                 return false;
1105             }
1106
1107             return true;
1108         }
1109
1110         /// <summary>
1111         /// WriteEvent, method to be used by generated code on a derived class
1112         /// </summary>
1113         /// <param name="eventDescriptor">
1114         /// Event Descriptor for this event. 
1115         /// </param>
1116         /// <param name="activityID">
1117         /// A pointer to the activity ID to log 
1118         /// </param>
1119         /// <param name="childActivityID">
1120         /// If this event is generating a child activity (WriteEventTransfer related activity) this is child activity
1121         /// This can be null for events that do not generate a child activity.  
1122         /// </param>
1123         /// <param name="dataCount">
1124         /// number of event descriptors 
1125         /// </param>
1126         /// <param name="data">
1127         /// pointer  do the event data
1128         /// </param>
1129         // <SecurityKernel Critical="True" Ring="0">
1130         // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" />
1131         // </SecurityKernel>
1132         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
1133         internal unsafe protected bool WriteEvent(ref EventDescriptor eventDescriptor, Guid* activityID, Guid* childActivityID, int dataCount, IntPtr data)
1134         {
1135             if (childActivityID != null)
1136             {
1137                 // activity transfers are supported only for events that specify the Send or Receive opcode
1138                 Debug.Assert((EventOpcode)eventDescriptor.Opcode == EventOpcode.Send ||
1139                                 (EventOpcode)eventDescriptor.Opcode == EventOpcode.Receive ||
1140                                 (EventOpcode)eventDescriptor.Opcode == EventOpcode.Start ||
1141                                 (EventOpcode)eventDescriptor.Opcode == EventOpcode.Stop);
1142             }
1143
1144             int status = m_eventProvider.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, dataCount, (EventData*)data);
1145
1146             if (status != 0)
1147             {
1148                 SetLastError(status);
1149                 return false;
1150             }
1151             return true;
1152         }
1153
1154         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
1155         internal unsafe bool WriteEventRaw(
1156             ref EventDescriptor eventDescriptor,
1157             Guid* activityID,
1158             Guid* relatedActivityID,
1159             int dataCount,
1160             IntPtr data)
1161         {
1162             int status;
1163
1164             status = m_eventProvider.EventWriteTransferWrapper(
1165                 m_regHandle,
1166                 ref eventDescriptor,
1167                 activityID,
1168                 relatedActivityID,
1169                 dataCount,
1170                 (EventData*)data);
1171
1172             if (status != 0)
1173             {
1174                 SetLastError(status);
1175                 return false;
1176             }
1177             return true;
1178         }
1179
1180
1181         // These are look-alikes to the Manifest based ETW OS APIs that have been shimmed to work
1182         // either with Manifest ETW or Classic ETW (if Manifest based ETW is not available).  
1183         private unsafe uint EventRegister(ref Guid providerId, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback)
1184         {
1185             m_providerId = providerId;
1186             m_etwCallback = enableCallback;
1187             return m_eventProvider.EventRegister(ref providerId, enableCallback, null, ref m_regHandle);
1188         }
1189
1190         private uint EventUnregister(long registrationHandle)
1191         {
1192             return m_eventProvider.EventUnregister(registrationHandle);
1193         }
1194
1195         static int[] nibblebits = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
1196         private static int bitcount(uint n)
1197         {
1198             int count = 0;
1199             for (; n != 0; n = n >> 4)
1200                 count += nibblebits[n & 0x0f];
1201             return count;
1202         }
1203         private static int bitindex(uint n)
1204         {
1205             Debug.Assert(bitcount(n) == 1);
1206             int idx = 0;
1207             while ((n & (1 << idx)) == 0)
1208                 idx++;
1209             return idx;
1210         }
1211     }
1212
1213 #if PLATFORM_WINDOWS
1214
1215     // A wrapper around the ETW-specific API calls.
1216     internal sealed class EtwEventProvider : IEventProvider
1217     {
1218         // Register an event provider.
1219         unsafe uint IEventProvider.EventRegister(
1220             ref Guid providerId,
1221             UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback,
1222             void* callbackContext,
1223             ref long registrationHandle)
1224         {
1225             return UnsafeNativeMethods.ManifestEtw.EventRegister(
1226                 ref providerId,
1227                 enableCallback,
1228                 callbackContext,
1229                 ref registrationHandle);
1230         }
1231
1232         // Unregister an event provider.
1233         uint IEventProvider.EventUnregister(long registrationHandle)
1234         {
1235             return UnsafeNativeMethods.ManifestEtw.EventUnregister(registrationHandle);
1236         }
1237
1238         // Write an event.
1239         unsafe int IEventProvider.EventWriteTransferWrapper(
1240             long registrationHandle,
1241             ref EventDescriptor eventDescriptor,
1242             Guid* activityId,
1243             Guid* relatedActivityId,
1244             int userDataCount,
1245             EventProvider.EventData* userData)
1246         {
1247             return UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(
1248                 registrationHandle,
1249                 ref eventDescriptor,
1250                 activityId,
1251                 relatedActivityId,
1252                 userDataCount,
1253                 userData);
1254         }
1255
1256         // Get or set the per-thread activity ID.
1257         int IEventProvider.EventActivityIdControl(UnsafeNativeMethods.ManifestEtw.ActivityControl ControlCode, ref Guid ActivityId)
1258         {
1259             return UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
1260                 ControlCode,
1261                 ref ActivityId);
1262         }
1263     }
1264
1265 #endif
1266 }
1267