protected internal override StackSource OpenStackSourceImpl()

in src/PerfView/PerfViewData.cs [4312:6203]


        protected internal override StackSource OpenStackSourceImpl(string streamName, TextWriter log, double startRelativeMSec = 0, double endRelativeMSec = double.PositiveInfinity, Predicate<TraceEvent> predicate = null)
        {
            var eventLog = GetTraceLog(log);
            bool showOptimizationTiers =
                App.CommandLineArgs.ShowOptimizationTiers || streamName.Contains("(with Optimization Tiers)");
            if (streamName.StartsWith("CPU"))
            {
                return eventLog.CPUStacks(null, App.CommandLineArgs, showOptimizationTiers, predicate);
            }

            // var stackSource = new InternTraceEventStackSource(eventLog);
            var stackSource = new MutableTraceEventStackSource(eventLog);

            stackSource.ShowUnknownAddresses = App.CommandLineArgs.ShowUnknownAddresses;
            stackSource.ShowOptimizationTiers = showOptimizationTiers;

            TraceEvents events = eventLog.Events;
            if (!streamName.Contains("TaskTree") && !streamName.Contains("Tasks)"))
            {
                if (predicate != null)
                {
                    events = events.Filter(predicate);
                }
            }
            else
            {
                startRelativeMSec = 0;    // These require activity computers and thus need earlier events.   
            }

            if (startRelativeMSec != 0 || endRelativeMSec != double.PositiveInfinity)
            {
                events = events.FilterByTime(startRelativeMSec, endRelativeMSec);
            }

            var eventSource = events.GetSource();
            var sample = new StackSourceSample(stackSource);

            if (streamName == "Thread Time (with Tasks)")
            {
                return eventLog.ThreadTimeWithTasksStacks();
            }
            else if (streamName == "Thread Time (with ReadyThread)")
            {
                return eventLog.ThreadTimeWithReadyThreadStacks();
            }
            else if (streamName.StartsWith("ASP.NET Thread Time"))
            {
                if (streamName == "ASP.NET Thread Time (with Tasks)")
                {
                    return eventLog.ThreadTimeWithTasksAspNetStacks();
                }
                else
                {
                    return eventLog.ThreadTimeAspNetStacks();
                }
            }
            else if (streamName.StartsWith("Thread Time (with StartStop Activities)"))
            {
                // Handles the normal and (CPU ONLY) case
                var startStopSource = new MutableTraceEventStackSource(eventLog);

                var computer = new ThreadTimeStackComputer(eventLog, App.GetSymbolReader(eventLog.FilePath));
                computer.UseTasks = true;
                computer.GroupByStartStopActivity = true;
                computer.ExcludeReadyThread = true;
                computer.NoAwaitTime = streamName.Contains("(CPU ONLY)");
                computer.GenerateThreadTimeStacks(startStopSource);

                return startStopSource;
            }
            else if (streamName == "Thread Time")
            {
                return eventLog.ThreadTimeStacks();
            }
            else if (streamName == "Processes / Files / Registry")
            {
                return GetProcessFileRegistryStackSource(eventSource, log);
            }
            else if (streamName == "GC Heap Alloc Ignore Free")
            {
                var gcHeapSimulators = new GCHeapSimulators(eventLog, eventSource, stackSource, log);
                gcHeapSimulators.OnNewGCHeapSimulator = delegate (GCHeapSimulator newHeap)
                {
                    newHeap.OnObjectCreate += delegate (Address objAddress, GCHeapSimulatorObject objInfo)
                    {
                        sample.Metric = objInfo.RepresentativeSize;
                        sample.Count = objInfo.GuessCountBasedOnSize();                                                          // We guess a count from the size.
                        sample.TimeRelativeMSec = objInfo.AllocationTimeRelativeMSec;
                        sample.StackIndex = stackSource.Interner.CallStackIntern(objInfo.ClassFrame, objInfo.AllocStack);        // Add the type as a pseudo frame.  
                        stackSource.AddSample(sample);
                        return true;
                    };
                };
                eventSource.Process();
                stackSource.DoneAddingSamples();
            }
            else if (streamName.StartsWith("GC Heap Net Mem"))
            {
                var gcHeapSimulators = new GCHeapSimulators(eventLog, eventSource, stackSource, log);
                if (streamName == "GC Heap Net Mem (Coarse Sampling)")
                {
                    gcHeapSimulators.UseOnlyAllocTicks = true;
                    m_extraTopStats = "Sampled only 100K bytes";
                }

                gcHeapSimulators.OnNewGCHeapSimulator = delegate (GCHeapSimulator newHeap)
                {
                    newHeap.OnObjectCreate += delegate (Address objAddress, GCHeapSimulatorObject objInfo)
                    {
                        sample.Metric = objInfo.RepresentativeSize;
                        sample.Count = objInfo.GuessCountBasedOnSize();                                                          // We guess a count from the size.
                        sample.TimeRelativeMSec = objInfo.AllocationTimeRelativeMSec;
                        sample.StackIndex = stackSource.Interner.CallStackIntern(objInfo.ClassFrame, objInfo.AllocStack);        // Add the type as a pseudo frame.  
                        stackSource.AddSample(sample);
                        return true;
                    };
                    newHeap.OnObjectDestroy += delegate (double time, int gen, Address objAddress, GCHeapSimulatorObject objInfo)
                    {
                        sample.Metric = -objInfo.RepresentativeSize;
                        sample.Count = -(objInfo.GuessCountBasedOnSize());                                            // We guess a count from the size.
                        sample.TimeRelativeMSec = time;
                        sample.StackIndex = stackSource.Interner.CallStackIntern(objInfo.ClassFrame, objInfo.AllocStack);       // We remove the same stack we added at alloc.  
                        stackSource.AddSample(sample);
                    };

                    newHeap.OnGC += delegate (double time, int gen)
                    {
                        sample.Metric = float.Epsilon;
                        sample.Count = 1;
                        sample.TimeRelativeMSec = time;
                        StackSourceCallStackIndex processStack = stackSource.GetCallStackForProcess(newHeap.Process);
                        StackSourceFrameIndex gcFrame = stackSource.Interner.FrameIntern("GC Occurred Gen(" + gen + ")");
                        sample.StackIndex = stackSource.Interner.CallStackIntern(gcFrame, processStack);
                        stackSource.AddSample(sample);
                    };
                };
                eventSource.Process();
                stackSource.DoneAddingSamples();
            }
            else if (streamName.StartsWith("Gen 2 Object Deaths"))
            {
                var gcHeapSimulators = new GCHeapSimulators(eventLog, eventSource, stackSource, log);

                if (streamName == "Gen 2 Object Deaths (Coarse Sampling)")
                {
                    gcHeapSimulators.UseOnlyAllocTicks = true;
                    m_extraTopStats = "Sampled only 100K bytes";
                }

                gcHeapSimulators.OnNewGCHeapSimulator = delegate (GCHeapSimulator newHeap)
                {
                    newHeap.OnObjectDestroy += delegate (double time, int gen, Address objAddress, GCHeapSimulatorObject objInfo)
                    {
                        if (2 <= gen)
                        {
                            sample.Metric = objInfo.RepresentativeSize;
                            sample.Count = objInfo.GuessCountBasedOnSize();                                         // We guess a count from the size.
                            sample.TimeRelativeMSec = objInfo.AllocationTimeRelativeMSec;
                            sample.StackIndex = stackSource.Interner.CallStackIntern(objInfo.ClassFrame, objInfo.AllocStack);
                            stackSource.AddSample(sample);
                        }
                    };

                    newHeap.OnGC += delegate (double time, int gen)
                    {
                        sample.Metric = float.Epsilon;
                        sample.Count = 1;
                        sample.TimeRelativeMSec = time;
                        StackSourceCallStackIndex processStack = stackSource.GetCallStackForProcess(newHeap.Process);
                        StackSourceFrameIndex gcFrame = stackSource.Interner.FrameIntern("GC Occurred Gen(" + gen + ")");
                        sample.StackIndex = stackSource.Interner.CallStackIntern(gcFrame, processStack);
                        stackSource.AddSample(sample);
                    };
                };

                eventSource.Process();
                stackSource.DoneAddingSamples();
            }
            else if (streamName == "GC Heap Alloc Ignore Free (Coarse Sampling)")
            {
                TypeNameSymbolResolver typeNameSymbolResolver = new TypeNameSymbolResolver(FilePath, log);

                bool seenBadAllocTick = false;

                eventSource.Clr.GCAllocationTick += delegate (GCAllocationTickTraceData data)
                {
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                    var stackIndex = stackSource.GetCallStack(data.CallStackIndex(), data);

                    var typeName = data.TypeName;
                    if (string.IsNullOrEmpty(typeName))
                    {
                        // Attempt to resolve the type name.
                        TraceLoadedModule module = data.Process().LoadedModules.GetModuleContainingAddress(data.TypeID, data.TimeStampRelativeMSec);
                        if (module != null)
                        {
                            // Resolve the type name.
                            typeName = typeNameSymbolResolver.ResolveTypeName((int)(data.TypeID - module.ModuleFile.ImageBase), module.ModuleFile, TypeNameSymbolResolver.TypeNameOptions.StripModuleName);
                        }
                    }

                    if (typeName != null && typeName.Length > 0)
                    {
                        var nodeIndex = stackSource.Interner.FrameIntern("Type " + typeName);
                        stackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackIndex);
                    }

                    sample.Metric = data.GetAllocAmount(ref seenBadAllocTick);

                    if (data.AllocationKind == GCAllocationKind.Large)
                    {

                        var nodeIndex = stackSource.Interner.FrameIntern("LargeObject");
                        stackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackIndex);
                    }

                    sample.StackIndex = stackIndex;
                    stackSource.AddSample(sample);
                };
                eventSource.Process();
                m_extraTopStats = "Sampled only 100K bytes";
            }
            else if (streamName == "Exceptions")
            {
                eventSource.Clr.ExceptionStart += delegate (ExceptionTraceData data)
                {
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                    // Create a call stack that ends with the 'throw'
                    var nodeName = "Throw(" + data.ExceptionType + ") " + data.ExceptionMessage;
                    var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                    sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
                    stackSource.AddSample(sample);
                };

                eventSource.Kernel.MemoryAccessViolation += delegate (MemoryPageFaultTraceData data)
                {
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                    // Create a call stack that ends with the 'throw'
                    var nodeName = "AccessViolation(ADDR=" + data.VirtualAddress.ToString("x") + ")";
                    var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                    sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
                    stackSource.AddSample(sample);
                };

                eventSource.Process();
            }

            else if (streamName == "Pinning At GC Time")
            {
                // Wire up the GC heap simulations.  
                GCHeapSimulators gcHeapSimulators = new GCHeapSimulators(eventLog, eventSource, stackSource, log);

                // Keep track of the current GC per process 
                var curGCGen = new int[eventLog.Processes.Count];
                var curGCIndex = new int[eventLog.Processes.Count];
                eventSource.Clr.GCStart += delegate (Microsoft.Diagnostics.Tracing.Parsers.Clr.GCStartTraceData data)
                {
                    var process = data.Process();
                    if (process == null)
                    {
                        return;
                    }

                    curGCGen[(int)process.ProcessIndex] = data.Depth;
                    curGCIndex[(int)process.ProcessIndex] = data.Count;
                };

                // Keep track of the live Pinning handles per process.  
                var allLiveHandles = new Dictionary<Address, GCHandleInfo>[eventLog.Processes.Count];
                Action<SetGCHandleTraceData> onSetHandle = delegate (SetGCHandleTraceData data)
                {
                    if (!(data.Kind == GCHandleKind.AsyncPinned || data.Kind == GCHandleKind.Pinned))
                    {
                        return;
                    }

                    var process = data.Process();
                    if (process == null)
                    {
                        return;
                    }

                    var liveHandles = allLiveHandles[(int)process.ProcessIndex];
                    if (liveHandles == null)
                    {
                        allLiveHandles[(int)process.ProcessIndex] = liveHandles = new Dictionary<Address, GCHandleInfo>();
                    }

                    GCHandleInfo info;
                    var handle = data.HandleID;
                    if (!liveHandles.TryGetValue(handle, out info))
                    {
                        liveHandles[handle] = info = new GCHandleInfo();
                        info.PinStartTimeRelativeMSec = data.TimeStampRelativeMSec;
                        info.ObjectAddress = data.ObjectID;
                        info.IsAsync = (data.Kind == GCHandleKind.AsyncPinned || data.Kind == GCHandleKind.DependendAsyncPinned);
                        info.GCGen = (byte)data.Generation;
                        info.PinStack = stackSource.GetCallStack(data.CallStackIndex(), data);

                        // watch this object as it GCs happen  (but frankly it should not move).  
                        gcHeapSimulators[process].TrackObject(info.ObjectAddress);
                    }
                };
                var clrPrivate = new ClrPrivateTraceEventParser(eventSource);
                clrPrivate.GCSetGCHandle += onSetHandle;
                eventSource.Clr.GCSetGCHandle += onSetHandle;

                Action<DestroyGCHandleTraceData> onDestroyHandle = delegate (DestroyGCHandleTraceData data)
                {
                    var process = data.Process();
                    if (process == null)
                    {
                        return;
                    }

                    var liveHandles = allLiveHandles[(int)process.ProcessIndex];
                    if (liveHandles == null)
                    {
                        allLiveHandles[(int)process.ProcessIndex] = liveHandles = new Dictionary<Address, GCHandleInfo>();
                    }

                    GCHandleInfo info;
                    var handle = data.HandleID;
                    if (liveHandles.TryGetValue(handle, out info))
                    {
                        liveHandles.Remove(handle);
                    }
                };
                clrPrivate.GCDestroyGCHandle += onDestroyHandle;
                eventSource.Clr.GCDestoryGCHandle += onDestroyHandle;

#if false 
                var cacheAllocated = new Dictionary<Address, bool>();
                Action<TraceEvent> onPinnableCacheAllocate = delegate(TraceEvent data) 
                {
                    var objectId = (Address) data.PayloadByName("objectId");
                    cacheAllocated[objectId] = true;
                };
                eventSource.Dynamic.AddCallbackForProviderEvent("AllocateBuffer", "Microsoft-DotNETRuntime-PinnableBufferCache", onPinnableCacheAllocate);
                eventSource.Dynamic.AddCallbackForProviderEvent("AllocateBuffer", "Microsoft-DotNETRuntime-PinnableBufferCache-Mscorlib", onPinnableCacheAllocate); 

                Action<PinPlugAtGCTimeTraceData> plugAtGCTime = delegate(PinPlugAtGCTimeTraceData data)
                {
                };
                clrPrivate.GCPinPlugAtGCTime += plugAtGCTime;
                eventSource.Clr.GCPinObjectAtGCTime += plugAtGCTime;
#endif
                // ThreadStacks maps locations in memory of the thread stack to and maps it to a thread.  
                var threadStacks = new Dictionary<Address, TraceThread>[eventLog.Processes.Count];

                // This per-thread information is used solely as a heuristic backstop to try to guess what
                // the Pinned handles are when we don't have other information.   We can remove it. 
                var lastHandleInfoForThreads = new PerThreadGCHandleInfo[eventLog.Threads.Count];

                // The main event, we have pinning that is happening at GC time.  
                Action<PinObjectAtGCTimeTraceData> objectAtGCTime = delegate (PinObjectAtGCTimeTraceData data)
                {
                    var thread = data.Thread();
                    if (thread == null)
                    {
                        return;
                    }

                    var process = thread.Process;
                    var liveHandles = allLiveHandles[(int)process.ProcessIndex];
                    if (liveHandles == null)
                    {
                        allLiveHandles[(int)process.ProcessIndex] = liveHandles = new Dictionary<Address, GCHandleInfo>();
                    }

                    string pinKind = "UnknownPinned";
                    double pinStartTimeRelativeMSec = 0;
                    StackSourceCallStackIndex pinStack = StackSourceCallStackIndex.Invalid;
                    StackSourceCallStackIndex allocStack = StackSourceCallStackIndex.Invalid;
                    int gcGen = curGCGen[(int)process.ProcessIndex];
                    int gcIndex = curGCIndex[(int)process.ProcessIndex];

                    GCHandleInfo info;
                    if (liveHandles.TryGetValue(data.HandleID, out info))
                    {
                        pinStack = info.PinStack;
                        if (pinStack != StackSourceCallStackIndex.Invalid)
                        {
                            pinStartTimeRelativeMSec = info.PinStartTimeRelativeMSec;
                            pinKind = "HandlePinned";
                            gcGen = info.GCGen;
                        }
                        else if (data.ObjectID == info.ObjectAddress)
                        {
                            pinStartTimeRelativeMSec = info.PinStartTimeRelativeMSec;
                        }
                        else
                        {
                            info.PinStartTimeRelativeMSec = data.TimeStampRelativeMSec;     // Restart trying to guess how long this lives
                            info.ObjectAddress = data.ObjectID;
                        }
                    }
                    else
                    {
                        liveHandles[data.HandleID] = info = new GCHandleInfo();
                        info.ObjectAddress = data.ObjectID;
                        info.PinStartTimeRelativeMSec = data.TimeStampRelativeMSec;         // We guess the pinning started at this GC.  
                    }

                    // This is heuristic logic to determine if the pin handles are async or not. 
                    // Basically async handles are themselves pinned and then point at pinned things.  Thus
                    // if you see handles that point near other handles that is likely an async handle. 
                    // TODO I think we can remove this, because we no longer pin the async handle.  
                    if (pinStack == StackSourceCallStackIndex.Invalid)
                    {
                        var lastHandleInfo = lastHandleInfoForThreads[(int)thread.ThreadIndex];
                        if (lastHandleInfo == null)
                        {
                            lastHandleInfoForThreads[(int)thread.ThreadIndex] = lastHandleInfo = new PerThreadGCHandleInfo();
                        }

                        // If we see a handle that 
                        if (data.HandleID - lastHandleInfo.LikelyAsyncHandleTable1 < 128)
                        {
                            pinKind = "LikelyAsyncPinned";
                            lastHandleInfo.LikelyAsyncHandleTable1 = data.HandleID;
                        }
                        else if (data.HandleID - lastHandleInfo.LikelyAsyncHandleTable2 < 128)
                        {
                            // This is here for the async array of buffers case.   
                            pinKind = "LikelyAsyncPinned";
                            lastHandleInfo.LikelyAsyncHandleTable2 = lastHandleInfo.LikelyAsyncHandleTable1;
                            lastHandleInfo.LikelyAsyncHandleTable1 = data.HandleID;
                        }
                        if (data.HandleID - lastHandleInfo.LastObject < 128)
                        {
                            pinKind = "LikelyAsyncDependentPinned";
                            lastHandleInfo.LikelyAsyncHandleTable2 = lastHandleInfo.LikelyAsyncHandleTable1;
                            lastHandleInfo.LikelyAsyncHandleTable1 = lastHandleInfo.LastHandle;
                        }

                        // Remember our values for heuristics we use to determine if it is an async 
                        lastHandleInfo.LastHandle = data.HandleID;
                        lastHandleInfo.LastObject = data.ObjectID;
                    }

                    var objectInfo = gcHeapSimulators[process].GetObjectInfo(data.ObjectID);
                    if (objectInfo != null)
                    {
                        allocStack = objectInfo.AllocStack;
                        if ((allocStack != StackSourceCallStackIndex.Invalid) && (objectInfo.ClassFrame != StackSourceFrameIndex.Invalid))
                        {
                            if (512 <= objectInfo.Size)
                            {
                                var frameName = stackSource.GetFrameName(objectInfo.ClassFrame, false);

                                var size = 1024;
                                while (size < objectInfo.Size)
                                {
                                    size = size * 2;
                                }

                                frameName += " <= " + (size / 1024).ToString() + "K";
                                allocStack = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(frameName), allocStack);
                            }
                            else
                            {
                                allocStack = stackSource.Interner.CallStackIntern(objectInfo.ClassFrame, allocStack);
                            }
                        }
                    }

                    // If we did not get pinning information, see if it is a stack pin
                    if (pinStack == StackSourceCallStackIndex.Invalid)
                    {
                        const Address allocQuantum = 0x10000 - 1;   // 64K, must be a power of 2.  

                        var threadStack = threadStacks[(int)process.ProcessIndex];
                        if (threadStack == null)
                        {
                            threadStacks[(int)process.ProcessIndex] = threadStack = new Dictionary<Address, TraceThread>();

                            foreach (var procThread in process.Threads)
                            {
                                // Round up to the next 64K boundary
                                var loc = (procThread.UserStackBase + allocQuantum) & ~allocQuantum;
                                // We assume thread stacks are .5 meg (8 * 64K)   Growing down.  
                                for (int i = 0; i < 8; i++)
                                {
                                    threadStack[loc] = procThread;
                                    loc -= (allocQuantum + 1);
                                }
                            }
                        }
                        Address roundUp = (data.HandleID + allocQuantum) & ~allocQuantum;
                        TraceThread stackThread;
                        if (threadStack.TryGetValue(roundUp, out stackThread) && stackThread.StartTimeRelativeMSec <= data.TimeStampRelativeMSec && data.TimeStampRelativeMSec < stackThread.EndTimeRelativeMSec)
                        {
                            pinKind = "StackPinned";
                            pinStack = stackSource.GetCallStackForThread(stackThread);
                        }
                    }

                    /*****  OK we now have all the information we collected, create the sample.  *****/
                    sample.StackIndex = StackSourceCallStackIndex.Invalid;

                    // Choose the stack to use 
                    if (allocStack != StackSourceCallStackIndex.Invalid)
                    {
                        sample.StackIndex = allocStack;
                        sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Allocation Location"), sample.StackIndex);
                    }
                    else if (pinStack != StackSourceCallStackIndex.Invalid)
                    {
                        sample.StackIndex = pinStack;
                        sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Pinning Location"), sample.StackIndex);
                    }
                    else
                    {
                        var gcThread = data.Thread();
                        if (gcThread == null)
                        {
                            return;             // TODO WARN
                        }

                        sample.StackIndex = stackSource.GetCallStackForThread(gcThread);
                        sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("GC Location"), sample.StackIndex);
                    }

                    // Add GC Number
                    sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("GC_NUM " + gcIndex), sample.StackIndex);

                    // Duration of the pin. 
                    var pinDuration = "UNKNOWN";
                    if (pinStartTimeRelativeMSec != 0)
                    {
                        var pinDurationMSec = data.TimeStampRelativeMSec - pinStartTimeRelativeMSec;
                        var roundedDuration = Math.Pow(10.0, Math.Ceiling(Math.Log10(pinDurationMSec)));
                        pinDuration = "<= " + roundedDuration.ToString("n");
                    }
                    var pinDurationInfo = "PINNED_FOR " + pinDuration + " msec";
                    sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(pinDurationInfo), sample.StackIndex);

                    // Add the Pin Kind;
                    sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(pinKind), sample.StackIndex);

                    // Add the type and size 
                    var typeName = data.TypeName;
                    if (data.ObjectSize > 0)
                    {
                        sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Type " + typeName + " Size: 0x" + data.ObjectSize.ToString("x")), sample.StackIndex);
                    }

                    // Add the generation.
                    sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Generation " + gcGen), sample.StackIndex);

                    // _sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Handle 0x" + data.HandleID.ToString("x") +  " Object 0x" + data.ObjectID.ToString("x")), _sample.StackIndex);

                    // We now have the stack, fill in the rest of the _sample and add it to the stack source.  
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    sample.Metric = 1;
                    stackSource.AddSample(sample);
                };
                eventSource.Clr.GCPinObjectAtGCTime += objectAtGCTime;
                clrPrivate.GCPinObjectAtGCTime += objectAtGCTime;         // TODO FIX NOW REMOVE AFTER PRIVATE IS GONE

                eventSource.Process();
                stackSource.DoneAddingSamples();
            }
            else if (streamName == "Pinning")
            {
                var clrPrivate = new ClrPrivateTraceEventParser(eventSource);
                var liveHandles = new Dictionary<long, GCHandleInfo>();
                int maxLiveHandles = 0;
                double maxLiveHandleRelativeMSec = 0;

                Action<SetGCHandleTraceData> onSetHandle = delegate (SetGCHandleTraceData data)
                {
                    if (!(data.Kind == GCHandleKind.AsyncPinned || data.Kind == GCHandleKind.Pinned))
                    {
                        return;
                    }

                    GCHandleInfo info;
                    var handle = (long)data.HandleID;
                    if (!liveHandles.TryGetValue(handle, out info))
                    {
                        liveHandles[handle] = info = new GCHandleInfo();
                        if (liveHandles.Count > maxLiveHandles)
                        {
                            maxLiveHandles = liveHandles.Count;
                            maxLiveHandleRelativeMSec = data.TimeStampRelativeMSec;
                        }
                        info.PinStartTimeRelativeMSec = data.TimeStampRelativeMSec;
                        info.ObjectAddress = data.ObjectID;

                        // TODO deal with nulling out. 
                        string nodeName = (data.Kind == GCHandleKind.Pinned) ? "SinglePinned" : "AsyncPinned";
                        StackSourceFrameIndex frameIndex = stackSource.Interner.FrameIntern(nodeName);
                        StackSourceCallStackIndex callStackIndex = stackSource.Interner.CallStackIntern(frameIndex, stackSource.GetCallStack(data.CallStackIndex(), data));

                        // Add the generation.
                        nodeName = "Generation " + data.Generation;
                        frameIndex = stackSource.Interner.FrameIntern(nodeName);
                        info.PinStack = stackSource.Interner.CallStackIntern(frameIndex, callStackIndex);
                    }
                };
                clrPrivate.GCSetGCHandle += onSetHandle;
                eventSource.Clr.GCSetGCHandle += onSetHandle;

                Action<DestroyGCHandleTraceData> onDestroyHandle = delegate (DestroyGCHandleTraceData data)
                {
                    GCHandleInfo info;
                    var handle = (long)data.HandleID;
                    if (liveHandles.TryGetValue(handle, out info))
                    {
                        LogGCHandleLifetime(stackSource, sample, info, data.TimeStampRelativeMSec, log);
                        liveHandles.Remove(handle);
                    }
                };
                clrPrivate.GCDestroyGCHandle += onDestroyHandle;
                eventSource.Clr.GCDestoryGCHandle += onDestroyHandle;

                eventSource.Process();
                // Pick up any handles that were never destroyed.  
                foreach (var info in liveHandles.Values)
                {
                    LogGCHandleLifetime(stackSource, sample, info, eventLog.SessionDuration.TotalMilliseconds, log);
                }

                stackSource.DoneAddingSamples();
                log.WriteLine("The maximum number of live pinning handles is {0} at {1:n3} Msec ", maxLiveHandles, maxLiveHandleRelativeMSec);
            }

            else if (streamName == "Heap Snapshot Pinning")
            {
                GCPinnedObjectAnalyzer pinnedObjectAnalyzer = new GCPinnedObjectAnalyzer(FilePath, eventLog, stackSource, sample, log);
                pinnedObjectAnalyzer.Execute(GCPinnedObjectViewType.PinnedHandles);
            }
            else if (streamName == "Heap Snapshot Pinned Object Allocation")
            {
                GCPinnedObjectAnalyzer pinnedObjectAnalyzer = new GCPinnedObjectAnalyzer(FilePath, eventLog, stackSource, sample, log);
                pinnedObjectAnalyzer.Execute(GCPinnedObjectViewType.PinnedObjectAllocations);
            }
            else if (streamName == "CCW Ref Count")
            {
                // TODO use the callback model.  We seem to have an issue getting the names however. 
                foreach (var data in events.ByEventType<CCWRefCountChangeTraceData>())
                {
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    var stackIndex = stackSource.GetCallStack(data.CallStackIndex(), data);

                    var operation = data.Operation;
                    if (operation.StartsWith("Release", StringComparison.OrdinalIgnoreCase))
                    {
                        sample.Metric = -1;
                    }

                    var ccwRefKindName = "CCW " + operation;
                    var ccwRefKindIndex = stackSource.Interner.FrameIntern(ccwRefKindName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwRefKindIndex, stackIndex);

                    var ccwRefCountName = "CCW NewRefCnt " + data.NewRefCount.ToString();
                    var ccwRefCountIndex = stackSource.Interner.FrameIntern(ccwRefCountName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwRefCountIndex, stackIndex);

                    var ccwInstanceName = "CCW Instance 0x" + data.COMInterfacePointer.ToString("x");
                    var ccwInstanceIndex = stackSource.Interner.FrameIntern(ccwInstanceName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwInstanceIndex, stackIndex);

                    var ccwTypeName = "CCW Type " + data.NameSpace + "." + data.ClassName;
                    var ccwTypeIndex = stackSource.Interner.FrameIntern(ccwTypeName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwTypeIndex, stackIndex);

                    sample.StackIndex = stackIndex;
                    stackSource.AddSample(sample);
                }
                foreach (var data in events.ByEventType<CCWRefCountChangeAnsiTraceData>())
                {
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    var stackIndex = stackSource.GetCallStack(data.CallStackIndex(), data);

                    var operation = data.Operation;
                    if (operation.StartsWith("Release", StringComparison.OrdinalIgnoreCase))
                    {
                        sample.Metric = -1;
                    }

                    var ccwRefKindName = "CCW " + operation;
                    var ccwRefKindIndex = stackSource.Interner.FrameIntern(ccwRefKindName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwRefKindIndex, stackIndex);

                    var ccwRefCountName = "CCW NewRefCnt " + data.NewRefCount.ToString();
                    var ccwRefCountIndex = stackSource.Interner.FrameIntern(ccwRefCountName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwRefCountIndex, stackIndex);

                    var ccwInstanceName = "CCW Instance 0x" + data.COMInterfacePointer.ToString("x");
                    var ccwInstanceIndex = stackSource.Interner.FrameIntern(ccwInstanceName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwInstanceIndex, stackIndex);

                    var ccwTypeName = "CCW Type " + data.NameSpace + "." + data.ClassName;
                    var ccwTypeIndex = stackSource.Interner.FrameIntern(ccwTypeName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwTypeIndex, stackIndex);

                    sample.StackIndex = stackIndex;
                    stackSource.AddSample(sample);
                }
            }
            else if (streamName == ".NET Native CCW Ref Count")
            {
                // TODO FIX NOW, investigate the missing events.  All we know is that incs and dec are not
                // consistent with the RefCount value that is in the events.
                GuiApp.MainWindow.Dispatcher.BeginInvoke((Action)delegate ()
                {
                    MessageBox.Show(GuiApp.MainWindow,
                        "Warning: the Interop CCW events on which this data is based seem to be incomplete.\r\n" +
                        "There seem to be missing instrumentation, which make the referenct counts unreliable\r\n"
                        , "Data May be Incorrect");
                });

                var objectToTypeMap = new Dictionary<long, Address>(1000);
                var typeToNameMap = new Dictionary<Address, string>(100);
                var interopTraceEventParser = new InteropTraceEventParser(eventSource);
                Action<double, long, int, int, StackSourceCallStackIndex> handleCWWInfoArgs = (double timestamp, long objectID, int refCount, int metric, StackSourceCallStackIndex stackIndex) =>
                {
                    sample.Metric = metric;
                    sample.TimeRelativeMSec = timestamp;

                    var ccwRefKindName = $"CCW {(metric >= 0 ? "AddRef" : "Release")}";
                    var ccwRefKindNameIndex = stackSource.Interner.FrameIntern(ccwRefKindName);
                    stackIndex = stackSource.Interner.CallStackIntern(ccwRefKindNameIndex, stackIndex);

                    var objectId = "Object ID 0x" + objectID.ToString("x");
                    var objectIdIndex = stackSource.Interner.FrameIntern(objectId);
                    stackIndex = stackSource.Interner.CallStackIntern(objectIdIndex, stackIndex);

                    Address typeId;
                    if (objectToTypeMap.TryGetValue(objectID, out typeId))
                    {
                        string objectType = "Object Type ";
                        string typeName;
                        if (typeToNameMap.TryGetValue(typeId, out typeName))
                        {
                            objectType += typeName;
                        }
                        else
                        {
                            objectType += "0x" + typeId;
                        }

                        var objectTypeIndex = stackSource.Interner.FrameIntern(objectType);
                        stackIndex = stackSource.Interner.CallStackIntern(objectTypeIndex, stackIndex);
                    }
                    var ccwRefCount = "CCW NewRefCnt " + refCount;
                    var ccwRefCountIndex = stackSource.Interner.FrameIntern(ccwRefCount.ToString());
                    stackIndex = stackSource.Interner.CallStackIntern(ccwRefCountIndex, stackIndex);

                    sample.StackIndex = stackIndex;
                    stackSource.AddSample(sample);
                };

                TypeNameSymbolResolver typeNameSymbolResolver = new TypeNameSymbolResolver(FilePath, log);

                interopTraceEventParser.AddCallbackForEvent<TaskCCWCreationArgs>(null, args =>
                {
                    if (!objectToTypeMap.ContainsKey(args.objectID))
                    {
                        objectToTypeMap.Add(args.objectID, args.targetObjectIDType);
                    }

                    // Attempt to resolve the type name.
                    if (!typeToNameMap.ContainsKey(args.targetObjectIDType))
                    {
                        TraceLoadedModule module = args.Process().LoadedModules.GetModuleContainingAddress(args.targetObjectIDType, args.TimeStampRelativeMSec);
                        if (module != null)
                        {
                            string typeName = typeNameSymbolResolver.ResolveTypeName((int)(args.targetObjectIDType - module.ModuleFile.ImageBase), module.ModuleFile, TypeNameSymbolResolver.TypeNameOptions.StripModuleName);
                            if (typeName != null)
                            {
                                typeToNameMap.Add(args.targetObjectIDType, typeName);
                            }
                        }
                    }
                });
                #region TaskCCWQueryRuntimeClassNameArgs commented for a while. TODO: get type info from pdb
                //interopTraceEventParser.AddCallbackForEvents<TaskCCWQueryRuntimeClassNameArgs>(args =>
                //{
                //    sample.Metric = 0;
                //    sample.TimeRelativeMSec = args.TimeStampRelativeMSec;
                //    var stackIndex = stackSource.GetCallStack(args.CallStackIndex(), args);

                //    var ccwRefKindName = "CCW QueryRuntimeClassName " + args.runtimeClassName;
                //    var ccwRefKindNameIndex = stackSource.Interner.FrameIntern(ccwRefKindName);
                //    stackIndex = stackSource.Interner.CallStackIntern(ccwRefKindNameIndex, stackIndex);

                //    sample.StackIndex = stackIndex;
                //    stackSource.AddSample(sample);
                //});
                #endregion
                interopTraceEventParser.AddCallbackForEvents<TaskCCWRefCountIncArgs>(args =>
                    handleCWWInfoArgs(args.TimeStampRelativeMSec, args.objectID, args.refCount, 1, stackSource.GetCallStack(args.CallStackIndex(), args)));
                interopTraceEventParser.AddCallbackForEvents<TaskCCWRefCountDecArgs>(args =>
                    handleCWWInfoArgs(args.TimeStampRelativeMSec, args.objectID, args.refCount, -1, stackSource.GetCallStack(args.CallStackIndex(), args)));
                eventSource.Process();
            }
            else if (streamName == "Windows Handle Ref Count")
            {
                var allocationsStacks = new Dictionary<long, StackSourceCallStackIndex>(200);

                Action<string, Address, int, int, TraceEvent> onHandleEvent = delegate (string handleTypeName, Address objectInstance, int handleInstance, int handleProcess, TraceEvent data)
                {
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    sample.StackIndex = StackSourceCallStackIndex.Invalid;

                    sample.Metric = 1;
                    // Closes use the call stack of the allocation site if possible (since that is more helpful)  
                    if (data.Opcode == (TraceEventOpcode)33)       // CloseHandle
                    {
                        sample.Metric = -1;

                        long key = (((long)handleProcess) << 32) + handleInstance;
                        StackSourceCallStackIndex stackIndex;
                        if (allocationsStacks.TryGetValue(key, out stackIndex))
                        {
                            sample.StackIndex = stackIndex;
                        }
                        // TODO should we keep track of the ref count and remove the entry when it drops past zero?  
                    }

                    // If this not a close() (Or if we could not find a stack for the close() make up a call stack from the event.  
                    if (sample.StackIndex == StackSourceCallStackIndex.Invalid)
                    {
                        StackSourceCallStackIndex stackIndex = stackSource.GetCallStack(data.CallStackIndex(), data);

                        // We want all stacks to be int he process where the handle exists.  But this not always the case
                        // If that happened abandon the stack and make up a pseudo stack that lets you know that happened. 
                        if (handleProcess != data.ProcessID)
                        {
                            stackIndex = StackSourceCallStackIndex.Invalid;
                            TraceProcess process = eventLog.Processes.GetProcess(handleProcess, data.TimeStampRelativeMSec);
                            if (process != null)
                            {
                                stackIndex = stackSource.GetCallStackForProcess(process);
                            }

                            var markerIndex = stackSource.Interner.FrameIntern("Handle Allocated Out of Process");
                            stackIndex = stackSource.Interner.CallStackIntern(markerIndex, stackIndex);
                        }

                        var nameIndex = stackSource.Interner.FrameIntern(data.EventName);
                        stackIndex = stackSource.Interner.CallStackIntern(nameIndex, stackIndex);

                        var instanceName = "Handle Instance " + handleInstance.ToString("n0") + " (0x" + handleInstance.ToString("x") + ")";
                        var instanceIndex = stackSource.Interner.FrameIntern(instanceName);
                        stackIndex = stackSource.Interner.CallStackIntern(instanceIndex, stackIndex);

                        var handleTypeIndex = stackSource.Interner.FrameIntern("Handle Type " + handleTypeName);
                        stackIndex = stackSource.Interner.CallStackIntern(handleTypeIndex, stackIndex);

                        //var objectName = "Object Instance 0x" + objectInstance.ToString("x");
                        //var objectIndex = stackSource.Interner.FrameIntern(objectName);
                        //stackIndex = stackSource.Interner.CallStackIntern(objectIndex, stackIndex);

                        sample.StackIndex = stackIndex;

                        long key = (((long)handleProcess) << 32) + handleInstance;
                        allocationsStacks[key] = stackIndex;
                    }

                    stackSource.AddSample(sample);
                };

                eventSource.Kernel.AddCallbackForEvents<ObjectHandleTraceData>(data => onHandleEvent(data.ObjectTypeName, data.Object, data.Handle, data.ProcessID, data));
                eventSource.Kernel.AddCallbackForEvents<ObjectDuplicateHandleTraceData>(data => onHandleEvent(data.ObjectTypeName, data.Object, data.TargetHandle, data.TargetProcessID, data));
                eventSource.Process();
            }
            else if (streamName.StartsWith("Processor"))
            {
                eventSource.Kernel.PerfInfoSample += delegate (SampledProfileTraceData data)
                {
                    StackSourceCallStackIndex stackIndex;
                    var callStackIdx = data.CallStackIndex();
                    if (callStackIdx == CallStackIndex.Invalid)
                    {
                        return;
                    }

                    stackIndex = stackSource.GetCallStack(callStackIdx, data);

                    var processorPriority = "Processor (" + data.ProcessorNumber + ") Priority (" + data.Priority + ")";
                    stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(processorPriority), stackIndex);

                    sample.StackIndex = stackIndex;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    sample.Metric = 1;
                    stackSource.AddSample(sample);
                };
                eventSource.Process();
            }
            else if (streamName.StartsWith("Any"))
            {
                ActivityComputer activityComputer = null;
                StartStopActivityComputer startStopComputer = null;
                bool isAnyTaskTree = (streamName == "Any TaskTree");
                bool isAnyWithTasks = (streamName == "Any Stacks (with Tasks)");
                bool isAnyWithStartStop = (streamName == "Any Stacks (with StartStop Activities)");          // These have the call stacks 
                bool isAnyStartStopTreeNoCallStack = (streamName == "Any StartStopTree");               // These have just the start-stop activities.  
                if (isAnyTaskTree || isAnyWithTasks || isAnyWithStartStop || isAnyStartStopTreeNoCallStack)
                {
                    activityComputer = new ActivityComputer(eventSource, GetSymbolReader(log));

                    // Log a pseudo-event that indicates when the activity dies
                    activityComputer.Stop += delegate (TraceActivity activity, TraceEvent data)
                    {
                        // TODO This is a clone of the logic below, factor it.  
                        TraceThread thread = data.Thread();
                        if (thread != null)
                        {
                            return;
                        }

                        StackSourceCallStackIndex stackIndex;
                        if (isAnyTaskTree)
                        {
                            // Compute the stack where frames using an activity Name as a frame name.
                            stackIndex = activityComputer.GetActivityStack(stackSource, activityComputer.GetCurrentActivity(thread));
                        }
                        else if (isAnyStartStopTreeNoCallStack)
                        {
                            stackIndex = startStopComputer.GetStartStopActivityStack(stackSource, startStopComputer.GetCurrentStartStopActivity(thread, data), thread.Process);
                        }
                        else
                        {
                            Func<TraceThread, StackSourceCallStackIndex> topFrames = null;
                            if (isAnyWithStartStop)
                            {
                                topFrames = delegate (TraceThread topThread) { return startStopComputer.GetCurrentStartStopActivityStack(stackSource, thread, topThread); };
                            }

                            // Use the call stack 
                            stackIndex = activityComputer.GetCallStack(stackSource, data, topFrames);
                        }

                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("ActivityStop " + activity.ToString()), stackIndex);
                        sample.StackIndex = stackIndex;
                        sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                        sample.Metric = 1;
                        stackSource.AddSample(sample);
                    };

                    if (isAnyWithStartStop || isAnyStartStopTreeNoCallStack)
                    {
                        startStopComputer = new StartStopActivityComputer(eventSource, activityComputer);
                    }
                }

                StackSourceFrameIndex blockingFrame = stackSource.Interner.FrameIntern("Event Kernel/Thread/BLOCKING CSwitch");
                StackSourceFrameIndex cswitchEventFrame = stackSource.Interner.FrameIntern("Event Windows Kernel/Thread/CSwitch");
                StackSourceFrameIndex readyThreadEventFrame = stackSource.Interner.FrameIntern("Event Windows Kernel/Dispatcher/ReadyThread");
                StackSourceFrameIndex sampledProfileFrame = stackSource.Interner.FrameIntern("Event Windows Kernel/PerfInfo/Sample");

                eventSource.AllEvents += delegate (TraceEvent data)
                {
                    // Get most of the stack (we support getting the normal call stack as well as the task stack.  
                    StackSourceCallStackIndex stackIndex;
                    if (activityComputer != null)
                    {
                        TraceThread thread = data.Thread();
                        if (thread == null)
                        {
                            return;
                        }

                        if (isAnyTaskTree)
                        {
                            // Compute the stack where frames using an activity Name as a frame name.
                            stackIndex = activityComputer.GetActivityStack(stackSource, activityComputer.GetCurrentActivity(thread));
                        }
                        else if (isAnyStartStopTreeNoCallStack)
                        {
                            stackIndex = startStopComputer.GetStartStopActivityStack(stackSource, startStopComputer.GetCurrentStartStopActivity(thread, data), thread.Process);
                        }
                        else
                        {
                            Func<TraceThread, StackSourceCallStackIndex> topFrames = null;
                            if (isAnyWithStartStop)
                            {
                                topFrames = delegate (TraceThread topThread) { return startStopComputer.GetCurrentStartStopActivityStack(stackSource, thread, topThread); };
                            }

                            // Use the call stack 
                            stackIndex = activityComputer.GetCallStack(stackSource, data, topFrames);
                        }
                    }
                    else
                    {
                        // Normal case, get the calls stack of frame names.  
                        var callStackIdx = data.CallStackIndex();
                        if (callStackIdx != CallStackIndex.Invalid)
                        {
                            stackIndex = stackSource.GetCallStack(callStackIdx, data);
                        }
                        else
                        {
                            stackIndex = StackSourceCallStackIndex.Invalid;
                        }
                    }

                    var asCSwitch = data as CSwitchTraceData;
                    if (asCSwitch != null)
                    {
                        if (activityComputer == null)  // Just a plain old any-stacks
                        {
                            var callStackIdx = asCSwitch.BlockingStack();
                            if (callStackIdx != CallStackIndex.Invalid)
                            {
                                StackSourceCallStackIndex blockingStackIndex = stackSource.GetCallStack(callStackIdx, data);
                                // Make an entry for the blocking stacks as well.  
                                blockingStackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("EventData OldThreadState " + asCSwitch.OldThreadState), blockingStackIndex);
                                sample.StackIndex = stackSource.Interner.CallStackIntern(blockingFrame, blockingStackIndex);
                                sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                                sample.Metric = 1;
                                stackSource.AddSample(sample);
                            }
                        }

                        if (stackIndex != StackSourceCallStackIndex.Invalid)
                        {
                            stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("EventData NewProcessName " + asCSwitch.NewProcessName), stackIndex);
                            stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("EventData OldProcessName " + asCSwitch.OldProcessName), stackIndex);

                            stackIndex = stackSource.Interner.CallStackIntern(cswitchEventFrame, stackIndex);
                        }

                        goto ADD_SAMPLE;
                    }

                    if (stackIndex == StackSourceCallStackIndex.Invalid)
                    {
                        return;
                    }

                    var asSampledProfile = data as SampledProfileTraceData;
                    if (asSampledProfile != null)
                    {
                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("EventData Priority " + asSampledProfile.Priority), stackIndex);
                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("EventData Processor " + asSampledProfile.ProcessorNumber), stackIndex);
                        stackIndex = stackSource.Interner.CallStackIntern(sampledProfileFrame, stackIndex);
                        goto ADD_SAMPLE;
                    }

                    var asReadyThread = data as DispatcherReadyThreadTraceData;
                    if (asReadyThread != null)
                    {
                        var awakenedName = "EventData Readied Thread " + asReadyThread.AwakenedThreadID +
                            " Proc " + asReadyThread.AwakenedProcessID;
                        var awakenedIndex = stackSource.Interner.FrameIntern(awakenedName);
                        stackIndex = stackSource.Interner.CallStackIntern(awakenedIndex, stackIndex);
                        stackIndex = stackSource.Interner.CallStackIntern(readyThreadEventFrame, stackIndex);
                        goto ADD_SAMPLE;
                    }

                    // TODO FIX NOW remove for debugging activity stuff.  
