public StackSource GetStackSource(TraceEvents events)
        {
            if (events == null)
            {
                ThrowHelper.ThrowArgumentNullException(nameof(events));
            }

            TraceLog log         = events.Log;
            var      stackSource = new MutableTraceEventStackSource(log)
            {
                ShowUnknownAddresses = true
            };
            var eventSource = events.GetSource();

            var sample = new StackSourceSample(stackSource);

            var kernelTraceEventParser = new KernelTraceEventParser(eventSource);

            kernelTraceEventParser.PerfInfoSample += delegate(SampledProfileTraceData data)
            {
                sample.Metric           = events.Log.SampleProfileInterval.Milliseconds;
                sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                sample.StackIndex       = stackSource.GetCallStack(data.CallStackIndex(), data);
                stackSource.AddSample(sample);
            };

            eventSource.Process();
            return(stackSource);
        }
Ejemplo n.º 2
0
        public StackSource GetStackSource(TraceEvents events)
        {
            if (events == null)
            {
                ThrowHelper.ThrowArgumentNullException(nameof(events));
            }

            var stackSource = new MutableTraceEventStackSource(events.Log)
            {
                ShowUnknownAddresses = true
            };
            var eventSource = events.GetSource();

            var sample = new StackSourceSample(stackSource);

            var clrTraceEventParser = new ClrTraceEventParser(eventSource);

            clrTraceEventParser.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.Process();
            return(stackSource);
        }
Ejemplo n.º 3
0
        public static StackSource AnyStacks(this TraceLog eventLog, TraceProcess process = null, bool showUnknownAddresses = false, Predicate <TraceEvent> predicate = null)
        {
            var stackSource = new MutableTraceEventStackSource(eventLog);
            var sample      = new StackSourceSample(stackSource);

            TraceEvents events = process == null?eventLog.Events.Filter(x => (predicate == null || predicate(x)) && x.ProcessID != 0) : process.EventsInProcess.Filter(x => predicate == null || predicate(x));

            var eventSource = events.GetSource();

            eventSource.AllEvents += data =>
            {
                var callStackIdx = data.CallStackIndex();
                StackSourceCallStackIndex stackIndex = callStackIdx != CallStackIndex.Invalid ? stackSource.GetCallStack(callStackIdx, data) : StackSourceCallStackIndex.Invalid;

                var eventNodeName = "Event " + data.ProviderName + "/" + data.EventName;
                stackIndex              = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern(eventNodeName), stackIndex);
                sample.StackIndex       = stackIndex;
                sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                sample.Metric           = 1;
                stackSource.AddSample(sample);
            };

            eventSource.Process();

            stackSource.DoneAddingSamples();

            return(stackSource);
        }
        /// <summary>
        /// Cache/split IP Module/Frame
        /// </summary>
        /// <param name="sample"></param>
        /// <param name="stackSource"></param>
        /// <returns></returns>
        private StackFrame GetIpStackFrame(StackSourceSample sample, ParallelLinuxPerfScriptStackSource stackSource)
        {
            if (sample.SampleIndex == StackSourceSampleIndex.Invalid)
            {
                return(new StackFrame(String.Empty, String.Empty));
            }

            var        si = sample.StackIndex;
            StackFrame sf;

            if (IpStackFrames.TryGetValue((int)si, out sf))
            {
                return(sf);
            }
            else
            {
                var frame     = stackSource.GetFrameIndex(si);
                var frameName = stackSource.Interner.GetFrameName(frame, false);

                var frameNameSplit = frameName.Split('!');
                if (frameNameSplit.Length == 2)
                {
                    sf = new StackFrame(frameNameSplit[0], frameNameSplit[1]);
                    IpStackFrames.TryAdd((int)si, sf);
                    return(sf);
                }
                return(null);
            }
        }
Ejemplo n.º 5
0
 internal TraceEventStackSource(TraceLog log)
 {
     m_log = log;
     m_goodTopModuleIndex = ModuleFileIndex.Invalid;
     m_curSample          = new StackSourceSample(this);
     m_curSample.Metric   = log.SampleProfileInterval100ns / 10000.0F;
 }
Ejemplo n.º 6
0
        public void Read(TextReader reader)
        {
            var sample = new StackSourceSample(this);

            sample.Metric = 1;
            for (; ;)
            {
                var line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }

                if (StackForLine != null)
                {
                    sample.StackIndex = StackForLine(Interner, line);
                }
                else
                {
                    // Form the stack for this entry (trivial one element stack)
                    var frameIndex = Interner.FrameIntern(line);
                    sample.StackIndex = Interner.CallStackIntern(frameIndex, StackSourceCallStackIndex.Invalid);
                }
                if (sample.StackIndex != StackSourceCallStackIndex.Invalid)
                {
                    AddSample(sample);
                }
            }
            Interner.DoneInterning();
        }
Ejemplo n.º 7
0
 internal TraceEventStackSource(TraceLog log)
 {
     m_log = log;
     m_goodTopModuleIndex = ModuleFileIndex.Invalid;
     m_curSample          = new StackSourceSample(this);
     m_curSample.Metric   = (float)log.SampleProfileInterval.TotalMilliseconds;
 }
Ejemplo n.º 8
0
        /// <summary>
        /// Convert a StackSourceSample produced by a sub-source into one suitable for the aggregate source.
        /// </summary>
        /// <param name="input">The StackSourceSample to convert.</param>
        /// <param name="storage">A place to but the returned sampled (will become the return value).</param>
        /// <param name="sourceIdx">The index of the source from which the sample came.</param>
        /// <returns>The converted sample.</returns>
        /// <remarks>
        /// If ConvertSample is called again, all previous samples produced by ConvertSample may no longer be used.
        /// </remarks>
        private StackSourceSample ConvertSample(StackSourceSample input, StackSourceSample storage, int sourceIdx)
        {
            storage.Metric = input.Metric;

            // We normalize all the scenarios so that they start on their first sample time.
            var timeOrigin = m_firstSampleTime[sourceIdx];

            if (timeOrigin < 0)
            {
                timeOrigin = m_firstSampleTime[sourceIdx] = input.TimeRelativeMSec;
            }

            storage.TimeRelativeMSec = input.TimeRelativeMSec - timeOrigin;

            storage.StackIndex = m_stackMap.IndexOf(sourceIdx, input.StackIndex);
            storage.Scenario   = sourceIdx - 1;

            if (m_sampleMap != null)
            {
                storage.SampleIndex = m_sampleMap.IndexOf(sourceIdx - 1, input.SampleIndex);
            }
            else
            {
                storage.SampleIndex = StackSourceSampleIndex.Invalid;
            }

            return(storage);
        }
