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