Exemplo n.º 1
0
        private void Reset()
        {
            if (!CanReset)
            {
                throw new InvalidOperationException("Event stream is not resetable (e.g. real time).");
            }

            for (int i = 0; i < handles.Length; i++)
            {
                if (handles[i] != TraceEventNativeMethods.INVALID_HANDLE_VALUE)
                {
                    TraceEventNativeMethods.CloseTrace(handles[i]);
                    handles[i] = TraceEventNativeMethods.INVALID_HANDLE_VALUE;
                }
                // Annoying.  The OS resets the LogFileMode field, so I have to set it up again.
                if (!useClassicETW)
                {
                    logFiles[i].LogFileMode  = TraceEventNativeMethods.PROCESS_TRACE_MODE_EVENT_RECORD;
                    logFiles[i].LogFileMode |= TraceEventNativeMethods.PROCESS_TRACE_MODE_RAW_TIMESTAMP;
                }

                handles[i] = TraceEventNativeMethods.OpenTrace(ref logFiles[i]);

                if (handles[i] == TraceEventNativeMethods.INVALID_HANDLE_VALUE)
                {
                    Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error());
                }
            }
        }
        public bool InitSymbolResolver(SymbolResolverContextInfo context)
        {
            symbolReader = new DefaultSymbolReader(context);
            contextInfo  = context;
            TraceEventNativeMethods.SymSetOptions(
                TraceEventNativeMethods.SymOptions.SYMOPT_DEBUG |
                TraceEventNativeMethods.SymOptions.SYMOPT_CASE_INSENSITIVE |
                // TraceEventNativeMethods.SymOptions.SYMOPT_DEFERRED_LOADS |
                //TraceEventNativeMethods.SymOptions.SYMOPT_LOAD_LINES |
                TraceEventNativeMethods.SymOptions.SYMOPT_EXACT_SYMBOLS |
                TraceEventNativeMethods.SymOptions.SYMOPT_UNDNAME // undecorated names
                );


            // for testing purpose
            Environment.SetEnvironmentVariable("_NT_SYMBOL_PATH", @"SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols");

            bool bInit = TraceEventNativeMethods.SymInitializeW(contextInfo.currentProcessHandle, null, false);

            if (bInit)
            {
                registerCallback = new TraceEventNativeMethods.SymRegisterCallbackProc(SymRegisterCallbackProcInfo);
                TraceEventNativeMethods.SymRegisterCallbackW64(contextInfo.currentProcessHandle, registerCallback, 0);
            }
            return(bInit);
        }