Ejemplo n.º 9
0
        public StackSource GetStackSource(TraceEvents events)
        {
            if (events == null)
            {
                ThrowHelper.ThrowArgumentNullException(nameof(events));
            }

            var stackSource = new MutableTraceEventStackSource(events.Log);
            var eventSource = events.GetSource();

            var sample = new StackSourceSample(stackSource);

            var clrTraceEventParser = new ClrTraceEventParser(eventSource);

            clrTraceEventParser.GCAllocationTick += delegate(GCAllocationTickTraceData data)
            {
                var size = data.AllocationAmount64;
                sample.Metric           = size;
                sample.Count            = 1;
                sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("Type " + data.TypeName), stackSource.GetCallStack(data.CallStackIndex(), data));
                if (data.AllocationKind == GCAllocationKind.Large)
                {
                    sample.StackIndex = stackSource.Interner.CallStackIntern(stackSource.Interner.FrameIntern("LargeObject"), sample.StackIndex);
                }

                stackSource.AddSample(sample);
            };

            eventSource.Process();
            return(stackSource);
        }
Ejemplo n.º 10
0
        public StackSource GetStackSource(TraceEvents events)
        {
            if (events == null)
            {
                ThrowHelper.ThrowArgumentNullException(nameof(events));
            }

            var stackSource = new MutableTraceEventStackSource(events.Log)
            {
                ShowUnknownAddresses = true
            };
            var eventSource = events.GetSource();

            var sample = new StackSourceSample(stackSource);

            var clrTraceEventParser = new ClrTraceEventParser(eventSource);

            clrTraceEventParser.ContentionStart += delegate(ContentionTraceData data)
            {
                sample.Metric           = 1;
                sample.TimeRelativeMSec = data.TimeStampRelativeMSec;

                string nodeName  = data.ContentionFlags == ContentionFlags.Native ? "Native Contention" : "Managed Contention";
                var    nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
                stackSource.AddSample(sample);
            };

            eventSource.Process();
            return(stackSource);
        }
 internal FileScanOperationCollection(TraceLog traceLog, MutableTraceEventStackSource stackSource)
 {
     _traceLog              = traceLog;
     _stackSource           = stackSource;
     _sample                = new StackSourceSample(_stackSource);
     _engineThreadToScanMap = new FileScanOperation[_traceLog.Threads.Count];
 }
Ejemplo n.º 12
0
        /// <summary>
        /// Create a stack source from 'graph'.   samplingRatio is the ratio of size of the graph to
        /// the size of the actual heap (if you only sampled part of it).   Counts are scaled by the
        /// inverse of this so that the expected size of the graph works out.
        ///
        /// log is were to send diagnostic messages (can be null)
        ///
        /// countMultipliers is an array (indexed by type Index), that will be used to multiply the
        /// counts in 'graph' when generating the stack source (thus if Type T has count 5 and
        /// countMultipliers[T] = 10 then the stack source will return 50.   This is used to scale
        /// sampled graphs.
        /// </summary>
        public MemoryGraphStackSource(Graph graph, TextWriter log, float[] countMultipliers = null)
        {
            m_asMemoryGraph    = graph as MemoryGraph;
            m_graph            = graph;
            m_log              = log;
            m_nodeStorage      = graph.AllocNodeStorage();
            m_childStorage     = graph.AllocNodeStorage();
            m_typeStorage      = graph.AllocTypeNodeStorage();
            m_sampleStorage    = new StackSourceSample(this);
            m_countMultipliers = countMultipliers;

            // We need to reduce the graph to a tree.   Each node is assigned a unique 'parent' which is its
            // parent in a spanning tree of the graph.
            // The +1 is for orphan node support.
            m_parent = new NodeIndex[(int)graph.NodeIndexLimit + 1];

            // If it is a memory stack source (it pretty much always is), figure out the maximum address.
            // We use addresses as 'time' for stacks so that the 'when' field in perfView is meaningful.
            MemoryGraph asMemoryGraph = graph as MemoryGraph;

            if (asMemoryGraph != null)
            {
                for (NodeIndex idx = 0; idx < asMemoryGraph.NodeIndexLimit; idx++)
                {
                    Address endAddress = asMemoryGraph.GetAddress(idx) + (uint)asMemoryGraph.GetNode(idx, m_nodeStorage).Size;
                    if (m_maxAddress < endAddress)
                    {
                        m_maxAddress = endAddress;
                    }
                }
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Initialize a new AggregateStackSource.
        /// </summary>
        /// <param name="sources">An IEnumerable of KeyValuePairs mapping source names to StackSources.</param>
        public AggregateStackSource(IEnumerable <KeyValuePair <string, StackSource> > sources)
        {
            m_sourceCount = sources.Count() + 1; // +1 for the pseudo-source.

            m_sourceNames = new string[m_sourceCount];
            m_sources     = new StackSource[m_sourceCount];

            // We initialize this when we see the first sample
            m_firstSampleTime = new double[m_sourceCount];
            for (int j = 0; j < m_firstSampleTime.Length; j++)
            {
                m_firstSampleTime[j] = double.NegativeInfinity;
            }

            // True if all sub-sources support retrieving samples by index.
            bool supportsSamples = true;

            // The time limit for this source.
            m_RelativeMSecLimit = 0.0f;
            int i = 1;

            // Unpack sources.
            foreach (var pair in sources)
            {
                var name   = pair.Key;
                var source = pair.Value;

                m_sourceNames[i] = name;
                m_sources[i]     = source;
                i++;

                m_RelativeMSecLimit = Math.Max(m_RelativeMSecLimit, source.SampleTimeRelativeMSecLimit);

                if (source.SampleIndexLimit == 0)
                {
                    supportsSamples = false;
                }
            }

            // Set up pseudo-source.
            m_sources[0] = m_pseudo = new PseudoStackSource(m_sourceNames);

            // Set up our returned sample.
            m_sampleStorage = new StackSourceSample(this);

            // Set up index maps.
            m_stackMap = new IndexMap(m_sources.Select(s => s.CallStackIndexLimit));
            m_frameMap = new IndexMap(m_sources.Select(s => s.CallFrameIndexLimit));

            if (supportsSamples)
            {
                // The sampleMap has size (m_sourceCount - 1) because m_pseudo doesn't have samples.
                m_sampleMap = new IndexMap(m_sources.Skip(1).Select(s => s.SampleIndexLimit));
            }
            else
            {
                m_sampleMap = null;
            }
        }
Ejemplo n.º 14
0
 /// <summary>
 /// Override
 /// </summary>
 public void ParallelForEach(Action <StackSourceSample> callback, bool[] scenariosIncluded, int desiredParallelism = 0)
 {
     Parallel.For(0, ScenarioCount, delegate(int src, ParallelLoopState state)
     {
         var sampleStorage = new StackSourceSample(this);
         if (scenariosIncluded == null || scenariosIncluded[src])
         {
             m_sources[src + 1].ForEach(sample => callback(ConvertSample(sample, sampleStorage, src + 1)));
         }
     });
 }
        private string GetProcess(StackSourceSample sample, ParallelLinuxPerfScriptStackSource stackSource)
        {
            if (sample.SampleIndex == StackSourceSampleIndex.Invalid)
            {
                return(String.Empty);
            }

            string process;

            SampleProcessDict.TryGetValue((long)sample.SampleIndex, out process);
            return(process);
        }
Ejemplo n.º 16
0
 private static void PrintStack(IConsole console, int threadId, StackSourceSample stackSourceSample, StackSource stackSource)
 {
     console.Out.WriteLine($"Thread (0x{threadId:X}):");
     var stackIndex = stackSourceSample.StackIndex;
     while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), verboseName: false).StartsWith("Thread ("))
     {
         console.Out.WriteLine($"  {stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), verboseName: false)}"
             .Replace("UNMANAGED_CODE_TIME", "[Native Frames]"));
         stackIndex = stackSource.GetCallerIndex(stackIndex);
     }
     console.Out.WriteLine();
 }
 public ComputingResourceStateMachine(MutableTraceEventStackSource outputStackSource, ScenarioConfiguration configuration, ComputingResourceViewType viewType)
 {
     m_OutputStackSource = outputStackSource;
     m_Sample            = new StackSourceSample(outputStackSource);
     m_Configuration     = configuration;
     m_ViewType          = viewType;
     m_ThreadState       = new ComputingResourceThreadState[configuration.TraceLog.Threads.Count];
     for (int i = 0; i < m_ThreadState.Length; ++i)
     {
         m_ThreadState[i] = new ComputingResourceThreadState(i);
     }
 }
