diff options
author | Massimiliano Mantione <massi@mono-cvs.ximian.com> | 2008-12-01 21:23:27 +0300 |
---|---|---|
committer | Massimiliano Mantione <massi@mono-cvs.ximian.com> | 2008-12-01 21:23:27 +0300 |
commit | 48df9f88661bae8fd2f5645da40a797ad86616ee (patch) | |
tree | 82236d685d742bb71a7ab48ffab596eef7555172 /Mono.Profiler | |
parent | 5597aebf54503c7ff6293a1688424cc6263c9edc (diff) |
Handle object allocator method and call stack in heap profiling.
svn path=/trunk/mono-tools/; revision=120368
Diffstat (limited to 'Mono.Profiler')
9 files changed, 688 insertions, 111 deletions
diff --git a/Mono.Profiler/heap-snapshot-explorer/ChangeLog b/Mono.Profiler/heap-snapshot-explorer/ChangeLog index 9a720d31..86224c01 100644 --- a/Mono.Profiler/heap-snapshot-explorer/ChangeLog +++ b/Mono.Profiler/heap-snapshot-explorer/ChangeLog @@ -1,3 +1,14 @@ +2008-12-01 Massimiliano Mantione <massi@ximian.com> + * HeapExplorerTreeModel.cs: Linked all root nodes n a list, so that it + is possible to find the allocation event of a given heap object (now + IRootNode implements the ProviderOfPreviousAllocationsSets interface). + * HeapSnapshotExplorer.cs: Created class StatisticsNodeMenuHandler + (implemented for classes, methods and call stacks) to rationalize the + hadling of menus), and turned ClassStatisticsNode into a more general + StatisticsNode because now it works with all the three abov concepts. + * LoadedClassChooser.cs: Make the dialog work with methods and call + stacks instead of just classes. + 2008-11-03 Massimiliano Mantione <massi@ximian.com> * HeapExplorerTreeModel.cs: Massive refactory of "Node" class, making it generic in terms of the node contents (HeapObject-AllocatedObject). diff --git a/Mono.Profiler/heap-snapshot-explorer/HeapExplorerTreeModel.cs b/Mono.Profiler/heap-snapshot-explorer/HeapExplorerTreeModel.cs index 64277c9c..8455be24 100644 --- a/Mono.Profiler/heap-snapshot-explorer/HeapExplorerTreeModel.cs +++ b/Mono.Profiler/heap-snapshot-explorer/HeapExplorerTreeModel.cs @@ -37,12 +37,13 @@ namespace Mono.Profiler string Description {get;} TreeIter TreeIter {get;} INode Parent {get;} - INode Root {get;} + IRootNode Root {get;} string Count {get;} string AllocatedBytes {get;} Menu ContextMenu {get;} } - public interface IRootNode : INode { + public interface IRootNode : INode, ProviderOfPreviousAllocationsSets { + AllocationsNode PreviousAllocationsNode {get;} } public abstract class Node<HI> : INode where HI : IHeapItem { @@ -90,9 +91,26 @@ namespace Mono.Profiler } } } - INode INode.Root { + IRootNode INode.Root { get { - return Root; + return (IRootNode) Root; + } + } + + AllocationsNode previousAllocationsNode; + public AllocationsNode PreviousAllocationsNode { + get { + return previousAllocationsNode; + } + } + public IEnumerable<HeapItemSet<AllocatedObject>> PreviousAllocationsSets () { + AllocationsNode currentNode = this.PreviousAllocationsNode; + while (currentNode != null) { + if (currentNode.Items == null) { + currentNode.ReadEvents (); + } + yield return currentNode.Items; + currentNode = currentNode.PreviousAllocationsNode; } } @@ -153,9 +171,10 @@ namespace Mono.Profiler public abstract Menu ContextMenu {get;} - protected Node (HeapExplorerTreeModel model, Node<HI> parent) { + protected Node (HeapExplorerTreeModel model, Node<HI> parent, AllocationsNode previousAllocationsNode) { this.model = model; this.parent = parent; + this.previousAllocationsNode = previousAllocationsNode; this.treeIter = HandleNodeCreation (); } } @@ -221,7 +240,7 @@ namespace Mono.Profiler } } - public SnapshotNode (HeapExplorerTreeModel model, SeekableLogFileReader.Block heapBlock) : base (model, null) { + public SnapshotNode (HeapExplorerTreeModel model, SeekableLogFileReader.Block heapBlock, AllocationsNode previousAllocationsNode) : base (model, null, previousAllocationsNode) { this.heapBlock = heapBlock; this.items = null; this.snapshot = null; @@ -283,7 +302,7 @@ namespace Mono.Profiler } } - public AllocationsNode (HeapExplorerTreeModel model, SeekableLogFileReader.Block[] eventBlocks) : base (model, null) { + public AllocationsNode (HeapExplorerTreeModel model, SeekableLogFileReader.Block[] eventBlocks, AllocationsNode previousAllocationsNode) : base (model, null, previousAllocationsNode) { this.eventBlocks = eventBlocks; this.items = null; } @@ -297,7 +316,7 @@ namespace Mono.Profiler } } - protected SubSetNode (HeapExplorerTreeModel model, Node<HI> parent, HeapItemSet<HI> items) : base (model, parent) { + protected SubSetNode (HeapExplorerTreeModel model, Node<HI> parent, HeapItemSet<HI> items) : base (model, parent, null) { this.items = items; } } @@ -402,10 +421,10 @@ namespace Mono.Profiler } } - AllocationsNode CreateAllocationsNode (List<SeekableLogFileReader.Block> eventBlocks) { + AllocationsNode CreateAllocationsNode (List<SeekableLogFileReader.Block> eventBlocks, AllocationsNode previousAllocationsNode) { AllocationsNode node; if (eventBlocks.Count > 0) { - node = new AllocationsNode (this, eventBlocks.ToArray ()); + node = new AllocationsNode (this, eventBlocks.ToArray (), previousAllocationsNode); rootNodes.Add (node); } else { node = null; @@ -416,12 +435,13 @@ namespace Mono.Profiler public void Initialize () { List<SeekableLogFileReader.Block> eventBlocks = new List<SeekableLogFileReader.Block> (); + AllocationsNode previousAllocationsNode = null; Reset (); foreach (SeekableLogFileReader.Block block in reader.Blocks) { if (block.Code == BlockCode.HEAP_DATA) { - CreateAllocationsNode (eventBlocks); - SnapshotNode node = new SnapshotNode (this, block); + previousAllocationsNode = CreateAllocationsNode (eventBlocks, previousAllocationsNode); + SnapshotNode node = new SnapshotNode (this, block, previousAllocationsNode); rootNodes.Add (node); } else if (block.Code == BlockCode.EVENTS) { eventBlocks.Add (block); @@ -435,7 +455,7 @@ namespace Mono.Profiler reader.ReadBlock (block).Decode (heapEventProcessor, reader); } } - CreateAllocationsNode (eventBlocks); + CreateAllocationsNode (eventBlocks, previousAllocationsNode); } public void Reset () { diff --git a/Mono.Profiler/heap-snapshot-explorer/HeapSnapshotExplorer.cs b/Mono.Profiler/heap-snapshot-explorer/HeapSnapshotExplorer.cs index 6d0ff0eb..3f935742 100644 --- a/Mono.Profiler/heap-snapshot-explorer/HeapSnapshotExplorer.cs +++ b/Mono.Profiler/heap-snapshot-explorer/HeapSnapshotExplorer.cs @@ -29,47 +29,220 @@ namespace Mono.Profiler } } + StatisticsNode currentListSelection; + public StatisticsNode CurrentListSelection { + get { + return currentListSelection; + } + } + + abstract class StatisticsNodeMenuHandler { + HeapSnapshotExplorer explorer; + public HeapSnapshotExplorer Explorer { + get { + return explorer; + } + } + protected abstract Menu GetContextMenu (); + public void ShowContextMenu () { + if ((explorer.CurrentSelection != null) && (explorer.CurrentListSelection != null)) { + Menu contextMenu = GetContextMenu (); + if (contextMenu != null) { + contextMenu.Popup (); + } + } + } + protected void FilterCurrentSet<HI> (IHeapItemFilter<HI> filter) where HI : IHeapItem { + HeapExplorerTreeModel.Node<HI> node = (HeapExplorerTreeModel.Node<HI>) Explorer.CurrentSelection; + node.Filter (filter); + } + protected StatisticsNodeMenuHandler (HeapSnapshotExplorer explorer) { + this.explorer = explorer; + } + } + class StatisticsNodeMenuHandlerForClasses : StatisticsNodeMenuHandler { + void FilterCurrentSetByCurrentClass () { + LoadedClass currentClass = (LoadedClass) Explorer.CurrentListSelection.Statistics.Subject; + if (Explorer.CurrentSelection is HeapExplorerTreeModel.Node<HeapObject>) { + FilterCurrentSet<HeapObject> (new HeapItemIsOfClass<HeapObject> (currentClass)); + } + if (Explorer.CurrentSelection is HeapExplorerTreeModel.Node<AllocatedObject>) { + FilterCurrentSet<AllocatedObject> (new HeapItemIsOfClass<AllocatedObject> (currentClass)); + } + } + + Menu menu; + protected override Menu GetContextMenu () { + return menu; + } + public StatisticsNodeMenuHandlerForClasses (HeapSnapshotExplorer explorer) : base (explorer) { + menu = new Menu (); + MenuItem menuItem; + menuItem = new MenuItem ("Filter current set by this class"); + menuItem.Activated += delegate { + FilterCurrentSetByCurrentClass (); + }; + menu.Add (menuItem); + menuItem = new MenuItem ("Show statistics by caller method"); + menuItem.Activated += delegate { + explorer.FillStatisticsListWithMethodData (); + }; + menu.Add (menuItem); + menu.ShowAll (); + } + } + class StatisticsNodeMenuHandlerForMethods : StatisticsNodeMenuHandler { + void FilterCurrentSetByCurrentMethod () { + LoadedMethod currentMethod = (LoadedMethod) Explorer.CurrentListSelection.Statistics.Subject; + if (Explorer.CurrentSelection is HeapExplorerTreeModel.Node<HeapObject>) { + FilterCurrentSet<HeapObject> (new HeapItemWasAllocatedByMethod<HeapObject> (currentMethod)); + } + if (Explorer.CurrentSelection is HeapExplorerTreeModel.Node<AllocatedObject>) { + FilterCurrentSet<AllocatedObject> (new HeapItemWasAllocatedByMethod<AllocatedObject> (currentMethod)); + } + } + + Menu menu; + protected override Menu GetContextMenu () { + return menu; + } + public StatisticsNodeMenuHandlerForMethods (HeapSnapshotExplorer explorer) : base (explorer) { + menu = new Menu (); + MenuItem menuItem; + menuItem = new MenuItem ("Filter current set by this method"); + menuItem.Activated += delegate { + FilterCurrentSetByCurrentMethod (); + }; + menu.Add (menuItem); + menuItem = new MenuItem ("Show statistics by call stack"); + menuItem.Activated += delegate { + explorer.FillStatisticsListWithCallStackData (); + }; + menu.Add (menuItem); + menu.ShowAll (); + } + } + class StatisticsNodeMenuHandlerForCallStacks : StatisticsNodeMenuHandler { + void FilterCurrentSetByCurrentCallStack () { + StackTrace currentCallStack = (StackTrace) Explorer.CurrentListSelection.Statistics.Subject; + if (Explorer.CurrentSelection is HeapExplorerTreeModel.Node<HeapObject>) { + FilterCurrentSet<HeapObject> (new FilterHeapItemByAllocationCallStack<HeapObject> (currentCallStack)); + } + if (Explorer.CurrentSelection is HeapExplorerTreeModel.Node<AllocatedObject>) { + FilterCurrentSet<AllocatedObject> (new FilterHeapItemByAllocationCallStack<AllocatedObject> (currentCallStack)); + } + } + + Menu menu; + protected override Menu GetContextMenu () { + return menu; + } + public StatisticsNodeMenuHandlerForCallStacks (HeapSnapshotExplorer explorer) : base (explorer) { + menu = new Menu (); + MenuItem menuItem; + menuItem = new MenuItem ("Filter current set by this call stack"); + menuItem.Activated += delegate { + FilterCurrentSetByCurrentCallStack (); + }; + menu.Add (menuItem); + menu.ShowAll (); + } + } + + StatisticsNodeMenuHandler currentListMenuHandler; + StatisticsNodeMenuHandlerForClasses listMenuHandlerForClasses; + StatisticsNodeMenuHandlerForMethods listMenuHandlerForMethods; + StatisticsNodeMenuHandlerForCallStacks listMenuHandlerForCallStacks; + void FillStatisticsListWithClassData () { + currentListMenuHandler = listMenuHandlerForClasses; + if (currentSelection != null) { + if (currentSelection.Items != null) { + FillTreeViewWithStatistics (PerClassStatistics, currentSelection.Items.ClassStatistics); + } else { + PerClassStatistics.NodeStore.Clear (); + } + } + currentListSelection = null; + } + void FillStatisticsListWithMethodData () { + currentListMenuHandler = listMenuHandlerForMethods; + if (currentSelection != null) { + if (currentSelection.Items != null) { + if (! currentSelection.Items.ObjectAllocationsArePresent) { + currentSelection.Items.FindObjectAllocations (currentSelection.Root); + } + FillTreeViewWithStatistics (PerClassStatistics, currentSelection.Items.AllocatorMethodStatistics); + } else { + PerClassStatistics.NodeStore.Clear (); + } + } + currentListSelection = null; + } + void FillStatisticsListWithCallStackData () { + currentListMenuHandler = listMenuHandlerForCallStacks; + if (currentSelection != null) { + if (currentSelection.Items != null) { + if (! currentSelection.Items.ObjectAllocationsArePresent) { + currentSelection.Items.FindObjectAllocations (currentSelection.Root); + } + FillTreeViewWithStatistics (PerClassStatistics, currentSelection.Items.AllocationCallStackStatistics); + } else { + PerClassStatistics.NodeStore.Clear (); + } + } + currentListSelection = null; + } + [Gtk.TreeNode (ListOnly=true)] - public class ClassStatisticsNode : Gtk.TreeNode { - HeapItemSetClassStatistics classStatistics; - public HeapItemSetClassStatistics ClassStatistics { + public class StatisticsNode : Gtk.TreeNode { + IHeapItemSetStatisticsBySubject statistics; + public IHeapItemSetStatisticsBySubject Statistics { get { - return classStatistics; + return statistics; } } - public string Name { + public string Description { get { - return classStatistics.Class.Name; + return statistics.Subject.Description; + } + } + public uint ItemsCount { + get { + return statistics.ItemsCount; } } public uint AllocatedBytes { get { - return classStatistics.AllocatedBytes; + return statistics.AllocatedBytes; } } - public ClassStatisticsNode (HeapItemSetClassStatistics classStatistics) { - this.classStatistics = classStatistics; + public StatisticsNode (IHeapItemSetStatisticsBySubject statistics) { + this.statistics = statistics; } } - public static void PrepareTreeViewForClassStatistics (NodeView view) { - view.AppendColumn ("Name", new Gtk.CellRendererText (), delegate (TreeViewColumn column, CellRenderer cell, ITreeNode node) { - ClassStatisticsNode classNode = (ClassStatisticsNode) node; - ((CellRendererText) cell).Markup = classNode.Name; + public static void PrepareTreeViewForStatisticsDisplay (NodeView view) { + view.AppendColumn ("Description", new Gtk.CellRendererText (), delegate (TreeViewColumn column, CellRenderer cell, ITreeNode treeNode) { + StatisticsNode node = (StatisticsNode) treeNode; + ((CellRendererText) cell).Markup = node.Description; + }); + view.AppendColumn ("Object count", new Gtk.CellRendererText (), delegate (TreeViewColumn column, CellRenderer cell, ITreeNode treeNode) { + StatisticsNode node = (StatisticsNode) treeNode; + ((CellRendererText) cell).Markup = node.ItemsCount.ToString (); }); - view.AppendColumn ("Allocated bytes", new Gtk.CellRendererText (), delegate (TreeViewColumn column, CellRenderer cell, ITreeNode node) { - ClassStatisticsNode classNode = (ClassStatisticsNode) node; - ((CellRendererText) cell).Markup = classNode.AllocatedBytes.ToString (); + view.AppendColumn ("Allocated bytes", new Gtk.CellRendererText (), delegate (TreeViewColumn column, CellRenderer cell, ITreeNode treeNode) { + StatisticsNode node = (StatisticsNode) treeNode; + ((CellRendererText) cell).Markup = node.AllocatedBytes.ToString (); }); - view.NodeStore = new Gtk.NodeStore (typeof (ClassStatisticsNode)); + view.NodeStore = new Gtk.NodeStore (typeof (StatisticsNode)); } - public static void FillTreeViewWithClassStatistics (NodeView view, HeapItemSetClassStatistics[] classes) { + public static void FillTreeViewWithStatistics (NodeView view, IHeapItemSetStatisticsBySubject[] statistics) { view.NodeStore.Clear (); - foreach (HeapItemSetClassStatistics c in classes) { - view.NodeStore.AddNode (new ClassStatisticsNode (c)); + foreach (IHeapItemSetStatisticsBySubject s in statistics) { + view.NodeStore.AddNode (new StatisticsNode (s)); } } @@ -291,20 +464,20 @@ namespace Mono.Profiler }; filterAllocationSetUsingSelection.Append (menuItem); - PrepareTreeViewForClassStatistics (PerClassStatistics); + PrepareTreeViewForStatisticsDisplay (PerClassStatistics); + PerClassStatistics.NodeSelection.Changed += delegate (object o, EventArgs args) { + currentListSelection = (StatisticsNode) PerClassStatistics.NodeSelection.SelectedNode; + }; + listMenuHandlerForClasses = new StatisticsNodeMenuHandlerForClasses (this); + listMenuHandlerForMethods = new StatisticsNodeMenuHandlerForMethods (this); + listMenuHandlerForCallStacks = new StatisticsNodeMenuHandlerForCallStacks (this); Tree.Selection.Changed += delegate (object o, EventArgs args) { TreeSelection selection = (TreeSelection) o; TreeIter iter; if (selection.GetSelected (out iter)) { currentSelection = (HeapExplorerTreeModel.INode) Tree.Model.GetValue (iter, 0); - if (currentSelection != null) { - if (currentSelection.Items != null) { - FillTreeViewWithClassStatistics (PerClassStatistics, currentSelection.Items.ClassStatistics); - } else { - PerClassStatistics.NodeStore.Clear (); - } - } + FillStatisticsListWithClassData (); } }; @@ -501,5 +674,13 @@ namespace Mono.Profiler } } } + + [GLib.ConnectBefore] + protected virtual void OnListButtonPress (object o, Gtk.ButtonPressEventArgs args) + { + if (args.Event.Button == 3) { + currentListMenuHandler.ShowContextMenu (); + } + } } } diff --git a/Mono.Profiler/heap-snapshot-explorer/LoadedClassChooser.cs b/Mono.Profiler/heap-snapshot-explorer/LoadedClassChooser.cs index 764885a1..5b779c3c 100644 --- a/Mono.Profiler/heap-snapshot-explorer/LoadedClassChooser.cs +++ b/Mono.Profiler/heap-snapshot-explorer/LoadedClassChooser.cs @@ -11,35 +11,35 @@ namespace Mono.Profiler { public partial class LoadedClassChooser : Gtk.Dialog { - LoadedClass result; - public LoadedClass Result { + IHeapItemSetStatisticsSubject result; + public IHeapItemSetStatisticsSubject Result { get { return result; } } - HeapItemSetClassStatistics currentSelection; + IHeapItemSetStatisticsBySubject currentSelection; LoadedClassChooser() { this.Build(); - HeapSnapshotExplorer.PrepareTreeViewForClassStatistics (ClassList); + HeapSnapshotExplorer.PrepareTreeViewForStatisticsDisplay (ClassList); ClassList.NodeSelection.Changed += new EventHandler (OnSelectionChanged); currentSelection = null; } void OnSelectionChanged (object o, System.EventArgs args) { Gtk.NodeSelection selection = (Gtk.NodeSelection) o; - HeapSnapshotExplorer.ClassStatisticsNode node = (HeapSnapshotExplorer.ClassStatisticsNode) selection.SelectedNode; + HeapSnapshotExplorer.StatisticsNode node = (HeapSnapshotExplorer.StatisticsNode) selection.SelectedNode; if (node != null) { - currentSelection = node.ClassStatistics; + currentSelection = node.Statistics; } else { currentSelection = null; } } - void FillList (HeapItemSetClassStatistics[] classes) { - HeapSnapshotExplorer.FillTreeViewWithClassStatistics (ClassList, classes); + void FillList (IHeapItemSetStatisticsBySubject[] statistics) { + HeapSnapshotExplorer.FillTreeViewWithStatistics (ClassList, statistics); currentSelection = null; } @@ -51,7 +51,7 @@ namespace Mono.Profiler protected virtual void OnOK (object sender, System.EventArgs e) { if (currentSelection != null) { - result = currentSelection.Class; + result = currentSelection.Subject; } else { result = null; } @@ -59,12 +59,13 @@ namespace Mono.Profiler static LoadedClassChooser chooser; - public static LoadedClass ChooseClass (HeapItemSetClassStatistics[] classes) { - LoadedClass result; + static IHeapItemSetStatisticsSubject ChooseSubject (IHeapItemSetStatisticsBySubject[] subjects, string subjectName) { + IHeapItemSetStatisticsSubject result; if (chooser == null) { chooser = new LoadedClassChooser (); } - chooser.FillList (classes); + chooser.Title = "Choose " + subjectName; + chooser.FillList (subjects); ResponseType response = (ResponseType) chooser.Run (); if (response == ResponseType.Ok) { result = chooser.result; @@ -74,5 +75,17 @@ namespace Mono.Profiler chooser.Hide (); return result; } + public static LoadedClass ChooseClass (IHeapItemSetStatisticsBySubject[] subjects) { + IHeapItemSetStatisticsSubject result = ChooseSubject (subjects, "class"); + return result as LoadedClass; + } + public static LoadedMethod ChooseMethod (IHeapItemSetStatisticsBySubject[] subjects) { + IHeapItemSetStatisticsSubject result = ChooseSubject (subjects, "method"); + return result as LoadedMethod; + } + public static StackTrace ChooseCallStack (IHeapItemSetStatisticsBySubject[] subjects) { + IHeapItemSetStatisticsSubject result = ChooseSubject (subjects, "call stack"); + return result as StackTrace; + } } } diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs index 047a8e3f..5bc2e238 100644 --- a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs @@ -63,6 +63,7 @@ namespace Mono.Profiler { } this.Show(); this.Tree.ButtonPressEvent += new Gtk.ButtonPressEventHandler(this.OnTreeButtonPress); + this.PerClassStatistics.ButtonPressEvent += new Gtk.ButtonPressEventHandler(this.OnListButtonPress); } } } diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.LoadedClassChooser.cs b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.LoadedClassChooser.cs index 4e0cf4c2..00ad64f1 100644 --- a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.LoadedClassChooser.cs +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.LoadedClassChooser.cs @@ -25,7 +25,7 @@ namespace Mono.Profiler { Stetic.Gui.Initialize(this); // Widget Mono.Profiler.LoadedClassChooser this.Name = "Mono.Profiler.LoadedClassChooser"; - this.Title = Mono.Unix.Catalog.GetString("Choose class"); + this.Title = Mono.Unix.Catalog.GetString("Choose item"); this.WindowPosition = ((Gtk.WindowPosition)(4)); this.Modal = true; this.HasSeparator = false; diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/gui.stetic b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/gui.stetic index 014db4ec..45a15c39 100644 --- a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/gui.stetic +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/gui.stetic @@ -65,6 +65,7 @@ <property name="CanFocus">True</property> <property name="ShowScrollbars">True</property> <property name="HeadersClickable">True</property> + <signal name="ButtonPressEvent" handler="OnListButtonPress" /> </widget> </child> </widget> @@ -74,7 +75,7 @@ </widget> <widget class="Gtk.Dialog" id="Mono.Profiler.LoadedClassChooser" design-size="400 300"> <property name="MemberName" /> - <property name="Title" translatable="yes">Choose class</property> + <property name="Title" translatable="yes">Choose item</property> <property name="WindowPosition">CenterOnParent</property> <property name="Modal">True</property> <property name="Buttons">2</property> diff --git a/Mono.Profiler/profiler-decoder-library/ChangeLog b/Mono.Profiler/profiler-decoder-library/ChangeLog index e33ea755..2c41120e 100644 --- a/Mono.Profiler/profiler-decoder-library/ChangeLog +++ b/Mono.Profiler/profiler-decoder-library/ChangeLog @@ -1,3 +1,20 @@ +2008-12-01 Massimiliano Mantione <massi@ximian.com> + * ObjectModel.cs: + - Added interface IHeapItemSetStatisticsSubject to represents criteria + by which heap item sets can be examined, implemented by LoadedClass, + StackTrace and LoadedMethod. + - Added methods to IHeapItem (and implementations) to get the item + allocator method and call stack. + - Added filter classes (FilterHeapItemByAllocatorMethod, + HeapItemWasAllocatedByMethod, FilterHeapItemByAllocationCallStack...) + to filter by those criteria. + - Added abstract class HeapItemSetStatisticsBySubject to factor the + building of statistics (implemented by HeapItemSetClassStatistics, + HeapItemSetMethodStatistics and HeapItemSetCallStackStatistics). + - Inside HeapItemSet added the BuildStatistics generic method (which + works with the previous classes). + - Inside HeapItemSet added the FindObjectAllocations functionality. + 2008-11-03 Massimiliano Mantione <massi@ximian.com> * BaseTypes.cs: Added "header start time" property to the event handler, so that there is always a "context" of when the event block diff --git a/Mono.Profiler/profiler-decoder-library/ObjectModel.cs b/Mono.Profiler/profiler-decoder-library/ObjectModel.cs index 1a6d6b20..937ecc99 100644 --- a/Mono.Profiler/profiler-decoder-library/ObjectModel.cs +++ b/Mono.Profiler/profiler-decoder-library/ObjectModel.cs @@ -30,7 +30,7 @@ using System.IO; using System.Collections.Generic; namespace Mono.Profiler { - public class LoadedClass : BaseLoadedClass { + public class LoadedClass : BaseLoadedClass, IHeapItemSetStatisticsSubject { uint allocatedBytes; public uint AllocatedBytes { get { @@ -47,6 +47,12 @@ namespace Mono.Profiler { return a.AllocatedBytes.CompareTo (b.AllocatedBytes); }; + string IHeapItemSetStatisticsSubject.Description { + get { + return Name; + } + } + internal void InstanceCreated (uint size, LoadedMethod method, bool jitTime, StackTrace stackTrace) { allocatedBytes += size; currentlyAllocatedBytes += size; @@ -211,6 +217,8 @@ namespace Mono.Profiler { } } + public static readonly LoadedClass LoadedClassUnavailable = new LoadedClass (0, "(CLASS UNAVAILABLE)", 0); + public LoadedClass (uint id, string name, uint size): base (id, name, size) { allocatedBytes = 0; currentlyAllocatedBytes = 0; @@ -218,7 +226,7 @@ namespace Mono.Profiler { } } - public class StackTrace { + public class StackTrace : IHeapItemSetStatisticsSubject { LoadedMethod topMethod; public LoadedMethod TopMethod { get { @@ -250,7 +258,32 @@ namespace Mono.Profiler { } } - static uint nextFreeId = 1; + public string FullDescription { + get { + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + sb.Append ("CallStack of id "); + sb.Append (id); + sb.Append ('\n'); + + StackTrace currentFrame = this; + while (currentFrame != null) { + StackTrace nextFrame = currentFrame.Caller; + sb.Append (" "); + sb.Append (currentFrame.TopMethod.Name); + if (nextFrame != null) { + sb.Append ('\n'); + } + currentFrame = nextFrame; + } + + return sb.ToString (); + } + } + string IHeapItemSetStatisticsSubject.Description { + get { + return FullDescription; + } + } StackTrace (LoadedMethod topMethod, StackTrace caller, bool methodIsBeingJitted) { this.topMethod = topMethod; @@ -286,6 +319,15 @@ namespace Mono.Profiler { return NewStackTrace (stack.StackTop); } + static uint nextFreeId; + static Dictionary<uint,List<StackTrace>> [] tracesByLevel; + public static readonly StackTrace StackTraceUnavailable; + static StackTrace () { + nextFreeId = 0; + tracesByLevel = new Dictionary<uint,List<StackTrace>> [64]; + StackTraceUnavailable = NewStackTrace (CallStack.StackFrame.StackFrameUnavailable); + } + static StackTrace NewStackTrace (CallStack.StackFrame frame) { if (frame == null) { return null; @@ -322,8 +364,6 @@ namespace Mono.Profiler { traces.Add (result); return result; } - - static Dictionary<uint,List<StackTrace>> [] tracesByLevel = new Dictionary<uint,List<StackTrace>> [64]; } class CallStack { @@ -375,6 +415,8 @@ namespace Mono.Profiler { level = (caller != null) ? (caller.Level + 1) : 1; } + public static readonly StackFrame StackFrameUnavailable = new StackFrame (LoadedMethod.LoadedMethodForStackTraceUnavailable, 0, false, null); + internal StackFrame (LoadedMethod method, ulong startCounter, bool isBeingJitted, StackFrame caller) { this.method = method; this.startCounter = startCounter; @@ -587,7 +629,7 @@ namespace Mono.Profiler { StatisticalHitItemCallCounts CallCounts {get;} } - public class LoadedMethod : BaseLoadedMethod<LoadedClass>, IStatisticalHitItem { + public class LoadedMethod : BaseLoadedMethod<LoadedClass>, IStatisticalHitItem, IHeapItemSetStatisticsSubject { ulong clicks; public ulong Clicks { get { @@ -604,6 +646,12 @@ namespace Mono.Profiler { return (a.Clicks - a.CalledClicks).CompareTo (b.Clicks - b.CalledClicks); }; + string IHeapItemSetStatisticsSubject.Description { + get { + return Name; + } + } + ulong calledClicks; public ulong CalledClicks { get { @@ -785,6 +833,9 @@ namespace Mono.Profiler { calledClicks += clicks; } + public static readonly LoadedMethod LoadedMethodUnavailable = new LoadedMethod (0, LoadedClass.LoadedClassUnavailable, "(METHOD UNAVAILABLE)"); + public static readonly LoadedMethod LoadedMethodForStackTraceUnavailable = new LoadedMethod (0, LoadedClass.LoadedClassUnavailable, "(CALL STACK UNAVAILABLE)"); + public LoadedMethod (uint id, LoadedClass c, string name): base (id, c, name) { clicks = 0; calledClicks = 0; @@ -909,9 +960,47 @@ namespace Mono.Profiler { } public interface IHeapItem : IAllocatedObject<LoadedClass> { + LoadedMethod AllocatorMethod {get;} + StackTrace AllocationCallStack {get;} + bool AllocationHappenedAtJitTime {get;} } public class HeapObject : BaseHeapObject<HeapObject,LoadedClass>, IHeapItem { + AllocatedObject allocation; + public AllocatedObject Allocation { + get { + return allocation; + } + set { + allocation = value; + if ((allocation.Class != Class) || (allocation.ID != ID)) { + throw new Exception (String.Format ("Cannot accept allocation of class {0} and ID {1} for object of class {2} and ID {3}", allocation.Class, allocation.ID, Class, ID)); + } + } + } + public void FindAllocation (ProviderOfPreviousAllocationsSets previousSetsProvider) { + foreach (HeapItemSet<AllocatedObject> allocations in previousSetsProvider.PreviousAllocationsSets ()) { + allocation = allocations [ID]; + if (allocation != null) { + return; + } + } + } + public LoadedMethod AllocatorMethod { + get { + return allocation != null ? allocation.AllocatorMethod : null; + } + } + public StackTrace AllocationCallStack { + get { + return allocation != null ? allocation.Trace : null; + } + } + public bool AllocationHappenedAtJitTime { + get { + return allocation != null ? allocation.AllocationHappenedAtJitTime : false; + } + } public HeapObject (ulong ID) : base (ID) {} } @@ -940,18 +1029,33 @@ namespace Mono.Profiler { return caller; } } + public LoadedMethod AllocatorMethod { + get { + return caller; + } + } bool jitTime; public bool JitTime { get { return jitTime; } } + public bool AllocationHappenedAtJitTime { + get { + return jitTime; + } + } StackTrace trace; public StackTrace Trace { get { return trace; } } + public StackTrace AllocationCallStack { + get { + return trace; + } + } public AllocatedObject (ulong id, LoadedClass c, uint size, LoadedMethod caller, bool jitTime, StackTrace trace) { this.id = id; @@ -1072,12 +1176,69 @@ namespace Mono.Profiler { } } - protected FilterHeapItemByClass (LoadedClass c, string description) { + public FilterHeapItemByClass (LoadedClass c, string description) { this.c = c; this.description = description; } } + public abstract class FilterHeapItemByAllocatorMethod<HI> : IHeapItemFilter<HI> where HI : IHeapItem { + protected LoadedMethod allocatorMethod; + public LoadedMethod AllocatorMethod { + get { + return allocatorMethod; + } + } + + public abstract bool Filter (HI heapItem); + + string description; + public string Description { + get { + return description; + } + } + + protected FilterHeapItemByAllocatorMethod (LoadedMethod allocatorMethod, string description) { + this.allocatorMethod = allocatorMethod; + this.description = description; + } + } + + public class HeapItemWasAllocatedByMethod<HI> : FilterHeapItemByAllocatorMethod<HI> where HI : IHeapItem { + public override bool Filter (HI heapItem) { + return heapItem.AllocatorMethod == AllocatorMethod; + } + + public HeapItemWasAllocatedByMethod (LoadedMethod allocatorMethod) : base (allocatorMethod, String.Format ("Object was allocated by {0}", allocatorMethod.Name)) { + } + } + + public class FilterHeapItemByAllocationCallStack<HI> : IHeapItemFilter<HI> where HI : IHeapItem { + protected StackTrace allocationCallStack; + public StackTrace AllocationCallStack { + get { + return allocationCallStack; + } + } + + public bool Filter (HI heapItem) { + return heapItem.AllocationCallStack == allocationCallStack; + } + + string description; + public string Description { + get { + return description; + } + } + + public FilterHeapItemByAllocationCallStack (StackTrace allocationCallStack) { + this.allocationCallStack = allocationCallStack; + this.description = String.Format ("Allocation has call stack:\n{0}", allocationCallStack.FullDescription); + } + } + public class HeapItemIsOfClass<HI> : FilterHeapItemByClass<HI> where HI : IHeapItem { protected static string BuildDescription (LoadedClass c) { return String.Format ("Object has class {0}", c.Name); @@ -1142,39 +1303,118 @@ namespace Mono.Profiler { } } - public class HeapItemSetClassStatistics { - LoadedClass c; - public LoadedClass Class { + public interface IHeapItemSetStatisticsSubject { + string Description {get;} + uint ID {get;} + } + public delegate HISSS GetHeapItemStatisticsSubject<HI,HISSS> (HI item) where HI : IHeapItem where HISSS : IHeapItemSetStatisticsSubject; + public delegate HISSBS NewHeapItemStatisticsBySubject<HISSBS,HISSS> (HISSS subject) where HISSS : IHeapItemSetStatisticsSubject where HISSBS : HeapItemSetStatisticsBySubject<HISSS>; + + public interface IHeapItemSetStatisticsBySubject { + IHeapItemSetStatisticsSubject Subject {get;} + uint ItemsCount {get;} + uint AllocatedBytes {get;} + } + + public abstract class HeapItemSetStatisticsBySubject<HISSS> : IHeapItemSetStatisticsBySubject where HISSS : IHeapItemSetStatisticsSubject { + HISSS subject; + protected HISSS Subject { get { - return c; + return subject; } } + IHeapItemSetStatisticsSubject IHeapItemSetStatisticsBySubject.Subject { + get { + return subject; + } + } + + uint itemsCount; + public uint ItemsCount { + get { + return itemsCount; + } + } + uint allocatedBytes; public uint AllocatedBytes { get { return allocatedBytes; } - internal set { - allocatedBytes = value; - } } - public HeapItemSetClassStatistics (LoadedClass c, uint allocatedBytes) { - this.c = c; - this.allocatedBytes = allocatedBytes; + + internal void AddItem (IHeapItem item) { + itemsCount ++; + allocatedBytes += item.Size; + } + + protected abstract HISSS GetUnavailableSubject (); + + public HeapItemSetStatisticsBySubject (HISSS subject) { + this.subject = subject != null ? subject : GetUnavailableSubject (); + this.itemsCount = 0; + this.allocatedBytes = 0; } - public static Comparison<HeapItemSetClassStatistics> CompareByAllocatedBytes = delegate (HeapItemSetClassStatistics a, HeapItemSetClassStatistics b) { + public static Comparison<HeapItemSetStatisticsBySubject<HISSS>> CompareByAllocatedBytes = delegate (HeapItemSetStatisticsBySubject<HISSS> a, HeapItemSetStatisticsBySubject<HISSS> b) { return a.AllocatedBytes.CompareTo (b.AllocatedBytes); }; } + public class HeapItemSetClassStatistics : HeapItemSetStatisticsBySubject<LoadedClass> { + public LoadedClass Class { + get { + return Subject; + } + } + protected override LoadedClass GetUnavailableSubject () { + return LoadedClass.LoadedClassUnavailable; + } + public HeapItemSetClassStatistics (LoadedClass c) : base (c) { + } + } + + public class HeapItemSetMethodStatistics : HeapItemSetStatisticsBySubject<LoadedMethod> { + public LoadedMethod Method { + get { + return Subject; + } + } + protected override LoadedMethod GetUnavailableSubject () { + return LoadedMethod.LoadedMethodUnavailable; + } + public HeapItemSetMethodStatistics (LoadedMethod method) : base (method) { + } + } + + public class HeapItemSetCallStackStatistics : HeapItemSetStatisticsBySubject<StackTrace> { + public StackTrace CallStack { + get { + return Subject; + } + } + protected override StackTrace GetUnavailableSubject () { + return StackTrace.StackTraceUnavailable; + } + public HeapItemSetCallStackStatistics (StackTrace callStack) : base (callStack) { + } + } + public interface IHeapItemSet { bool ContainsItem (ulong id); string ShortDescription {get;} string LongDescription {get;} IHeapItem[] Elements {get;} HeapItemSetClassStatistics[] ClassStatistics {get;} + HeapItemSetMethodStatistics[] AllocatorMethodStatistics {get;} + HeapItemSetCallStackStatistics[] AllocationCallStackStatistics {get;} uint AllocatedBytes {get;} + bool ObjectAllocationsArePresent {get;} + void FindObjectAllocations (ProviderOfPreviousAllocationsSets previousSetsProvider); + } + + public interface ProviderOfPreviousAllocationsSets { + IEnumerable<HeapItemSet<AllocatedObject>> PreviousAllocationsSets (); } public abstract class HeapItemSet<HI> : IHeapItemSet where HI : IHeapItem { @@ -1220,6 +1460,73 @@ namespace Mono.Profiler { } } + protected HISSBS[] BuildStatistics<HISSS,HISSBS> (GetHeapItemStatisticsSubject<HI,HISSS> getSubject, NewHeapItemStatisticsBySubject<HISSBS,HISSS> newStatistics) where HISSS : IHeapItemSetStatisticsSubject where HISSBS : HeapItemSetStatisticsBySubject<HISSS> { + Dictionary<uint,HISSBS> statistics = new Dictionary<uint,HISSBS> (); + + foreach (HI hi in elements) { + HISSS subject = getSubject (hi); + HISSBS s; + uint id; + if (subject != null) { + id = subject.ID; + } else { + id = 0;; + } + if (statistics.ContainsKey (id)) { + s = statistics [id]; + } else { + s = newStatistics (subject); + statistics [id] = s; + } + s.AddItem (hi); + } + HISSBS[] result = new HISSBS [statistics.Values.Count]; + statistics.Values.CopyTo (result, 0); + Array.Sort (result, HeapItemSetStatisticsBySubject<HISSS>.CompareByAllocatedBytes); + Array.Reverse (result); + + return result; + } + + HeapItemSetMethodStatistics[] allocatorMethodStatistics; + public HeapItemSetMethodStatistics[] AllocatorMethodStatistics { + get { + if ((allocatorMethodStatistics == null) && ObjectAllocationsArePresent) { + allocatorMethodStatistics = BuildStatistics<LoadedMethod,HeapItemSetMethodStatistics> (delegate (HI item) { + return item.AllocatorMethod; + }, delegate (LoadedMethod m) { + return new HeapItemSetMethodStatistics (m); + }); + } + return allocatorMethodStatistics; + } + } + public bool HasAllocatorMethodStatistics { + get { + return allocatorMethodStatistics != null; + } + } + + HeapItemSetCallStackStatistics[] allocationCallStackStatistics; + public HeapItemSetCallStackStatistics[] AllocationCallStackStatistics { + get { + if ((allocationCallStackStatistics == null) && ObjectAllocationsArePresent) { + allocationCallStackStatistics = BuildStatistics<StackTrace,HeapItemSetCallStackStatistics> (delegate (HI item) { + return item.AllocationCallStack; + }, delegate (StackTrace s) { + return new HeapItemSetCallStackStatistics (s); + }); + } + return allocationCallStackStatistics; + } + } + public bool HasAllocationCallStackStatistics { + get { + return allocationCallStackStatistics != null; + } + } + + public void CompareWithSet<OHI> (HeapItemSet<OHI> otherSet, out HeapItemSet<HI> onlyInThisSet, out HeapItemSet<OHI> onlyInOtherSet) where OHI : IHeapItem { HeapItemSetFromComparison<HI,OHI>.PerformComparison<HI,OHI> (this, otherSet, out onlyInThisSet, out onlyInOtherSet); } @@ -1228,29 +1535,40 @@ namespace Mono.Profiler { return HeapItemSetFromComparison<HI,OHI>.PerformIntersection<HI,OHI> (this, otherSet); } - public bool ContainsItem (ulong id) { - int lowIndex = -1; - int highIndex = elements.Length; - - while (true) { - int span = (highIndex - lowIndex) / 2; + public HI this [ulong id] { + get { + int lowIndex = -1; + int highIndex = elements.Length; - if (span > 0) { - int middleIndex = lowIndex + span; - HI middleElement = elements [middleIndex]; - ulong middleID = middleElement.ID; - if (middleID > id) { - highIndex = middleIndex; - } else if (middleID < id) { - lowIndex = middleIndex; + while (true) { + int span = (highIndex - lowIndex) / 2; + + if (span > 0) { + int middleIndex = lowIndex + span; + HI middleElement = elements [middleIndex]; + ulong middleID = middleElement.ID; + if (middleID > id) { + highIndex = middleIndex; + } else if (middleID < id) { + lowIndex = middleIndex; + } else { + return middleElement; + } } else { - return true; + return default (HI); } - } else { - return false; } } } + public HI this [HI item] { + get { + return this [item.ID]; + } + } + + public bool ContainsItem (ulong id) { + return this [id] != null; + } public HeapItemSet<HeapObject> ObjectsReferencingItemInSet (HeapItemSet<HeapObject> objectSet) { return Mono.Profiler.HeapItemSetFromComparison<HI,HeapObject>.ObjectsReferencingItemInSet (this, objectSet); @@ -1259,30 +1577,45 @@ namespace Mono.Profiler { return Mono.Profiler.HeapItemSetFromComparison<HI,HeapObject>.ObjectsReferencedByItemInSet (this, objectSet); } - protected HeapItemSet (string shortDescription, string longDescription, HI[] elements) { + static void FindObjectAllocations (HeapItemSet<HeapObject> baseSet, ProviderOfPreviousAllocationsSets previousSetsProvider) { + foreach (HeapObject heapObject in baseSet.Elements) { + if (heapObject.Allocation == null) { + heapObject.FindAllocation (previousSetsProvider); + } + } + } + + bool objectAllocationsArePresent; + public bool ObjectAllocationsArePresent { + get { + return objectAllocationsArePresent; + } + } + public void FindObjectAllocations (ProviderOfPreviousAllocationsSets previousSetsProvider) { + if ((! objectAllocationsArePresent)) { + HeapItemSet<HeapObject> baseSet = this as HeapItemSet<HeapObject>; + if (baseSet != null) { + FindObjectAllocations (baseSet, previousSetsProvider); + objectAllocationsArePresent = true; + } + } + } + + protected HeapItemSet (string shortDescription, string longDescription, HI[] elements, bool objectAllocationsArePresent) { this.shortDescription = shortDescription; this.longDescription = longDescription; this.elements = elements; + this.objectAllocationsArePresent = objectAllocationsArePresent; allocatedBytes = 0; Array.Sort (this.elements, CompareHeapItemsByID); - Dictionary<ulong,HeapItemSetClassStatistics> statistics = new Dictionary<ulong,HeapItemSetClassStatistics> (); - foreach (HI hi in elements) { - HeapItemSetClassStatistics cs; - if (statistics.ContainsKey (hi.Class.ID)) { - cs = statistics [hi.Class.ID]; - cs.AllocatedBytes += hi.Size; - allocatedBytes += hi.Size; - } else { - cs = new HeapItemSetClassStatistics (hi.Class, hi.Size); - statistics [hi.Class.ID] = cs; - } - } - classStatistics = new HeapItemSetClassStatistics [statistics.Values.Count]; - statistics.Values.CopyTo (classStatistics, 0); - Array.Sort (classStatistics, HeapItemSetClassStatistics.CompareByAllocatedBytes); - Array.Reverse (classStatistics); + classStatistics = BuildStatistics<LoadedClass,HeapItemSetClassStatistics> (delegate (HI item) { + allocatedBytes += item.Size; + return item.Class; + }, delegate (LoadedClass c) { + return new HeapItemSetClassStatistics (c); + }); } } @@ -1297,7 +1630,7 @@ namespace Mono.Profiler { public HeapObjectSetFromSnapshot (HeapSnapshot heapSnapshot): base (String.Format ("Heap at {0}.{1:000}s", heapSnapshot.HeaderStartTime.Seconds, heapSnapshot.HeaderStartTime.Milliseconds), String.Format ("Heap snapshot taken at {0}.{1:000}s", heapSnapshot.HeaderStartTime.Seconds, heapSnapshot.HeaderStartTime.Milliseconds), - heapSnapshot.HeapObjects) { + heapSnapshot.HeapObjects, false) { this.heapSnapshot = heapSnapshot; } } @@ -1306,7 +1639,7 @@ namespace Mono.Profiler { public AllocatedObjectSetFromEvents (TimeSpan timeFromStart, AllocatedObject[] allocations): base (String.Format ("Allocations {0}.{1:000}s", timeFromStart.Seconds, timeFromStart.Milliseconds), String.Format ("Allocations taken from {0}.{1:000}s", timeFromStart.Seconds, timeFromStart.Milliseconds), - allocations) { + allocations, true) { } } @@ -1337,7 +1670,7 @@ namespace Mono.Profiler { return result; } - public HeapItemSetFromFilter (HeapItemSet<HI> baseSet, IHeapItemFilter<HI> filter): base (filter.Description, String.Format ("{0} and {1}", filter.Description, baseSet.LongDescription), filterSet (baseSet, filter)) { + public HeapItemSetFromFilter (HeapItemSet<HI> baseSet, IHeapItemFilter<HI> filter): base (filter.Description, String.Format ("{0} and {1}", filter.Description, baseSet.LongDescription), filterSet (baseSet, filter), baseSet.ObjectAllocationsArePresent) { this.baseSet = baseSet; this.filter = filter; } @@ -1475,7 +1808,7 @@ namespace Mono.Profiler { return new HeapItemSetFromComparison<HeapObject,HI>(objectSet, itemSet, result.ToArray (), "references item"); } - HeapItemSetFromComparison (HeapItemSet<HI> baseSet, HeapItemSet<OHI> otherSet, HI[] heapItems, string relation): base (buildShortDescription (otherSet, relation), buildLongDescription (otherSet, relation), heapItems) { + HeapItemSetFromComparison (HeapItemSet<HI> baseSet, HeapItemSet<OHI> otherSet, HI[] heapItems, string relation): base (buildShortDescription (otherSet, relation), buildLongDescription (otherSet, relation), heapItems, baseSet.ObjectAllocationsArePresent) { this.baseSet = baseSet; this.otherSet = otherSet; } |