public bool EnableKernelProvider(KernelTraceEventParser.Keywords flags) { return EnableKernelProvider(flags, KernelTraceEventParser.Keywords.None); }
/// <summary> /// #EnableKernelProvider /// Enable the kernel provider for the session. If the session must be called 'NT Kernel Session'. /// <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) { bool systemTraceProvider = false; var version = Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor; if (m_SessionName != KernelTraceEventParser.KernelSessionName) { systemTraceProvider = true; if (version < 62) throw new NotSupportedException("System Tracing is only supported on Windows 8 and above."); } else { if (m_SessionHandle != TraceEventNativeMethods.INVALID_HANDLE_VALUE) throw new Exception("The kernel provider must be enabled as the only provider."); if (version < 60) throw new NotSupportedException("Kernel Event Tracing is only supported on Windows 6.0 (Vista) and above."); } // The Profile event requires the SeSystemProfilePrivilege to succeed, so set it. if ((flags & (KernelTraceEventParser.Keywords.Profile | KernelTraceEventParser.Keywords.PMCProfile)) != 0) { TraceEventNativeMethods.SetSystemProfilePrivilege(); // TODO FIX NOW never fails. if (CpuSampleIntervalMSec != 1) { if (!TraceEventNativeMethods.CanSetCpuSamplingRate()) throw new ApplicationException("Changing the CPU sampling rate is currently not supported on this OS."); } var cpu100ns = (CpuSampleIntervalMSec * 10000.0 + .5); // The API seems to have an upper bound of 1 second. if (cpu100ns >= int.MaxValue || ((int)cpu100ns) > 10000000) throw new ApplicationException("CPU Sampling rate is too high."); var succeeded = TraceEventNativeMethods.SetCpuSamplingRate((int)cpu100ns); // Always try to set, since it may not be the default if (!succeeded && CpuSampleIntervalMSec != 1.0F) throw new InvalidOperationException("Can't set CPU sampling to " + CpuSampleIntervalMSec.ToString("f3") + "Msec."); } var propertiesBuff = stackalloc byte[PropertiesSize]; var properties = GetProperties(propertiesBuff); // Initialize the stack collecting information const int stackTracingIdsMax = 96; int numIDs = 0; var stackTracingIds = stackalloc TraceEventNativeMethods.STACK_TRACING_EVENT_ID[stackTracingIdsMax]; if (stackCapture != KernelTraceEventParser.Keywords.None) numIDs = SetStackTraceIds(stackCapture, stackTracingIds, stackTracingIdsMax); bool ret = false; int dwErr; try { if (systemTraceProvider) { properties->LogFileMode = properties->LogFileMode | TraceEventNativeMethods.EVENT_TRACE_SYSTEM_LOGGER_MODE; InsureStarted(properties); dwErr = TraceEventNativeMethods.TraceSetInformation(m_SessionHandle, TraceEventNativeMethods.TRACE_INFO_CLASS.TraceStackTracingInfo, stackTracingIds, (numIDs * sizeof(TraceEventNativeMethods.STACK_TRACING_EVENT_ID))); Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(dwErr)); ulong* systemTraceFlags = stackalloc ulong[1]; systemTraceFlags[0] = (ulong)(flags & ~KernelTraceEventParser.Keywords.NonOSKeywords); dwErr = TraceEventNativeMethods.TraceSetInformation(m_SessionHandle, TraceEventNativeMethods.TRACE_INFO_CLASS.TraceSystemTraceEnableFlagsInfo, systemTraceFlags, sizeof(ulong)); Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(dwErr)); ret = true; } else { properties->Wnode.Guid = KernelTraceEventParser.ProviderGuid; properties->EnableFlags = (uint)flags; dwErr = StartKernelTrace(out m_SessionHandle, properties, stackTracingIds, numIDs); if (dwErr == 0xB7) // STIERR_HANDLEEXISTS { ret = true; Stop(); m_Stopped = false; Thread.Sleep(100); // Give it some time to stop. dwErr = StartKernelTrace(out m_SessionHandle, properties, stackTracingIds, numIDs); } } } 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 easiestC:\Users\vancem\Documents\etw\traceEvent\TraceEventSession.cs // 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)); m_IsActive = true; if (version >= 62 && StackCompression) TraceEventNativeMethods.EnableStackCaching(m_SessionHandle); return ret; }
/// <summary> /// Given a mask of kernel flags, set the array stackTracingIds of size stackTracingIdsMax to match. /// It returns the number of entries in stackTracingIds that were filled in. /// </summary> private unsafe int SetStackTraceIds(KernelTraceEventParser.Keywords stackCapture, TraceEventNativeMethods.STACK_TRACING_EVENT_ID* stackTracingIds, int stackTracingIdsMax) { int curID = 0; // PerfInfo (sample profiling) if ((stackCapture & KernelTraceEventParser.Keywords.Profile) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.PerfInfoTaskGuid; stackTracingIds[curID].Type = 0x2e; // Sample Profile curID++; } // PCM sample profiling if ((stackCapture & KernelTraceEventParser.Keywords.PMCProfile) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.PerfInfoTaskGuid; stackTracingIds[curID].Type = 0x2f; // PMC Sample Profile curID++; } if ((stackCapture & KernelTraceEventParser.Keywords.SystemCall) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.PerfInfoTaskGuid; stackTracingIds[curID].Type = 0x33; // SysCall curID++; } // Thread if ((stackCapture & KernelTraceEventParser.Keywords.Thread) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.ThreadTaskGuid; stackTracingIds[curID].Type = 0x01; // Thread Create curID++; } if ((stackCapture & KernelTraceEventParser.Keywords.ContextSwitch) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.ThreadTaskGuid; stackTracingIds[curID].Type = 0x24; // Context Switch curID++; } if ((stackCapture & KernelTraceEventParser.Keywords.Dispatcher) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.ThreadTaskGuid; stackTracingIds[curID].Type = 0x32; // Ready Thread curID++; } // Image if ((stackCapture & KernelTraceEventParser.Keywords.ImageLoad) != 0) { // Confirm this is not ImageTaskGuid stackTracingIds[curID].EventGuid = KernelTraceEventParser.ProcessTaskGuid; stackTracingIds[curID].Type = 0x0A; // Image Load curID++; } // Process if ((stackCapture & KernelTraceEventParser.Keywords.Process) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.ProcessTaskGuid; stackTracingIds[curID].Type = 0x01; // Process Create curID++; } // Disk if ((stackCapture & KernelTraceEventParser.Keywords.DiskIOInit) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.DiskIoTaskGuid; stackTracingIds[curID].Type = 0x0c; // Read Init curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.DiskIoTaskGuid; stackTracingIds[curID].Type = 0x0d; // Write Init curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.DiskIoTaskGuid; stackTracingIds[curID].Type = 0x0f; // Flush Init curID++; } // Virtual Alloc if ((stackCapture & KernelTraceEventParser.Keywords.VirtualAlloc) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.VirtualAllocTaskGuid; stackTracingIds[curID].Type = 0x62; // Flush Init curID++; } // Hard Faults if ((stackCapture & KernelTraceEventParser.Keywords.MemoryHardFaults) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid; stackTracingIds[curID].Type = 0x20; // Hard Fault curID++; } // Page Faults if ((stackCapture & KernelTraceEventParser.Keywords.MemoryPageFaults) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid; stackTracingIds[curID].Type = 0x0A; // Transition Fault curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid; stackTracingIds[curID].Type = 0x0B; // Demand zero Fault curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid; stackTracingIds[curID].Type = 0x0C; // Copy on Write Fault curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid; stackTracingIds[curID].Type = 0x0D; // Guard Page Fault curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid; stackTracingIds[curID].Type = 0x0E; // Hard Page Fault curID++; // TODO these look interesting. // ! %02 49 ! Pagefile Mapped Section Create // ! %02 69 ! Pagefile Backed Image Mapping // ! %02 71 ! Contiguous Memory Generation } if ((stackCapture & KernelTraceEventParser.Keywords.FileIOInit) != 0) { // TODO allow stacks only on open and close; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x40; // Create curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x41; // Cleanup curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x42; // Close curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x43; // Read curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x44; // Write curID++; } if ((stackCapture & KernelTraceEventParser.Keywords.Registry) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x0A; // NtCreateKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x0B; // NtOpenKey curID++; } // TODO put these in for advanced procedure calls. //! %1A 21 ! ALPC: SendMessage //! %1A 22 ! ALPC: ReceiveMessage //! %1A 23 ! ALPC: WaitForReply //! %1A 24 ! ALPC: WaitForNewMessage //! %1A 25 ! ALPC: UnWait // I don't have heap or threadpool. // Confirm we did not overflow. Debug.Assert(curID <= stackTracingIdsMax); return curID; }