Ejemplo n.º 18
0
        private static void PrintStack(string threadName, StackSourceSample stackSourceSample, StackSource stackSource)
        {
            Console.WriteLine($"Stack for {threadName}:");
            var stackIndex = stackSourceSample.StackIndex;

            while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), verboseName: false).StartsWith("Thread ("))
            {
                Console.WriteLine($"  {stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), verboseName: false)}");
                stackIndex = stackSource.GetCallerIndex(stackIndex);
            }
            Console.WriteLine();
        }
        private int GetThreadId(StackSourceSample sample, ParallelLinuxPerfScriptStackSource stackSource)
        {
            if (sample.SampleIndex == StackSourceSampleIndex.Invalid)
            {
                return(-1);
            }

            int tid;

            SampleThreadDict.TryGetValue((long)sample.SampleIndex, out tid);
            return(tid);
        }
Ejemplo n.º 20
0
        /// <summary>
        /// After creating a MultableTraceEventStackSource, you add the samples you want and then call DoneAddingSamples
        /// From that point on you have a fine, read-only stacks source.
        /// </summary>
        public StackSourceSample AddSample(StackSourceSample sample)
        {
            var sampleCopy = new StackSourceSample(sample);

            sampleCopy.SampleIndex = (StackSourceSampleIndex)m_samples.Count;
            m_samples.Add(sampleCopy);
            if (sampleCopy.TimeRelMSec > m_sampleTimeRelMSecLimit)
            {
                m_sampleTimeRelMSecLimit = sampleCopy.TimeRelMSec;
            }
            return(sampleCopy);
        }
        public static StackSource Exceptions(this TraceEvents events, TraceProcess process = null, Predicate <TraceEvent> predicate = null)
        {
            // optimization only
            if (process != null)
            {
                var start = Math.Max(events.StartTimeRelativeMSec, process.StartTimeRelativeMsec);
                var end   = Math.Min(events.EndTimeRelativeMSec, process.EndTimeRelativeMsec);
                events = events.FilterByTime(start, end);
                events = events.Filter(x => (predicate == null || predicate(x)) && x.ProcessID == process.ProcessID);
            }
            else
            {
                events = events.Filter(x => (predicate == null || predicate(x)) && x.ProcessID != 0); // TODO: Is it really correc that x.ProcessID != 0 should be there? What if we want see these?
            }

            var eventSource = events.GetSource();
            var stackSource = new MutableTraceEventStackSource(events.Log)
            {
                ShowUnknownAddresses = true
            };
            var sample = new StackSourceSample(stackSource);

            eventSource.Clr.ExceptionStart += 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 += 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();

            stackSource.DoneAddingSamples();

            return(stackSource);
        }
Ejemplo n.º 22
0
 /// <summary>
 /// Creates a new TraceEventStackSource given a list of events 'events' from a TraceLog
 /// </summary>
 /// <param name="events"></param>
 public TraceEventStackSource(TraceEvents events)
 {
     Debug.Assert(m_log == null);
     if (events != null)
     {
         m_log = events.Log;
     }
     m_goodTopModuleIndex = ModuleFileIndex.Invalid;
     m_curSample          = new StackSourceSample(this);
     m_curSample.Metric   = (float)events.Log.SampleProfileInterval.TotalMilliseconds;
     m_events             = events;
     m_maxPseudoStack     = m_log.CodeAddresses.Count * 2 + m_log.Threads.Count;  // This really is a guess as to how many stacks we need.   You can have as many as codeAddresses*threads
 }
