Пример #1
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;
                    }
                }
            }
        }
Пример #2
0
        public override void ForEach(Action <StackSourceSample> callback)
        {
            // Initialize the priority
            if (m_typePriorities == null)
            {
                PriorityRegExs = DefaultPriorities;
            }
            Debug.Assert(m_typePriorities != null);

            // Initialize the breadth-first work queue.
            var nodesToVisit = new PriorityQueue(1024);

            nodesToVisit.Enqueue(m_graph.RootIndex, 0.0F);

            // reset the visited information.
            for (int i = 0; i < m_parent.Length; i++)
            {
                m_parent[i] = NodeIndex.Invalid;
            }

            // We keep track of node depth so that we can limit it.
            ushort[]    nodeDepth      = new ushort[m_parent.Length];
            float[]     nodePriorities = new float[m_parent.Length];
            MemoryGraph asMemoryGraph  = m_graph as MemoryGraph;

            bool  scanedForOrphans = false;
            var   epsilon          = 1E-7F; // Something that is big enough not to bet lost in roundoff error.
            float order            = 0;

            for (int i = 0; ; i++)
            {
                if ((i & 0x1FFF) == 0)                // Every 8K
                {
                    System.Threading.Thread.Sleep(0); // Allow interruption.
                }
                NodeIndex nodeIndex;
                float     nodePriority;
                if (nodesToVisit.Count == 0)
                {
                    nodePriority = 0;
                    if (!scanedForOrphans)
                    {
                        scanedForOrphans = true;
                        AddOrphansToQueue(nodesToVisit);
                    }
                    if (nodesToVisit.Count == 0)
                    {
                        return;
                    }
                }
                nodeIndex = nodesToVisit.Dequeue(out nodePriority);

                // Insert any children that have not already been visited (had a parent assigned) into the work queue).
                Node node           = m_graph.GetNode(nodeIndex, m_nodeStorage);
                var  parentPriority = nodePriorities[(int)node.Index];
                for (var childIndex = node.GetFirstChildIndex(); childIndex != NodeIndex.Invalid; childIndex = node.GetNextChildIndex())
                {
                    if (m_parent[(int)childIndex] == NodeIndex.Invalid && childIndex != m_graph.RootIndex)
                    {
                        m_parent[(int)childIndex] = nodeIndex;
                        ushort parentDepth = nodeDepth[(int)nodeIndex];
                        if (parentDepth > MaxDepth)
                        {
                            m_log.WriteLine("WARNING: Orphaned node with index {0} because its depth from root exceeded {1}", childIndex, MaxDepth);
                            continue;                   // TODO today we just drop it, but we should add it to some special overflow node.
                        }
                        nodeDepth[(int)childIndex] = (ushort)(parentDepth + 1);

                        // the priority of the child is determined by its type and 1/10 by its parent.
                        var child         = m_graph.GetNode(childIndex, m_childStorage);
                        var childPriority = m_typePriorities[(int)child.TypeIndex] + parentPriority / 10;
                        nodePriorities[(int)childIndex] = childPriority;

                        // Subtract a small increasing value to keep the queue in order if the priorities are the same.
                        // This is a bit of a hack since it can get big and perturb the user-defined order.
                        order += epsilon;
                        nodesToVisit.Enqueue(childIndex, childPriority - order);
                    }
                }

                // Return the node.
                m_sampleStorage.Metric = node.Size;
                // We use the address as the timestamp.  This allows you to pick particular instances
                // and see where particular instances are in memory by looking at the 'time'.
                if (asMemoryGraph != null)
                {
                    m_sampleStorage.TimeRelativeMSec = asMemoryGraph.GetAddress(node.Index);
                }

                m_sampleStorage.SampleIndex = (StackSourceSampleIndex)node.Index;
                m_sampleStorage.StackIndex  = (StackSourceCallStackIndex)node.Index;
                if (m_countMultipliers != null)
                {
                    m_sampleStorage.Count  = m_countMultipliers[(int)node.TypeIndex];
                    m_sampleStorage.Metric = node.Size * m_sampleStorage.Count;
                }
                Debug.Assert(m_sampleStorage.Metric >= 0);
                Debug.Assert(m_sampleStorage.Count >= 0);
                Debug.Assert(0 < m_sampleStorage.Count && m_sampleStorage.Count <= float.MaxValue);
                Debug.Assert(0 <= m_sampleStorage.Metric && m_sampleStorage.Metric <= float.MaxValue);
                callback(m_sampleStorage);
            }
        }