public async Task <Heapshot> TakeHeapshotAndMakeReport() { var newHeapshot = await TakeHeapshot(); if (Options.PrintReportTypes.HasFlag(ProfilerOptions.PrintReport.ObjectsTotal)) { Console.WriteLine($"Total objects per type({newHeapshot.Types.Count}):"); foreach (var heapTypeInfo in newHeapshot.Types.Values.OrderByDescending(p => p.Objects.Count)) { var name = heapTypeInfo.TypeInfo.Name; if (ShouldReportItem(name)) { Console.WriteLine($"{name}:{heapTypeInfo.Objects.Count}"); } } } if (Options.PrintReportTypes.HasFlag(ProfilerOptions.PrintReport.ObjectsDiff)) { Heapshot oldHeapshot = lastHeapshot; lastHeapshot = newHeapshot; if (oldHeapshot == null) { Console.WriteLine("No objects diff report on 1st Heapshot."); return(newHeapshot); } var diffCounter = new List <Tuple <string, int> > (); foreach (var kvp in newHeapshot.Types) //ClassInfos is not Heapshot specific, all heapshot has same { var typeId = kvp.Key; var name = kvp.Value.TypeInfo.Name; int oldCount = oldHeapshot.GetObjectCount(typeId); int newCount = newHeapshot.GetObjectCount(typeId); if (newCount - oldCount != 0) { diffCounter.Add(Tuple.Create(name, newCount - oldCount)); } } Console.WriteLine($"Heapshot diff has {diffCounter.Count} entries:"); foreach (var diff in diffCounter.OrderByDescending(d => d.Item2)) { var name = diff.Item1; if (ShouldReportItem(name)) { Console.WriteLine($"{name}:{diff.Item2}"); } } } return(newHeapshot); bool ShouldReportItem(string name) => Options.PrintReportObjectNames.Count == 0 || Options.PrintReportObjectNames.Contains(name); }
Dictionary <string, LeakItem> DetectLeakedObjects(Heapshot heapshot, bool isCleanup, ResultIterationData previousData, string iterationName) { if (heapshot == null || ProfilerOptions.Type == ProfilerOptions.ProfilerType.Disabled) { return(new Dictionary <string, LeakItem> ()); } var trackedLeaks = scenario.GetLeakAttributes(isCleanup); if (trackedLeaks.Count == 0) { return(new Dictionary <string, LeakItem> ()); } Directory.CreateDirectory(graphsDirectory); Console.WriteLine("Live objects count per type:"); var leakedObjects = new Dictionary <string, LeakItem> (trackedLeaks.Count); foreach (var kvp in trackedLeaks) { var name = kvp.Key; if (!heapshot.TryGetHeapshotTypeInfo(name, out var heapshotTypeInfo)) { continue; } var resultFile = ReportPathsToRoots(heapshot, heapshotTypeInfo, iterationName, out int objectCount); if (resultFile == null) { // We have determined the leak is not an actual leak. continue; } // We need to check if the root is finalizer or ephemeron, and not report the value. leakedObjects.Add(name, new LeakItem(name, objectCount, resultFile)); } foreach (var kvp in leakedObjects) { var leak = kvp.Value; int delta = 0; if (previousData != null && previousData.Leaks.TryGetValue(kvp.Key, out var previousLeak)) { int previousCount = previousLeak.Count; delta = leak.Count - previousCount; } Console.WriteLine("{0}: {1} {2:+0;-#}", leak.ClassName, leak.Count, delta); } return(leakedObjects); }
string ReportPathsToRoots(Heapshot heapshot, HeapshotTypeInfo typeInfo, string iterationName, out int objectCount) { var visitedRoots = new HashSet <HeapObject> (); string outputPath = null; var rootTypeName = typeInfo.TypeInfo.Name; var objects = typeInfo.Objects; objectCount = objects.Count; // Look for the first object that is definitely leaked. foreach (var obj in objects) { visitedRoots.Clear(); bool isLeak = false; var paths = heapshot.Graph.GetPredecessors(obj, vertex => { if (heapshot.Roots.TryGetValue(vertex.Address, out var heapRootRegisterEvent)) { visitedRoots.Add(vertex); isLeak |= IsActualLeakSource(heapRootRegisterEvent.Source); } }); if (outputPath == null && isLeak) { var objectRetentionGraph = new AdjacencyGraph <HeapObject, SReversedEdge <HeapObject, Edge <HeapObject> > > (); foreach (var root in visitedRoots) { if (paths.TryGetPath(root, out var edges)) { objectRetentionGraph.AddVerticesAndEdgeRange(edges); } } var graphviz = objectRetentionGraph.ToLeakGraphviz(heapshot); var dotPath = Path.Combine(graphsDirectory, iterationName + "_" + rootTypeName + ".dot"); outputPath = graphviz.Generate(DotEngine.Instance, dotPath); } else { objectCount--; } } // We have not found a definite leak if outputPath is null. return(outputPath); }
public void Process(Heapshot heapshot, bool isCleanup, string iterationName, Components.AutoTest.AutoTestSession.MemoryStats memoryStats) { if (heapshot == null) { return; } // TODO: Make this async. var previousData = result.Iterations.LastOrDefault(); var leakedObjects = DetectLeakedObjects(heapshot, isCleanup, previousData, iterationName); var leakResult = new ResultIterationData(iterationName, leakedObjects, memoryStats); result.Iterations.Add(leakResult); }
void ReportMemoryUsage(int iteration) { //Make sure IDE stops doing what it was doing UserInterfaceTests.Ide.WaitForIdeIdle(); // This is to prevent leaking of AppQuery instances. TestService.Session.DisconnectQueries(); Heapshot heapshot = null; if (profilerProcessor != null) { heapshot = profilerProcessor.TakeHeapshotAndMakeReport().Result; } var memoryStats = TestService.Session.MemoryStats; string iterationName; if (iteration == cleanupIteration) { iterationName = "Cleanup"; } else if (iteration == setupIteration) { iterationName = "Setup"; } else { iterationName = string.Format("Run_{0}", iteration + 1); } Console.WriteLine(iterationName); Console.WriteLine(" NonPagedSystemMemory: " + memoryStats.NonPagedSystemMemory); Console.WriteLine(" PagedMemory: " + memoryStats.PagedMemory); Console.WriteLine(" PagedSystemMemory: " + memoryStats.PagedSystemMemory); Console.WriteLine(" PeakVirtualMemory: " + memoryStats.PeakVirtualMemory); Console.WriteLine(" PrivateMemory: " + memoryStats.PrivateMemory); Console.WriteLine(" VirtualMemory: " + memoryStats.VirtualMemory); Console.WriteLine(" WorkingSet: " + memoryStats.WorkingSet); Console.WriteLine(); leakProcessor.Process(heapshot, iteration == cleanupIteration, iterationName, memoryStats); }
public override void Visit(HeapBeginEvent ev) { currentHeapshot = new Heapshot(); }
public static GraphvizAlgorithm <HeapObject, TEdge> ToLeakGraphviz <TEdge> (this IEdgeListGraph <HeapObject, TEdge> graph, Heapshot heapshot) where TEdge : IEdge <HeapObject> { var graphviz = new GraphvizAlgorithm <HeapObject, TEdge> (graph); graphviz.FormatVertex += (sender, e) => { var currentObj = e.Vertex; // Look up the object and set its type name. var typeName = currentObj.TypeInfo.Name; var formatter = e.VertexFormatter; e.VertexFormatter.Label = typeName; // Append root information. if (heapshot.Roots.TryGetValue(currentObj.Address, out var rootRegisterEvent)) { e.VertexFormatter.Label = $"{typeName}\\nRoot Kind: {rootRegisterEvent.Source.ToString ()}"; e.VertexFormatter.Shape = QuickGraph.Graphviz.Dot.GraphvizVertexShape.Box; } else { e.VertexFormatter.Label = typeName; } }; return(graphviz); }