Ejemplo n.º 23
0
 internal GCPinnedObjectAnalyzer(
     string etlFilePath,
     TraceLog traceLog,
     MutableTraceEventStackSource stackSource,
     StackSourceSample sample,
     TextWriter log)
 {
     _HeapSnapshotFilePath = GetHeapSnapshotPath(etlFilePath);
     _TraceLog             = traceLog;
     _StackSource          = stackSource;
     _Sample = sample;
     _Log    = log;
 }
Ejemplo n.º 24
0
 public TraceEventStackSource(TraceEvents events)
 {
     Debug.Assert(m_log == null);
     if (events != null)
     {
         m_log = events.Log;
     }
     m_goodTopModuleIndex = ModuleFileIndex.Invalid;
     m_curSample          = new StackSourceSample(this);
     m_curSample.Metric   = events.Log.SampleProfileInterval100ns / 10000.0F;
     m_events             = events;
     m_maxPseudoStack     = m_log.CodeAddresses.MaxCodeAddressIndex; // This really is a guess as to how many stacks we need.
 }
Ejemplo n.º 25
0
        /// <summary>
        /// Implements HistogramController interface
        /// </summary>
        public override void AddSample(Histogram histogram, StackSourceSample sample)
        {
            double bucketDuration      = BucketDuration;
            double startSampleInBucket = sample.TimeRelativeMSec;
            int    bucketIndex         = (int)((sample.TimeRelativeMSec - Start) / bucketDuration);

            Debug.Assert(0 <= bucketIndex && bucketIndex <= BucketCount);

            if (Tree.ScalingPolicy == ScalingPolicyKind.TimeMetric)
            {
                // place the metric in each of the buckets it overlaps with.
                var nextBucketStart = GetStartTimeForBucket((HistogramCharacterIndex)(bucketIndex + 1));

                // The Math.Abs is a bit of a hack.  The problem is that that sample does not
                // represent time for a DIFF (because we negated it) but I rely on the fact
                // that we only negate it so I can undo it
                double endSample  = sample.TimeRelativeMSec + Math.Abs(sample.Metric);
                var    metricSign = sample.Metric > 0 ? 1 : -1;
                for (; ;)
                {
                    if (BucketCount <= bucketIndex)
                    {
                        break;
                    }

                    var metricInBucket = Math.Min(nextBucketStart, endSample) - startSampleInBucket;
                    histogram.AddMetric((float)metricInBucket * metricSign, bucketIndex);

                    bucketIndex++;
                    startSampleInBucket = nextBucketStart;
                    nextBucketStart    += bucketDuration;
                    if (startSampleInBucket > endSample)
                    {
                        break;
                    }
                }
            }
            else
            {
                // Put the sample in the right bucket.  Note that because we allow inclusive times on the end
                // point we could get bucketIndex == Length, so put that sample in the last bucket.
                if (bucketIndex >= BucketCount)
                {
                    bucketIndex = BucketCount - 1;
                }

                histogram.AddMetric(sample.Metric, bucketIndex);
            }
        }
Ejemplo n.º 26
0
        public override StackSourceSample GetSampleByIndex(StackSourceSampleIndex sampleIndex)
        {
            if (m_sample == null)
            {
                m_sample = new StackSourceSample(this);
            }

            var allocs = m_gcHeap.Allocs;

            m_sample.SampleIndex      = sampleIndex;;
            m_sample.StackIndex       = (StackSourceCallStackIndex)(m_clrProfiler.StackIdLimit + (int)allocs[(int)sampleIndex].AllocId);
            m_sample.Metric           = allocs[(int)sampleIndex].Size;
            m_sample.TimeRelativeMSec = allocs[(int)sampleIndex].MsecFromStart;
            return(m_sample);
        }
        /// <summary>
        /// Given a Linux event gotten from the trace, make its corresponding sample for the stack source.
        /// </summary>
        public StackSourceSample CreateSampleFor(LinuxEvent linuxEvent, BlockedTimeAnalyzer blockedTimeAnalyzer)
        {
            IEnumerable <Frame>       frames     = linuxEvent.CallerStacks;
            StackSourceCallStackIndex stackIndex = this.currentStackIndex;

            var sample = new StackSourceSample(this);

            sample.TimeRelativeMSec = linuxEvent.TimeMSec;
            sample.Metric           = 1;

            stackIndex        = this.InternFrames(frames.GetEnumerator(), stackIndex, linuxEvent.ProcessID, linuxEvent.ThreadID, blockedTimeAnalyzer);
            sample.StackIndex = stackIndex;

            return(sample);
        }
        /// <summary>
        /// Given a Linux event gotten from the trace, make its corresponding sample for the stack source.
        /// </summary>
        public StackSourceSample CreateSampleFor(LinuxEvent linuxEvent, BlockedTimeAnalyzer blockedTimeAnalyzer)
        {
            IEnumerable <Frame>       frames     = linuxEvent.CallerStacks;
            StackSourceCallStackIndex stackIndex = currentStackIndex;

            var sample = new StackSourceSample(this);

            sample.TimeRelativeMSec = linuxEvent.TimeMSec - StartTimeStampMSec;
            sample.Metric           = (float)linuxEvent.Period;

            stackIndex        = InternFrames(frames.GetEnumerator(), stackIndex, linuxEvent.ProcessID, linuxEvent.ThreadID, doThreadTime ? blockedTimeAnalyzer : null);
            sample.StackIndex = stackIndex;

            return(sample);
        }
        /// <summary>
        /// Mark the thread as unblocked.
        /// </summary>
        public void LogBlockingStop(
            ComputingResourceStateMachine stateMachine,
            TraceThread thread,
            TraceEvent data)
        {
            if ((null == stateMachine) || (null == thread) || (null == data))
            {
                return;
            }

            // Only add a sample if the thread was blocked.
            if (ThreadBlocked)
            {
                StackSourceSample            sample      = stateMachine.Sample;
                MutableTraceEventStackSource stackSource = stateMachine.StackSource;

                // Set the time and metric.
                sample.TimeRelativeMSec = this.BlockTimeStartRelativeMSec;
                sample.Metric           = (float)(data.TimeStampRelativeMSec - this.BlockTimeStartRelativeMSec);

                /* Generate the stack trace. */

                CallStackIndex            traceLogCallStackIndex = data.CallStackIndex();
                ScenarioThreadState       scenarioThreadState    = stateMachine.Configuration.ScenarioThreadState[ThreadIndex];
                StackSourceCallStackIndex callStackIndex         = scenarioThreadState.GetCallStackIndex(stateMachine.StackSource, thread, data);

                // Add the thread.
                StackSourceFrameIndex threadFrameIndex = stackSource.Interner.FrameIntern(thread.VerboseThreadName);
                callStackIndex = stackSource.Interner.CallStackIntern(threadFrameIndex, callStackIndex);

                // Add the full call stack.
                callStackIndex = stackSource.GetCallStack(traceLogCallStackIndex, callStackIndex, null);

                // Add Pseud-frames representing the kind of resource.
                StackSourceFrameIndex frameIndex = stackSource.Interner.FrameIntern("BLOCKED TIME");
                callStackIndex = stackSource.Interner.CallStackIntern(frameIndex, callStackIndex);

                // Add the call stack to the sample.
                sample.StackIndex = callStackIndex;

                // Add the sample.
                stackSource.AddSample(sample);

                // Mark the thread as executing.
                BlockTimeStartRelativeMSec = -1;
            }
        }
