MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.ComponentModel;5using System.Diagnostics.CodeAnalysis;6using System.Globalization;7using System.Runtime.InteropServices;8using System.Security;9using System.Threading;10 11namespace System.Diagnostics.Eventing12{13 public class EventProvider : IDisposable14 {15 [SecurityCritical]16 private UnsafeNativeMethods.EtwEnableCallback _etwCallback; // Trace Callback function17 18 private long _regHandle; // Trace Registration Handle19 private byte _level; // Tracing Level20 private long _anyKeywordMask; // Trace Enable Flags21 private long _allKeywordMask; // Match all keyword22 private int _enabled; // Enabled flag from Trace callback23 private readonly Guid _providerId; // Control Guid24 private int _disposed; // when 1, provider has unregister25 26 [ThreadStatic]27 private static WriteEventErrorCode t_returnCode; // thread slot to keep last error28 29 [ThreadStatic]30 private static Guid t_activityId;31 32 private const int s_basicTypeAllocationBufferSize = 16;33 private const int s_etwMaxNumberArguments = 32;34 private const int s_etwAPIMaxStringCount = 8;35 private const int s_maxEventDataDescriptors = 128;36 private const int s_traceEventMaximumSize = 65482;37 private const int s_traceEventMaximumStringSize = 32724;38 39 [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]40 public enum WriteEventErrorCode : int41 {42 // check mapping to runtime codes43 NoError = 0,44 NoFreeBuffers = 1,45 EventTooBig = 246 }47 48 [StructLayout(LayoutKind.Explicit, Size = 16)]49 private struct EventData50 {51 [FieldOffset(0)]52 internal ulong DataPointer;53 54 [FieldOffset(8)]55 internal uint Size;56 57 [FieldOffset(12)]58 internal int Reserved;59 }60 61 private enum ActivityControl : uint62 {63 EVENT_ACTIVITY_CTRL_GET_ID = 1,64 EVENT_ACTIVITY_CTRL_SET_ID = 2,65 EVENT_ACTIVITY_CTRL_CREATE_ID = 3,66 EVENT_ACTIVITY_CTRL_GET_SET_ID = 4,67 EVENT_ACTIVITY_CTRL_CREATE_SET_ID = 568 }69 70 /// <summary>71 /// Constructor for EventProvider class.72 /// </summary>73 /// <param name="providerGuid">74 /// Unique GUID among all trace sources running on a system75 /// </param>76 [SecuritySafeCritical]77 [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]78 public EventProvider(Guid providerGuid)79 {80 _providerId = providerGuid;81 82 //83 // EtwRegister the ProviderId with ETW84 //85 EtwRegister();86 }87 88 /// <summary>89 /// This method registers the controlGuid of this class with ETW.90 /// We need to be running on Vista or above. If not an91 /// PlatformNotSupported exception will be thrown.92 /// If for some reason the ETW EtwRegister call failed93 /// a NotSupported exception will be thrown.94 /// </summary>95 [System.Security.SecurityCritical]96 private unsafe void EtwRegister()97 {98 uint status;99 100 _etwCallback = new UnsafeNativeMethods.EtwEnableCallback(EtwEnableCallBack);101 102 status = UnsafeNativeMethods.EventRegister(in _providerId, _etwCallback, null, ref _regHandle);103 if (status != 0)104 {105 throw new Win32Exception((int)status);106 }107 }108 109 //110 // implement Dispose Pattern to early deregister from ETW instead of waiting for111 // the finalizer to call deregistration.112 // Once the user is done with the provider it needs to call Close() or Dispose()113 // If neither are called the finalizer will unregister the provider anyway114 //115 public void Dispose()116 {117 Dispose(true);118 GC.SuppressFinalize(this);119 }120 121 [System.Security.SecuritySafeCritical]122 protected virtual void Dispose(bool disposing)123 {124 //125 // explicit cleanup is done by calling Dispose with true from126 // Dispose() or Close(). The disposing argument is ignored because there127 // are no unmanaged resources.128 // The finalizer calls Dispose with false.129 //130 131 //132 // check if the object has been already disposed133 //134 if (_disposed == 1)135 {136 return;137 }138 139 if (Interlocked.Exchange(ref _disposed, 1) != 0)140 {141 // somebody is already disposing the provider142 return;143 }144 145 //146 // Disables Tracing in the provider, then unregister147 //148 149 _enabled = 0;150 151 Deregister();152 }153 154 /// <summary>155 /// This method deregisters the controlGuid of this class with ETW.156 /// </summary>157 public virtual void Close()158 {159 Dispose();160 }161 162 ~EventProvider()163 {164 Dispose(false);165 }166 167 /// <summary>168 /// This method un-registers from ETW.169 /// </summary>170 [System.Security.SecurityCritical]171 private unsafe void Deregister()172 {173 //174 // Unregister from ETW using the RegHandle saved from175 // the register call.176 //177 178 if (_regHandle != 0)179 {180 UnsafeNativeMethods.EventUnregister(_regHandle);181 _regHandle = 0;182 }183 }184 185 [System.Security.SecurityCritical]186 private unsafe void EtwEnableCallBack(187 [In] ref System.Guid sourceId,188 [In] int isEnabled,189 [In] byte setLevel,190 [In] long anyKeyword,191 [In] long allKeyword,192 [In] void* filterData,193 [In] void* callbackContext194 )195 {196 _enabled = isEnabled;197 _level = setLevel;198 _anyKeywordMask = anyKeyword;199 _allKeywordMask = allKeyword;200 return;201 }202 203 /// <summary>204 /// IsEnabled, method used to test if provider is enabled.205 /// </summary>206 public bool IsEnabled()207 {208 return _enabled != 0;209 }210 211 /// <summary>212 /// IsEnabled, method used to test if event is enabled.213 /// </summary>214 /// <param name="level">215 /// Level to test216 /// </param>217 /// <param name="keywords">218 /// Keyword to test219 /// </param>220 public bool IsEnabled(byte level, long keywords)221 {222 //223 // If not enabled at all, return false.224 //225 226 if (_enabled == 0)227 {228 return false;229 }230 231 // This also covers the case of Level == 0.232 if ((level <= _level) ||233 (_level == 0))234 {235 //236 // Check if Keyword is enabled237 //238 239 if ((keywords == 0) ||240 (((keywords & _anyKeywordMask) != 0) &&241 ((keywords & _allKeywordMask) == _allKeywordMask)))242 {243 return true;244 }245 }246 247 return false;248 }249 250 public static WriteEventErrorCode GetLastWriteEventError()251 {252 return t_returnCode;253 }254 255 //256 // Helper function to set the last error on the thread257 //258 private static void SetLastError(int error)259 {260 switch (error)261 {262 case UnsafeNativeMethods.ERROR_ARITHMETIC_OVERFLOW:263 case UnsafeNativeMethods.ERROR_MORE_DATA:264 t_returnCode = WriteEventErrorCode.EventTooBig;265 break;266 case UnsafeNativeMethods.ERROR_NOT_ENOUGH_MEMORY:267 t_returnCode = WriteEventErrorCode.NoFreeBuffers;268 break;269 }270 }271 272 [System.Security.SecurityCritical]273 private static unsafe string EncodeObject(ref object data, EventData* dataDescriptor, byte* dataBuffer)274 /*++275 276 Routine Description:277 278 This routine is used by WriteEvent to unbox the object type and279 to fill the passed in ETW data descriptor.280 281 Arguments:282 283 data - argument to be decoded284 285 dataDescriptor - pointer to the descriptor to be filled286 287 dataBuffer - storage buffer for storing user data, needed because cant get the address of the object288 289 Return Value:290 291 null if the object is a basic type other than string. String otherwise292 293 --*/294 {295 dataDescriptor->Reserved = 0;296 297 if (data is string sRet)298 {299 dataDescriptor->Size = (uint)((sRet.Length + 1) * 2);300 return sRet;301 }302 303 if (data == null)304 {305 dataDescriptor->Size = 0;306 dataDescriptor->DataPointer = 0;307 }308 else if (data is IntPtr)309 {310 dataDescriptor->Size = (uint)sizeof(IntPtr);311 IntPtr* intptrPtr = (IntPtr*)dataBuffer;312 *intptrPtr = (IntPtr)data;313 dataDescriptor->DataPointer = (ulong)intptrPtr;314 }315 else if (data is int)316 {317 dataDescriptor->Size = (uint)sizeof(int);318 int* intptrPtr = (int*)dataBuffer;319 *intptrPtr = (int)data;320 dataDescriptor->DataPointer = (ulong)intptrPtr;321 }322 else if (data is long)323 {324 dataDescriptor->Size = (uint)sizeof(long);325 long* longptr = (long*)dataBuffer;326 *longptr = (long)data;327 dataDescriptor->DataPointer = (ulong)longptr;328 }329 else if (data is uint)330 {331 dataDescriptor->Size = (uint)sizeof(uint);332 uint* uintptr = (uint*)dataBuffer;333 *uintptr = (uint)data;334 dataDescriptor->DataPointer = (ulong)uintptr;335 }336 else if (data is ulong)337 {338 dataDescriptor->Size = (uint)sizeof(ulong);339 ulong* ulongptr = (ulong*)dataBuffer;340 *ulongptr = (ulong)data;341 dataDescriptor->DataPointer = (ulong)ulongptr;342 }343 else if (data is char)344 {345 dataDescriptor->Size = (uint)sizeof(char);346 char* charptr = (char*)dataBuffer;347 *charptr = (char)data;348 dataDescriptor->DataPointer = (ulong)charptr;349 }350 else if (data is byte)351 {352 dataDescriptor->Size = (uint)sizeof(byte);353 byte* byteptr = (byte*)dataBuffer;354 *byteptr = (byte)data;355 dataDescriptor->DataPointer = (ulong)byteptr;356 }357 else if (data is short)358 {359 dataDescriptor->Size = (uint)sizeof(short);360 short* shortptr = (short*)dataBuffer;361 *shortptr = (short)data;362 dataDescriptor->DataPointer = (ulong)shortptr;363 }364 else if (data is sbyte)365 {366 dataDescriptor->Size = (uint)sizeof(sbyte);367 sbyte* sbyteptr = (sbyte*)dataBuffer;368 *sbyteptr = (sbyte)data;369 dataDescriptor->DataPointer = (ulong)sbyteptr;370 }371 else if (data is ushort)372 {373 dataDescriptor->Size = (uint)sizeof(ushort);374 ushort* ushortptr = (ushort*)dataBuffer;375 *ushortptr = (ushort)data;376 dataDescriptor->DataPointer = (ulong)ushortptr;377 }378 else if (data is float)379 {380 dataDescriptor->Size = (uint)sizeof(float);381 float* floatptr = (float*)dataBuffer;382 *floatptr = (float)data;383 dataDescriptor->DataPointer = (ulong)floatptr;384 }385 else if (data is double)386 {387 dataDescriptor->Size = (uint)sizeof(double);388 double* doubleptr = (double*)dataBuffer;389 *doubleptr = (double)data;390 dataDescriptor->DataPointer = (ulong)doubleptr;391 }392 else if (data is bool)393 {394 dataDescriptor->Size = (uint)sizeof(bool);395 bool* boolptr = (bool*)dataBuffer;396 *boolptr = (bool)data;397 dataDescriptor->DataPointer = (ulong)boolptr;398 }399 else if (data is Guid)400 {401 dataDescriptor->Size = (uint)sizeof(Guid);402 Guid* guidptr = (Guid*)dataBuffer;403 *guidptr = (Guid)data;404 dataDescriptor->DataPointer = (ulong)guidptr;405 }406 else if (data is decimal)407 {408 dataDescriptor->Size = (uint)sizeof(decimal);409 decimal* decimalptr = (decimal*)dataBuffer;410 *decimalptr = (decimal)data;411 dataDescriptor->DataPointer = (ulong)decimalptr;412 }413 else414 {415 // To our eyes, everything else is a just a string416 sRet = data.ToString();417 dataDescriptor->Size = (uint)((sRet.Length + 1) * 2);418 return sRet;419 }420 421 return null;422 }423 424 /// <summary>425 /// WriteMessageEvent, method to write a string with level and Keyword.426 /// The activity ID will be propagated only if the call stays on the same native thread as SetActivityId().427 /// </summary>428 /// <param name="eventMessage">429 /// Message to write430 /// </param>431 /// <param name="eventLevel">432 /// Level to test433 /// </param>434 /// <param name="eventKeywords">435 /// Keyword to test436 /// </param>437 [System.Security.SecurityCritical]438 public bool WriteMessageEvent(string eventMessage, byte eventLevel, long eventKeywords)439 {440 int status = 0;441 442 ArgumentNullException.ThrowIfNull(eventMessage);443 444 if (IsEnabled(eventLevel, eventKeywords))445 {446 if (eventMessage.Length > s_traceEventMaximumStringSize)447 {448 t_returnCode = WriteEventErrorCode.EventTooBig;449 return false;450 }451 452 unsafe453 {454 fixed (char* pdata = eventMessage)455 {456 status = (int)UnsafeNativeMethods.EventWriteString(_regHandle, eventLevel, eventKeywords, pdata);457 }458 459 if (status != 0)460 {461 SetLastError(status);462 return false;463 }464 }465 }466 467 return true;468 }469 470 /// <summary>471 /// WriteMessageEvent, method to write a string with level=0 and Keyword=0472 /// The activity ID will be propagated only if the call stays on the same native thread as SetActivityId().473 /// </summary>474 /// <param name="eventMessage">475 /// Message to log476 /// </param>477 public bool WriteMessageEvent(string eventMessage)478 {479 return WriteMessageEvent(eventMessage, 0, 0);480 }481 482 /// <summary>483 /// WriteEvent method to write parameters with event schema properties.484 /// </summary>485 /// <param name="eventDescriptor">486 /// Event Descriptor for this event.487 /// </param>488 /// <param name="eventPayload">489 /// </param>490 public bool WriteEvent(in EventDescriptor eventDescriptor, params object[] eventPayload)491 {492 return WriteTransferEvent(in eventDescriptor, Guid.Empty, eventPayload);493 }494 495 /// <summary>496 /// WriteEvent, method to write a string with event schema properties.497 /// </summary>498 /// <param name="eventDescriptor">499 /// Event Descriptor for this event.500 /// </param>501 /// <param name="data">502 /// string to log.503 /// </param>504 [System.Security.SecurityCritical]505 [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]506 public bool WriteEvent(in EventDescriptor eventDescriptor, string data)507 {508 uint status = 0;509 510 ArgumentNullException.ThrowIfNull(data);511 512 if (IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords))513 {514 if (data.Length > s_traceEventMaximumStringSize)515 {516 t_returnCode = WriteEventErrorCode.EventTooBig;517 return false;518 }519 520 EventData userData;521 522 userData.Size = (uint)((data.Length + 1) * 2);523 userData.Reserved = 0;524 525 unsafe526 {527 fixed (char* pdata = data)528 {529 Guid activityId = GetActivityId();530 userData.DataPointer = (ulong)pdata;531 532 status = UnsafeNativeMethods.EventWriteTransfer(_regHandle,533 in eventDescriptor,534 (activityId == Guid.Empty) ? null : &activityId,535 null,536 1,537 &userData);538 }539 }540 }541 542 if (status != 0)543 {544 SetLastError((int)status);545 return false;546 }547 548 return true;549 }550 551 /// <summary>552 /// WriteEvent, method to be used by generated code on a derived class.553 /// </summary>554 /// <param name="eventDescriptor">555 /// Event Descriptor for this event.556 /// </param>557 /// <param name="dataCount">558 /// number of event descriptors559 /// </param>560 /// <param name="data">561 /// pointer do the event data562 /// </param>563 [System.Security.SecurityCritical]564 protected bool WriteEvent(in EventDescriptor eventDescriptor, int dataCount, IntPtr data)565 {566 uint status = 0;567 568 unsafe569 {570 Guid activityId = GetActivityId();571 572 status = UnsafeNativeMethods.EventWriteTransfer(573 _regHandle,574 in eventDescriptor,575 (activityId == Guid.Empty) ? null : &activityId,576 null,577 (uint)dataCount,578 (void*)data);579 }580 581 if (status != 0)582 {583 SetLastError((int)status);584 return false;585 }586 587 return true;588 }589 590 /// <summary>591 /// WriteTransferEvent, method to write a parameters with event schema properties.592 /// </summary>593 /// <param name="eventDescriptor">594 /// Event Descriptor for this event.595 /// </param>596 /// <param name="relatedActivityId">597 /// </param>598 /// <param name="eventPayload">599 /// </param>600 [System.Security.SecurityCritical]601 public bool WriteTransferEvent(in EventDescriptor eventDescriptor, Guid relatedActivityId, params object[] eventPayload)602 {603 uint status = 0;604 605 if (IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords))606 {607 Guid activityId = GetActivityId();608 609 unsafe610 {611 int argCount = 0;612 EventData* userDataPtr = null;613 614 if ((eventPayload != null) && (eventPayload.Length != 0))615 {616 argCount = eventPayload.Length;617 if (argCount > s_etwMaxNumberArguments)618 {619 //620 // too many arguments to log621 //622 throw new ArgumentOutOfRangeException(nameof(eventPayload),623 string.Format(CultureInfo.CurrentCulture, DotNetEventingStrings.ArgumentOutOfRange_MaxArgExceeded, s_etwMaxNumberArguments));624 }625 626 uint totalEventSize = 0;627 int index;628 int stringIndex = 0;629 int[] stringPosition = new int[s_etwAPIMaxStringCount]; // used to keep the position of strings in the eventPayload parameter630 string[] dataString = new string[s_etwAPIMaxStringCount]; // string arrays from the eventPayload parameter631 EventData* userData = stackalloc EventData[argCount]; // allocation for the data descriptors632 userDataPtr = (EventData*)userData;633 byte* dataBuffer = stackalloc byte[s_basicTypeAllocationBufferSize * argCount]; // 16 byte for unboxing non-string argument634 byte* currentBuffer = dataBuffer;635 636 //637 // The loop below goes through all the arguments and fills in the data638 // descriptors. For strings save the location in the dataString array.639 // Calculates the total size of the event by adding the data descriptor640 // size value set in EncodeObjec method.641 //642 for (index = 0; index < eventPayload.Length; index++)643 {644 string isString;645 isString = EncodeObject(ref eventPayload[index], userDataPtr, currentBuffer);646 currentBuffer += s_basicTypeAllocationBufferSize;647 totalEventSize += userDataPtr->Size;648 userDataPtr++;649 if (isString != null)650 {651 if (stringIndex < s_etwAPIMaxStringCount)652 {653 dataString[stringIndex] = isString;654 stringPosition[stringIndex] = index;655 stringIndex++;656 }657 else658 {659 throw new ArgumentOutOfRangeException(nameof(eventPayload),660 string.Format(CultureInfo.CurrentCulture, DotNetEventingStrings.ArgumentOutOfRange_MaxStringsExceeded, s_etwAPIMaxStringCount));661 }662 }663 }664 665 if (totalEventSize > s_traceEventMaximumSize)666 {667 t_returnCode = WriteEventErrorCode.EventTooBig;668 return false;669 }670 671 fixed (char* v0 = dataString[0], v1 = dataString[1], v2 = dataString[2], v3 = dataString[3],672 v4 = dataString[4], v5 = dataString[5], v6 = dataString[6], v7 = dataString[7])673 {674 userDataPtr = (EventData*)userData;675 if (dataString[0] != null)676 {677 userDataPtr[stringPosition[0]].DataPointer = (ulong)v0;678 }679 680 if (dataString[1] != null)681 {682 userDataPtr[stringPosition[1]].DataPointer = (ulong)v1;683 }684 685 if (dataString[2] != null)686 {687 userDataPtr[stringPosition[2]].DataPointer = (ulong)v2;688 }689 690 if (dataString[3] != null)691 {692 userDataPtr[stringPosition[3]].DataPointer = (ulong)v3;693 }694 695 if (dataString[4] != null)696 {697 userDataPtr[stringPosition[4]].DataPointer = (ulong)v4;698 }699 700 if (dataString[5] != null)701 {702 userDataPtr[stringPosition[5]].DataPointer = (ulong)v5;703 }704 705 if (dataString[6] != null)706 {707 userDataPtr[stringPosition[6]].DataPointer = (ulong)v6;708 }709 710 if (dataString[7] != null)711 {712 userDataPtr[stringPosition[7]].DataPointer = (ulong)v7;713 }714 }715 }716 717 status = UnsafeNativeMethods.EventWriteTransfer(_regHandle,718 in eventDescriptor,719 (activityId == Guid.Empty) ? null : &activityId,720 (relatedActivityId == Guid.Empty) ? null : &relatedActivityId,721 (uint)argCount,722 userDataPtr);723 }724 }725 726 if (status != 0)727 {728 SetLastError((int)status);729 return false;730 }731 732 return true;733 }734 735 [System.Security.SecurityCritical]736 protected bool WriteTransferEvent(in EventDescriptor eventDescriptor, Guid relatedActivityId, int dataCount, IntPtr data)737 {738 uint status = 0;739 740 Guid activityId = GetActivityId();741 742 unsafe743 {744 status = UnsafeNativeMethods.EventWriteTransfer(745 _regHandle,746 in eventDescriptor,747 (activityId == Guid.Empty) ? null : &activityId,748 &relatedActivityId,749 (uint)dataCount,750 (void*)data);751 }752 753 if (status != 0)754 {755 SetLastError((int)status);756 return false;757 }758 759 return true;760 }761 762 [System.Security.SecurityCritical]763 private static Guid GetActivityId()764 {765 return t_activityId;766 }767 768 [System.Security.SecurityCritical]769 public static void SetActivityId(ref Guid id)770 {771 t_activityId = id;772 UnsafeNativeMethods.EventActivityIdControl((int)ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID, ref id);773 }774 775 [System.Security.SecurityCritical]776 public static Guid CreateActivityId()777 {778 Guid newId = new();779 UnsafeNativeMethods.EventActivityIdControl((int)ActivityControl.EVENT_ACTIVITY_CTRL_CREATE_ID, ref newId);780 return newId;781 }782 }783}784 