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); }
/// <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(); }
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; }
internal MemoryNode(MemoryGraph graph) : base(graph) { m_memoryGraph = graph; }
public AddressDictionary(int size, MemoryGraph graph) { m_hashArray = new NodeIndex[size]; m_collisions = new Dictionary <Address, NodeIndex>(size / 20 + 1); m_graph = graph; }
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); } }