Ejemplo n.º 30
0
        public override StackSourceSample GetSampleByIndex(StackSourceSampleIndex sampleIndex)
        {
            if (m_sample == null)
            {
                m_sample = new StackSourceSample(this);
            }

            var stackId = (ProfilerStackTraceID)m_calls[(int)sampleIndex];

            m_sample.SampleIndex = sampleIndex;
            // subtract 1 so 0 (clrprofiler Scentinal) becomes CallStackIndex Sentinal
            m_sample.StackIndex = (StackSourceCallStackIndex)(stackId - 1);
            var method = m_clrProfiler.Method(stackId);
            var stats  = (MethodStats)method.UserData;

            m_sample.Metric = (float)(((double)method.size) / stats.count);
            return(m_sample);
        }
Ejemplo n.º 31
0
        internal static IDictionary<int, GCProcess> Collect(
            TraceEventSource source, 
            float sampleIntervalMSec, 
            IDictionary<int, GCProcess> perProc = null, 
            MutableTraceEventStackSource stackSource = null,
            Predicate<TraceEvent> filterFunc = null)
        {
            if (perProc == null)
            {
                perProc = new Dictionary<int, GCProcess>();
            }

            source.Kernel.AddCallbackForEvents(delegate (ProcessCtrTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                var stats = perProc.GetOrCreate(data.ProcessID);
                stats.PeakVirtualMB = ((double)data.PeakVirtualSize) / 1000000.0;
                stats.PeakWorkingSetMB = ((double)data.PeakWorkingSetSize) / 1000000.0;
            });

            Action<RuntimeInformationTraceData> doAtRuntimeStart = delegate (RuntimeInformationTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                var stats = perProc.GetOrCreate(data.ProcessID);
                stats.RuntimeVersion = "V " + data.VMMajorVersion.ToString() + "." + data.VMMinorVersion + "." + data.VMBuildNumber
                    + "." + data.VMQfeNumber;
                stats.StartupFlags = data.StartupFlags;
                stats.Bitness = (data.RuntimeDllPath.ToLower().Contains("framework64") ? 64 : 32);
                if (stats.CommandLine == null)
                    stats.CommandLine = data.CommandLine;
            };

            // log at both startup and rundown
            //var clrRundown = new ClrRundownTraceEventParser(source);
            //clrRundown.RuntimeStart += doAtRuntimeStart;
            source.Clr.AddCallbackForEvent("Runtime/Start", doAtRuntimeStart);

            Action<ProcessTraceData> processStartCallback = data =>
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                var stats = perProc.GetOrCreate(data.ProcessID);

                if (!string.IsNullOrEmpty(data.KernelImageFileName))
                {
                    // When we just have an EventSource (eg, the source was created by 
                    // ETWTraceEventSource), we don't necessarily have the process names
                    // decoded - it all depends on whether we have seen a ProcessStartGroup 
                    // event or not. When the trace was taken after the process started we 
                    // know we didn't see such an event.
                    string name = GetImageName(data.KernelImageFileName);

                    // Strictly speaking, this is not really fixing it 'cause 
                    // it doesn't handle when a process with the same name starts
                    // with the same pid. The chance of that happening is really small.
                    if (stats.isDead == true)
                    {
                        stats = new GCProcess();
                        stats.Init(data);
                        perProc[data.ProcessID] = stats;
                    }
                }

                var commandLine = data.CommandLine;
                if (!string.IsNullOrEmpty(commandLine))
                    stats.CommandLine = commandLine;
            };

            source.Kernel.AddCallbackForEvent("Process/Start", processStartCallback);
            source.Kernel.AddCallbackForEvent("Process/DCStart", processStartCallback);

            Action<ProcessTraceData> processEndCallback = delegate (ProcessTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                var stats = perProc.GetOrCreate(data.ProcessID);

                if (string.IsNullOrEmpty(stats.ProcessName))
                {
                    stats.ProcessName = GetImageName(data.KernelImageFileName);
                }

                if (data.OpcodeName == "Stop")
                {
                    stats.isDead = true;
                }
            };

            source.Kernel.AddCallbackForEvent("Process/Stop", processEndCallback);
            source.Kernel.AddCallbackForEvent("Process/DCStop", processEndCallback);