Exemplo n.º 3
0
        public EventPipeEventSourceV1(Deserializer deserializer, string fileName, int version)
            : base(deserializer)
        {
            // V1 EventPipe doesn't have process info.
            // Since it's from a single process, use the file name as the process name.
            _processName = Path.GetFileNameWithoutExtension(fileName);
            _processId   = 0;
            _version     = version;

            // V1 EventPipe doesn't have osVersion, cpuSpeedMHz, pointerSize, numberOfProcessors
            osVersion          = new Version("0.0.0.0");
            cpuSpeedMHz        = 10;
            pointerSize        = 8; // V1 EventPipe only supports Linux which is x64 only.
            numberOfProcessors = 1;

            // We need to read the header to get the sync time information here
            ReadEventPipeTimeInfo();

            IntPtr mem = Marshal.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD));

            TraceEventNativeMethods.ZeroMemory(mem, sizeof(TraceEventNativeMethods.EVENT_RECORD));
            _header = (TraceEventNativeMethods.EVENT_RECORD *)mem;

            _eventParser = new EventPipeTraceEventParser(this);
        }
        private void symbolParser_DbgIDRSDSTraceData(DbgIDRSDSData obj)
        {
            string pdbFile = obj.PdbFileName;
            Guid   pdbGuid = obj.GuidSig;

            if (!symbolFiles.ContainsKey(pdbFile))
            {
                // unsafe, but OK to do for now,
                // other options is to Marshal.AllocHGlobal.
                // other option is to use fixed in unsafe code
                // other option ....?1

                GCHandle handle = GCHandle.Alloc(pdbGuid, GCHandleType.Pinned);

                IntPtr guidPtr = handle.AddrOfPinnedObject();

                //
                StringBuilder pdbFullPath = new StringBuilder(1024);
                // Try To locate the symbol file.

                //tmm.pdbWith Guid2d244915-4bd6-4d74-a496-8877396f5510
                bool foundPDB = TraceEventNativeMethods.SymFindFileInPathW(contextInfo.currentProcessHandle,
                                                                           null,
                                                                           pdbFile,
                                                                           guidPtr,
                                                                           (int)obj.Age,
                                                                           0,
                                                                           TraceEventNativeMethods.SSRVOPT_GUIDPTR,
                                                                           pdbFullPath,
                                                                           null,
                                                                           IntPtr.Zero);
                int lastError = Marshal.GetLastWin32Error();
                if (lastError == 0x2) // path not found
                {
                    Console.WriteLine("Can't find symbol for " + pdbFile);
                    Console.WriteLine("are you sure _NT_SYMBOL_PATH is set?"); // check if _NT_SYMBOL_PATH is set
                }

                if (foundPDB)
                {
                    PDBInfo info = new PDBInfo()
                    {
                        pdbFullPath = pdbFullPath.ToString(), pdbImageBase = (ulong)obj.ImageBase
                    };
                    symbolFiles.Add(pdbFile, info);
                }
                else
                {
                    symbolFiles.Add(pdbFile, null);
                }
                handle.Free();
            }
        }
Exemplo n.º 5
0
        public override bool Process()
        {
            if (processTraceCalled)
            {
                Reset();
            }
            processTraceCalled = true;
            stopProcessing     = false;
            int dwErr = TraceEventNativeMethods.ProcessTrace(handles, (uint)handles.Length, (IntPtr)0, (IntPtr)0);

            Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(dwErr));
            return(!stopProcessing);
        }
Exemplo n.º 6
0
 protected override void Dispose(bool disposing)
 {
     if (handles != null)
     {
         foreach (ulong handle in handles)
         {
             if (handle != TraceEventNativeMethods.INVALID_HANDLE_VALUE)
             {
                 TraceEventNativeMethods.CloseTrace(handle);
             }
         }
         handles = null;
     }
     base.Dispose(disposing);
     GC.SuppressFinalize(this);
 }
        public unsafe ulong LoadSymModule(string moduleName, ulong moduleBase)
        {
            // given a module file name *.exe  or .dll, we will try to find a matching .PDB symbol and load it.
            Console.WriteLine("Trying to load symbol for file" + moduleName);
            // lookup the image name
            string moduleFileName = System.IO.Path.GetFileName(moduleName);
            string pdbFileName    = System.IO.Path.ChangeExtension(moduleFileName, ".pdb");

            if (symbolFiles.ContainsKey(pdbFileName))
            {
                PDBInfo info = symbolFiles[pdbFileName];
                if (info != null)
                {
                    return(TraceEventNativeMethods.SymLoadModuleExW(contextInfo.currentProcessHandle,
                                                                    IntPtr.Zero, info.pdbFullPath, null, info.pdbImageBase, (uint)0x10000000, null, (uint)0));
                }
            }
            return(0);
        }
