internal MemoryGraph CreateGenerationAwareMemoryGraph()
        {
            // Create a new memory graph.
            // TODO: Is this size appropriate?
            _NewMemoryGraph = new MemoryGraph(1024);

            // Create the new root node.
            _RootNode = new MemoryNodeBuilder(_NewMemoryGraph, "[.NET Generation Aware Roots]");

            // Create old gen root notes.
            if (_GenerationToCondemn < 1)
            {
                _Gen1RootNode = _RootNode.FindOrCreateChild("[Gen1 Roots]");
            }
            _Gen2RootNode    = _RootNode.FindOrCreateChild("[Gen2 Roots]");
            _UnknownRootNode = _RootNode.FindOrCreateChild("[not reachable from roots]");

            // Traverse the input graph and re-build it as a generation aware graph.
            // This means that all types are re-written to include the generation.
            // We also add additional edges to account for old generation roots.

            // NOTE: This API will also visit nodes that have no path to root.
            _OriginalStackSource.ForEach(VisitNodeFromSample);

            _NewMemoryGraph.RootIndex = _RootNode.Build();
            _NewMemoryGraph.AllowReading();

            // Return the new graph.
            return(_NewMemoryGraph);
        }
Example #2
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;
                    }
                }
            }
        }
        internal static MemoryGraphStackSource CreateStackSource(
            GCHeapDump gcDump, TextWriter log, int generationToCondemn)
        {
            MemoryGraph graph = gcDump.MemoryGraph;

            if (generationToCondemn >= 0 && generationToCondemn < 2)
            {
                GenerationAwareMemoryGraphBuilder builder = new GenerationAwareMemoryGraphBuilder(log, generationToCondemn, graph, gcDump.DotNetHeapInfo);
                graph = builder.CreateGenerationAwareMemoryGraph();
            }

            return(new MemoryGraphStackSource(graph, log, gcDump.CountMultipliersByType));
        }
 private GenerationAwareMemoryGraphBuilder(
     TextWriter log,
     int generationToCondemn,
     MemoryGraph memGraph,
     DotNetHeapInfo heapInfo)
 {
     _Log = log;
     _GenerationToCondemn   = generationToCondemn;
     _OriginalMemoryGraph   = memGraph;
     _OriginalStackSource   = new MemoryGraphStackSource(memGraph, log);
     _HeapInfo              = heapInfo;
     _OldNodeToNewParentMap = new MemoryNodeBuilder[(int)_OriginalMemoryGraph.NodeIndexLimit];
     _OldNodeTypeStorage    = _OriginalMemoryGraph.AllocTypeNodeStorage();
 }
Example #5
0
 public MemoryNodeBuilder(MemoryGraph graph, string typeName, string moduleName = null, NodeIndex nodeIndex = NodeIndex.Invalid)
 {
     Debug.Assert(typeName != null);
     m_graph  = graph;
     TypeName = typeName;
     Index    = nodeIndex;
     if (Index == NodeIndex.Invalid)
     {
         Index = m_graph.CreateNode();
     }
     Debug.Assert(m_graph.m_nodes[(int)Index] == m_graph.m_undefinedObjDef, "SetNode cannot be called on the nodeIndex passed");
     ModuleName        = moduleName;
     m_mutableChildren = new List <MemoryNodeBuilder>();
     m_typeIndex       = NodeTypeIndex.Invalid;
 }
Example #6
0
 internal MemoryNode(MemoryGraph graph)
     : base(graph)
 {
     m_memoryGraph = graph;
 }
Example #7
0
 public AddressDictionary(int size, MemoryGraph graph)
 {
     m_hashArray  = new NodeIndex[size];
     m_collisions = new Dictionary <Address, NodeIndex>(size / 20 + 1);
     m_graph      = graph;
 }
Example #8
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);
            }
        }