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