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); } }