public void Start() { try { m_TraceEventSession = new TraceEventSession(SessionName, null); m_TraceEventSession.EnableKernelProvider(GetKeyEvents()); m_TraceEventSource = new ETWTraceEventSource(SessionName, TraceEventSourceType.Session); m_TraceEventSource.Kernel.All += new Action<TraceEvent>(Kernel_All); Running = true; } catch (Exception ex) { Stop(); throw; } }
/// <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); // TODO enumerate providers in session and turn them off #if false string regKeyName = @"Software\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + providerGuid + "}"; Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(regKeyName, true); regKey.DeleteValue("ControllerData", false); regKey.Close(); #endif 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; } }
/// <summary> /// TraceEventSessions may have both a kernel session and a user session turned on. To simplify /// error handling, call code:StopSession to stop both. This is equivalent it attaching to the /// combined session and calling Stop, but also works in all error cases (if it was possible to stop /// the sessions they are stopped), and is silent if the sessions are already stopped. /// </summary> /// <param name="userSessionName">The name of the user session to stop</param> public static void StopUserAndKernelSession(string userSessionName) { Exception eToThow = null; try { TraceEventSession kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName); kernelSession.Stop(); } catch (COMException e) { if ((uint)e.ErrorCode != 0x80071069) // Could not find provider, that is OK. eToThow = e; } catch (Exception e) { eToThow = e; // we will throw this later. } try { TraceEventSession userSession = new TraceEventSession(userSessionName); userSession.Stop(); } catch (COMException e) { if ((uint)e.ErrorCode != 0x80071069) // Could not find provider, that is OK throw; } // If we got an error closing down the kernel provider, throw it here. if (eToThow != null) throw eToThow; }
/// <summary> /// #EnableKernelProvider /// Enable the kernel provider for the session. If the session name is 'NT Kernel Session' then it /// operates on that. This can be used to manipuate the kernel session. If the name is not 'NT /// Kernel Session' AND it is a moduleFile based session, then it tries to approximate attaching the /// kernel session by creating another session logs to [basename].kernel.etl. There is support in /// ETWTraceEventSource for looking for these files automatically, which give a good illusion that /// you can have a session that has both kernel and user events turned on. /// <param name="flags"> /// Specifies the particular kernel events of interest</param> /// <param name="stackCapture"> /// Specifies which events should have their eventToStack traces captured too (VISTA only)</param> /// <returns>Returns true if the session had existed before and is now restarted</returns> /// </summary> public unsafe bool EnableKernelProvider(KernelTraceEventParser.Keywords flags, KernelTraceEventParser.Keywords stackCapture) { if (sessionName != KernelTraceEventParser.KernelSessionName) { if (kernelSession != null) throw new Exception("A kernel session is already active."); if (string.IsNullOrEmpty(FileName)) throw new Exception("Cannot enable kernel events to a real time session unless it is named " + KernelTraceEventParser.KernelSessionName); string kernelFileName = System.IO.Path.ChangeExtension(FileName, ".kernel.etl"); kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, kernelFileName); return kernelSession.EnableKernelProvider(flags, stackCapture); } if (sessionHandle != TraceEventNativeMethods.INVALID_HANDLE_VALUE) throw new Exception("The kernel provider must be enabled as the only provider."); properties.Wnode.Guid = KernelTraceEventParser.ProviderGuid; if (Environment.OSVersion.Version.Major <= 5) { // TODO should we fail, or should we silently ignore? if (stackCapture != KernelTraceEventParser.Keywords.None) throw new Exception("Stack trace capture only available on Windows 6 (VISTA) and above."); KernelTraceEventParser.Keywords vistaOnlyFlags = KernelTraceEventParser.Keywords.ProcessCounters | KernelTraceEventParser.Keywords.ContextSwitch | KernelTraceEventParser.Keywords.Interrupt | KernelTraceEventParser.Keywords.DiskIOInit | KernelTraceEventParser.Keywords.Driver | KernelTraceEventParser.Keywords.Profile | KernelTraceEventParser.Keywords.FileIO | KernelTraceEventParser.Keywords.FileIOInit | KernelTraceEventParser.Keywords.Dispatcher | KernelTraceEventParser.Keywords.VirtualAlloc; KernelTraceEventParser.Keywords setVistaFlags = flags & vistaOnlyFlags; if (setVistaFlags != KernelTraceEventParser.Keywords.None) throw new Exception("A Kernel Event Flags {" + setVistaFlags.ToString() + "} specified that is not supported on Pre-VISTA OSes."); properties.EnableFlags = (uint) flags; return StartTrace(); } // Initialize the stack collecting information const int stackTracingIdsMax = 96; int curID = 0; var stackTracingIds = stackalloc TraceEventNativeMethods.STACK_TRACING_EVENT_ID[stackTracingIdsMax]; #if DEBUG // Try setting all flags, if we overflow an assert in SetStackTraceIds will fire. SetStackTraceIds((KernelTraceEventParser.Keywords)(-1), stackTracingIds, stackTracingIdsMax); #endif if (stackCapture != KernelTraceEventParser.Keywords.None) curID = SetStackTraceIds(stackCapture, stackTracingIds, stackTracingIdsMax); // The Profile event requires the SeSystemProfilePrivilege to succeed, so set it. if ((flags & KernelTraceEventParser.Keywords.Profile) != 0) TraceEventNativeMethods.SetSystemProfilePrivilege(); bool ret = false; properties.EnableFlags = (uint)flags; int dwErr; try { dwErr = TraceEventNativeMethods.StartKernelTrace(out sessionHandle, ToUnmanagedBuffer(properties, fileName), stackTracingIds, curID); if (dwErr == 0xB7) // STIERR_HANDLEEXISTS { ret = true; Stop(); Thread.Sleep(100); // Give it some time to stop. dwErr = TraceEventNativeMethods.StartKernelTrace(out sessionHandle, ToUnmanagedBuffer(properties, fileName), stackTracingIds, curID); } } catch (BadImageFormatException) { // We use a small native DLL called KernelTraceControl that needs to be // in the same directory as the EXE that used TraceEvent.dll. Unlike IL // Native DLLs are specific to a processor type (32 or 64 bit) so the easiest // way to insure this is that the EXE that uses TraceEvent is built for 32 bit // and that you use the 32 bit version of KernelTraceControl.dll throw new BadImageFormatException("Could not load KernelTraceControl.dll (likely 32-64 bit process mismatch)"); } catch (DllNotFoundException) { // In order to start kernel session, we need a support DLL called KernelTraceControl.dll // This DLL is available by downloading the XPERF.exe tool (see // http://msdn.microsoft.com/en-us/performance/cc825801.aspx for instructions) // It is recommended that you get the 32 bit version of this (it works on 64 bit machines) // and build your EXE that uses TraceEvent to launch as a 32 bit application (This is // the default for VS 2010 projects). throw new DllNotFoundException("KernelTraceControl.dll missing from distribution."); } 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> /// 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(); } } }