private void Initialize(string fileOrSessionName, TraceEventSourceType type) { // Allocate the LOGFILE and structures and arrays that hold them // Figure out how many log files we have if (type == TraceEventSourceType.MergeAll) { List <string> allLogFiles = GetMergeAllLogFiles(fileOrSessionName); logFiles = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW[allLogFiles.Count]; for (int i = 0; i < allLogFiles.Count; i++) { logFiles[i].LogFileName = allLogFiles[i]; } } else { logFiles = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW[1]; if (type == TraceEventSourceType.FileOnly) { logFiles[0].LogFileName = fileOrSessionName; } else { Debug.Assert(type == TraceEventSourceType.Session); logFiles[0].LoggerName = fileOrSessionName; logFiles[0].LogFileMode |= TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE; IsRealTime = true; } } InitializeFiles(); }
/// <summary> /// Open multiple etl files as one trace for processing. /// </summary> /// <param name="fileOrSessionName"> /// <param name="type">If type == MergeAll, call Initialize.</param> // [SecuritySafeCritical] public ETWTraceEventSource(IEnumerable <string> fileNames, TraceEventSourceType type) { if (type == TraceEventSourceType.MergeAll) { Initialize(fileNames); } else { this.fileNames = fileNames; } }
public static Task <bool> RealTimeTraceEventSessionAsync ( string providerName , string sessionName , string tracingFileName = null , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSessionOptions traceEventSessionOptions = TraceEventSessionOptions.Create , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { return (Task .Factory .StartNew <bool> ( () => { return RealTimeTraceEventSession ( providerName , sessionName , tracingFileName , traceEvents , onOneEventTracedOnceProcessAction , traceEventProviderOptions , traceEventSessionOptions , traceEventSourceType , traceEventLevel , matchKeywords , needCountHits ); } , TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach )); }
public bool RealTimeTraceEventSession ( string providerName , string sessionName , string tracingFileName = null , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSessionOptions traceEventSessionOptions = TraceEventSessionOptions.Create , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false) { var r = false; if ( traceEvents != null && traceEvents.Length > 0 && onOneEventTracedOnceProcessAction != null ) { r = TraceEventsHelper .RealTimeTraceEventSession ( providerName , sessionName , tracingFileName , traceEvents , onOneEventTracedOnceProcessAction , traceEventProviderOptions , traceEventSessionOptions , traceEventSourceType , traceEventLevel , matchKeywords , needCountHits ); } return(r); }
public Task <bool> TraceETWTraceEventSourceAsync ( string providerName , string tracedFileName , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { return (Task .Factory .StartNew <bool> ( () => { return TraceETWTraceEventSource ( providerName , tracedFileName , traceEvents , onOneEventTracedOnceProcessAction , traceEventProviderOptions , traceEventSourceType , traceEventLevel , matchKeywords , needCountHits ); } )); }
/// <summary> /// Create an ETWReloggerTraceEventSource that can takes its input from a variety of sources (either a single file, /// a set of files, or a real time ETW session (based on 'type'), and can write these events to a new ETW output /// file 'outputFileName. /// </summary> public ETWReloggerTraceEventSource(string fileOrSessionName, TraceEventSourceType type, string outputFileName) : base() { var version = Environment.OSVersion.Version.Major * 10 + Environment.OSVersion.Version.Minor; if (version < 62) { throw new NotSupportedException("System Tracing is only supported on Windows 8 and above."); } m_relogger = new CTraceRelogger(); if (type == TraceEventSourceType.FileOnly) { m_traceHandleForFirstStream = m_relogger.AddLogfileTraceStream(fileOrSessionName, IntPtr.Zero); } else if (type == TraceEventSourceType.Session) { m_traceHandleForFirstStream = m_relogger.AddRealtimeTraceStream(fileOrSessionName, IntPtr.Zero); } else { Debug.Assert(type == TraceEventSourceType.MergeAll); List <string> logFileNames = ETWTraceEventSource.GetMergeAllLogFiles(fileOrSessionName); bool first = true; foreach (var logFileName in logFileNames) { var handle = m_relogger.AddLogfileTraceStream(logFileName, IntPtr.Zero); if (first) { m_traceHandleForFirstStream = handle; first = false; } } } m_relogger.SetOutputFilename(outputFileName); m_myCallbacks = new ReloggerCallbacks(this); m_relogger.RegisterCallback(m_myCallbacks); }
public static bool TraceETWTraceEventSource ( string providerName , string tracedFileName , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { var r = false; if (!(TraceEventSession.IsElevated() ?? false)) { Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return r; } if ( traceEvents != null && traceEvents.Length > 0 && onOneEventTracedOnceProcessAction != null ) { using (var source = new ETWTraceEventSource(tracedFileName, traceEventSourceType)) { //闭包 long sequence = 0; RegisterCallbacks ( providerName , traceEvents , source , null , (x, y, z) => { long id = 0; if (needCountHits) { id = Interlocked.Increment(ref sequence); } onOneEventTracedOnceProcessAction ( id , x , y , z ); } ); source.Process(); // call the callbacks for each event } } return true; }
public bool TraceETWTraceEventSource ( string providerName , string tracedFileName , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { var r = false; if ( traceEvents != null && traceEvents.Length > 0 && onOneEventTracedOnceProcessAction != null ) { r = TraceEventsHelper .TraceETWTraceEventSource ( providerName , tracedFileName , traceEvents , onOneEventTracedOnceProcessAction , traceEventProviderOptions , traceEventSourceType , traceEventLevel , matchKeywords , needCountHits ); } return r; }
public Task<bool> TraceETWTraceEventSourceAsync ( string providerName , string tracedFileName , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { return Task .Factory .StartNew<bool> ( () => { return TraceETWTraceEventSource ( providerName , tracedFileName , traceEvents , onOneEventTracedOnceProcessAction , traceEventProviderOptions , traceEventSourceType , traceEventLevel , matchKeywords , needCountHits ); } ); }
/// <summary> /// Open a ETW event source for processing. This can either be a moduleFile or a real time ETW session /// </summary> /// <param name="fileOrSessionName"> /// If type == ModuleFile this is the name of the moduleFile to open. /// If type == Session this is the name of real time session to open.</param> /// <param name="type"></param> // [SecuritySafeCritical] public ETWTraceEventSource(string fileOrSessionName, TraceEventSourceType type) { Initialize(fileOrSessionName, type); }
private void Initialize(string fileOrSessionName, TraceEventSourceType type) { // Allocate the LOGFILE and structures and arrays that hold them // Figure out how many log files we have if (type == TraceEventSourceType.MergeAll) { List <string> allLogFiles = GetMergeAllLogFiles(fileOrSessionName); logFiles = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW[allLogFiles.Count]; for (int i = 0; i < allLogFiles.Count; i++) { logFiles[i].LogFileName = allLogFiles[i]; } } else { logFiles = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW[1]; if (type == TraceEventSourceType.FileOnly) { logFiles[0].LogFileName = fileOrSessionName; } else { Debug.Assert(type == TraceEventSourceType.Session); logFiles[0].LoggerName = fileOrSessionName; logFiles[0].LogFileMode |= TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE; IsRealTime = true; } } handles = new ulong[logFiles.Length]; // Fill out the first log file information (we will clone it later if we have multiple files). logFiles[0].BufferCallback = this.TraceEventBufferCallback; handles[0] = TraceEventNativeMethods.INVALID_HANDLE_VALUE; useClassicETW = !OperatingSystemVersion.AtLeast(OperatingSystemVersion.Vista); if (useClassicETW) { IntPtr mem = Marshal.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD)); TraceEventNativeMethods.ZeroMemory(mem, sizeof(TraceEventNativeMethods.EVENT_RECORD)); convertedHeader = (TraceEventNativeMethods.EVENT_RECORD *)mem; logFiles[0].EventCallback = RawDispatchClassic; } else { logFiles[0].LogFileMode |= TraceEventNativeMethods.PROCESS_TRACE_MODE_EVENT_RECORD; logFiles[0].EventCallback = RawDispatch; } // We want the raw timestamp because it is needed to match up stacks with the event they go with. logFiles[0].LogFileMode |= TraceEventNativeMethods.PROCESS_TRACE_MODE_RAW_TIMESTAMP; // Copy the information to any additional log files for (int i = 1; i < logFiles.Length; i++) { logFiles[i].BufferCallback = logFiles[0].BufferCallback; logFiles[i].EventCallback = logFiles[0].EventCallback; logFiles[i].LogFileMode = logFiles[0].LogFileMode; handles[i] = handles[0]; } DateTime minSessionStartTimeUTC = DateTime.MaxValue; DateTime maxSessionEndTimeUTC = DateTime.MinValue + new TimeSpan(1 * 365, 0, 0, 0); // TO avoid roundoff error when converting to QPC add a year. // Open all the traces for (int i = 0; i < handles.Length; i++) { handles[i] = TraceEventNativeMethods.OpenTrace(ref logFiles[i]); if (handles[i] == TraceEventNativeMethods.INVALID_HANDLE_VALUE) { Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error()); } // Start time is minimum of all start times DateTime logFileStartTimeUTC = SafeFromFileTimeUtc(logFiles[i].LogfileHeader.StartTime); DateTime logFileEndTimeUTC = SafeFromFileTimeUtc(logFiles[i].LogfileHeader.EndTime); if (logFileStartTimeUTC < minSessionStartTimeUTC) { minSessionStartTimeUTC = logFileStartTimeUTC; } // End time is maximum of all start times if (logFileEndTimeUTC > maxSessionEndTimeUTC) { maxSessionEndTimeUTC = logFileEndTimeUTC; } // TODO do we even need log pointer size anymore? // We take the max pointer size. if ((int)logFiles[i].LogfileHeader.PointerSize > pointerSize) { pointerSize = (int)logFiles[i].LogfileHeader.PointerSize; } } _QPCFreq = logFiles[0].LogfileHeader.PerfFreq; if (_QPCFreq == 0) { _QPCFreq = Stopwatch.Frequency; } // Real time providers don't set this to something useful if ((logFiles[0].LogFileMode & TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE) != 0) { DateTime nowUTC = DateTime.UtcNow; long nowQPC = QPCTime.GetUTCTimeAsQPC(nowUTC); _syncTimeQPC = nowQPC; _syncTimeUTC = nowUTC; sessionStartTimeQPC = nowQPC - _QPCFreq / 10; // Subtract 1/10 sec to keep now and nowQPC in sync. sessionEndTimeQPC = long.MaxValue; // Represents infinity. Debug.Assert(SessionStartTime < SessionEndTime); } else { _syncTimeUTC = minSessionStartTimeUTC; // UTCDateTimeToQPC is actually going to give the wrong value for these because we have // not set _syncTimeQPC, but will be adjusted when we see the event Header and know _syncTypeQPC. sessionStartTimeQPC = this.UTCDateTimeToQPC(minSessionStartTimeUTC); sessionEndTimeQPC = this.UTCDateTimeToQPC(maxSessionEndTimeUTC); } Debug.Assert(_QPCFreq != 0); if (pointerSize == 0) // Real time does not set this (grrr). { pointerSize = sizeof(IntPtr); Debug.Assert((logFiles[0].LogFileMode & TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE) != 0); } Debug.Assert(pointerSize == 4 || pointerSize == 8); cpuSpeedMHz = (int)logFiles[0].LogfileHeader.CpuSpeedInMHz; numberOfProcessors = (int)logFiles[0].LogfileHeader.NumberOfProcessors; // We ask for raw timestamps, but the log file may have used system time as its raw timestamp. // SystemTime is like a QPC time that happens 10M times a second (100ns). // ReservedFlags is actually the ClockType 0 = Raw, 1 = QPC, 2 = SystemTimne 3 = CpuTick (we don't support) if (logFiles[0].LogfileHeader.ReservedFlags == 2) // If ClockType == EVENT_TRACE_CLOCK_SYSTEMTIME { _QPCFreq = 10000000; } Debug.Assert(_QPCFreq != 0); int ver = (int)logFiles[0].LogfileHeader.Version; osVersion = new Version((byte)ver, (byte)(ver >> 8)); // Logic for looking up process names processNameForID = new Dictionary <int, string>(); var kernelParser = new KernelTraceEventParser(this, KernelTraceEventParser.ParserTrackingOptions.None); kernelParser.ProcessStartGroup += delegate(ProcessTraceData data) { // Get just the file name without the extension. Can't use the 'Path' class because // it tests to make certain it does not have illegal chars etc. Since KernelImageFileName // is not a true user mode path, we can get failures. string path = data.KernelImageFileName; int startIdx = path.LastIndexOf('\\'); if (0 <= startIdx) { startIdx++; } else { startIdx = 0; } int endIdx = path.LastIndexOf('.'); if (endIdx <= startIdx) { endIdx = path.Length; } processNameForID[data.ProcessID] = path.Substring(startIdx, endIdx - startIdx); }; kernelParser.ProcessEndGroup += delegate(ProcessTraceData data) { processNameForID.Remove(data.ProcessID); }; kernelParser.EventTraceHeader += delegate(EventTraceHeaderTraceData data) { if (_syncTimeQPC == 0) { // In merged files there can be more of these, we only set the QPC time on the first one // We were using a 'start location' of 0, but we want it to be the timestamp of this events, so we add this to our // existing QPC values. _syncTimeQPC = data.TimeStampQPC; sessionStartTimeQPC += data.TimeStampQPC; sessionEndTimeQPC += data.TimeStampQPC; } }; }
public ETWTraceEventSource(string fileOrSessionName, TraceEventSourceType type) { long now = DateTime.Now.ToFileTime() - 100000; // subtract 10ms to avoid negative times. primaryLogFile = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW(); primaryLogFile.BufferCallback = this.TraceEventBufferCallback; useClassicETW = Environment.OSVersion.Version.Major < 6; if (useClassicETW) { IntPtr mem = TraceEventNativeMethods.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD)); TraceEventNativeMethods.ZeroMemory(mem, (uint)sizeof(TraceEventNativeMethods.EVENT_RECORD)); convertedHeader = (TraceEventNativeMethods.EVENT_RECORD*)mem; primaryLogFile.EventCallback = RawDispatchClassic; } else { primaryLogFile.LogFileMode = TraceEventNativeMethods.PROCESS_TRACE_MODE_EVENT_RECORD; primaryLogFile.EventCallback = RawDispatch; } if (type == TraceEventSourceType.Session) { primaryLogFile.LoggerName = fileOrSessionName; primaryLogFile.LogFileMode |= TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE; } else { if (type == TraceEventSourceType.UserAndKernelFile) { // See if we have also have kernel log moduleFile. if (fileOrSessionName.Length > 4 && string.Compare(fileOrSessionName, fileOrSessionName.Length - 4, ".etl", 0, 4, StringComparison.OrdinalIgnoreCase) == 0) { string kernelFileName = fileOrSessionName.Substring(0, fileOrSessionName.Length - 4) + ".kernel.etl"; if (File.Exists(kernelFileName)) { if (File.Exists(fileOrSessionName)) { handles = new ulong[2]; handles[0] = TraceEventNativeMethods.INVALID_HANDLE_VALUE; kernelModeLogFile = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW(); kernelModeLogFile.BufferCallback = primaryLogFile.BufferCallback; kernelModeLogFile.EventCallback = primaryLogFile.EventCallback; kernelModeLogFile.LogFileName = kernelFileName; kernelModeLogFile.LogFileMode = primaryLogFile.LogFileMode; handles[1] = TraceEventNativeMethods.OpenTrace(ref kernelModeLogFile); if (TraceEventNativeMethods.INVALID_HANDLE_VALUE == handles[1]) Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error()); } else { // we have ONLY a *.kernel.etl moduleFile, treat it as the primary moduleFile. fileOrSessionName = kernelFileName; } } } if (!File.Exists(fileOrSessionName)) throw new FileNotFoundException("Unable to find the file " + fileOrSessionName); } primaryLogFile.LogFileName = fileOrSessionName; } // Open the main data source if (handles == null) handles = new ulong[1]; handles[0] = TraceEventNativeMethods.OpenTrace(ref primaryLogFile); if (TraceEventNativeMethods.INVALID_HANDLE_VALUE == handles[0]) Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error()); // Session offset time is the minimum of all times. sessionStartTime100ns = primaryLogFile.LogfileHeader.StartTime; if (handles.Length == 2 && kernelModeLogFile.LogfileHeader.StartTime < sessionStartTime100ns) sessionStartTime100ns = kernelModeLogFile.LogfileHeader.StartTime; // Real time providers don't set this to something useful if (sessionStartTime100ns == 0) sessionStartTime100ns = now; sessionEndTime100ns = primaryLogFile.LogfileHeader.EndTime; if (handles.Length == 2 && sessionEndTime100ns < kernelModeLogFile.LogfileHeader.EndTime) sessionEndTime100ns = kernelModeLogFile.LogfileHeader.EndTime; if (sessionEndTime100ns == 0) sessionEndTime100ns = sessionStartTime100ns; // TODO remove when we do per-event stuff. pointerSize = (int)primaryLogFile.LogfileHeader.PointerSize; if (handles.Length == 2) pointerSize = (int) kernelModeLogFile.LogfileHeader.PointerSize; if (pointerSize == 0) { pointerSize = sizeof(IntPtr); Debug.Assert((primaryLogFile.LogFileMode & TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE) != 0); } Debug.Assert(pointerSize == 4 || pointerSize == 8); eventsLost = (int)primaryLogFile.LogfileHeader.EventsLost; cpuSpeedMHz = (int)primaryLogFile.LogfileHeader.CpuSpeedInMHz; numberOfProcessors = (int)primaryLogFile.LogfileHeader.NumberOfProcessors; // Logic for looking up process names processNameForID = new Dictionary<int, string>(); Kernel.ProcessStartGroup += delegate(ProcessTraceData data) { // Get just the file name without the extension. Can't use the 'Path' class because // it tests to make certain it does not have illegal chars etc. Since KernelImageFileName // is not a true user mode path, we can get failures. string path = data.KernelImageFileName; int startIdx = path.LastIndexOf('\\'); if (0 <= startIdx) startIdx++; else startIdx = 0; int endIdx = path.LastIndexOf('.', startIdx); if (endIdx < 0) endIdx = path.Length; processNameForID[data.ProcessID] = path.Substring(startIdx, endIdx - startIdx); }; }
public static Task<bool> RealTimeTraceEventSessionAsync ( string providerName , string sessionName , string tracingFileName = null , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSessionOptions traceEventSessionOptions = TraceEventSessionOptions.Create , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { return Task .Factory .StartNew<bool> ( () => { return RealTimeTraceEventSession ( providerName , sessionName , tracingFileName , traceEvents , onOneEventTracedOnceProcessAction , traceEventProviderOptions , traceEventSessionOptions , traceEventSourceType , traceEventLevel , matchKeywords , needCountHits ); } , TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach ); }
public static bool TraceETWTraceEventSource ( string providerName , string tracedFileName , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { var r = false; if (!(TraceEventSession.IsElevated() ?? false)) { Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return(r); } if ( traceEvents != null && traceEvents.Length > 0 && onOneEventTracedOnceProcessAction != null ) { using (var source = new ETWTraceEventSource(tracedFileName, traceEventSourceType)) { //闭包 long sequence = 0; RegisterCallbacks ( providerName , traceEvents , source , null , (x, y, z) => { long id = 0; if (needCountHits) { id = Interlocked.Increment(ref sequence); } onOneEventTracedOnceProcessAction ( id , x , y , z ); } ); source.Process(); // call the callbacks for each event } } return(true); }
/// <summary> /// Open a ETW event source for processing. This can either be a moduleFile or a real time ETW session /// </summary> /// <param name="fileOrSessionName"> /// If type == ModuleFile this is the name of the moduleFile to open. /// If type == Session this is the name of real time sessing to open.</param> /// <param name="type"></param> // [SecuritySafeCritical] public ETWTraceEventSource(string fileOrSessionName, TraceEventSourceType type) { long now = DateTime.Now.ToFileTime() - 100000; // used as the start time for real time sessions (sub 10msec to avoid negative times) // Allocate the LOGFILE and structures and arrays that hold them // Figure out how many log files we have if (type == TraceEventSourceType.MergeAll) { string fileBaseName = Path.GetFileNameWithoutExtension(fileOrSessionName); string dir = Path.GetDirectoryName(fileOrSessionName); if (dir.Length == 0) dir = "."; List<string> allLogFiles = new List<string>(); allLogFiles.AddRange(Directory.GetFiles(dir, fileBaseName + ".etl")); allLogFiles.AddRange(Directory.GetFiles(dir, fileBaseName + ".kernel.etl")); allLogFiles.AddRange(Directory.GetFiles(dir, fileBaseName + ".clr*.etl")); allLogFiles.AddRange(Directory.GetFiles(dir, fileBaseName + ".user*.etl")); if (allLogFiles.Count == 0) throw new FileNotFoundException("Could not find file " + fileOrSessionName); logFiles = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW[allLogFiles.Count]; for (int i = 0; i < allLogFiles.Count; i++) logFiles[i].LogFileName = allLogFiles[i]; } else { logFiles = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW[1]; if (type == TraceEventSourceType.FileOnly) logFiles[0].LogFileName = fileOrSessionName; else { Debug.Assert(type == TraceEventSourceType.Session); logFiles[0].LoggerName = fileOrSessionName; logFiles[0].LogFileMode |= TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE; } } handles = new ulong[logFiles.Length]; // Fill out the first log file information (we will clone it later if we have mulitple files). logFiles[0].BufferCallback = this.TraceEventBufferCallback; handles[0] = TraceEventNativeMethods.INVALID_HANDLE_VALUE; useClassicETW = Environment.OSVersion.Version.Major < 6; if (useClassicETW) { IntPtr mem = Marshal.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD)); TraceEventNativeMethods.ZeroMemory(mem, (uint)sizeof(TraceEventNativeMethods.EVENT_RECORD)); convertedHeader = (TraceEventNativeMethods.EVENT_RECORD*)mem; logFiles[0].EventCallback = RawDispatchClassic; } else { logFiles[0].LogFileMode |= TraceEventNativeMethods.PROCESS_TRACE_MODE_EVENT_RECORD; logFiles[0].EventCallback = RawDispatch; } // We want the raw timestamp because it is needed to match up stacks with the event they go with. logFiles[0].LogFileMode |= TraceEventNativeMethods.PROCESS_TRACE_MODE_RAW_TIMESTAMP; // Copy the information to any additional log files for (int i = 1; i < logFiles.Length; i++) { logFiles[i].BufferCallback = logFiles[0].BufferCallback; logFiles[i].EventCallback = logFiles[0].EventCallback; logFiles[i].LogFileMode = logFiles[0].LogFileMode; handles[i] = handles[0]; } sessionStartTime100ns = long.MaxValue; sessionEndTime100ns = long.MinValue; eventsLost = 0; // Open all the traces for (int i = 0; i < handles.Length; i++) { handles[i] = TraceEventNativeMethods.OpenTrace(ref logFiles[i]); if (handles[i] == TraceEventNativeMethods.INVALID_HANDLE_VALUE) Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error()); // Start time is minimum of all start times if (logFiles[i].LogfileHeader.StartTime < sessionStartTime100ns) sessionStartTime100ns = logFiles[i].LogfileHeader.StartTime; // End time is maximum of all start times if (logFiles[i].LogfileHeader.EndTime > sessionEndTime100ns) sessionEndTime100ns = logFiles[i].LogfileHeader.EndTime; // TODO do we even need log pointer size anymore? // We take the max pointer size. if ((int)logFiles[i].LogfileHeader.PointerSize > pointerSize) pointerSize = (int)logFiles[i].LogfileHeader.PointerSize; eventsLost += (int)logFiles[i].LogfileHeader.EventsLost; } // Real time providers don't set this to something useful if (sessionStartTime100ns == 0) sessionStartTime100ns = now; if (sessionEndTime100ns == 0) sessionEndTime100ns = long.MaxValue; if (pointerSize == 0) // Real time does not set this (grrr). { pointerSize = sizeof(IntPtr); Debug.Assert((logFiles[0].LogFileMode & TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE) != 0); } Debug.Assert(pointerSize == 4 || pointerSize == 8); cpuSpeedMHz = (int)logFiles[0].LogfileHeader.CpuSpeedInMHz; numberOfProcessors = (int)logFiles[0].LogfileHeader.NumberOfProcessors; _QPCFreq = logFiles[0].LogfileHeader.PerfFreq; if (_QPCFreq == 0) // Real time does not set this all the time { _QPCFreq = Stopwatch.Frequency; Debug.Assert((logFiles[0].LogFileMode & TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE) != 0); } Debug.Assert(_QPCFreq != 0); int ver = (int) logFiles[0].LogfileHeader.Version; osVersion = new Version((byte) ver, (byte) (ver>> 8)); // Logic for looking up process names processNameForID = new Dictionary<int, string>(); Kernel.ProcessStartGroup += delegate(ProcessTraceData data) { // Get just the file name without the extension. Can't use the 'Path' class because // it tests to make certain it does not have illegal chars etc. Since KernelImageFileName // is not a true user mode path, we can get failures. string path = data.KernelImageFileName; int startIdx = path.LastIndexOf('\\'); if (0 <= startIdx) startIdx++; else startIdx = 0; int endIdx = path.LastIndexOf('.'); if (endIdx <= startIdx) endIdx = path.Length; processNameForID[data.ProcessID] = path.Substring(startIdx, endIdx - startIdx); }; }
/// <summary> /// Open a ETW event source for processing. This can either be a moduleFile or a real time ETW session /// </summary> /// <param name="fileOrSessionName"> /// If type == ModuleFile this is the name of the moduleFile to open. /// If type == Session this is the name of real time sessing to open.</param> /// <param name="type"></param> // [SecuritySafeCritical] public ETWTraceEventSource(string fileOrSessionName, TraceEventSourceType type) { long now = DateTime.Now.ToFileTime() - 100000; // used as the start time for real time sessions (sub 10msec to avoid negative times) // Allocate the LOGFILE and structures and arrays that hold them // Figure out how many log files we have if (type == TraceEventSourceType.MergeAll) { string fileBaseName = Path.GetFileNameWithoutExtension(fileOrSessionName); string dir = Path.GetDirectoryName(fileOrSessionName); if (dir.Length == 0) { dir = "."; } List <string> allLogFiles = new List <string>(); allLogFiles.AddRange(Directory.GetFiles(dir, fileBaseName + ".etl")); allLogFiles.AddRange(Directory.GetFiles(dir, fileBaseName + ".kernel.etl")); allLogFiles.AddRange(Directory.GetFiles(dir, fileBaseName + ".clr*.etl")); allLogFiles.AddRange(Directory.GetFiles(dir, fileBaseName + ".user*.etl")); if (allLogFiles.Count == 0) { throw new FileNotFoundException("Could not find file " + fileOrSessionName); } logFiles = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW[allLogFiles.Count]; for (int i = 0; i < allLogFiles.Count; i++) { logFiles[i].LogFileName = allLogFiles[i]; } } else { logFiles = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW[1]; if (type == TraceEventSourceType.FileOnly) { logFiles[0].LogFileName = fileOrSessionName; } else { Debug.Assert(type == TraceEventSourceType.Session); logFiles[0].LoggerName = fileOrSessionName; logFiles[0].LogFileMode |= TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE; } } handles = new ulong[logFiles.Length]; // Fill out the first log file information (we will clone it later if we have mulitple files). logFiles[0].BufferCallback = this.TraceEventBufferCallback; handles[0] = TraceEventNativeMethods.INVALID_HANDLE_VALUE; useClassicETW = Environment.OSVersion.Version.Major < 6; if (useClassicETW) { IntPtr mem = Marshal.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD)); TraceEventNativeMethods.ZeroMemory(mem, (uint)sizeof(TraceEventNativeMethods.EVENT_RECORD)); convertedHeader = (TraceEventNativeMethods.EVENT_RECORD *)mem; logFiles[0].EventCallback = RawDispatchClassic; } else { logFiles[0].LogFileMode |= TraceEventNativeMethods.PROCESS_TRACE_MODE_EVENT_RECORD; logFiles[0].EventCallback = RawDispatch; } // We want the raw timestamp because it is needed to match up stacks with the event they go with. logFiles[0].LogFileMode |= TraceEventNativeMethods.PROCESS_TRACE_MODE_RAW_TIMESTAMP; // Copy the information to any additional log files for (int i = 1; i < logFiles.Length; i++) { logFiles[i].BufferCallback = logFiles[0].BufferCallback; logFiles[i].EventCallback = logFiles[0].EventCallback; logFiles[i].LogFileMode = logFiles[0].LogFileMode; handles[i] = handles[0]; } sessionStartTime100ns = long.MaxValue; sessionEndTime100ns = long.MinValue; eventsLost = 0; // Open all the traces for (int i = 0; i < handles.Length; i++) { handles[i] = TraceEventNativeMethods.OpenTrace(ref logFiles[i]); if (handles[i] == TraceEventNativeMethods.INVALID_HANDLE_VALUE) { Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error()); } // Start time is minimum of all start times if (logFiles[i].LogfileHeader.StartTime < sessionStartTime100ns) { sessionStartTime100ns = logFiles[i].LogfileHeader.StartTime; } // End time is maximum of all start times if (logFiles[i].LogfileHeader.EndTime > sessionEndTime100ns) { sessionEndTime100ns = logFiles[i].LogfileHeader.EndTime; } // TODO do we even need log pointer size anymore? // We take the max pointer size. if ((int)logFiles[i].LogfileHeader.PointerSize > pointerSize) { pointerSize = (int)logFiles[i].LogfileHeader.PointerSize; } eventsLost += (int)logFiles[i].LogfileHeader.EventsLost; } // Real time providers don't set this to something useful if (sessionStartTime100ns == 0) { sessionStartTime100ns = now; } if (sessionEndTime100ns == 0) { sessionEndTime100ns = long.MaxValue; } if (pointerSize == 0) // Real time does not set this (grrr). { pointerSize = sizeof(IntPtr); Debug.Assert((logFiles[0].LogFileMode & TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE) != 0); } Debug.Assert(pointerSize == 4 || pointerSize == 8); cpuSpeedMHz = (int)logFiles[0].LogfileHeader.CpuSpeedInMHz; numberOfProcessors = (int)logFiles[0].LogfileHeader.NumberOfProcessors; _QPCFreq = logFiles[0].LogfileHeader.PerfFreq; if (_QPCFreq == 0) // Real time does not set this all the time { _QPCFreq = Stopwatch.Frequency; Debug.Assert((logFiles[0].LogFileMode & TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE) != 0); } Debug.Assert(_QPCFreq != 0); int ver = (int)logFiles[0].LogfileHeader.Version; osVersion = new Version((byte)ver, (byte)(ver >> 8)); // Logic for looking up process names processNameForID = new Dictionary <int, string>(); Kernel.ProcessStartGroup += delegate(ProcessTraceData data) { // Get just the file name without the extension. Can't use the 'Path' class because // it tests to make certain it does not have illegal chars etc. Since KernelImageFileName // is not a true user mode path, we can get failures. string path = data.KernelImageFileName; int startIdx = path.LastIndexOf('\\'); if (0 <= startIdx) { startIdx++; } else { startIdx = 0; } int endIdx = path.LastIndexOf('.'); if (endIdx <= startIdx) { endIdx = path.Length; } processNameForID[data.ProcessID] = path.Substring(startIdx, endIdx - startIdx); }; }
public static bool RealTimeTraceEventSession ( string providerName , string sessionName , string tracingFileName = null , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSessionOptions traceEventSessionOptions = TraceEventSessionOptions.Create , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { var r = false; if (!(TraceEventSession.IsElevated() ?? false)) { Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return(r); } var needTracingFile = !string.IsNullOrEmpty(tracingFileName); if ( traceEvents != null && traceEvents.Length > 0 && onOneEventTracedOnceProcessAction != null ) { using ( var session = ( needTracingFile ? new TraceEventSession ( sessionName , tracingFileName , traceEventSessionOptions ) { StopOnDispose = true } : new TraceEventSession ( sessionName , traceEventSessionOptions ) { StopOnDispose = true } ) ) { using ( var source = ( needTracingFile ? new ETWTraceEventSource(tracingFileName) : session.Source ) ) { long sequence = 0; RegisterCallbacks ( providerName , traceEvents , source , session , (x, y, z) => { long id = 0; if (needCountHits) { id = Interlocked.Increment(ref sequence); } onOneEventTracedOnceProcessAction ( id , x , y , z ); } ); var restarted = session .EnableProvider ( providerName , traceEventLevel , matchKeywords , traceEventProviderOptions ); source .Process(); r = true; } } } return(r); }
public static bool RealTimeTraceEventSession ( string providerName , string sessionName , string tracingFileName = null , string[] traceEvents = null , Action < long , TraceEventDispatcher , TraceEventSession , TraceEvent > onOneEventTracedOnceProcessAction = null , TraceEventProviderOptions traceEventProviderOptions = null , TraceEventSessionOptions traceEventSessionOptions = TraceEventSessionOptions.Create , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll , TraceEventLevel traceEventLevel = TraceEventLevel.Always , ulong matchKeywords = ulong.MaxValue , bool needCountHits = false ) { var r = false; if (!(TraceEventSession.IsElevated() ?? false)) { Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process."); return r; } var needTracingFile = !string.IsNullOrEmpty(tracingFileName); if ( traceEvents != null && traceEvents.Length > 0 && onOneEventTracedOnceProcessAction != null ) { using ( var session = ( needTracingFile ? new TraceEventSession ( sessionName , tracingFileName , traceEventSessionOptions ) { StopOnDispose = true } : new TraceEventSession ( sessionName , traceEventSessionOptions ) { StopOnDispose = true } ) ) { using ( var source = ( needTracingFile ? new ETWTraceEventSource(tracingFileName) : session.Source ) ) { long sequence = 0; RegisterCallbacks ( providerName , traceEvents , source , session , (x, y, z) => { long id = 0; if (needCountHits) { id = Interlocked.Increment(ref sequence); } onOneEventTracedOnceProcessAction ( id , x , y , z ); } ); var restarted = session .EnableProvider ( providerName , traceEventLevel , matchKeywords , traceEventProviderOptions ); source .Process(); r = true; } } } return r; }
public ETWTraceEventSource(string fileOrSessionName, TraceEventSourceType type) { long now = DateTime.Now.ToFileTime() - 100000; // subtract 10ms to avoid negative times. primaryLogFile = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW(); primaryLogFile.BufferCallback = this.TraceEventBufferCallback; useClassicETW = Environment.OSVersion.Version.Major < 6; if (useClassicETW) { IntPtr mem = TraceEventNativeMethods.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD)); TraceEventNativeMethods.ZeroMemory(mem, (uint)sizeof(TraceEventNativeMethods.EVENT_RECORD)); convertedHeader = (TraceEventNativeMethods.EVENT_RECORD *)mem; primaryLogFile.EventCallback = RawDispatchClassic; } else { primaryLogFile.LogFileMode = TraceEventNativeMethods.PROCESS_TRACE_MODE_EVENT_RECORD; primaryLogFile.EventCallback = RawDispatch; } if (type == TraceEventSourceType.Session) { primaryLogFile.LoggerName = fileOrSessionName; primaryLogFile.LogFileMode |= TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE; } else { if (type == TraceEventSourceType.UserAndKernelFile) { // See if we have also have kernel log moduleFile. if (fileOrSessionName.Length > 4 && string.Compare(fileOrSessionName, fileOrSessionName.Length - 4, ".etl", 0, 4, StringComparison.OrdinalIgnoreCase) == 0) { string kernelFileName = fileOrSessionName.Substring(0, fileOrSessionName.Length - 4) + ".kernel.etl"; if (File.Exists(kernelFileName)) { if (File.Exists(fileOrSessionName)) { handles = new ulong[2]; handles[0] = TraceEventNativeMethods.INVALID_HANDLE_VALUE; kernelModeLogFile = new TraceEventNativeMethods.EVENT_TRACE_LOGFILEW(); kernelModeLogFile.BufferCallback = primaryLogFile.BufferCallback; kernelModeLogFile.EventCallback = primaryLogFile.EventCallback; kernelModeLogFile.LogFileName = kernelFileName; kernelModeLogFile.LogFileMode = primaryLogFile.LogFileMode; handles[1] = TraceEventNativeMethods.OpenTrace(ref kernelModeLogFile); if (TraceEventNativeMethods.INVALID_HANDLE_VALUE == handles[1]) { Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error()); } } else { // we have ONLY a *.kernel.etl moduleFile, treat it as the primary moduleFile. fileOrSessionName = kernelFileName; } } } if (!File.Exists(fileOrSessionName)) { throw new FileNotFoundException("Unable to find the file " + fileOrSessionName); } } primaryLogFile.LogFileName = fileOrSessionName; } // Open the main data source if (handles == null) { handles = new ulong[1]; } handles[0] = TraceEventNativeMethods.OpenTrace(ref primaryLogFile); if (TraceEventNativeMethods.INVALID_HANDLE_VALUE == handles[0]) { Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error()); } // Session offset time is the minimum of all times. sessionStartTime100ns = primaryLogFile.LogfileHeader.StartTime; if (handles.Length == 2 && kernelModeLogFile.LogfileHeader.StartTime < sessionStartTime100ns) { sessionStartTime100ns = kernelModeLogFile.LogfileHeader.StartTime; } // Real time providers don't set this to something useful if (sessionStartTime100ns == 0) { sessionStartTime100ns = now; } sessionEndTime100ns = primaryLogFile.LogfileHeader.EndTime; if (handles.Length == 2 && sessionEndTime100ns < kernelModeLogFile.LogfileHeader.EndTime) { sessionEndTime100ns = kernelModeLogFile.LogfileHeader.EndTime; } if (sessionEndTime100ns == 0) { sessionEndTime100ns = sessionStartTime100ns; } pointerSize = (int)primaryLogFile.LogfileHeader.PointerSize; if (handles.Length == 2) { pointerSize = (int)kernelModeLogFile.LogfileHeader.PointerSize; } if (pointerSize == 0) { pointerSize = sizeof(IntPtr); Debug.Assert((primaryLogFile.LogFileMode & TraceEventNativeMethods.EVENT_TRACE_REAL_TIME_MODE) != 0); } Debug.Assert(pointerSize == 4 || pointerSize == 8); eventsLost = (int)primaryLogFile.LogfileHeader.EventsLost; cpuSpeedMHz = (int)primaryLogFile.LogfileHeader.CpuSpeedInMHz; numberOfProcessors = (int)primaryLogFile.LogfileHeader.NumberOfProcessors; // Logic for looking up process names processNameForID = new Dictionary <int, string>(); Kernel.ProcessStartGroup += delegate(ProcessTraceData data) { // Get just the file name without the extension. Can't use the 'Path' class because // it tests to make certain it does not have illegal chars etc. Since KernelImageFileName // is not a true user mode path, we can get failures. string path = data.KernelImageFileName; int startIdx = path.LastIndexOf('\\'); if (0 <= startIdx) { startIdx++; } else { startIdx = 0; } int endIdx = path.LastIndexOf('.', startIdx); if (endIdx < 0) { endIdx = path.Length; } processNameForID[data.ProcessID] = path.Substring(startIdx, endIdx - startIdx); }; }