#if (!CAP)
            CircularBuffer<ThreadWorkSpan> RecentThreadSwitches = new CircularBuffer<ThreadWorkSpan>(10000);
            source.Kernel.AddCallbackForEvent("Thread/CSwitch", delegate (CSwitchTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                RecentThreadSwitches.Add(new ThreadWorkSpan(data));
                GCProcess stats;
                if (perProc.TryGetValue(data.ProcessID, out stats))
                {
                    stats.ThreadId2Priority[data.NewThreadID] = data.NewThreadPriority;
                    if (stats.IsServerGCThread(data.ThreadID) > -1)
                    {
                        stats.ServerGcHeap2ThreadId[data.ProcessorNumber] = data.ThreadID;
                    }
                }

                foreach (var gcProcess in perProc.Values)
                {
                    GCEvent _event = gcProcess.GetCurrentGC();
                    // If we are in the middle of a GC.
                    if (_event != null)
                    {
                        if ((_event.Type != GCType.BackgroundGC) && (gcProcess.isServerGCUsed == 1))
                        {
                            _event.AddServerGcThreadSwitch(new ThreadWorkSpan(data));
                        }
                    }
                }
            });

            CircularBuffer<ThreadWorkSpan> RecentCpuSamples = new CircularBuffer<ThreadWorkSpan>(1000);
            StackSourceSample sample = new StackSourceSample(stackSource);

            source.Kernel.AddCallbackForEvent("PerfInfo/Sample", delegate (SampledProfileTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                RecentCpuSamples.Add(new ThreadWorkSpan(data));
                GCProcess processWithGc = null;
                foreach (var gcProcess in perProc.Values)
                {
                    GCEvent e = gcProcess.GetCurrentGC();
                    // If we are in the middle of a GC.
                    if (e != null)
                    {
                        if ((e.Type != GCType.BackgroundGC) && (gcProcess.isServerGCUsed == 1))
                        {
                            e.AddServerGcSample(new ThreadWorkSpan(data));
                            processWithGc = gcProcess;
                        }
                    }
                }

                if (stackSource != null && processWithGc != null)
                {
                    GCEvent e = processWithGc.GetCurrentGC();
                    sample.Metric = 1;
                    sample.TimeRelativeMSec = data.TimeStampRelativeMSec;
                    var nodeName = string.Format("Server GCs #{0} in {1} (PID:{2})", e.GCNumber, processWithGc.ProcessName, processWithGc.ProcessID);
                    var nodeIndex = stackSource.Interner.FrameIntern(nodeName);
                    sample.StackIndex = stackSource.Interner.CallStackIntern(nodeIndex, stackSource.GetCallStack(data.CallStackIndex(), data));
                    stackSource.AddSample(sample);
                }

                GCProcess stats;
                if (perProc.TryGetValue(data.ProcessID, out stats))
                {
                    if (stats.IsServerGCThread(data.ThreadID) > -1)
                    {
                        stats.ServerGcHeap2ThreadId[data.ProcessorNumber] = data.ThreadID;
                    }

                    var cpuIncrement = sampleIntervalMSec;
                    stats.ProcessCpuMSec += cpuIncrement;

                    GCEvent _event = stats.GetCurrentGC();
                    // If we are in the middle of a GC.
                    if (_event != null)
                    {
                        bool isThreadDoingGC = false;
                        if ((_event.Type != GCType.BackgroundGC) && (stats.isServerGCUsed == 1))
                        {
                            int heapIndex = stats.IsServerGCThread(data.ThreadID);
                            if (heapIndex != -1)
                            {
                                _event.AddServerGCThreadTime(heapIndex, cpuIncrement);
                                isThreadDoingGC = true;
                            }
                        }
                        else if (data.ThreadID == stats.suspendThreadIDGC)
                        {
                            _event.GCCpuMSec += cpuIncrement;
                            isThreadDoingGC = true;
                        }
                        else if (stats.IsBGCThread(data.ThreadID))
                        {
                            Debug.Assert(stats.currentBGC != null);
                            if (stats.currentBGC != null)
                                stats.currentBGC.GCCpuMSec += cpuIncrement;
                            isThreadDoingGC = true;
                        }

                        if (isThreadDoingGC)
                        {
                            stats.GCCpuMSec += cpuIncrement;
                        }
                    }
                }
            });