#if false
                    var activityId = data.ActivityID;
                    if (activityId != Guid.Empty && ActivityComputer.IsActivityPath(activityId))
                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("ActivityPath " + ActivityComputer.ActivityPathString(activityId)), stackIndex);
#endif
                    var asObjectAllocated = data as ObjectAllocatedArgs;
                    if (asObjectAllocated != null)
                    {
                        var size = "EventData Size 0x" + asObjectAllocated.Size.ToString("x");
                        var sizeIndex = stackSource.Interner.FrameIntern(size);
                        stackIndex = stackSource.Interner.CallStackIntern(sizeIndex, stackIndex);
                        goto ADD_EVENT_FRAME;
                    }

                    var asAllocTick = data as GCAllocationTickTraceData;
                    if (asAllocTick != null)
                    {
                        var frameIdx = stackSource.Interner.FrameIntern("EventData Kind " + asAllocTick.AllocationKind);
                        stackIndex = stackSource.Interner.CallStackIntern(frameIdx, stackIndex);

                        frameIdx = stackSource.Interner.FrameIntern("EventData Size " + asAllocTick.AllocationAmount64);
                        stackIndex = stackSource.Interner.CallStackIntern(frameIdx, stackIndex);

                        var typeName = asAllocTick.TypeName;
                        if (string.IsNullOrEmpty(typeName))
                        {
                            typeName = "TypeId 0x" + asAllocTick.TypeID;
                        }

                        frameIdx = stackSource.Interner.FrameIntern("EventData TypeName " + typeName);
                        stackIndex = stackSource.Interner.CallStackIntern(frameIdx, stackIndex);
                        goto ADD_EVENT_FRAME;
                    }

                    var asSampleObjectAllocated = data as GCSampledObjectAllocationTraceData;
                    if (asSampleObjectAllocated != null)
                    {
                        var size = "EventData Size 0x" + asSampleObjectAllocated.TotalSizeForTypeSample.ToString("x");
                        var sizeIndex = stackSource.Interner.FrameIntern(size);
                        stackIndex = stackSource.Interner.CallStackIntern(sizeIndex, stackIndex);
                        goto ADD_EVENT_FRAME;
                    }

                    var asSetGCHandle = data as SetGCHandleTraceData;
                    if (asSetGCHandle != null)
                    {
                        var handleName = "EventData GCHandleKind " + asSetGCHandle.Kind.ToString();
                        var handleIndex = stackSource.Interner.FrameIntern(handleName);
                        stackIndex = stackSource.Interner.CallStackIntern(handleIndex, stackIndex);
                        goto ADD_EVENT_FRAME;
                    }

                    var asPageAccess = data as MemoryPageAccessTraceData;
                    if (asPageAccess != null)
                    {
                        sample.Metric = 4;      // Convenience since these are 4K pages 

                        // EMit the kind, which may have a file name argument.  
                        var pageKind = asPageAccess.PageKind;
                        string fileName = asPageAccess.FileName;
                        if (fileName == null)
                        {
                            fileName = "";
                        }

                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(pageKind.ToString() + " " + fileName), stackIndex);

                        // If it is the range of a module, log that as well, as well as it bucket.  
                        var address = asPageAccess.VirtualAddress;
                        var process = data.Process();
                        if (process != null)
                        {
                            var module = process.LoadedModules.GetModuleContainingAddress(address, asPageAccess.TimeStampRelativeMSec);
                            if (module != null)
                            {
                                if (module.ModuleFile != null && module.ModuleFile.ImageSize != 0)
                                {
                                    // Create a node that indicates where in the file (in buckets) the access was from 
                                    double normalizeDistance = (address - module.ImageBase) / ((double)module.ModuleFile.ImageSize);
                                    if (0 <= normalizeDistance && normalizeDistance < 1)
                                    {
                                        const int numBuckets = 20;
                                        int bucket = (int)(normalizeDistance * numBuckets);
                                        int bucketSizeInPages = module.ModuleFile.ImageSize / (numBuckets * 4096);
                                        string bucketName = "Image Bucket " + bucket + " Size " + bucketSizeInPages + " Pages";
                                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(bucketName), stackIndex);
                                    }
                                }
                                stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("EventData Image  " + module.ModuleFile.FilePath), stackIndex);
                            }
                        }
                        goto ADD_EVENT_FRAME;
                    }

                    var asPMCCounter = data as PMCCounterProfTraceData;
                    if (asPMCCounter != null)
                    {
                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("EventData Processor " + asPMCCounter.ProcessorNumber), stackIndex);
                        var source = "EventData ProfileSourceID " + asPMCCounter.ProfileSource;
                        var sourceIndex = stackSource.Interner.FrameIntern(source);
                        stackIndex = stackSource.Interner.CallStackIntern(sourceIndex, stackIndex);
                        goto ADD_EVENT_FRAME;
                    }

                    var asFileCreate = data as FileIOCreateTraceData;
                    if (asFileCreate != null)
                    {
                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("CreateOptions: " + asFileCreate.CreateOptions), stackIndex);
                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("FileAttributes: " + asFileCreate.FileAttributes), stackIndex);
                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("ShareAccess: " + asFileCreate.ShareAccess), stackIndex);
                        // stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("CreateDispostion: " + asFileCreate.CreateDispostion), stackIndex);
                        stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("FileName: " + asFileCreate.FileName), stackIndex);
                        goto ADD_EVENT_FRAME;
                    }

                    // Tack on additional info about the event. 
                    var fieldNames = data.PayloadNames;
                    for (int i = 0; i < fieldNames.Length; i++)
                    {
                        var fieldName = fieldNames[i];
                        if (0 <= fieldName.IndexOf("Name", StringComparison.OrdinalIgnoreCase) ||
                            fieldName == "OpenPath" || fieldName == "Url" || fieldName == "Uri" || fieldName == "ConnectionId" ||
                            fieldName == "ExceptionType" || 0 <= fieldName.IndexOf("Message", StringComparison.OrdinalIgnoreCase))
                        {
                            var value = data.PayloadString(i);
                            var fieldNodeName = "EventData " + fieldName + " " + value;
                            var fieldNodeIndex = stackSource.Interner.FrameIntern(fieldNodeName);
                            stackIndex = stackSource.Interner.CallStackIntern(fieldNodeIndex, stackIndex);
                        }
                    }

                    ADD_EVENT_FRAME:
                    // Tack on event name 
                    var eventNodeName = "Event " + data.ProviderName + "/" + data.EventName;
                    stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(eventNodeName), stackIndex);
                    ADD_SAMPLE:
                    sample.StackIndex = stackIndex;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    sample.Metric = 1;
                    stackSource.AddSample(sample);
                };
                eventSource.Process();
            }
            else if (streamName == "Managed Load")
            {
                eventSource.Clr.LoaderModuleLoad += delegate (ModuleLoadUnloadTraceData data)
                {
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                    // Create a call stack that ends with 'Disk READ <fileName> (<fileDirectory>)'
                    var nodeName = "Load " + data.ModuleILPath;
                    var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                    sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
                    stackSource.AddSample(sample);
                };
                eventSource.Process();
            }
            else if (streamName == "Disk I/O")
            {
                var diskStartStack = new Dictionary<Address, StackSourceCallStackIndex>(50);
                // On a per-disk basis remember when the last Disk I/O completed.  
                var lastDiskEndMSec = new GrowableArray<double>(4);

                eventSource.Kernel.AddCallbackForEvents<DiskIOInitTraceData>(delegate (DiskIOInitTraceData data)
                                {
                                    diskStartStack[data.Irp] = stackSource.GetCallStack(data.CallStackIndex(), data);
                                });

                eventSource.Kernel.AddCallbackForEvents<DiskIOTraceData>(delegate (DiskIOTraceData data)
                {
                    StackSourceCallStackIndex stackIdx;
                    if (diskStartStack.TryGetValue(data.Irp, out stackIdx))
                    {
                        diskStartStack.Remove(data.Irp);
                    }
                    else
                    {
                        stackIdx = StackSourceCallStackIndex.Invalid;
                    }

                    var diskNumber = data.DiskNumber;
                    if (diskNumber >= lastDiskEndMSec.Count)
                    {
                        lastDiskEndMSec.Count = diskNumber + 1;
                    }

                    // Create a call stack that ends with 'Disk READ <fileName> (<fileDirectory>)'
                    var filePath = data.FileName;
                    if (filePath.Length == 0)
                    {
                        filePath = "UNKNOWN";
                    }

                    var nodeName = "I/O Size 0x" + data.TransferSize.ToString("x");
                    var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                    stackIdx = stackSource.Interner.CallStackIntern(nodeIndex, stackIdx);

                    nodeName = string.Format("Disk {0} DiskNum({1}) {2} ({3})", data.OpcodeName, diskNumber,
                        GetFileName(filePath), GetDirectoryName(filePath));

                    // The time it took actually using the disk is the smaller of either
                    // The elapsed time (because there were no other entries in the disk queue)
                    // OR the time since the last I/O completed (since that is when this one will start using the disk.
                    var elapsedMSec = data.ElapsedTimeMSec;
                    double serviceTimeMSec = elapsedMSec;
                    double durationSinceLastIOMSec = data.TimeStampRelativeMSec - lastDiskEndMSec[diskNumber];
                    lastDiskEndMSec[diskNumber] = elapsedMSec;
                    if (durationSinceLastIOMSec < serviceTimeMSec)
                    {
                        serviceTimeMSec = durationSinceLastIOMSec;
                        // There is queuing delay, indicate this by adding a sample that represents the queueing delay. 

                        var queueStackIdx = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Time in Disk Queue " + diskNumber), stackIdx);
                        sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(nodeName), queueStackIdx);
                        sample.Metric = (float)(elapsedMSec - serviceTimeMSec);
                        sample.TimeRelativeMSec = data.TimeStampRelativeMSec - elapsedMSec;
                        stackSource.AddSample(sample);
                    }

                    stackIdx = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Service Time Disk " + diskNumber), stackIdx);
                    sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(nodeName), stackIdx);
                    sample.Metric = (float)serviceTimeMSec;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec - serviceTimeMSec;
                    stackSource.AddSample(sample);
                });
                eventSource.Process();
                m_extraTopStats = " Metric is MSec";
            }
            else if (streamName == "Server Request CPU")
            {
                ServerRequestScenarioConfiguration scenarioConfiguration = new ServerRequestScenarioConfiguration(eventLog);
                ComputingResourceStateMachine stateMachine = new ComputingResourceStateMachine(stackSource, scenarioConfiguration, ComputingResourceViewType.CPU);
                stateMachine.Execute();
            }
            else if (streamName == "Server Request Thread Time")
            {
                ServerRequestScenarioConfiguration scenarioConfiguration = new ServerRequestScenarioConfiguration(eventLog);
                ComputingResourceStateMachine stateMachine = new ComputingResourceStateMachine(stackSource, scenarioConfiguration, ComputingResourceViewType.ThreadTime);
                stateMachine.Execute();
            }
            else if (streamName == "Server Request Managed Allocation")
            {
                ServerRequestScenarioConfiguration scenarioConfiguration = new ServerRequestScenarioConfiguration(eventLog);
                ComputingResourceStateMachine stateMachine = new ComputingResourceStateMachine(stackSource, scenarioConfiguration, ComputingResourceViewType.Allocations);
                stateMachine.Execute();
            }
            else if (streamName == "Execution Tracing")
            {
                TraceLogEventSource source = eventLog.Events.GetSource();

                Action<TraceEvent> tracingCallback = delegate (TraceEvent data)
                {
                    string assemblyName = (string)data.PayloadByName("assembly");
                    string typeName = (string)data.PayloadByName("type");
                    string methodName = (string)data.PayloadByName("method");

                    string frameName = string.Format("{0}!{1}.{2}", assemblyName, typeName, methodName);

                    StackSourceCallStackIndex callStackIndex = stackSource.GetCallStack(data.CallStackIndex(), data);
                    StackSourceFrameIndex nodeIndex = stackSource.Interner.FrameIntern(frameName);
                    callStackIndex = stackSource.Interner.CallStackIntern(nodeIndex, callStackIndex);

                    sample.Count = 1;
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    sample.StackIndex = callStackIndex;

                    stackSource.AddSample(sample);
                };

                source.Dynamic.AddCallbackForProviderEvent("MethodCallLogger", "MethodEntry", tracingCallback);

                source.Process();
            }
            else if (streamName == "File Queries")
            {
                eventSource.Kernel.AddCallbackForEvents<FileIOInfoTraceData>(delegate (FileIOInfoTraceData data)
                {
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                    StackSourceCallStackIndex stackIdx = stackSource.GetCallStack(data.CallStackIndex(), data);

                    // Create a call stack that ends with 'Disk READ <fileName> (<fileDirectory>)'
                    var filePath = data.FileName;
                    if (filePath.Length == 0)
                    {
                        filePath = "UNKNOWN";
                    }

                    var nodeName = string.Format("File {0}: {1} ({2})", data.OpcodeName,
                        GetFileName(filePath), GetDirectoryName(filePath));
                    var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                    stackIdx = stackSource.Interner.CallStackIntern(nodeIndex, stackIdx);

                    sample.StackIndex = stackIdx;
                    stackSource.AddSample(sample);
                });
                eventSource.Process();
            }
            else if (streamName == "Directory Enumerations")
            {
                eventSource.Kernel.AddCallbackForEvents<FileIODirEnumTraceData>(delegate (FileIODirEnumTraceData data)
                {
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                    StackSourceCallStackIndex stackIdx = stackSource.GetCallStack(data.CallStackIndex(), data);

                    var directoryPath = data.DirectoryName;
                    if (directoryPath.Length == 0)
                    {
                        directoryPath = "UNKNOWN";
                    }

                    var nodeName = string.Format("Directory {0}: {1}", data.OpcodeName, directoryPath);
                    var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                    stackIdx = stackSource.Interner.CallStackIntern(nodeIndex, stackIdx);

                    sample.StackIndex = stackIdx;
                    stackSource.AddSample(sample);
                });
                eventSource.Process();
            }
            else if (streamName == "File I/O")
            {
                eventSource.Kernel.AddCallbackForEvents<FileIOReadWriteTraceData>(delegate (FileIOReadWriteTraceData data)
                {
                    sample.Metric = (float)data.IoSize;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                    StackSourceCallStackIndex stackIdx = stackSource.GetCallStack(data.CallStackIndex(), data);

                    // Create a call stack that ends with 'Disk READ <fileName> (<fileDirectory>)'
                    var filePath = data.FileName;
                    if (filePath.Length == 0)
                    {
                        filePath = "UNKNOWN";
                    }

                    var nodeName = string.Format("File {0}: {1} ({2})", data.OpcodeName,
                        GetFileName(filePath), GetDirectoryName(filePath));
                    var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                    stackIdx = stackSource.Interner.CallStackIntern(nodeIndex, stackIdx);

                    sample.StackIndex = stackIdx;
                    stackSource.AddSample(sample);
                });
                eventSource.Process();
            }
            else if (streamName == "Image Load")
            {
                var loadedImages = new Dictionary<Address, StackSourceCallStackIndex>(100);
                Action<ImageLoadTraceData> imageLoadUnload = delegate (ImageLoadTraceData data)
                {
                    // TODO this is not really correct, it assumes process IDs < 64K and images bases don't use lower bits
                    // but it is true 
                    Address imageKey = data.ImageBase + (Address)data.ProcessID;

                    sample.Metric = data.ImageSize;
                    if (data.Opcode == TraceEventOpcode.Stop)
                    {
                        sample.StackIndex = StackSourceCallStackIndex.Invalid;
                        StackSourceCallStackIndex allocIdx;
                        if (loadedImages.TryGetValue(imageKey, out allocIdx))
                        {
                            sample.StackIndex = allocIdx;
                        }

                        sample.Metric = -sample.Metric;
                    }
                    else
                    {
                        // Create a call stack that ends with 'Load <fileName> (<fileDirectory>)'
                        var fileName = data.FileName;
                        var nodeName = "Image Load " + GetFileName(fileName) + " (" + GetDirectoryName(fileName) + ")";
                        var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                        sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
                        loadedImages[imageKey] = sample.StackIndex;
                    }
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    stackSource.AddSample(sample);
                };
                eventSource.Kernel.ImageLoad += imageLoadUnload;
                eventSource.Kernel.ImageUnload += imageLoadUnload;
                eventSource.Process();
            }
            else if (streamName == "Net Virtual Alloc")
            {
                var droppedEvents = 0;
                var memStates = new MemState[eventLog.Processes.Count];
                eventSource.Kernel.AddCallbackForEvents<VirtualAllocTraceData>(delegate (VirtualAllocTraceData data)
                                {
                                    bool isAlloc = false;
                                    if ((data.Flags & (
                                        VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT |
                                        VirtualAllocTraceData.VirtualAllocFlags.MEM_DECOMMIT |
                                        VirtualAllocTraceData.VirtualAllocFlags.MEM_RELEASE)) != 0)
                                    {
                                        // Can't use data.Process() because some of the virtual allocs occur in the process that started the
                                        // process and occur before the process start event, which is what Process() uses to find it. 
                                        // TODO this code assumes that process launch is within 1 second and process IDs are not aggressively reused. 
                                        var processWhereMemoryAllocated = data.Log().Processes.GetProcess(data.ProcessID, data.TimeStampRelativeMSec + 1000);
                                        if (processWhereMemoryAllocated == null)
                                        {
                                            droppedEvents++;
                                            return;
                                        }

                                        var processIndex = processWhereMemoryAllocated.ProcessIndex;
                                        var memState = memStates[(int)processIndex];
                                        if (memState == null)
                                        {
                                            memState = memStates[(int)processIndex] = new MemState();
                                        }

                                        // Commit and decommit not both on together.  
                                        Debug.Assert((data.Flags &
                                                                    (VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT | VirtualAllocTraceData.VirtualAllocFlags.MEM_DECOMMIT)) !=
                                                                    (VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT | VirtualAllocTraceData.VirtualAllocFlags.MEM_DECOMMIT));

                                        var stackIndex = StackSourceCallStackIndex.Invalid;
                                        if ((data.Flags & VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT) != 0)
                                        {
                                            isAlloc = true;
                                            // Some of the early allocations are actually by the process that starts this process.  Don't use their stacks 
                                            // But do count them.  
                                            var processIDAllocatingMemory = processWhereMemoryAllocated.ProcessID;  // This is not right, but it sets the condition properly below 
                                            var thread = data.Thread();
                                            if (thread != null)
                                            {
                                                processIDAllocatingMemory = thread.Process.ProcessID;
                                            }

                                            if (data.TimeStampRelativeMSec >= processWhereMemoryAllocated.StartTimeRelativeMsec && processIDAllocatingMemory == processWhereMemoryAllocated.ProcessID)
                                            {
                                                stackIndex = stackSource.GetCallStack(data.CallStackIndex(), data);
                                            }
                                            else
                                            {
                                                stackIndex = stackSource.GetCallStackForProcess(processWhereMemoryAllocated);
                                                stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Allocated In Parent Process"), stackIndex);
                                            }
                                        }
                                        memState.Update(data.BaseAddr, data.Length, isAlloc, stackIndex,
                                            delegate (long metric, StackSourceCallStackIndex allocStack)
                                            {
                                                Debug.Assert(allocStack != StackSourceCallStackIndex.Invalid);
                                                Debug.Assert(metric != 0);                                                  // They should trim this already.  
                                                sample.Metric = metric;
                                                sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                                                sample.StackIndex = allocStack;
                                                stackSource.AddSample(sample);
                                                // Debug.WriteLine("Sample Proc {0,12} Time {1,8:f3} Length 0x{2:x} Metric 0x{3:x} Stack {4,8} Cum {5,8}", process.Name, sample.TimeRelativeMSec, data.Length, (int) sample.Metric, (int)sample.StackIndex, memState.TotalMem);
                                            });
                                    }
                                });
                eventSource.Process();
                if (droppedEvents != 0)
                {
                    log.WriteLine("WARNING: {0} events were dropped because their process could not be determined.", droppedEvents);
                }
            }
            else if (streamName == "Net Virtual Reserve")
            {
                // Mapped file (which includes image loads) logic. 
                var mappedImages = new Dictionary<Address, StackSourceCallStackIndex>(100);
                Action<MapFileTraceData> mapUnmapFile = delegate (MapFileTraceData data)
                {
                    sample.Metric = data.ViewSize;
                    // If it is a UnMapFile or MapFileDCStop event
                    if (data.Opcode == (TraceEventOpcode)38)
                    {
                        Debug.Assert(data.OpcodeName == "UnmapFile");
                        sample.StackIndex = StackSourceCallStackIndex.Invalid;
                        StackSourceCallStackIndex allocIdx;
                        if (mappedImages.TryGetValue(data.FileKey, out allocIdx))
                        {
                            sample.StackIndex = allocIdx;
                            mappedImages.Remove(data.FileKey);
                        }
                        sample.Metric = -sample.Metric;
                    }
                    else
                    {
                        Debug.Assert(data.OpcodeName == "MapFile" || data.OpcodeName == "MapFileDCStart");
                        // Create a call stack that ends with 'MapFile <fileName> (<fileDirectory>)'
                        var nodeName = "MapFile";
                        var fileName = data.FileName;
                        if (fileName.Length > 0)
                        {
                            nodeName = nodeName + " " + GetFileName(fileName) + " (" + GetDirectoryName(fileName) + ")";
                        }

                        var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                        sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
                        mappedImages[data.FileKey] = sample.StackIndex;
                    }
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    stackSource.AddSample(sample);
                };
                eventSource.Kernel.FileIOMapFile += mapUnmapFile;
                eventSource.Kernel.FileIOUnmapFile += mapUnmapFile;
                eventSource.Kernel.FileIOMapFileDCStart += mapUnmapFile;

                // Virtual Alloc logic
                var droppedEvents = 0;
                var memStates = new MemState[eventLog.Processes.Count];
                var virtualReserverFrame = stackSource.Interner.FrameIntern("VirtualReserve");
                eventSource.Kernel.AddCallbackForEvents<VirtualAllocTraceData>(delegate (VirtualAllocTraceData data)
                {
                    bool isAlloc = false;
                    if ((data.Flags & (
                        VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT |
                        VirtualAllocTraceData.VirtualAllocFlags.MEM_RESERVE |
                        VirtualAllocTraceData.VirtualAllocFlags.MEM_RELEASE)) != 0)
                    {
                        // Can't use data.Process() because some of the virtual allocs occur in the process that started the
                        // process and occur before the process start event, which is what Process() uses to find it. 
                        // TODO this code assumes that process launch is within 1 second and process IDs are not aggressively reused. 
                        var processWhereMemoryAllocated = data.Log().Processes.GetProcess(data.ProcessID, data.TimeStampRelativeMSec + 1000);
                        if (processWhereMemoryAllocated == null)
                        {
                            droppedEvents++;
                            return;
                        }

                        var processIndex = processWhereMemoryAllocated.ProcessIndex;
                        var memState = memStates[(int)processIndex];
                        if (memState == null)
                        {
                            memState = memStates[(int)processIndex] = new MemState();
                        }

                        // Commit and decommit not both on together.  
                        Debug.Assert((data.Flags &
                                                    (VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT | VirtualAllocTraceData.VirtualAllocFlags.MEM_DECOMMIT)) !=
                                                    (VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT | VirtualAllocTraceData.VirtualAllocFlags.MEM_DECOMMIT));
                        // Reserve and release not both on together.
                        Debug.Assert((data.Flags &
                                            (VirtualAllocTraceData.VirtualAllocFlags.MEM_RESERVE | VirtualAllocTraceData.VirtualAllocFlags.MEM_RELEASE)) !=
                                            (VirtualAllocTraceData.VirtualAllocFlags.MEM_RESERVE | VirtualAllocTraceData.VirtualAllocFlags.MEM_RELEASE));

                        // You allocate by committing or reserving.  We have already filtered out decommits which have no effect on reservation.  
                        // Thus the only memRelease is the only one that frees.  
                        var stackIndex = StackSourceCallStackIndex.Invalid;
                        if ((data.Flags & (VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT | VirtualAllocTraceData.VirtualAllocFlags.MEM_RESERVE)) != 0)
                        {
                            isAlloc = true;
                            // Some of the early allocations are actually by the process that starts this process.  Don't use their stacks 
                            // But do count them.  
                            var processIDAllocatingMemory = processWhereMemoryAllocated.ProcessID;  // This is not right, but it sets the condition properly below 
                            var thread = data.Thread();
                            if (thread != null)
                            {
                                processIDAllocatingMemory = thread.Process.ProcessID;
                            }

                            if (data.TimeStampRelativeMSec >= processWhereMemoryAllocated.StartTimeRelativeMsec && processIDAllocatingMemory == processWhereMemoryAllocated.ProcessID)
                            {
                                stackIndex = stackSource.GetCallStack(data.CallStackIndex(), data);
                            }
                            else
                            {
                                stackIndex = stackSource.GetCallStackForProcess(processWhereMemoryAllocated);
                                stackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Allocated In Parent Process"), stackIndex);
                            }
                            stackIndex = stackSource.Interner.CallStackIntern(virtualReserverFrame, stackIndex);
                        }
                        memState.Update(data.BaseAddr, data.Length, isAlloc, stackIndex,
                            delegate (long metric, StackSourceCallStackIndex allocStack)
                            {
                                Debug.Assert(allocStack != StackSourceCallStackIndex.Invalid);
                                Debug.Assert(metric != 0);                                                  // They should trim this already.  
                                sample.Metric = metric;
                                sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                                sample.StackIndex = allocStack;
                                stackSource.AddSample(sample);
                                // Debug.WriteLine("Sample Proc {0,12} Time {1,8:f3} Length 0x{2:x} Metric 0x{3:x} Stack {4,8} Cum {5,8}", process.Name, sample.TimeRelativeMSec, data.Length, (int) sample.Metric, (int)sample.StackIndex, memState.TotalMem);
                            });
                    }
                });
                eventSource.Process();
                if (droppedEvents != 0)
                {
                    log.WriteLine("WARNING: {0} events were dropped because their process could not be determined.", droppedEvents);
                }
            }
            else if (streamName == "Net OS Heap Alloc")
            {
                // We index by heap address and then within the heap we remember the allocation stack
                var heaps = new Dictionary<Address, Dictionary<Address, StackSourceSample>>();

                var heapParser = new HeapTraceProviderTraceEventParser(eventSource);
                Dictionary<Address, StackSourceSample> lastHeapAllocs = null;

                // These three variables are used in the local function GetAllocationType defined below.
                // and are used to look up type names associated with the native allocations.   
                var loadedModules = new Dictionary<TraceModuleFile, NativeSymbolModule>();
                var allocationTypeNames = new Dictionary<CallStackIndex, string>();
                var symReader = GetSymbolReader(log, SymbolReaderOptions.CacheOnly);

                Address lastHeapHandle = 0;

                float peakMetric = 0;
                StackSourceSample peakSample = null;
                float cumMetric = 0;
                float sumCumMetric = 0;
                int cumCount = 0;

                heapParser.HeapTraceAlloc += delegate (HeapAllocTraceData data)
                {
                    var allocs = lastHeapAllocs;
                    if (data.HeapHandle != lastHeapHandle)
                        allocs = GetHeap(data.HeapHandle, heaps, ref lastHeapAllocs, ref lastHeapHandle);

                    var callStackIndex = data.CallStackIndex();
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    sample.Metric = data.AllocSize;
                    sample.StackIndex = stackSource.GetCallStack(callStackIndex, data);

                    // Add the 'Alloc < XXX' pseudo node. 
                    var nodeIndex = stackSource.Interner.FrameIntern(GetAllocName((uint)data.AllocSize));
                    sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, sample.StackIndex);

                    // Add the 'Type ALLOCATION_TYPE' if available.  
                    string allocationType = GetAllocationType(callStackIndex);
                    if (allocationType != null)
                    {
                        nodeIndex = stackSource.Interner.FrameIntern("Type " + allocationType);
                        sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, sample.StackIndex);
                    }

                    var addedSample = stackSource.AddSample(sample);
                    allocs[data.AllocAddress] = addedSample;

                    cumMetric += sample.Metric;
                    if (cumMetric > peakMetric)
                    {
                        peakMetric = cumMetric;
                        peakSample = addedSample;
                    }
                    sumCumMetric += cumMetric;
                    cumCount++;

                    /*****************************************************************************/
                    // Performs a stack crawl to match the best typename to this allocation. 
                    // Returns null if no typename was found.
                    // This updates loadedModules and allocationTypeNames. It reads symReader/eventLog.
                    string GetAllocationType(CallStackIndex csi)
                    {
                        if (!allocationTypeNames.TryGetValue(csi, out var typeName))
                        {
                            const int frameLimit = 25; // typically you need about 10 frames to get out of the OS functions 
                                                       // to get to a frame that has type information.   We'll search up this many frames
                                                       // before giving up on getting type information for the allocation.  

                            int frameCount = 0;
                            for (var current = csi; current != CallStackIndex.Invalid && frameCount < frameLimit; current = eventLog.CallStacks.Caller(current), frameCount++)
                            {
                                var module = eventLog.CodeAddresses.ModuleFile(eventLog.CallStacks.CodeAddressIndex(current));
                                if (module == null)
                                    continue;

                                if (!loadedModules.TryGetValue(module, out var symbolModule))
                                {
                                    loadedModules[module] = symbolModule =
                                        (module.PdbSignature != Guid.Empty
                                            ? symReader.FindSymbolFilePath(module.PdbName, module.PdbSignature, module.PdbAge, module.FilePath)
                                            : symReader.FindSymbolFilePathForModule(module.FilePath)) is string pdb
                                        ? symReader.OpenNativeSymbolFile(pdb)
                                        : null;
                                }

                                typeName = symbolModule?.GetTypeForHeapAllocationSite(
                                        (uint)(eventLog.CodeAddresses.Address(eventLog.CallStacks.CodeAddressIndex(current)) - module.ImageBase)
                                    ) ?? typeName;
                            }
                            allocationTypeNames[csi] = typeName;
                        }
                        return typeName;
                    }
                };

                heapParser.HeapTraceFree += delegate (HeapFreeTraceData data)
                {
                    var allocs = lastHeapAllocs;
                    if (data.HeapHandle != lastHeapHandle)
                    {
                        allocs = GetHeap(data.HeapHandle, heaps, ref lastHeapAllocs, ref lastHeapHandle);
                    }

                    StackSourceSample alloc;
                    if (allocs.TryGetValue(data.FreeAddress, out alloc))
                    {
                        cumMetric -= alloc.Metric;
                        sumCumMetric += cumMetric;
                        cumCount++;

                        allocs.Remove(data.FreeAddress);

                        Debug.Assert(alloc.Metric >= 0);
                        sample.Metric = -alloc.Metric;
                        sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                        sample.StackIndex = alloc.StackIndex;
                        stackSource.AddSample(sample);
                    }
                };

                heapParser.HeapTraceReAlloc += delegate (HeapReallocTraceData data)
                {
                    // Reallocs that actually move stuff will cause a Alloc and delete event
                    // so there is nothing to do for those events.  But when the address is
                    // the same we need to resize 
                    if (data.OldAllocAddress != data.NewAllocAddress)
                    {
                        return;
                    }

                    var allocs = lastHeapAllocs;
                    if (data.HeapHandle != lastHeapHandle)
                    {
                        allocs = GetHeap(data.HeapHandle, heaps, ref lastHeapAllocs, ref lastHeapHandle);
                    }

                    // This is a clone of the Free code 
                    StackSourceSample alloc;
                    if (allocs.TryGetValue(data.OldAllocAddress, out alloc))
                    {
                        cumMetric -= alloc.Metric;
                        sumCumMetric += cumMetric;
                        cumCount++;

                        allocs.Remove(data.OldAllocAddress);

                        Debug.Assert(alloc.Metric == data.OldAllocSize);
                        sample.Metric = -alloc.Metric;
                        sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                        sample.StackIndex = alloc.StackIndex;
                        stackSource.AddSample(sample);
                    }

                    // This is a clone of the Alloc code (sigh don't clone code)
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    sample.Metric = data.NewAllocSize;
                    var nodeIndex = stackSource.Interner.FrameIntern(GetAllocName((uint)data.NewAllocSize));
                    sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
                    var addedSample = stackSource.AddSample(sample);
                    allocs[data.NewAllocAddress] = addedSample;

                    cumMetric += sample.Metric;
                    if (cumMetric > peakMetric)
                    {
                        peakMetric = cumMetric;
                        peakSample = addedSample;
                    }
                    sumCumMetric += cumMetric;
                    cumCount++;
                };

                heapParser.HeapTraceDestroy += delegate (HeapTraceData data)
                {
                    // Heap is dieing, kill all objects in it.   
                    var allocs = lastHeapAllocs;
                    if (data.HeapHandle != lastHeapHandle)
                    {
                        allocs = GetHeap(data.HeapHandle, heaps, ref lastHeapAllocs, ref lastHeapHandle);
                    }

                    foreach (StackSourceSample alloc in allocs.Values)
                    {
                        // TODO this is a clone of the free code.  
                        cumMetric -= alloc.Metric;
                        sumCumMetric += cumMetric;
                        cumCount++;

                        Debug.Assert(alloc.Metric >= 0);
                        sample.Metric = -alloc.Metric;
                        sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                        sample.StackIndex = alloc.StackIndex;
                        stackSource.AddSample(sample);
                    }
                };
                eventSource.Process();

                var aveCumMetric = sumCumMetric / cumCount;
                log.WriteLine("Peak Heap Size: {0:n3} MB   Average Heap size: {1:n3} MB", peakMetric / 1000000.0F, aveCumMetric / 1000000.0F);
                if (peakSample != null)
                {
                    log.WriteLine("Peak happens at {0:n3} Msec into the trace.", peakSample.TimeRelativeMSec);
                }

                log.WriteLine("Trimming alloc-free pairs < 3 msec apart: Before we have {0:n1}K events now {1:n1}K events",
                    cumCount / 1000.0, stackSource.SampleIndexLimit / 1000.0);
                return stackSource;
            }
            else if (streamName == "Server GC")
            {
                Microsoft.Diagnostics.Tracing.Analysis.TraceLoadedDotNetRuntimeExtensions.NeedLoadedDotNetRuntimes(eventSource);
                Microsoft.Diagnostics.Tracing.Analysis.TraceProcessesExtensions.AddCallbackOnProcessStart(eventSource, proc =>
                {
                    Microsoft.Diagnostics.Tracing.Analysis.TraceProcessesExtensions.SetSampleIntervalMSec(proc, (float)eventLog.SampleProfileInterval.TotalMilliseconds);
                    Microsoft.Diagnostics.Tracing.Analysis.TraceLoadedDotNetRuntimeExtensions.SetMutableTraceEventStackSource(proc, stackSource);
                });
                eventSource.Process();
                return stackSource;
            }
            else if(streamName == "Anti-Malware Real-Time Scan")
            {
                RealtimeAntimalwareComputer computer = new RealtimeAntimalwareComputer(eventSource, stackSource);
                computer.Execute();

                return stackSource;
            }
            else
            {
                throw new Exception("Unknown stream " + streamName);
            }

            log.WriteLine("Produced {0:n3}K events", stackSource.SampleIndexLimit / 1000.0);
            stackSource.DoneAddingSamples();
            return stackSource;
        }