/// <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> /// 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++; } if ((stackCapture & KernelTraceEventParser.Keywords.SystemCall) != 0) { stackTracingIds[curID].EventGuid = KernelTraceEventParser.PerfInfoTaskGuid; stackTracingIds[curID].Type = 0x33; // SysCall curID++; } // TODO SysCall? // 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; if ((stackCapture & KernelTraceEventParser.Keywords.Thread) != 0) 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++; // ! %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 false stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x45; // SetInformation curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x46; // Delete curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x47; // Rename curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x48; // DirEnum curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x49; // Flush curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x4A; // QueryInformation curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x4B; // FSControl curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid; stackTracingIds[curID].Type = 0x4D; // DirNotify curID++; #endif } 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++; #if false stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x0C; // NtDeleteKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x0D; // NtQueryKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x0E; // NtSetValueKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x0F; // NtDeleteValueKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x10; // NtQueryValueKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x11; // NtEnumerateKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x12; // NtEnumerateValueKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x13; // NtQueryMultipleValueKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x14; // NtSetInformationKey curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x15; // NtFlushKey curID++; // TODO What are these? stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x16; // KcbCreate curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x17; // KcbDelete curID++; stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid; stackTracingIds[curID].Type = 0x1A; // VirtualizeKey curID++; #endif } // Confirm we did not overflow. Debug.Assert(curID <= stackTracingIdsMax); return curID; }
/// <summary> /// Shortcut that enables the kernel provider with no eventToStack trace capturing. /// See code:#EnableKernelProvider (flags, stackCapture) /// </summary> public bool EnableKernelProvider(KernelTraceEventParser.Keywords flags) { return EnableKernelProvider(flags, KernelTraceEventParser.Keywords.None); }