#endif


            source.Clr.AddCallbackForEvent("GC/SuspendEEStart", delegate (GCSuspendEETraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                var stats = perProc.GetOrCreate(data.ProcessID);
                switch (data.Reason)
                {
                    case GCSuspendEEReason.SuspendForGC:
                        stats.suspendThreadIDGC = data.ThreadID;
                        break;
                    case GCSuspendEEReason.SuspendForGCPrep:
                        stats.suspendThreadIDBGC = data.ThreadID;
                        break;
                    default:
                        stats.suspendThreadIDOther = data.ThreadID;
                        break;
                }

                stats.suspendTimeRelativeMSec = data.TimeStampRelativeMSec;
            });

            // In 2.0 we didn't have this event.

            source.Clr.AddCallbackForEvent("GC/SuspendEEStop", delegate (GCNoUserDataTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);

                if ((stats.suspendThreadIDBGC > 0) && (stats.currentBGC != null))
                {
                    stats.currentBGC._SuspendDurationMSec += data.TimeStampRelativeMSec - stats.suspendTimeRelativeMSec;
                }

                stats.suspendEndTimeRelativeMSec = data.TimeStampRelativeMSec;
            });

            source.Clr.AddCallbackForEvent("GC/RestartEEStop", delegate (GCNoUserDataTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    if (_event.Type == GCType.BackgroundGC)
                    {
                        stats.AddConcurrentPauseTime(_event, data.TimeStampRelativeMSec);
                    }
                    else
                    {
                        Debug.Assert(_event.PauseStartRelativeMSec != 0);
                        // In 2.0 Concurrent GC, since we don't know the GC's type we can't tell if it's concurrent 
                        // or not. But we know we don't have nested GCs there so simply check if we have received the
                        // GCStop event; if we have it means it's a blocking GC; otherwise it's a concurrent GC so 
                        // simply add the pause time to the GC without making the GC complete.
                        if (_event.GCDurationMSec == 0)
                        {
                            Debug.Assert(_event.is20Event);
                            _event.isConcurrentGC = true;
                            stats.AddConcurrentPauseTime(_event, data.TimeStampRelativeMSec);
                        }
                        else
                        {
                            _event.PauseDurationMSec = data.TimeStampRelativeMSec - _event.PauseStartRelativeMSec;
                            if (_event.HeapStats != null)
                            {
                                _event.isComplete = true;
                                stats.lastCompletedGC = _event;
                            }
                        }
                    }
                }

                // We don't change between a GC end and the pause resume.   
                //Debug.Assert(stats.allocTickAtLastGC == stats.allocTickCurrentMB);
                // Mark that we are not in suspension anymore.  
                stats.suspendTimeRelativeMSec = -1;
                stats.suspendThreadIDOther = -1;
                stats.suspendThreadIDBGC = -1;
                stats.suspendThreadIDGC = -1;
            });

            source.Clr.AddCallbackForEvent("GC/AllocationTick", delegate (GCAllocationTickTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);

                if (stats.HasAllocTickEvents == false)
                {
                    stats.HasAllocTickEvents = true;
                }

                double valueMB = data.GetAllocAmount(ref stats.SeenBadAllocTick) / 1000000.0;

                if (data.AllocationKind == GCAllocationKind.Small)
                {
                    // Would this do the right thing or is it always 0 for SOH since AllocationAmount 
                    // is an int??? 
                    stats.allocTickCurrentMB[0] += valueMB;
                }
                else
                {
                    stats.allocTickCurrentMB[1] += valueMB;
                }
            });

            source.Clr.AddCallbackForEvent("GC/Start", delegate (GCStartTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);

                // We need to filter the scenario where we get 2 GCStart events for each GC.
                if ((stats.suspendThreadIDGC > 0) &&
                    !((stats.events.Count > 0) && stats.events[stats.events.Count - 1].GCNumber == data.Count))
                {
                    GCEvent _event = new GCEvent(stats);
                    Debug.Assert(0 <= data.Depth && data.Depth <= 2);
                    // _event.GCGeneration = data.Depth;   Old style events only have this in the GCStop event.  
                    _event.Reason = data.Reason;
                    _event.GCNumber = data.Count;
                    _event.Type = data.Type;
                    _event.Index = stats.events.Count;
                    _event.is20Event = data.IsClassicProvider;
                    bool isEphemeralGCAtBGCStart = false;
                    // Detecting the ephemeral GC that happens at the beginning of a BGC.
                    if (stats.events.Count > 0)
                    {
                        GCEvent lastGCEvent = stats.events[stats.events.Count - 1];
                        if ((lastGCEvent.Type == GCType.BackgroundGC) &&
                            (!lastGCEvent.isComplete) &&
                            (data.Type == GCType.NonConcurrentGC))
                        {
                            isEphemeralGCAtBGCStart = true;
                        }
                    }

                    Debug.Assert(stats.suspendTimeRelativeMSec != -1);
                    if (isEphemeralGCAtBGCStart)
                    {
                        _event.PauseStartRelativeMSec = data.TimeStampRelativeMSec;
                    }
                    else
                    {
                        _event.PauseStartRelativeMSec = stats.suspendTimeRelativeMSec;
                        if (stats.suspendEndTimeRelativeMSec == -1)
                        {
                            stats.suspendEndTimeRelativeMSec = data.TimeStampRelativeMSec;
                        }

                        _event._SuspendDurationMSec = stats.suspendEndTimeRelativeMSec - stats.suspendTimeRelativeMSec;
                    }

                    _event.GCStartRelativeMSec = data.TimeStampRelativeMSec;
                    stats.events.Add(_event);

                    if (_event.Type == GCType.BackgroundGC)
                    {
                        stats.currentBGC = _event;
                        _event.ProcessCpuAtLastGC = stats.ProcessCpuAtLastGC;
                    }

#if (!CAP)
                    if ((_event.Type != GCType.BackgroundGC) && (stats.isServerGCUsed == 1))
                    {
                        _event.SetUpServerGcHistory();
                        foreach (var s in RecentCpuSamples)
                            _event.AddServerGcSample(s);
                        foreach (var s in RecentThreadSwitches)
                            _event.AddServerGcThreadSwitch(s);
                    }
#endif 
                }
            });

            source.Clr.AddCallbackForEvent("GC/PinObjectAtGCTime", delegate (PinObjectAtGCTimeTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    if (!_event.PinnedObjects.ContainsKey(data.ObjectID))
                    {
                        _event.PinnedObjects.Add(data.ObjectID, data.ObjectSize);
                    }
                    else
                    {
                        _event.duplicatedPinningReports++;
                    }
                }
            });

            // Some builds have this as a public event, and some have it as a private event.
            // All will move to the private event, so we'll remove this code afterwards.
            source.Clr.AddCallbackForEvent("GC/PinPlugAtGCTime", delegate (PinPlugAtGCTimeTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    // ObjectID is supposed to be an IntPtr. But "Address" is defined as UInt64 in 
                    // TraceEvent.
                    _event.PinnedPlugs.Add(new GCEvent.PinnedPlug(data.PlugStart, data.PlugEnd));
                }
            });

            source.Clr.AddCallbackForEvent("GC/Mark", delegate (GCMarkWithTypeTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                stats.AddServerGCThreadFromMark(data.ThreadID, data.HeapNum);

                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    if (_event.PerHeapMarkTimes == null)
                    {
                        _event.PerHeapMarkTimes = new Dictionary<int, GCEvent.MarkInfo>();
                    }

                    if (!_event.PerHeapMarkTimes.ContainsKey(data.HeapNum))
                    {
                        _event.PerHeapMarkTimes.Add(data.HeapNum, new GCEvent.MarkInfo());
                    }

                    _event.PerHeapMarkTimes[data.HeapNum].MarkTimes[(int)data.Type] = data.TimeStampRelativeMSec;
                    _event.PerHeapMarkTimes[data.HeapNum].MarkPromoted[(int)data.Type] = data.Promoted;
                }
            });

            source.Clr.AddCallbackForEvent("GC/GlobalHeapHistory", delegate (GCGlobalHeapHistoryTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                stats.ProcessGlobalHistory(data);
            });

            source.Clr.AddCallbackForEvent("GC/PerHeapHistory", delegate (GCPerHeapHistoryTraceData3 data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                stats.ProcessPerHeapHistory(data);
            });

