/// <summary> /// ETW trace sessions survive process shutdown. Thus you can attach to existing active sessions. /// GetActiveSessionNames() returns a list of currently existing session names. These can be passed /// to the code:TraceEventSession constructor to control it. /// </summary> /// <returns>A enumeration of strings, each of which is a name of a session</returns> public unsafe static IEnumerable <string> GetActiveSessionNames() { const int MAX_SESSIONS = 64; int sizeOfProperties = sizeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES) + sizeof(char) * MaxNameSize + // For log moduleFile name sizeof(char) * MaxNameSize; // For session name byte *sessionsArray = stackalloc byte[MAX_SESSIONS * sizeOfProperties]; TraceEventNativeMethods.EVENT_TRACE_PROPERTIES **propetiesArray = stackalloc TraceEventNativeMethods.EVENT_TRACE_PROPERTIES *[MAX_SESSIONS]; for (int i = 0; i < MAX_SESSIONS; i++) { TraceEventNativeMethods.EVENT_TRACE_PROPERTIES *properties = (TraceEventNativeMethods.EVENT_TRACE_PROPERTIES *) & sessionsArray[sizeOfProperties * i]; properties->Wnode.BufferSize = (uint)sizeOfProperties; properties->LoggerNameOffset = (uint)sizeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES); properties->LogFileNameOffset = (uint)sizeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES) + sizeof(char) * MaxNameSize; propetiesArray[i] = properties; } int sessionCount = 0; int hr = TraceEventNativeMethods.QueryAllTraces((IntPtr)propetiesArray, MAX_SESSIONS, ref sessionCount); Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr)); List <string> activeTraceNames = new List <string>(); for (int i = 0; i < sessionCount; i++) { byte * propertiesBlob = (byte *)propetiesArray[i]; string sessionName = new string((char *)(&propertiesBlob[propetiesArray[i]->LoggerNameOffset])); activeTraceNames.Add(sessionName); } return(activeTraceNames); }
/// <summary> /// Open an existing Windows Event Tracing Session, with name 'sessionName'. /// /// If you are opening a new session use TraceEventSession(string, string). /// /// To support the illusion that you can have a session with both kernel and user events, /// TraceEventSession might start up both a kernel and a user session. When you want to 'attach' /// to such a combined session, the constructor needs to know if you want to control the kernel /// session or not. If attachKernelSession is true, then it opens both sessions (and thus 'Close' /// will operation on both sessions. /// </summary> public TraceEventSession(string sessionName, bool attachKernelSession) { Init(sessionName); int hr = TraceEventNativeMethods.ControlTrace(0UL, sessionName, ToUnmanagedBuffer(properties, null), TraceEventNativeMethods.EVENT_TRACE_CONTROL_QUERY); Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr)); isActive = true; properties = (TraceEventNativeMethods.EVENT_TRACE_PROPERTIES)Marshal.PtrToStructure(unmanagedPropertiesBuffer, typeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES)); if (properties.LogFileNameOffset != 0) { fileName = Marshal.PtrToStringUni((IntPtr)(unmanagedPropertiesBuffer.ToInt64() + properties.LogFileNameOffset)); } if (attachKernelSession) { bool success = false; try { kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName); success = true; } finally { if (!success) { Stop(); } } } }
private unsafe bool EnableProvider(Guid providerGuid, TraceEventLevel providerLevel, ulong matchAnyKeywords, ulong matchAllKeywords, int providerDataType, byte[] providerData, int providerDataSize) { bool ret = InsureSession(); TraceEventNativeMethods.EVENT_FILTER_DESCRIPTOR *dataDescrPtr = null; fixed(byte *providerDataPtr = providerData) { string regKeyName = @"Software\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + providerGuid + "}"; byte[] registryData = null; if (providerData != null || providerDataType != 0) { TraceEventNativeMethods.EVENT_FILTER_DESCRIPTOR dataDescr = new TraceEventNativeMethods.EVENT_FILTER_DESCRIPTOR(); dataDescr.Ptr = null; dataDescr.Size = providerDataSize; dataDescr.Type = providerDataType; dataDescrPtr = &dataDescr; if (providerData == null) { providerData = new byte[0]; } else { dataDescr.Ptr = providerDataPtr; } // Set the registry key so providers get the information even if they are not active now registryData = new byte[providerDataSize + 4]; registryData[0] = (byte)(providerDataType); registryData[1] = (byte)(providerDataType >> 8); registryData[2] = (byte)(providerDataType >> 16); registryData[3] = (byte)(providerDataType >> 24); Array.Copy(providerData, 0, registryData, 4, providerDataSize); } SetOrDelete(regKeyName, "ControllerData", registryData); int hr; try { hr = TraceEventNativeMethods.EnableTraceEx(ref providerGuid, null, sessionHandle, 1, (byte)providerLevel, matchAnyKeywords, matchAllKeywords, 0, dataDescrPtr); } catch (EntryPointNotFoundException) { // Try with the old pre-vista API hr = TraceEventNativeMethods.EnableTrace(1, (int)matchAnyKeywords, (int)providerLevel, ref providerGuid, sessionHandle); } Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr)); } isActive = true; return(ret); }
public void Dispose() { if (unmanagedPropertiesBuffer != IntPtr.Zero) { TraceEventNativeMethods.FreeHGlobal(unmanagedPropertiesBuffer); } unmanagedPropertiesBuffer = IntPtr.Zero; if (sessionHandle != TraceEventNativeMethods.INVALID_HANDLE_VALUE) { TraceEventNativeMethods.CloseTrace(sessionHandle); } sessionHandle = TraceEventNativeMethods.INVALID_HANDLE_VALUE; GC.SuppressFinalize(this); }
/// <summary> /// Do intialization common to the contructors. /// </summary> private void Init(string sessionName) { this.sessionHandle = TraceEventNativeMethods.INVALID_HANDLE_VALUE; if (sessionName.Length > MaxNameSize - 1) { throw new ArgumentException("File name too long", "sessionName"); } this.sessionName = sessionName; properties = new TraceEventNativeMethods.EVENT_TRACE_PROPERTIES(); properties.Wnode.Flags = TraceEventNativeMethods.WNODE_FLAG_TRACED_GUID; properties.LoggerNameOffset = (uint)Marshal.SizeOf(typeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES)); properties.LogFileNameOffset = properties.LoggerNameOffset + MaxNameSize * sizeof(char); extentionSpaceOffset = properties.LogFileNameOffset + MaxNameSize * sizeof(char); // #sizeCalculation properties.Wnode.BufferSize = extentionSpaceOffset + 8 + 8 * 4 + 4 + maxStackTraceProviders * 4; unmanagedPropertiesBuffer = TraceEventNativeMethods.AllocHGlobal((int)properties.Wnode.BufferSize); TraceEventNativeMethods.ZeroMemory(unmanagedPropertiesBuffer, properties.Wnode.BufferSize); }
/// <summary> /// Actually starts the trace, with added logic to retry if the session already exists. /// </summary> private bool StartTrace() { bool ret = false; int dwErr = TraceEventNativeMethods.StartTrace(out sessionHandle, sessionName, ToUnmanagedBuffer(properties, fileName)); if (dwErr == 0xB7) // STIERR_HANDLEEXISTS { ret = true; Stop(); Thread.Sleep(100); // Give it some time to stop. dwErr = TraceEventNativeMethods.StartTrace(out sessionHandle, sessionName, ToUnmanagedBuffer(properties, fileName)); } if (dwErr == 5 && Environment.OSVersion.Version.Major > 5) // On Vista and we get a 'Accessed Denied' message { throw new UnauthorizedAccessException("Error Starting ETW: Access Denied (Administrator rights required to start ETW)"); } Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(dwErr)); return(ret); }
/// <summary> /// Once started, event sessions will persist even after the process that created them dies. They are /// only stoped by this explicit Stop() API. If you used both kernel and user events, consider /// using the code:StopUserAndKernelSession API instead. /// </summary> public void Stop() { if (stopped) { return; } stopped = true; int hr = TraceEventNativeMethods.ControlTrace(0UL, sessionName, ToUnmanagedBuffer(properties, null), TraceEventNativeMethods.EVENT_TRACE_CONTROL_STOP); if (hr != 4201) // Instance name not found. This means we did not start { Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr)); } if (kernelSession != null) { kernelSession.Stop(); kernelSession = null; } }