2016-03-30 14:20:52 -07:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// <copyright file="etwprovider.cs" company="Microsoft">
|
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
|
// </copyright>
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Security;
|
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
namespace System.Diagnostics.Eventing
|
|
|
|
|
{
|
|
|
|
|
public class EventProvider : IDisposable
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
[SecurityCritical]
|
2016-07-29 13:02:49 -07:00
|
|
|
private UnsafeNativeMethods.EtwEnableCallback _etwCallback; // Trace Callback function
|
|
|
|
|
private long _regHandle; // Trace Registration Handle
|
|
|
|
|
private byte _level; // Tracing Level
|
|
|
|
|
private long _anyKeywordMask; // Trace Enable Flags
|
|
|
|
|
private long _allKeywordMask; // Match all keyword
|
|
|
|
|
private int _enabled; // Enabled flag from Trace callback
|
|
|
|
|
private Guid _providerId; // Control Guid
|
|
|
|
|
private int _disposed; // when 1, provider has unregister
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
[ThreadStatic]
|
|
|
|
|
private static WriteEventErrorCode t_returnCode; // thread slot to keep last error
|
|
|
|
|
[ThreadStatic]
|
|
|
|
|
private static Guid t_activityId;
|
|
|
|
|
|
|
|
|
|
private const int s_basicTypeAllocationBufferSize = 16;
|
2016-08-28 07:16:49 +00:00
|
|
|
private const int s_etwMaxNumberArguments = 32;
|
2016-03-30 14:20:52 -07:00
|
|
|
private const int s_etwAPIMaxStringCount = 8;
|
|
|
|
|
private const int s_maxEventDataDescriptors = 128;
|
|
|
|
|
private const int s_traceEventMaximumSize = 65482;
|
|
|
|
|
private const int s_traceEventMaximumStringSize = 32724;
|
|
|
|
|
|
|
|
|
|
[SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
|
|
|
|
|
public enum WriteEventErrorCode : int
|
|
|
|
|
{
|
|
|
|
|
//check mapping to runtime codes
|
|
|
|
|
NoError = 0,
|
|
|
|
|
NoFreeBuffers = 1,
|
|
|
|
|
EventTooBig = 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Explicit, Size = 16)]
|
|
|
|
|
private struct EventData
|
|
|
|
|
{
|
|
|
|
|
[FieldOffset(0)]
|
|
|
|
|
internal ulong DataPointer;
|
|
|
|
|
[FieldOffset(8)]
|
|
|
|
|
internal uint Size;
|
|
|
|
|
[FieldOffset(12)]
|
|
|
|
|
internal int Reserved;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private enum ActivityControl : uint
|
|
|
|
|
{
|
|
|
|
|
EVENT_ACTIVITY_CTRL_GET_ID = 1,
|
|
|
|
|
EVENT_ACTIVITY_CTRL_SET_ID = 2,
|
|
|
|
|
EVENT_ACTIVITY_CTRL_CREATE_ID = 3,
|
|
|
|
|
EVENT_ACTIVITY_CTRL_GET_SET_ID = 4,
|
|
|
|
|
EVENT_ACTIVITY_CTRL_CREATE_SET_ID = 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructor for EventProvider class.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="providerGuid">
|
|
|
|
|
/// Unique GUID among all trace sources running on a system
|
|
|
|
|
/// </param>
|
|
|
|
|
[SecuritySafeCritical]
|
|
|
|
|
[SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
|
|
|
|
|
public EventProvider(Guid providerGuid)
|
2016-07-29 13:02:49 -07:00
|
|
|
{
|
|
|
|
|
_providerId = providerGuid;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// EtwRegister the ProviderId with ETW
|
|
|
|
|
//
|
|
|
|
|
EtwRegister();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This method registers the controlGuid of this class with ETW.
|
|
|
|
|
/// We need to be running on Vista or above. If not an
|
|
|
|
|
/// PlatformNotSupported exception will be thrown.
|
|
|
|
|
/// If for some reason the ETW EtwRegister call failed
|
|
|
|
|
/// a NotSupported exception will be thrown.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
private unsafe void EtwRegister()
|
|
|
|
|
{
|
|
|
|
|
uint status;
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
_etwCallback = new UnsafeNativeMethods.EtwEnableCallback(EtwEnableCallBack);
|
|
|
|
|
|
|
|
|
|
status = UnsafeNativeMethods.EventRegister(ref _providerId, _etwCallback, null, ref _regHandle);
|
2016-03-30 14:20:52 -07:00
|
|
|
if (status != 0)
|
|
|
|
|
{
|
|
|
|
|
throw new Win32Exception((int)status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// implement Dispose Pattern to early deregister from ETW insted of waiting for
|
|
|
|
|
// the finalizer to call deregistration.
|
|
|
|
|
// Once the user is done with the provider it needs to call Close() or Dispose()
|
|
|
|
|
// If neither are called the finalizer will unregister the provider anyway
|
|
|
|
|
//
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
Dispose(true);
|
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[System.Security.SecuritySafeCritical]
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// explicit cleanup is done by calling Dispose with true from
|
2016-08-26 16:46:03 -04:00
|
|
|
// Dispose() or Close(). The disposing argument is ignored because there
|
2016-03-30 14:20:52 -07:00
|
|
|
// are no unmanaged resources.
|
|
|
|
|
// The finalizer calls Dispose with false.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// check if the object has been already disposed
|
|
|
|
|
//
|
2016-07-29 13:02:49 -07:00
|
|
|
if (_disposed == 1) return;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
if (Interlocked.Exchange(ref _disposed, 1) != 0)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
2016-08-26 16:46:03 -04:00
|
|
|
// somebody is already disposing the provider
|
2016-03-30 14:20:52 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Disables Tracing in the provider, then unregister
|
|
|
|
|
//
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
_enabled = 0;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
Deregister();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This method deregisters the controlGuid of this class with ETW.
|
|
|
|
|
///
|
|
|
|
|
/// </summary>
|
|
|
|
|
public virtual void Close()
|
|
|
|
|
{
|
|
|
|
|
Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~EventProvider()
|
|
|
|
|
{
|
|
|
|
|
Dispose(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This method un-registers from ETW.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
private unsafe void Deregister()
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Unregister from ETW using the RegHandle saved from
|
|
|
|
|
// the register call.
|
|
|
|
|
//
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
if (_regHandle != 0)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
UnsafeNativeMethods.EventUnregister(_regHandle);
|
|
|
|
|
_regHandle = 0;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[System.Security.SecurityCritical]
|
2016-07-29 13:02:49 -07:00
|
|
|
private unsafe void EtwEnableCallBack(
|
2016-03-30 14:20:52 -07:00
|
|
|
[In] ref System.Guid sourceId,
|
|
|
|
|
[In] int isEnabled,
|
|
|
|
|
[In] byte setLevel,
|
|
|
|
|
[In] long anyKeyword,
|
|
|
|
|
[In] long allKeyword,
|
|
|
|
|
[In] void* filterData,
|
|
|
|
|
[In] void* callbackContext
|
|
|
|
|
)
|
|
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
_enabled = isEnabled;
|
|
|
|
|
_level = setLevel;
|
|
|
|
|
_anyKeywordMask = anyKeyword;
|
|
|
|
|
_allKeywordMask = allKeyword;
|
2016-03-30 14:20:52 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// IsEnabled, method used to test if provider is enabled
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsEnabled()
|
|
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
return (_enabled != 0) ? true : false;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// IsEnabled, method used to test if event is enabled
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="level">
|
|
|
|
|
/// Level to test
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="keywords">
|
|
|
|
|
/// Keyword to test
|
|
|
|
|
/// </param>
|
|
|
|
|
public bool IsEnabled(byte level, long keywords)
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// If not enabled at all, return false.
|
|
|
|
|
//
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
if (_enabled == 0)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This also covers the case of Level == 0.
|
2016-07-29 13:02:49 -07:00
|
|
|
if ((level <= _level) ||
|
|
|
|
|
(_level == 0))
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Check if Keyword is enabled
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
if ((keywords == 0) ||
|
2016-07-29 13:02:49 -07:00
|
|
|
(((keywords & _anyKeywordMask) != 0) &&
|
|
|
|
|
((keywords & _allKeywordMask) == _allKeywordMask)))
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WriteEventErrorCode GetLastWriteEventError()
|
|
|
|
|
{
|
|
|
|
|
return t_returnCode;
|
2016-07-29 13:02:49 -07:00
|
|
|
}
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Helper function to set the last error on the thread
|
|
|
|
|
//
|
2016-07-29 13:02:49 -07:00
|
|
|
private static void SetLastError(int error)
|
|
|
|
|
{
|
2016-03-30 14:20:52 -07:00
|
|
|
switch (error)
|
|
|
|
|
{
|
|
|
|
|
case UnsafeNativeMethods.ERROR_ARITHMETIC_OVERFLOW:
|
|
|
|
|
case UnsafeNativeMethods.ERROR_MORE_DATA:
|
|
|
|
|
t_returnCode = WriteEventErrorCode.EventTooBig;
|
|
|
|
|
break;
|
|
|
|
|
case UnsafeNativeMethods.ERROR_NOT_ENOUGH_MEMORY:
|
|
|
|
|
t_returnCode = WriteEventErrorCode.NoFreeBuffers;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
private static unsafe string EncodeObject(ref object data, EventData* dataDescriptor, byte* dataBuffer)
|
|
|
|
|
/*++
|
|
|
|
|
|
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
|
|
This routine is used by WriteEvent to unbox the object type and
|
|
|
|
|
to fill the passed in ETW data descriptor.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
|
|
data - argument to be decoded
|
|
|
|
|
|
|
|
|
|
dataDescriptor - pointer to the descriptor to be filled
|
|
|
|
|
|
|
|
|
|
dataBuffer - storage buffer for storing user data, needed because cant get the address of the object
|
|
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
|
|
null if the object is a basic type other than string. String otherwise
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Reserved = 0;
|
|
|
|
|
|
|
|
|
|
string sRet = data as string;
|
|
|
|
|
if (sRet != null)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)((sRet.Length + 1) * 2);
|
|
|
|
|
return sRet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data == null)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = 0;
|
|
|
|
|
dataDescriptor->DataPointer = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (data is IntPtr)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(IntPtr);
|
|
|
|
|
IntPtr* intptrPtr = (IntPtr*)dataBuffer;
|
|
|
|
|
*intptrPtr = (IntPtr)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)intptrPtr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is int)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(int);
|
|
|
|
|
int* intptrPtr = (int*)dataBuffer;
|
|
|
|
|
*intptrPtr = (int)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)intptrPtr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is long)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(long);
|
|
|
|
|
long* longptr = (long*)dataBuffer;
|
|
|
|
|
*longptr = (long)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)longptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is uint)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(uint);
|
|
|
|
|
uint* uintptr = (uint*)dataBuffer;
|
|
|
|
|
*uintptr = (uint)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)uintptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is UInt64)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(ulong);
|
|
|
|
|
UInt64* ulongptr = (ulong*)dataBuffer;
|
|
|
|
|
*ulongptr = (ulong)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)ulongptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is char)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(char);
|
|
|
|
|
char* charptr = (char*)dataBuffer;
|
|
|
|
|
*charptr = (char)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)charptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is byte)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(byte);
|
|
|
|
|
byte* byteptr = (byte*)dataBuffer;
|
|
|
|
|
*byteptr = (byte)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)byteptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is short)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(short);
|
|
|
|
|
short* shortptr = (short*)dataBuffer;
|
|
|
|
|
*shortptr = (short)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)shortptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is sbyte)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(sbyte);
|
|
|
|
|
sbyte* sbyteptr = (sbyte*)dataBuffer;
|
|
|
|
|
*sbyteptr = (sbyte)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)sbyteptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is ushort)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(ushort);
|
|
|
|
|
ushort* ushortptr = (ushort*)dataBuffer;
|
|
|
|
|
*ushortptr = (ushort)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)ushortptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is float)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(float);
|
|
|
|
|
float* floatptr = (float*)dataBuffer;
|
|
|
|
|
*floatptr = (float)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)floatptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is double)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(double);
|
|
|
|
|
double* doubleptr = (double*)dataBuffer;
|
|
|
|
|
*doubleptr = (double)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)doubleptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is bool)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(bool);
|
|
|
|
|
bool* boolptr = (bool*)dataBuffer;
|
|
|
|
|
*boolptr = (bool)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)boolptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is Guid)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(Guid);
|
|
|
|
|
Guid* guidptr = (Guid*)dataBuffer;
|
|
|
|
|
*guidptr = (Guid)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)guidptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is decimal)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(decimal);
|
|
|
|
|
decimal* decimalptr = (decimal*)dataBuffer;
|
|
|
|
|
*decimalptr = (decimal)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)decimalptr;
|
|
|
|
|
}
|
|
|
|
|
else if (data is Boolean)
|
|
|
|
|
{
|
|
|
|
|
dataDescriptor->Size = (uint)sizeof(Boolean);
|
|
|
|
|
Boolean* booleanptr = (Boolean*)dataBuffer;
|
|
|
|
|
*booleanptr = (Boolean)data;
|
|
|
|
|
dataDescriptor->DataPointer = (ulong)booleanptr;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//To our eyes, everything else is a just a string
|
|
|
|
|
sRet = data.ToString();
|
|
|
|
|
dataDescriptor->Size = (uint)((sRet.Length + 1) * 2);
|
|
|
|
|
return sRet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// WriteMessageEvent, method to write a string with level and Keyword.
|
|
|
|
|
/// The activity ID will be propagated only if the call stays on the same native thread as SetActivityId().
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="eventMessage">
|
|
|
|
|
/// Message to write
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="eventLevel">
|
|
|
|
|
/// Level to test
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="eventKeywords">
|
|
|
|
|
/// Keyword to test
|
|
|
|
|
/// </param>
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
public bool WriteMessageEvent(string eventMessage, byte eventLevel, long eventKeywords)
|
|
|
|
|
{
|
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
|
|
if (eventMessage == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("eventMessage");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsEnabled(eventLevel, eventKeywords))
|
|
|
|
|
{
|
|
|
|
|
if (eventMessage.Length > s_traceEventMaximumStringSize)
|
|
|
|
|
{
|
|
|
|
|
t_returnCode = WriteEventErrorCode.EventTooBig;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
unsafe
|
|
|
|
|
{
|
|
|
|
|
fixed (char* pdata = eventMessage)
|
|
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
status = (int)UnsafeNativeMethods.EventWriteString(_regHandle, eventLevel, eventKeywords, pdata);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
|
{
|
|
|
|
|
SetLastError(status);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// WriteMessageEvent, method to write a string with level=0 and Keyword=0
|
|
|
|
|
/// The activity ID will be propagated only if the call stays on the same native thread as SetActivityId().
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="eventMessage">
|
|
|
|
|
/// Message to log
|
|
|
|
|
/// </param>
|
|
|
|
|
public bool WriteMessageEvent(string eventMessage)
|
|
|
|
|
{
|
|
|
|
|
return WriteMessageEvent(eventMessage, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// WriteEvent method to write parameters with event schema properties
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="eventDescriptor">
|
|
|
|
|
/// Event Descriptor for this event.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="eventPayload">
|
|
|
|
|
/// </param>
|
2016-07-29 13:02:49 -07:00
|
|
|
public bool WriteEvent(ref EventDescriptor eventDescriptor, params object[] eventPayload)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
return WriteTransferEvent(ref eventDescriptor, Guid.Empty, eventPayload);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// WriteEvent, method to write a string with event schema properties
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="eventDescriptor">
|
|
|
|
|
/// Event Descriptor for this event.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="data">
|
|
|
|
|
/// string to log.
|
|
|
|
|
/// </param>
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
|
|
|
|
|
public bool WriteEvent(ref EventDescriptor eventDescriptor, string data)
|
|
|
|
|
{
|
|
|
|
|
uint status = 0;
|
|
|
|
|
|
|
|
|
|
if (data == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("dataString");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords))
|
|
|
|
|
{
|
|
|
|
|
if (data.Length > s_traceEventMaximumStringSize)
|
|
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
t_returnCode = WriteEventErrorCode.EventTooBig;
|
2016-03-30 14:20:52 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventData userData;
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
userData.Size = (uint)((data.Length + 1) * 2);
|
2016-03-30 14:20:52 -07:00
|
|
|
userData.Reserved = 0;
|
|
|
|
|
|
|
|
|
|
unsafe
|
|
|
|
|
{
|
|
|
|
|
fixed (char* pdata = data)
|
|
|
|
|
{
|
|
|
|
|
Guid activityId = GetActivityId();
|
|
|
|
|
userData.DataPointer = (ulong)pdata;
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
status = UnsafeNativeMethods.EventWriteTransfer(_regHandle,
|
2016-03-30 14:20:52 -07:00
|
|
|
ref eventDescriptor,
|
|
|
|
|
(activityId == Guid.Empty) ? null : &activityId,
|
|
|
|
|
null,
|
|
|
|
|
1,
|
|
|
|
|
&userData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
|
{
|
|
|
|
|
SetLastError((int)status);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// WriteEvent, method to be used by generated code on a derived class
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="eventDescriptor">
|
|
|
|
|
/// Event Descriptor for this event.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="dataCount">
|
|
|
|
|
/// number of event descriptors
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="data">
|
|
|
|
|
/// pointer do the event data
|
|
|
|
|
/// </param>
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
protected bool WriteEvent(ref EventDescriptor eventDescriptor, int dataCount, IntPtr data)
|
|
|
|
|
{
|
|
|
|
|
uint status = 0;
|
|
|
|
|
|
|
|
|
|
unsafe
|
|
|
|
|
{
|
|
|
|
|
Guid activityId = GetActivityId();
|
|
|
|
|
|
|
|
|
|
status = UnsafeNativeMethods.EventWriteTransfer(
|
2016-07-29 13:02:49 -07:00
|
|
|
_regHandle,
|
2016-03-30 14:20:52 -07:00
|
|
|
ref eventDescriptor,
|
|
|
|
|
(activityId == Guid.Empty) ? null : &activityId,
|
|
|
|
|
null,
|
|
|
|
|
(uint)dataCount,
|
|
|
|
|
(void*)data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
|
{
|
|
|
|
|
SetLastError((int)status);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// WriteTransferEvent, method to write a parameters with event schema properties
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="eventDescriptor">
|
|
|
|
|
/// Event Descriptor for this event.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="relatedActivityId">
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="eventPayload">
|
|
|
|
|
/// </param>
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
public bool WriteTransferEvent(ref EventDescriptor eventDescriptor, Guid relatedActivityId, params object[] eventPayload)
|
|
|
|
|
{
|
|
|
|
|
uint status = 0;
|
|
|
|
|
|
|
|
|
|
if (IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords))
|
|
|
|
|
{
|
|
|
|
|
Guid activityId = GetActivityId();
|
|
|
|
|
|
|
|
|
|
unsafe
|
|
|
|
|
{
|
|
|
|
|
int argCount = 0;
|
|
|
|
|
EventData* userDataPtr = null;
|
2016-07-29 13:02:49 -07:00
|
|
|
|
2016-03-30 14:20:52 -07:00
|
|
|
if ((eventPayload != null) && (eventPayload.Length != 0))
|
|
|
|
|
{
|
|
|
|
|
argCount = eventPayload.Length;
|
2016-08-28 07:16:49 +00:00
|
|
|
if (argCount > s_etwMaxNumberArguments)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
//too many arguments to log
|
|
|
|
|
//
|
|
|
|
|
throw new ArgumentOutOfRangeException("eventPayload",
|
2016-08-28 07:16:49 +00:00
|
|
|
string.Format(CultureInfo.CurrentCulture, DotNetEventingStrings.ArgumentOutOfRange_MaxArgExceeded, s_etwMaxNumberArguments));
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint totalEventSize = 0;
|
|
|
|
|
int index;
|
|
|
|
|
int stringIndex = 0;
|
|
|
|
|
int[] stringPosition = new int[s_etwAPIMaxStringCount]; //used to keep the position of strings in the eventPayload parameter
|
|
|
|
|
string[] dataString = new string[s_etwAPIMaxStringCount]; // string arrays from the eventPayload parameter
|
|
|
|
|
EventData* userData = stackalloc EventData[argCount]; // allocation for the data descriptors
|
|
|
|
|
userDataPtr = (EventData*)userData;
|
|
|
|
|
byte* dataBuffer = stackalloc byte[s_basicTypeAllocationBufferSize * argCount]; // 16 byte for unboxing non-string argument
|
|
|
|
|
byte* currentBuffer = dataBuffer;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The loop below goes through all the arguments and fills in the data
|
|
|
|
|
// descriptors. For strings save the location in the dataString array.
|
2016-08-26 16:46:03 -04:00
|
|
|
// Calculates the total size of the event by adding the data descriptor
|
2016-03-30 14:20:52 -07:00
|
|
|
// size value set in EncodeObjec method.
|
|
|
|
|
//
|
|
|
|
|
for (index = 0; index < eventPayload.Length; index++)
|
2016-07-29 13:02:49 -07:00
|
|
|
{
|
2016-03-30 14:20:52 -07:00
|
|
|
string isString;
|
|
|
|
|
isString = EncodeObject(ref eventPayload[index], userDataPtr, currentBuffer);
|
|
|
|
|
currentBuffer += s_basicTypeAllocationBufferSize;
|
|
|
|
|
totalEventSize += userDataPtr->Size;
|
|
|
|
|
userDataPtr++;
|
|
|
|
|
if (isString != null)
|
|
|
|
|
{
|
|
|
|
|
if (stringIndex < s_etwAPIMaxStringCount)
|
|
|
|
|
{
|
|
|
|
|
dataString[stringIndex] = isString;
|
|
|
|
|
stringPosition[stringIndex] = index;
|
|
|
|
|
stringIndex++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentOutOfRangeException("eventPayload",
|
|
|
|
|
string.Format(CultureInfo.CurrentCulture, DotNetEventingStrings.ArgumentOutOfRange_MaxStringsExceeded, s_etwAPIMaxStringCount));
|
|
|
|
|
}
|
2016-07-29 13:02:49 -07:00
|
|
|
}
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (totalEventSize > s_traceEventMaximumSize)
|
|
|
|
|
{
|
|
|
|
|
t_returnCode = WriteEventErrorCode.EventTooBig;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fixed (char* v0 = dataString[0], v1 = dataString[1], v2 = dataString[2], v3 = dataString[3],
|
|
|
|
|
v4 = dataString[4], v5 = dataString[5], v6 = dataString[6], v7 = dataString[7])
|
|
|
|
|
{
|
|
|
|
|
userDataPtr = (EventData*)userData;
|
|
|
|
|
if (dataString[0] != null)
|
|
|
|
|
{
|
|
|
|
|
userDataPtr[stringPosition[0]].DataPointer = (ulong)v0;
|
|
|
|
|
}
|
|
|
|
|
if (dataString[1] != null)
|
|
|
|
|
{
|
|
|
|
|
userDataPtr[stringPosition[1]].DataPointer = (ulong)v1;
|
|
|
|
|
}
|
|
|
|
|
if (dataString[2] != null)
|
|
|
|
|
{
|
|
|
|
|
userDataPtr[stringPosition[2]].DataPointer = (ulong)v2;
|
|
|
|
|
}
|
|
|
|
|
if (dataString[3] != null)
|
|
|
|
|
{
|
|
|
|
|
userDataPtr[stringPosition[3]].DataPointer = (ulong)v3;
|
|
|
|
|
}
|
|
|
|
|
if (dataString[4] != null)
|
|
|
|
|
{
|
|
|
|
|
userDataPtr[stringPosition[4]].DataPointer = (ulong)v4;
|
|
|
|
|
}
|
|
|
|
|
if (dataString[5] != null)
|
|
|
|
|
{
|
|
|
|
|
userDataPtr[stringPosition[5]].DataPointer = (ulong)v5;
|
|
|
|
|
}
|
|
|
|
|
if (dataString[6] != null)
|
|
|
|
|
{
|
|
|
|
|
userDataPtr[stringPosition[6]].DataPointer = (ulong)v6;
|
|
|
|
|
}
|
|
|
|
|
if (dataString[7] != null)
|
|
|
|
|
{
|
|
|
|
|
userDataPtr[stringPosition[7]].DataPointer = (ulong)v7;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
status = UnsafeNativeMethods.EventWriteTransfer(_regHandle,
|
2016-03-30 14:20:52 -07:00
|
|
|
ref eventDescriptor,
|
|
|
|
|
(activityId == Guid.Empty) ? null : &activityId,
|
|
|
|
|
(relatedActivityId == Guid.Empty) ? null : &relatedActivityId,
|
|
|
|
|
(uint)argCount,
|
|
|
|
|
userDataPtr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
|
{
|
|
|
|
|
SetLastError((int)status);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
protected bool WriteTransferEvent(ref EventDescriptor eventDescriptor, Guid relatedActivityId, int dataCount, IntPtr data)
|
|
|
|
|
{
|
|
|
|
|
uint status = 0;
|
2016-07-29 13:02:49 -07:00
|
|
|
|
|
|
|
|
Guid activityId = GetActivityId();
|
|
|
|
|
|
2016-03-30 14:20:52 -07:00
|
|
|
unsafe
|
2016-07-29 13:02:49 -07:00
|
|
|
{
|
2016-03-30 14:20:52 -07:00
|
|
|
status = UnsafeNativeMethods.EventWriteTransfer(
|
2016-07-29 13:02:49 -07:00
|
|
|
_regHandle,
|
2016-03-30 14:20:52 -07:00
|
|
|
ref eventDescriptor,
|
2016-07-29 13:02:49 -07:00
|
|
|
(activityId == Guid.Empty) ? null : &activityId,
|
|
|
|
|
&relatedActivityId,
|
|
|
|
|
(uint)dataCount,
|
2016-03-30 14:20:52 -07:00
|
|
|
(void*)data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
|
{
|
|
|
|
|
SetLastError((int)status);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
private static Guid GetActivityId()
|
2016-07-29 13:02:49 -07:00
|
|
|
{
|
2016-03-30 14:20:52 -07:00
|
|
|
return t_activityId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
public static void SetActivityId(ref Guid id)
|
|
|
|
|
{
|
|
|
|
|
t_activityId = id;
|
|
|
|
|
UnsafeNativeMethods.EventActivityIdControl((int)ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID, ref id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[System.Security.SecurityCritical]
|
|
|
|
|
public static Guid CreateActivityId()
|
|
|
|
|
{
|
|
|
|
|
Guid newId = new Guid();
|
|
|
|
|
UnsafeNativeMethods.EventActivityIdControl((int)ActivityControl.EVENT_ACTIVITY_CTRL_CREATE_ID, ref newId);
|
|
|
|
|
return newId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|