#if HAS_PRIVATE_GC_EVENTS
            // See if the source knows about the CLR Private provider, if it does, then 
            var gcPrivate = new ClrPrivateTraceEventParser(source);

            gcPrivate.GCPinPlugAtGCTime += delegate (PinPlugAtGCTimeTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    // ObjectID is supposed to be an IntPtr. But "Address" is defined as UInt64 in 
                    // TraceEvent.
                    _event.PinnedPlugs.Add(new GCEvent.PinnedPlug(data.PlugStart, data.PlugEnd));
                }
            };

            // Sometimes at the end of a trace I see only some mark events are included in the trace and they
            // are not in order, so need to anticipate that scenario.
            gcPrivate.GCMarkStackRoots += delegate (GCMarkTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                stats.AddServerGCThreadFromMark(data.ThreadID, data.HeapNum);

                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    if (_event.PerHeapMarkTimes == null)
                    {
                        _event.PerHeapMarkTimes = new Dictionary<int, GCEvent.MarkInfo>();
                    }

                    if (!_event.PerHeapMarkTimes.ContainsKey(data.HeapNum))
                    {
                        _event.PerHeapMarkTimes.Add(data.HeapNum, new GCEvent.MarkInfo(false));
                    }

                    _event.PerHeapMarkTimes[data.HeapNum].MarkTimes[(int)MarkRootType.MarkStack] = data.TimeStampRelativeMSec;
                }
            };

            gcPrivate.GCMarkFinalizeQueueRoots += delegate (GCMarkTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    if ((_event.PerHeapMarkTimes != null) && _event.PerHeapMarkTimes.ContainsKey(data.HeapNum))
                    {
                        _event.PerHeapMarkTimes[data.HeapNum].MarkTimes[(int)MarkRootType.MarkFQ] =
                            data.TimeStampRelativeMSec;
                    }
                }
            };

            gcPrivate.GCMarkHandles += delegate (GCMarkTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    if ((_event.PerHeapMarkTimes != null) && _event.PerHeapMarkTimes.ContainsKey(data.HeapNum))
                    {
                        _event.PerHeapMarkTimes[data.HeapNum].MarkTimes[(int)MarkRootType.MarkHandles] =
                           data.TimeStampRelativeMSec;
                    }
                }
            };

            gcPrivate.GCMarkCards += delegate (GCMarkTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    if ((_event.PerHeapMarkTimes != null) && _event.PerHeapMarkTimes.ContainsKey(data.HeapNum))
                    {
                        _event.PerHeapMarkTimes[data.HeapNum].MarkTimes[(int)MarkRootType.MarkOlder] =
                            data.TimeStampRelativeMSec;
                    }
                }
            };

            gcPrivate.GCGlobalHeapHistory += delegate (GCGlobalHeapHistoryTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                stats.ProcessGlobalHistory(data);
            };

            gcPrivate.GCPerHeapHistory += delegate (GCPerHeapHistoryTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                stats.ProcessPerHeapHistory(data);
            };

            gcPrivate.GCBGCStart += delegate (GCNoUserDataTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                if (stats.currentBGC != null)
                {
                    if (stats.backgroundGCThreads == null)
                    {
                        stats.backgroundGCThreads = new Dictionary<int, object>(16);
                    }
                    stats.backgroundGCThreads[data.ThreadID] = null;
                }
            };
#endif

            source.Clr.AddCallbackForEvent("GC/Stop", delegate (GCEndTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                GCEvent _event = stats.GetCurrentGC();
                if (_event != null)
                {
                    _event.GCDurationMSec = data.TimeStampRelativeMSec - _event.GCStartRelativeMSec;
                    _event.GCGeneration = data.Depth;
                    _event.GCEnd();
                }
            });

            source.Clr.AddCallbackForEvent("GC/HeapStats", delegate (GCHeapStatsTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                GCEvent _event = stats.GetCurrentGC();

                var sizeAfterMB = (data.GenerationSize1 + data.GenerationSize2 + data.GenerationSize3) / 1000000.0;
                if (_event != null)
                {
                    _event.HeapStats = (GCHeapStatsTraceData)data.Clone();

                    if (_event.Type == GCType.BackgroundGC)
                    {
                        _event.ProcessCpuMSec = stats.ProcessCpuMSec - _event.ProcessCpuAtLastGC;
                        _event.DurationSinceLastRestartMSec = data.TimeStampRelativeMSec - stats.lastRestartEndTimeRelativeMSec;
                    }
                    else
                    {
                        _event.ProcessCpuMSec = stats.ProcessCpuMSec - stats.ProcessCpuAtLastGC;
                        _event.DurationSinceLastRestartMSec = _event.PauseStartRelativeMSec - stats.lastRestartEndTimeRelativeMSec;
                    }

                    if (stats.HasAllocTickEvents)
                    {
                        _event.HasAllocTickEvents = true;
                        _event.AllocedSinceLastGCBasedOnAllocTickMB[0] = stats.allocTickCurrentMB[0] - stats.allocTickAtLastGC[0];
                        _event.AllocedSinceLastGCBasedOnAllocTickMB[1] = stats.allocTickCurrentMB[1] - stats.allocTickAtLastGC[1];
                    }

                    // This is where a background GC ends.
                    if ((_event.Type == GCType.BackgroundGC) && (stats.currentBGC != null))
                    {
                        stats.currentBGC.isComplete = true;
                        stats.lastCompletedGC = stats.currentBGC;
                        stats.currentBGC = null;
                    }

                    if (_event.isConcurrentGC)
                    {
                        Debug.Assert(_event.is20Event);
                        _event.isComplete = true;
                        stats.lastCompletedGC = _event;
                    }
                }

                stats.ProcessCpuAtLastGC = stats.ProcessCpuMSec;
                stats.allocTickAtLastGC[0] = stats.allocTickCurrentMB[0];
                stats.allocTickAtLastGC[1] = stats.allocTickCurrentMB[1];
                stats.lastRestartEndTimeRelativeMSec = data.TimeStampRelativeMSec;
            });

            source.Clr.AddCallbackForEvent("GC/TerminateConcurrentThread", delegate (GCTerminateConcurrentThreadTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc.GetOrCreate(data.ProcessID);
                if (stats.backgroundGCThreads != null)
                {
                    stats.backgroundGCThreads = null;
                }
            });

#if HAS_PRIVATE_GC_EVENTS
            gcPrivate.GCBGCAllocWaitStart += delegate (BGCAllocWaitTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];
                Debug.Assert(stats.currentBGC != null);

                if (stats.currentBGC != null)
                {
                    stats.currentBGC.AddLOHWaitThreadInfo(data.ThreadID, data.TimeStampRelativeMSec, data.Reason, true);
                }
            };

            gcPrivate.GCBGCAllocWaitStop += delegate (BGCAllocWaitTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess stats = perProc[data];

                GCEvent _event = stats.GetLastBGC();

                if (_event != null)
                {
                    _event.AddLOHWaitThreadInfo(data.ThreadID, data.TimeStampRelativeMSec, data.Reason, false);
                }
            };

            gcPrivate.GCJoin += delegate (GCJoinTraceData data)
            {
                if (filterFunc != null && !filterFunc.Invoke(data))
                {
                    return;
                }

                GCProcess gcProcess = perProc[data];
                GCEvent _event = gcProcess.GetCurrentGC();
                if (_event != null)
                {
                    _event.AddGcJoin(data);
                }
            };

            source.Process();
#endif
            return perProc;
        }