Exemplo n.º 8
0
        private void Reset()
        {
            if (!CanReset)
            {
                throw new InvalidOperationException("Event stream is not resetable (eg Real time)");
            }

            // Annoying.  The OS resets the LogFileMode field, so I have to set it up again.
            if (!useClassicETW)
            {
                primaryLogFile.LogFileMode = TraceEventNativeMethods.PROCESS_TRACE_MODE_EVENT_RECORD;
            }

            if (handles.Length > 1)
            {
                if (handles[1] != TraceEventNativeMethods.INVALID_HANDLE_VALUE)
                {
                    TraceEventNativeMethods.CloseTrace(handles[1]);
                }
                kernelModeLogFile.LogFileMode = primaryLogFile.LogFileMode;
                handles[1] = TraceEventNativeMethods.OpenTrace(ref kernelModeLogFile);

                if (TraceEventNativeMethods.INVALID_HANDLE_VALUE == handles[1])
                {
                    Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error());
                }
            }

            if (handles[0] != TraceEventNativeMethods.INVALID_HANDLE_VALUE)
            {
                TraceEventNativeMethods.CloseTrace(handles[0]);
            }
            handles[0] = TraceEventNativeMethods.OpenTrace(ref primaryLogFile);

            if (TraceEventNativeMethods.INVALID_HANDLE_VALUE == handles[0])
            {
                Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRForLastWin32Error());
            }
        }
Exemplo n.º 9
0
        // Process is called after all desired subscriptions have been registered.
        /// <summary>
        /// Processes all the events in the data soruce, issuing callbacks that were subscribed to.  See
        /// code:#Introduction for more
        /// </summary>
        /// <returns>false If StopProcesing was called</returns>
        // [SecuritySafeCritical]
        public override bool Process()
        {
            if (processTraceCalled)
            {
                Reset();
            }
            processTraceCalled = true;
            stopProcessing     = false;
            int dwErr = TraceEventNativeMethods.ProcessTrace(handles, (uint)handles.Length, (IntPtr)0, (IntPtr)0);

            if (dwErr == 6)
            {
                throw new ApplicationException("Error opening ETL file.  Most likely caused by opening a Win8 Trace on a Pre Win8 OS.");
            }

            // ETW returns 1223 when you stop processing explicitly
            if (!(dwErr == 1223 && stopProcessing))
            {
                Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(dwErr));
            }

            return(!stopProcessing);
        }
Exemplo n.º 10
0
        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);
            };
        }
Exemplo n.º 11
0
        private unsafe string GetXml()
        {
            int uncompressedSize = GetInt32At(4);

            if (0x10000 <= uncompressedSize)
            {
                return("");
            }

            byte[] uncompressedData = new byte[uncompressedSize];
            fixed(byte *uncompressedPtr = uncompressedData)
            {
                byte *compressedData = ((byte *)DataStart) + 8; // Skip header (State + UncompressedLength)
                int   compressedSize = EventDataLength - 8;     // Compressed size is total size minus header.

                int resultSize = 0;
                int hr         = TraceEventNativeMethods.RtlDecompressBuffer(
                    TraceEventNativeMethods.COMPRESSION_FORMAT_LZNT1 | TraceEventNativeMethods.COMPRESSION_ENGINE_MAXIMUM,
                    uncompressedPtr,
                    uncompressedSize,
                    compressedData,
                    compressedSize,
                    out resultSize);

                if (hr == 0 && resultSize == uncompressedSize)
                {
                    var indent = 0;
                    // PrettyPrint the XML
                    char *        charPtr    = (Char *)uncompressedPtr;
                    StringBuilder sb         = new StringBuilder();
                    char *        charEnd    = &charPtr[uncompressedSize / 2];
                    bool          noChildren = true;
                    while (charPtr < charEnd)
                    {
                        char c = *charPtr;
                        if (c == 0)
                        {
                            break;      // we will assume null termination
                        }
                        if (c == '<')
                        {
                            var  c1      = charPtr[1];
                            bool newLine = false;
                            if (c1 == '/')
                            {
                                newLine    = !noChildren;
                                noChildren = false;
                            }
                            else if (Char.IsLetter(c1))
                            {
                                noChildren = true;
                                newLine    = true;
                                indent++;
                            }
                            if (newLine)
                            {
                                sb.AppendLine();
                                for (int i = 0; i < indent; i++)
                                {
                                    sb.Append(' ');
                                }
                            }
                            if (c1 == '/')
                            {
                                --indent;
                            }
                        }
                        sb.Append(c);
                        charPtr++;
                    }
                    return(sb.ToString());
                }
            }

            return("");
        }
Exemplo n.º 12
0
        /// <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);
            };
        }