Exemplo n.º 1
0
        /// <summary>
        /// This routine is only useful/valid for real-time sessions.
        ///
        /// TraceEvent.TimeStamp internally is stored using a high resolution clock called the Query Performance Counter (QPC).
        /// This clock is INDEPENDENT of the system clock used by DateTime.   These two clocks are synchronized to within 2 msec at
        /// session startup but they can drift from there (typically 2msec / min == 3 seconds / day).   Thus if you have long
        /// running real time session it becomes problematic to compare the timestamps with those in another session or something
        /// timestamped with the system clock.   SynchronizeClock will synchronize the TraceEvent.Timestamp clock with the system
        /// clock again.   If you do this right before you start another session, then the two sessions will be within 2 msec of
        /// each other, and their timestamps will correlate.     Doing it periodically (e.g. hourly), will keep things reasonably close.
        ///
        /// TODO: we can achieve perfect synchronization by exposing the QPC tick sync point so we could read the sync point
        /// from one session and set that exact sync point for another session.
        /// </summary>
        public void SynchronizeClock()
        {
            if (!IsRealTime)
            {
                throw new InvalidOperationException("SynchronizeClock is only for Real-Time Sessions");
            }
            DateTime utcNow = DateTime.UtcNow;

            _syncTimeQPC = QPCTime.GetUTCTimeAsQPC(utcNow);
            _syncTimeUTC = utcNow;
        }
Exemplo n.º 2
0
        // Process is called after all desired subscriptions have been registered.
        /// <summary>
        /// Processes all the events in the data source, issuing callbacks that were subscribed to.  See
        /// #Introduction for more
        /// </summary>
        /// <returns>false If StopProcesing was called</returns>
        // [SecuritySafeCritical]
        public override bool Process()
        {
            stopProcessing = false;
            if (processTraceCalled)
            {
                Reset();
            }

            processTraceCalled = true;

            if (fileNames != null)
            {
                foreach (var fileName in fileNames)
                {
                    if (handles != null)
                    {
                        Debug.Assert(handles.Length == 1);
                        if (handles[0] != TraceEventNativeMethods.INVALID_HANDLE_VALUE)
                        {
                            TraceEventNativeMethods.CloseTrace(handles[0]);
                        }
                    }

                    Initialize(fileName, TraceEventSourceType.FileOnly);
                    if (!ProcessOneFile())
                    {
                        OnCompleted();
                        Debug.Assert(sessionEndTimeQPC != long.MaxValue);       // Not a real time session
                        return(false);
                    }
                }
                OnCompleted();
                Debug.Assert(sessionEndTimeQPC != long.MaxValue);       // Not a real time session
                return(true);
            }
            else
            {
                var ret = ProcessOneFile();

                // If the session is real time, set he sessionEndTime (since the session is stopping).
                if (sessionEndTimeQPC == long.MaxValue)
                {
                    sessionEndTimeQPC = QPCTime.GetUTCTimeAsQPC(DateTime.UtcNow);
                }

                OnCompleted();
                return(ret);
            }
        }
Exemplo n.º 3
0
        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;
                }
            };
        }