2006-11-13 Lluis Sanchez Gual <lluis@novell.com>
* profiler/outfile-writer.h: * profiler/outfile-writer.c: Removed unused constants. Added some totals in the file headers. * HeapShot.Gui.Widgets/ReferenceTreeViewer.cs: Track api changes. Load the tree in the background. * HeapShot.Gui.Widgets/gtk-gui/gui.stetic, HeapShot.Gui.Widgets/gtk-gui/objects.xml: Updated. * HeapShot.Gui.Widgets/gtk-gui/library.dat: Removed from build. * HeapShot.Gui.Widgets/ObjectMapViewer.cs: Track api changes. * configure.in: Fix directory name. * HeapShot/ReferenceTreeReport.cs: Track api changes. * HeapShot.Reader/TypeInfo.cs: * HeapShot.Reader/FieldInfo.cs: * HeapShot.Reader/ObjectInfo.cs: * HeapShot.Reader/ReferenceNode.cs: * HeapShot.Reader/ObjectMapFileReader.cs: Redesigned the way object information is loaded in memory. It's now using big arrays of structs, which is much more efficient that reference objects. svn path=/trunk/heap-shot/; revision=67764
This commit is contained in:
Родитель
243b00ea73
Коммит
b17c3cad5c
23
ChangeLog
23
ChangeLog
|
@ -1,3 +1,26 @@
|
|||
2006-11-13 Lluis Sanchez Gual <lluis@novell.com>
|
||||
|
||||
* profiler/outfile-writer.h:
|
||||
* profiler/outfile-writer.c: Removed unused constants. Added some
|
||||
totals in the file headers.
|
||||
|
||||
* HeapShot.Gui.Widgets/ReferenceTreeViewer.cs: Track api changes.
|
||||
Load the tree in the background.
|
||||
* HeapShot.Gui.Widgets/gtk-gui/gui.stetic,
|
||||
HeapShot.Gui.Widgets/gtk-gui/objects.xml: Updated.
|
||||
* HeapShot.Gui.Widgets/gtk-gui/library.dat: Removed from build.
|
||||
* HeapShot.Gui.Widgets/ObjectMapViewer.cs: Track api changes.
|
||||
* configure.in: Fix directory name.
|
||||
* HeapShot/ReferenceTreeReport.cs: Track api changes.
|
||||
|
||||
* HeapShot.Reader/TypeInfo.cs:
|
||||
* HeapShot.Reader/FieldInfo.cs:
|
||||
* HeapShot.Reader/ObjectInfo.cs:
|
||||
* HeapShot.Reader/ReferenceNode.cs:
|
||||
* HeapShot.Reader/ObjectMapFileReader.cs: Redesigned the way object
|
||||
information is loaded in memory. It's now using big arrays of structs,
|
||||
which is much more efficient that reference objects.
|
||||
|
||||
2006-11-06 Raja R Harinath <rharinath@novell.com>
|
||||
|
||||
* configure.in (HeapShot/heap-shot): Separate out output command.
|
||||
|
|
|
@ -50,6 +50,11 @@ namespace HeapShot.Gui.Widgets
|
|||
labelName.Text = "";
|
||||
}
|
||||
|
||||
public event ProgressEventHandler ProgressEvent {
|
||||
add { allObjectsTree.ProgressEvent += value; }
|
||||
remove { allObjectsTree.ProgressEvent -= value; }
|
||||
}
|
||||
|
||||
public void AddFile (string fileName)
|
||||
{
|
||||
ObjectMapReader map = new ObjectMapReader (fileName);
|
||||
|
@ -78,14 +83,8 @@ namespace HeapShot.Gui.Widgets
|
|||
else
|
||||
labelName.Text = System.IO.Path.GetFileName (map.Name);
|
||||
|
||||
int ni = 0;
|
||||
uint mem = 0;
|
||||
foreach (TypeInfo t in map.GetTypes ()) {
|
||||
ni += t.Objects.Count;
|
||||
mem += t.TotalSize;
|
||||
}
|
||||
labelCount.Text = ni.ToString ("n0");
|
||||
labelMemory.Text = mem.ToString ("n0") + " bytes";
|
||||
labelCount.Text = map.NumObjects.ToString ("n0");
|
||||
labelMemory.Text = map.TotalMemory.ToString ("n0") + " bytes";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,8 +182,7 @@ namespace HeapShot.Gui.Widgets
|
|||
return dif[2];
|
||||
}
|
||||
|
||||
ObjectMapReader res = new ObjectMapReader (m2.Name);
|
||||
res.RemoveData (m1);
|
||||
ObjectMapReader res = ObjectMapReader.GetDiff (m1, m2);
|
||||
difs.Add (new ObjectMapReader[] { m1, m2, res });
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ using HeapShot.Reader;
|
|||
|
||||
namespace HeapShot.Gui.Widgets
|
||||
{
|
||||
public delegate void ProgressEventHandler (int current, int max, string message);
|
||||
|
||||
public class ReferenceTreeViewer : Gtk.Bin
|
||||
{
|
||||
protected Gtk.TreeView treeview;
|
||||
|
@ -21,10 +23,14 @@ namespace HeapShot.Gui.Widgets
|
|||
const int InstancesCol = 6;
|
||||
const int RefsCol = 7;
|
||||
int TreeColRefs;
|
||||
bool reloadRequested;
|
||||
bool loading;
|
||||
|
||||
ObjectMapReader file;
|
||||
string typeName;
|
||||
protected Gtk.HBox boxFilter;
|
||||
|
||||
public event ProgressEventHandler ProgressEvent;
|
||||
|
||||
public ReferenceTreeViewer()
|
||||
{
|
||||
|
@ -111,10 +117,35 @@ namespace HeapShot.Gui.Widgets
|
|||
boxFilter.Visible = true;
|
||||
treeview.Columns [TreeColRefs].Visible = InverseReferences;
|
||||
|
||||
store.Clear ();
|
||||
foreach (TypeInfo t in file.GetTypes ()) {
|
||||
InternalFillType (file, t.Name);
|
||||
if (loading) {
|
||||
// If the tree is already being loaded, notify that loading
|
||||
// has to start again, since the file has changed.
|
||||
reloadRequested = true;
|
||||
return;
|
||||
}
|
||||
|
||||
loading = true;
|
||||
store.Clear ();
|
||||
int n=0;
|
||||
foreach (int t in file.GetTypes ()) {
|
||||
if (++n == 20) {
|
||||
if (ProgressEvent != null) {
|
||||
ProgressEvent (n, file.GetTypeCount (), null);
|
||||
}
|
||||
while (Gtk.Application.EventsPending ())
|
||||
Gtk.Application.RunIteration ();
|
||||
if (reloadRequested) {
|
||||
loading = false;
|
||||
reloadRequested = false;
|
||||
FillAllTypes (this.file);
|
||||
return;
|
||||
}
|
||||
n = 0;
|
||||
}
|
||||
if (file.GetObjectCountForType (t) > 0)
|
||||
InternalFillType (file, t);
|
||||
}
|
||||
loading = false;
|
||||
}
|
||||
|
||||
public void FillType (ObjectMapReader file, string typeName)
|
||||
|
@ -124,13 +155,13 @@ namespace HeapShot.Gui.Widgets
|
|||
store.Clear ();
|
||||
boxFilter.Visible = false;
|
||||
treeview.Columns [TreeColRefs].Visible = InverseReferences;
|
||||
TreeIter iter = InternalFillType (file, typeName);
|
||||
TreeIter iter = InternalFillType (file, file.GetTypeFromName (typeName));
|
||||
treeview.ExpandRow (store.GetPath (iter), false);
|
||||
}
|
||||
|
||||
TreeIter InternalFillType (ObjectMapReader file, string typeName)
|
||||
TreeIter InternalFillType (ObjectMapReader file, int type)
|
||||
{
|
||||
ReferenceNode node = file.GetReferenceTree (typeName, checkInverse.Active);
|
||||
ReferenceNode node = file.GetReferenceTree (type, checkInverse.Active);
|
||||
return AddNode (TreeIter.Zero, node);
|
||||
}
|
||||
|
||||
|
@ -221,23 +252,34 @@ namespace HeapShot.Gui.Widgets
|
|||
SortType type;
|
||||
store.GetSortColumnId (out col, out type);
|
||||
|
||||
ReferenceNode nod1 = (ReferenceNode) model.GetValue (a, ReferenceCol);
|
||||
ReferenceNode nod2 = (ReferenceNode) model.GetValue (b, ReferenceCol);
|
||||
switch (col) {
|
||||
case 0:
|
||||
return string.Compare (nod1.TypeName, nod2.TypeName);
|
||||
case 1:
|
||||
return nod1.RefCount.CompareTo (nod2.RefCount);
|
||||
case 2:
|
||||
return nod1.RefsToParent.CompareTo (nod2.RefsToParent);
|
||||
case 3:
|
||||
return nod1.TotalMemory.CompareTo (nod2.TotalMemory);
|
||||
case 4:
|
||||
return nod1.AverageSize.CompareTo (nod2.AverageSize);
|
||||
default:
|
||||
Console.WriteLine ("PPP: " + col);
|
||||
return 1;
|
||||
// throw new InvalidOperationException ();
|
||||
object o1 = model.GetValue (a, ReferenceCol);
|
||||
object o2 = model.GetValue (b, ReferenceCol);
|
||||
|
||||
if (o1 is ReferenceNode && o2 is ReferenceNode) {
|
||||
ReferenceNode nod1 = (ReferenceNode) o1;
|
||||
ReferenceNode nod2 = (ReferenceNode) o2;
|
||||
switch (col) {
|
||||
case 0:
|
||||
return string.Compare (nod1.TypeName, nod2.TypeName);
|
||||
case 1:
|
||||
return nod1.RefCount.CompareTo (nod2.RefCount);
|
||||
case 2:
|
||||
return nod1.RefsToParent.CompareTo (nod2.RefsToParent);
|
||||
case 3:
|
||||
return nod1.TotalMemory.CompareTo (nod2.TotalMemory);
|
||||
case 4:
|
||||
return nod1.AverageSize.CompareTo (nod2.AverageSize);
|
||||
default:
|
||||
Console.WriteLine ("PPP: " + col);
|
||||
return 1;
|
||||
// throw new InvalidOperationException ();
|
||||
}
|
||||
} else if (o1 is FieldReference && o2 is FieldReference) {
|
||||
return ((FieldReference)o1).FiledName.CompareTo (((FieldReference)o2).FiledName);
|
||||
} else if (o1 is FieldReference) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,79 +60,20 @@
|
|||
<property name="Events">0</property>
|
||||
<property name="MemberName" />
|
||||
<child>
|
||||
<widget class="Gtk.Label" id="label3">
|
||||
<property name="LabelProp" translatable="yes" context="yes" comments="">|Name:</property>
|
||||
<property name="Xalign">0</property>
|
||||
<property name="Events">0</property>
|
||||
<property name="MemberName" />
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="YExpand">False</property>
|
||||
<property name="AutoSize">True</property>
|
||||
<property name="YShrink">False</property>
|
||||
<property name="XShrink">False</property>
|
||||
<property name="XExpand">False</property>
|
||||
<property name="YFill">True</property>
|
||||
<property name="XFill">True</property>
|
||||
<property name="YOptions">Fill</property>
|
||||
<property name="XOptions">Fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="Gtk.Label" id="label4">
|
||||
<property name="LabelProp" translatable="yes" context="yes" comments="">|Object count:</property>
|
||||
<property name="Xalign">0</property>
|
||||
<property name="Events">0</property>
|
||||
<property name="MemberName" />
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="BottomAttach">2</property>
|
||||
<property name="YExpand">False</property>
|
||||
<property name="AutoSize">True</property>
|
||||
<property name="YShrink">False</property>
|
||||
<property name="TopAttach">1</property>
|
||||
<property name="XShrink">False</property>
|
||||
<property name="XExpand">False</property>
|
||||
<property name="YFill">True</property>
|
||||
<property name="XFill">True</property>
|
||||
<property name="YOptions">Fill</property>
|
||||
<property name="XOptions">Fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="Gtk.Label" id="label5">
|
||||
<property name="LabelProp" translatable="yes" context="yes" comments="">|Total memory:</property>
|
||||
<property name="Xalign">0</property>
|
||||
<property name="Events">0</property>
|
||||
<property name="MemberName" />
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="BottomAttach">3</property>
|
||||
<property name="YExpand">False</property>
|
||||
<property name="AutoSize">True</property>
|
||||
<property name="YShrink">False</property>
|
||||
<property name="TopAttach">2</property>
|
||||
<property name="XShrink">False</property>
|
||||
<property name="XExpand">False</property>
|
||||
<property name="YFill">True</property>
|
||||
<property name="XFill">True</property>
|
||||
<property name="YOptions">Fill</property>
|
||||
<property name="XOptions">Fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="Gtk.Label" id="labelName">
|
||||
<widget class="Gtk.Label" id="labelMemory">
|
||||
<property name="LabelProp" />
|
||||
<property name="Xalign">0</property>
|
||||
<property name="Events">0</property>
|
||||
<property name="MemberName">labelName</property>
|
||||
<property name="MemberName">labelMemory</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="BottomAttach">3</property>
|
||||
<property name="LeftAttach">1</property>
|
||||
<property name="YExpand">False</property>
|
||||
<property name="RightAttach">2</property>
|
||||
<property name="AutoSize">True</property>
|
||||
<property name="YShrink">False</property>
|
||||
<property name="TopAttach">2</property>
|
||||
<property name="XShrink">False</property>
|
||||
<property name="XExpand">False</property>
|
||||
<property name="YFill">True</property>
|
||||
|
@ -165,19 +106,38 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="Gtk.Label" id="labelMemory">
|
||||
<widget class="Gtk.Label" id="labelName">
|
||||
<property name="LabelProp" />
|
||||
<property name="Xalign">0</property>
|
||||
<property name="Events">0</property>
|
||||
<property name="MemberName">labelMemory</property>
|
||||
<property name="MemberName">labelName</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="BottomAttach">3</property>
|
||||
<property name="LeftAttach">1</property>
|
||||
<property name="YExpand">False</property>
|
||||
<property name="RightAttach">2</property>
|
||||
<property name="AutoSize">True</property>
|
||||
<property name="YShrink">False</property>
|
||||
<property name="XShrink">False</property>
|
||||
<property name="XExpand">False</property>
|
||||
<property name="YFill">True</property>
|
||||
<property name="XFill">True</property>
|
||||
<property name="YOptions">Fill</property>
|
||||
<property name="XOptions">Fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="Gtk.Label" id="label5">
|
||||
<property name="LabelProp" translatable="yes" context="yes" comments="">|Total memory:</property>
|
||||
<property name="Xalign">0</property>
|
||||
<property name="Events">0</property>
|
||||
<property name="MemberName" />
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="BottomAttach">3</property>
|
||||
<property name="YExpand">False</property>
|
||||
<property name="AutoSize">True</property>
|
||||
<property name="YShrink">False</property>
|
||||
<property name="TopAttach">2</property>
|
||||
<property name="XShrink">False</property>
|
||||
<property name="XExpand">False</property>
|
||||
|
@ -187,6 +147,46 @@
|
|||
<property name="XOptions">Fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="Gtk.Label" id="label4">
|
||||
<property name="LabelProp" translatable="yes" context="yes" comments="">|Object count:</property>
|
||||
<property name="Xalign">0</property>
|
||||
<property name="Events">0</property>
|
||||
<property name="MemberName" />
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="BottomAttach">2</property>
|
||||
<property name="YExpand">False</property>
|
||||
<property name="AutoSize">True</property>
|
||||
<property name="YShrink">False</property>
|
||||
<property name="TopAttach">1</property>
|
||||
<property name="XShrink">False</property>
|
||||
<property name="XExpand">False</property>
|
||||
<property name="YFill">True</property>
|
||||
<property name="XFill">True</property>
|
||||
<property name="YOptions">Fill</property>
|
||||
<property name="XOptions">Fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="Gtk.Label" id="label3">
|
||||
<property name="LabelProp" translatable="yes" context="yes" comments="">|Name:</property>
|
||||
<property name="Xalign">0</property>
|
||||
<property name="Events">0</property>
|
||||
<property name="MemberName" />
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="YExpand">False</property>
|
||||
<property name="AutoSize">True</property>
|
||||
<property name="YShrink">False</property>
|
||||
<property name="XShrink">False</property>
|
||||
<property name="XExpand">False</property>
|
||||
<property name="YFill">True</property>
|
||||
<property name="XFill">True</property>
|
||||
<property name="YOptions">Fill</property>
|
||||
<property name="XOptions">Fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="TabExpand">False</property>
|
||||
|
|
Двоичные данные
HeapShot.Gui.Widgets/gtk-gui/library.dat
Двоичные данные
HeapShot.Gui.Widgets/gtk-gui/library.dat
Двоичный файл не отображается.
|
@ -10,6 +10,7 @@
|
|||
<itemgroup ref="Gtk.Widget" />
|
||||
<itemgroup label="ReferenceTreeViewer Signals">
|
||||
<signal name="TypeActivated" />
|
||||
<signal name="ProgressEvent" />
|
||||
</itemgroup>
|
||||
</signals>
|
||||
</object>
|
||||
|
@ -19,6 +20,9 @@
|
|||
</itemgroups>
|
||||
<signals>
|
||||
<itemgroup ref="Gtk.Widget" />
|
||||
<itemgroup label="ObjectMapViewer Signals">
|
||||
<signal name="ProgressEvent" />
|
||||
</itemgroup>
|
||||
</signals>
|
||||
</object>
|
||||
</objects>
|
|
@ -27,9 +27,9 @@ using System.Text.RegularExpressions;
|
|||
|
||||
namespace HeapShot.Reader {
|
||||
|
||||
public class FieldInfo
|
||||
public struct FieldInfo
|
||||
{
|
||||
public uint Code;
|
||||
public ulong Code;
|
||||
public string Name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,46 +27,14 @@ using System.Text.RegularExpressions;
|
|||
|
||||
namespace HeapShot.Reader {
|
||||
|
||||
public class ObjectInfo
|
||||
public struct ObjectInfo
|
||||
{
|
||||
TypeInfo type;
|
||||
uint size;
|
||||
ObjectReference[] references;
|
||||
uint code;
|
||||
|
||||
public ArrayList Referencers;
|
||||
|
||||
public ObjectInfo (uint code, TypeInfo type, uint size, ObjectReference[] references)
|
||||
{
|
||||
this.code = code;
|
||||
this.type = type;
|
||||
this.size = size + (uint)IntPtr.Size + (uint)IntPtr.Size; // Add MonoObject overhead
|
||||
this.references = references;
|
||||
}
|
||||
|
||||
public uint Code {
|
||||
get { return code; }
|
||||
}
|
||||
|
||||
public TypeInfo Type {
|
||||
get { return type; }
|
||||
}
|
||||
|
||||
public uint Size {
|
||||
get { return size; }
|
||||
}
|
||||
|
||||
public ObjectReference[] References {
|
||||
get { return references; }
|
||||
}
|
||||
|
||||
public string GetReferencerField (ObjectInfo referenced)
|
||||
{
|
||||
foreach (ObjectReference oref in References) {
|
||||
if (oref.Object == referenced)
|
||||
return this.Type.GetFieldName (oref.FieldCode);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public uint Code;
|
||||
public int Type;
|
||||
public uint Size;
|
||||
public int RefsIndex;
|
||||
public int RefsCount;
|
||||
public int InverseRefsIndex;
|
||||
public int InverseRefsCount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,15 +30,45 @@ namespace HeapShot.Reader {
|
|||
|
||||
public class ObjectMapReader
|
||||
{
|
||||
const uint magic_number = 0x4eabbdd1;
|
||||
const int expected_log_version = 5;
|
||||
const uint magic_number = 0x4eabfdd1;
|
||||
const int expected_log_version = 6;
|
||||
const int expected_summary_version = 2;
|
||||
const string log_file_label = "heap-shot logfile";
|
||||
const string summary_file_label = "heap-shot summary";
|
||||
|
||||
bool terminated_normally = true;
|
||||
string name;
|
||||
DateTime timestamp;
|
||||
uint numTypes;
|
||||
uint numObjects;
|
||||
uint numReferences;
|
||||
uint numFields;
|
||||
uint totalMemory;
|
||||
uint objectCount;
|
||||
|
||||
int curObject;
|
||||
int curType;
|
||||
int curField;
|
||||
int curRef;
|
||||
|
||||
ObjectInfo[] objects;
|
||||
TypeInfo[] types;
|
||||
string[] fieldNames;
|
||||
int[] objectIndices;
|
||||
int[] typeIndices;
|
||||
int[] references;
|
||||
int[] inverseRefs;
|
||||
int[] fieldReferences;
|
||||
bool[] filteredObjects;
|
||||
|
||||
uint[] referenceCodes;
|
||||
uint[] objectTypeCodes;
|
||||
uint[] fieldCodes;
|
||||
uint[] fieldReferenceCodes;
|
||||
uint[] objectCodes;
|
||||
|
||||
internal ObjectMapReader ()
|
||||
{
|
||||
}
|
||||
|
||||
public ObjectMapReader (string filename)
|
||||
{
|
||||
|
@ -66,6 +96,14 @@ namespace HeapShot.Reader {
|
|||
get { return timestamp; }
|
||||
}
|
||||
|
||||
public uint TotalMemory {
|
||||
get { return totalMemory; }
|
||||
}
|
||||
|
||||
public uint NumObjects {
|
||||
get { return objectCount; }
|
||||
}
|
||||
|
||||
public static ObjectMapReader CreateProcessSnapshot (int pid)
|
||||
{
|
||||
string dumpFile = "/tmp/heap-shot-dump";
|
||||
|
@ -108,8 +146,7 @@ namespace HeapShot.Reader {
|
|||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Return true if this is a summary file, false if it is a log file.
|
||||
private bool ReadPreamble (BinaryReader reader)
|
||||
private void ReadPreamble (BinaryReader reader)
|
||||
{
|
||||
uint this_magic;
|
||||
this_magic = reader.ReadUInt32 ();
|
||||
|
@ -124,16 +161,11 @@ namespace HeapShot.Reader {
|
|||
this_version = reader.ReadInt32 ();
|
||||
|
||||
string this_label;
|
||||
bool is_summary;
|
||||
int expected_version;
|
||||
|
||||
this_label = reader.ReadString ();
|
||||
if (this_label == log_file_label) {
|
||||
is_summary = false;
|
||||
expected_version = expected_log_version;
|
||||
} else if (this_label == summary_file_label) {
|
||||
is_summary = true;
|
||||
expected_version = expected_summary_version;
|
||||
} else
|
||||
throw new Exception ("Unknown file label in heap-shot outfile");
|
||||
|
||||
|
@ -143,8 +175,11 @@ namespace HeapShot.Reader {
|
|||
this_label, expected_version, this_version);
|
||||
throw new Exception (msg);
|
||||
}
|
||||
|
||||
return is_summary;
|
||||
numTypes = reader.ReadUInt32 ();
|
||||
numObjects = reader.ReadUInt32 ();
|
||||
numReferences = reader.ReadUInt32 ();
|
||||
numFields = reader.ReadUInt32 ();
|
||||
objectCount = numObjects;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -153,12 +188,20 @@ namespace HeapShot.Reader {
|
|||
|
||||
// These need to agree w/ the definitions in outfile-writer.c
|
||||
const byte TAG_TYPE = 0x01;
|
||||
const byte TAG_OBJECT = 0x06;
|
||||
const byte TAG_OBJECT = 0x02;
|
||||
const byte TAG_EOS = 0xff;
|
||||
|
||||
private void ReadLogFile (BinaryReader reader)
|
||||
{
|
||||
int chunk_count = 0;
|
||||
|
||||
objects = new ObjectInfo [numObjects];
|
||||
types = new TypeInfo [numTypes];
|
||||
objectTypeCodes = new uint [numObjects];
|
||||
referenceCodes = new uint [numReferences];
|
||||
fieldReferenceCodes = new uint [numReferences];
|
||||
fieldCodes = new uint [numFields];
|
||||
fieldNames = new string [numFields];
|
||||
|
||||
try {
|
||||
while (ReadLogFileChunk (reader))
|
||||
|
@ -175,6 +218,11 @@ namespace HeapShot.Reader {
|
|||
}
|
||||
BuildMap ();
|
||||
Spew ("Processed {0} chunks", chunk_count);
|
||||
|
||||
objectTypeCodes = null;
|
||||
referenceCodes = null;
|
||||
fieldReferenceCodes = null;
|
||||
fieldCodes = null;
|
||||
}
|
||||
|
||||
private bool ReadLogFileChunk (BinaryReader reader)
|
||||
|
@ -206,174 +254,312 @@ namespace HeapShot.Reader {
|
|||
|
||||
private void ReadLogFileChunk_Type (BinaryReader reader)
|
||||
{
|
||||
uint code;
|
||||
code = reader.ReadUInt32 ();
|
||||
|
||||
string name;
|
||||
name = reader.ReadString ();
|
||||
uint code = reader.ReadUInt32 ();
|
||||
string name = reader.ReadString ();
|
||||
|
||||
ArrayList fields = new ArrayList ();
|
||||
types [curType].Code = code;
|
||||
types [curType].Name = name;
|
||||
types [curType].FieldsIndex = curField;
|
||||
|
||||
int nf = 0;
|
||||
uint fcode;
|
||||
while ((fcode = reader.ReadUInt32 ()) != 0) {
|
||||
FieldInfo f = new FieldInfo ();
|
||||
f.Code = fcode;
|
||||
f.Name = reader.ReadString ();
|
||||
fields.Add (f);
|
||||
fieldCodes [curField] = fcode;
|
||||
fieldNames [curField] = reader.ReadString ();
|
||||
curField++;
|
||||
nf++;
|
||||
}
|
||||
|
||||
RegisterType (code, name, (FieldInfo[]) fields.ToArray (typeof(FieldInfo)));
|
||||
types [curType].FieldsCount = nf;
|
||||
curType++;
|
||||
}
|
||||
|
||||
private void ReadLogFileChunk_Object (BinaryReader reader)
|
||||
{
|
||||
uint code;
|
||||
code = reader.ReadUInt32 ();
|
||||
|
||||
uint typeCode;
|
||||
typeCode = reader.ReadUInt32 ();
|
||||
|
||||
uint size;
|
||||
size = reader.ReadUInt32 ();
|
||||
objects [curObject].Code = reader.ReadUInt32 ();
|
||||
objectTypeCodes [curObject] = reader.ReadUInt32 ();
|
||||
objects [curObject].Size = reader.ReadUInt32 ();
|
||||
objects [curObject].RefsIndex = curRef;
|
||||
totalMemory += objects [curObject].Size;
|
||||
|
||||
// Read references
|
||||
// Read referenceCodes
|
||||
|
||||
ArrayList refs = new ArrayList ();
|
||||
int nr = 0;
|
||||
uint oref;
|
||||
while ((oref = reader.ReadUInt32 ()) != 0) {
|
||||
ObjectReference o = new ObjectReference ();
|
||||
o.ObjectCode = oref;
|
||||
o.FieldCode = reader.ReadUInt32 ();
|
||||
refs.Add (o);
|
||||
referenceCodes [curRef] = oref;
|
||||
fieldReferenceCodes [curRef] = reader.ReadUInt32 ();
|
||||
nr++;
|
||||
curRef++;
|
||||
}
|
||||
|
||||
ObjectReference[] array = refs.Count > 0 ? (ObjectReference[]) refs.ToArray (typeof(ObjectReference)) : null;
|
||||
RegisterObject (code, typeCode, size, array);
|
||||
}
|
||||
|
||||
Hashtable types = new Hashtable ();
|
||||
Dictionary<uint,ObjectInfo> objects = new Dictionary<uint,ObjectInfo> ();
|
||||
Hashtable typesByName = new Hashtable ();
|
||||
|
||||
void RegisterType (uint id, string name, FieldInfo[] fields)
|
||||
{
|
||||
TypeInfo t = new TypeInfo (id, name, fields);
|
||||
types [id] = t;
|
||||
typesByName [name] = t;
|
||||
}
|
||||
|
||||
void RegisterObject (uint id, uint typeId, uint size, ObjectReference[] refs)
|
||||
{
|
||||
TypeInfo type = (TypeInfo) types [typeId];
|
||||
if (type == null) {
|
||||
Spew ("Type not found");
|
||||
return;
|
||||
}
|
||||
ObjectInfo ob = new ObjectInfo (id, type, size, refs);
|
||||
type.Objects.Add (ob);
|
||||
objects [id] = ob;
|
||||
objects [curObject].RefsCount = nr;
|
||||
curObject++;
|
||||
}
|
||||
|
||||
void BuildMap ()
|
||||
{
|
||||
foreach (ObjectInfo o in objects.Values) {
|
||||
if (o.References != null) {
|
||||
for (int n=0; n<o.References.Length; n++) {
|
||||
ObjectInfo refo = GetObject (o.References [n].ObjectCode);
|
||||
if (refo != null) {
|
||||
o.References [n].Object = refo;
|
||||
if (refo.Referencers == null)
|
||||
refo.Referencers = new ArrayList (2);
|
||||
refo.Referencers.Add (o);
|
||||
// Build an array of object indices and sort it
|
||||
|
||||
RefComparer objectComparer = new RefComparer ();
|
||||
objectComparer.objects = objects;
|
||||
|
||||
objectIndices = new int [numObjects];
|
||||
for (int n=0; n < numObjects; n++)
|
||||
objectIndices [n] = n;
|
||||
Array.Sort<int> (objectIndices, objectComparer);
|
||||
// Sorted array of codes needed for the binary search
|
||||
objectCodes = new uint [numObjects];
|
||||
for (int n=0; n < numObjects; n++)
|
||||
objectCodes [n] = objects [objectIndices[n]].Code;
|
||||
|
||||
// Build an array of type indices and sort it
|
||||
|
||||
TypeComparer typeComparer = new TypeComparer ();
|
||||
typeComparer.types = types;
|
||||
|
||||
typeIndices = new int [numTypes];
|
||||
for (int n=0; n < numTypes; n++)
|
||||
typeIndices [n] = n;
|
||||
Array.Sort<int> (typeIndices, typeComparer);
|
||||
// Sorted array of codes needed for the binary search
|
||||
uint[] typeCodes = new uint [numTypes];
|
||||
for (int n=0; n < numTypes; n++) {
|
||||
typeCodes [n] = types [typeIndices[n]].Code;
|
||||
}
|
||||
|
||||
Console.WriteLine ("--");
|
||||
// Assign the type index to each object
|
||||
|
||||
for (int n=0; n<numObjects; n++) {
|
||||
int i = Array.BinarySearch<uint> (typeCodes, objectTypeCodes [n]);
|
||||
if (i < 0)
|
||||
Console.WriteLine ("TNF: " + objectTypeCodes [n]);
|
||||
else {
|
||||
objects [n].Type = typeIndices [i];
|
||||
types [objects [n].Type].ObjectCount++;
|
||||
types [objects [n].Type].TotalSize += objects [n].Size;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the array of referenceCodes, but using indexes
|
||||
references = new int [numReferences];
|
||||
|
||||
for (int n=0; n<numReferences; n++) {
|
||||
int i = Array.BinarySearch (objectCodes, referenceCodes[n]);
|
||||
if (i >= 0) {
|
||||
references[n] = objectIndices [i];
|
||||
objects [objectIndices [i]].InverseRefsCount++;
|
||||
} else
|
||||
references[n] = -1;
|
||||
}
|
||||
|
||||
// Calculate the array index of inverse referenceCodes for each object
|
||||
|
||||
int[] invPositions = new int [numObjects]; // Temporary array to hold reference positions
|
||||
int rp = 0;
|
||||
for (int n=0; n<numObjects; n++) {
|
||||
objects [n].InverseRefsIndex = rp;
|
||||
invPositions [n] = rp;
|
||||
rp += objects [n].InverseRefsCount;
|
||||
}
|
||||
|
||||
// Build the array of inverse referenceCodes
|
||||
// Also calculate the index of each field name
|
||||
|
||||
inverseRefs = new int [numReferences];
|
||||
fieldReferences = new int [numReferences];
|
||||
|
||||
for (int ob=0; ob < numObjects; ob++) {
|
||||
int fi = types [objects [ob].Type].FieldsIndex;
|
||||
int nf = fi + types [objects [ob].Type].FieldsCount;
|
||||
int sr = objects [ob].RefsIndex;
|
||||
int er = sr + objects [ob].RefsCount;
|
||||
for (; sr<er; sr++) {
|
||||
int i = references [sr];
|
||||
if (i != -1) {
|
||||
inverseRefs [invPositions [i]] = ob;
|
||||
invPositions [i]++;
|
||||
}
|
||||
// If the reference is bound to a field, locate the field
|
||||
uint fr = fieldReferenceCodes [sr];
|
||||
if (fr != 0) {
|
||||
for (int k=fi; k<nf; k++) {
|
||||
if (fieldCodes [k] == fr) {
|
||||
fieldReferences [sr] = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (TypeInfo t in types.Values) {
|
||||
t.Objects.TrimToSize();
|
||||
}
|
||||
|
||||
class RefComparer: IComparer <int> {
|
||||
public ObjectInfo[] objects;
|
||||
|
||||
public int Compare (int x, int y) {
|
||||
return objects [x].Code.CompareTo (objects [y].Code);
|
||||
}
|
||||
}
|
||||
|
||||
class TypeComparer: IComparer <int> {
|
||||
public TypeInfo[] types;
|
||||
|
||||
public int Compare (int x, int y) {
|
||||
return types [x].Code.CompareTo (types [y].Code);
|
||||
}
|
||||
}
|
||||
|
||||
public ReferenceNode GetReferenceTree (string typeName, bool inverse)
|
||||
{
|
||||
ReferenceNode nod = new ReferenceNode (typeName, inverse);
|
||||
foreach (ObjectInfo obj in GetObjectsByType (typeName)) {
|
||||
nod.AddReference (obj);
|
||||
}
|
||||
int type = GetTypeFromName (typeName);
|
||||
if (type != -1)
|
||||
return GetReferenceTree (type, inverse);
|
||||
else
|
||||
return new ReferenceNode (this, type, inverse);
|
||||
}
|
||||
|
||||
public ReferenceNode GetReferenceTree (int type, bool inverse)
|
||||
{
|
||||
ReferenceNode nod = new ReferenceNode (this, type, inverse);
|
||||
nod.AddGlobalReferences ();
|
||||
nod.Flush ();
|
||||
return nod;
|
||||
}
|
||||
|
||||
public ObjectInfo GetObject (uint id)
|
||||
public int GetTypeCount ()
|
||||
{
|
||||
ObjectInfo val;
|
||||
if (!objects.TryGetValue (id, out val))
|
||||
return null;
|
||||
else
|
||||
return val;
|
||||
return (int) numTypes;
|
||||
}
|
||||
|
||||
public ICollection GetObjectsByType (string typeName)
|
||||
public int GetTypeFromName (string name)
|
||||
{
|
||||
TypeInfo t = (TypeInfo) typesByName [typeName];
|
||||
if (t == null)
|
||||
return new ObjectInfo [0];
|
||||
else
|
||||
return t.Objects;
|
||||
for (int n=0; n<numTypes; n++) {
|
||||
if (name == types [n].Name)
|
||||
return n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public ICollection GetTypes ()
|
||||
public IEnumerable<int> GetObjectsByType (int type)
|
||||
{
|
||||
ArrayList list = new ArrayList ();
|
||||
list.AddRange (types.Values);
|
||||
list.Sort (new TypeSorter ());
|
||||
return list;
|
||||
for (int n=0; n<numObjects; n++) {
|
||||
if (objects [n].Type == type && (filteredObjects == null || !filteredObjects[n])) {
|
||||
yield return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjectMapReader GetDiff (ObjectMapReader oldMap, ObjectMapReader newMap)
|
||||
{
|
||||
ObjectMapReader dif = new ObjectMapReader ();
|
||||
dif.fieldNames = newMap.fieldNames;
|
||||
dif.fieldReferences = newMap.fieldReferences;
|
||||
dif.inverseRefs = newMap.inverseRefs;
|
||||
dif.numFields = newMap.numFields;
|
||||
dif.numObjects = newMap.numObjects;
|
||||
dif.numReferences = newMap.numReferences;
|
||||
dif.numTypes = newMap.numTypes;
|
||||
dif.objectCount = newMap.objectCount;
|
||||
dif.objectIndices = newMap.objectIndices;
|
||||
dif.objects = newMap.objects;
|
||||
dif.objectCodes = newMap.objectCodes;
|
||||
dif.references = newMap.references;
|
||||
dif.totalMemory = newMap.totalMemory;
|
||||
dif.typeIndices = newMap.typeIndices;
|
||||
dif.types = newMap.types;
|
||||
dif.RemoveData (oldMap);
|
||||
return dif;
|
||||
}
|
||||
|
||||
public void RemoveData (ObjectMapReader otherReader)
|
||||
{
|
||||
Hashtable toDelete = new Hashtable ();
|
||||
|
||||
foreach (uint code in otherReader.objects.Keys) {
|
||||
if (objects.ContainsKey (code)) {
|
||||
ObjectInfo oi = objects [code];
|
||||
toDelete [oi] = oi;
|
||||
types = (TypeInfo[]) types.Clone ();
|
||||
filteredObjects = new bool [numObjects];
|
||||
for (int n=0; n<otherReader.numObjects; n++) {
|
||||
int i = Array.BinarySearch (objectCodes, otherReader.objects[n].Code);
|
||||
if (i >= 0) {
|
||||
i = objectIndices [i];
|
||||
filteredObjects [i] = true;
|
||||
int t = objects[i].Type;
|
||||
types [t].ObjectCount--;
|
||||
types [t].TotalSize -= objects[i].Size;
|
||||
this.objectCount--;
|
||||
this.totalMemory -= objects[i].Size;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList emptyTypes = new ArrayList ();
|
||||
foreach (TypeInfo t in types.Values) {
|
||||
for (int n = t.Objects.Count - 1; n >= 0; n--) {
|
||||
if (toDelete.Contains (t.Objects [n]))
|
||||
t.Objects.RemoveAt (n);
|
||||
}
|
||||
if (t.Objects.Count == 0)
|
||||
emptyTypes.Add (t);
|
||||
}
|
||||
|
||||
foreach (TypeInfo t in emptyTypes) {
|
||||
types.Remove (t.Code);
|
||||
typesByName.Remove (t.Name);
|
||||
}
|
||||
|
||||
// Rebuild referencers list
|
||||
foreach (ObjectInfo o in objects.Values)
|
||||
o.Referencers = null;
|
||||
BuildMap ();
|
||||
}
|
||||
}
|
||||
|
||||
class TypeSorter: IComparer
|
||||
{
|
||||
public int Compare (object x, object y)
|
||||
|
||||
public IEnumerable<int> GetReferencers (int obj)
|
||||
{
|
||||
TypeInfo t1 = (TypeInfo) x;
|
||||
TypeInfo t2 = (TypeInfo) y;
|
||||
if (t1.Objects.Count == t2.Objects.Count)
|
||||
return 0;
|
||||
else if (t1.Objects.Count > t2.Objects.Count)
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
int n = objects [obj].InverseRefsIndex;
|
||||
int end = n + objects [obj].InverseRefsCount;
|
||||
for (; n<end; n++) {
|
||||
int ro = inverseRefs [n];
|
||||
if (filteredObjects == null || !filteredObjects [ro])
|
||||
yield return ro;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetReferences (int obj)
|
||||
{
|
||||
int n = objects [obj].RefsIndex;
|
||||
int end = n + objects [obj].RefsCount;
|
||||
for (; n<end; n++) {
|
||||
int ro = inverseRefs [n];
|
||||
if (filteredObjects == null || !filteredObjects [ro])
|
||||
yield return references [n];
|
||||
}
|
||||
}
|
||||
|
||||
public string GetReferencerField (int obj, int refObj)
|
||||
{
|
||||
int n = objects [obj].RefsIndex;
|
||||
int end = n + objects [obj].RefsCount;
|
||||
for (; n<end; n++) {
|
||||
if (references [n] == refObj) {
|
||||
if (fieldReferences [n] != 0)
|
||||
return fieldNames [fieldReferences [n]];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetObjectTypeName (int obj)
|
||||
{
|
||||
return types [objects [obj].Type].Name;
|
||||
}
|
||||
|
||||
public int GetObjectType (int obj)
|
||||
{
|
||||
return objects [obj].Type;
|
||||
}
|
||||
|
||||
public uint GetObjectSize (int obj)
|
||||
{
|
||||
return objects [obj].Size;
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetTypes ()
|
||||
{
|
||||
for (int n=0; n<numTypes; n++)
|
||||
yield return n;
|
||||
}
|
||||
|
||||
public string GetTypeName (int type)
|
||||
{
|
||||
return types [type].Name;
|
||||
}
|
||||
|
||||
public int GetObjectCountForType (int type)
|
||||
{
|
||||
return types [type].ObjectCount;
|
||||
}
|
||||
|
||||
public uint GetObjectSizeForType (int type)
|
||||
{
|
||||
return types [type].TotalSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
@ -30,19 +31,25 @@ namespace HeapShot.Reader
|
|||
public class ReferenceNode
|
||||
{
|
||||
bool inverse;
|
||||
ObjectMapReader map;
|
||||
|
||||
public string TypeName;
|
||||
public int RefCount;
|
||||
public int RefsToParent;
|
||||
public uint TotalMemory;
|
||||
int type;
|
||||
bool globalRefs;
|
||||
|
||||
public ArrayList references;
|
||||
public ArrayList fieldReferences;
|
||||
public Hashtable refObjects = new Hashtable ();
|
||||
public Hashtable parentObjects = new Hashtable ();
|
||||
public Dictionary<int,int> refObjects = new Dictionary<int,int> ();
|
||||
public Dictionary<int,int> parentObjects = new Dictionary<int,int> ();
|
||||
|
||||
public ReferenceNode (string typeName, bool inverse)
|
||||
public ReferenceNode (ObjectMapReader map, int type, bool inverse)
|
||||
{
|
||||
TypeName = typeName;
|
||||
this.map = map;
|
||||
this.type = type;
|
||||
TypeName = map.GetTypeName (type);
|
||||
this.inverse = inverse;
|
||||
}
|
||||
|
||||
|
@ -54,19 +61,27 @@ namespace HeapShot.Reader
|
|||
get { return fieldReferences != null ? fieldReferences : (ICollection) Type.EmptyTypes; }
|
||||
}
|
||||
|
||||
public void AddReference (ObjectInfo obj)
|
||||
public void AddGlobalReferences ()
|
||||
{
|
||||
AddReference (null, obj, null);
|
||||
RefCount = map.GetObjectCountForType (type);
|
||||
RefsToParent = 0;
|
||||
TotalMemory = map.GetObjectSizeForType (type);
|
||||
globalRefs = true;
|
||||
}
|
||||
|
||||
public void AddReference (ObjectInfo parentObject, ObjectInfo obj)
|
||||
public void AddReference (int obj)
|
||||
{
|
||||
AddReference (-1, obj, null);
|
||||
}
|
||||
|
||||
public void AddReference (int parentObject, int obj)
|
||||
{
|
||||
AddReference (parentObject, obj, (string) null);
|
||||
}
|
||||
|
||||
void AddReference (ObjectInfo parentObject, ObjectInfo obj, string fieldName)
|
||||
void AddReference (int parentObject, int obj, string fieldName)
|
||||
{
|
||||
if (parentObject != null && !parentObjects.ContainsKey (parentObject)) {
|
||||
if (parentObject != -1 && !parentObjects.ContainsKey (parentObject)) {
|
||||
parentObjects [parentObject] = parentObject;
|
||||
RefsToParent++;
|
||||
}
|
||||
|
@ -99,7 +114,7 @@ namespace HeapShot.Reader
|
|||
RefCount++;
|
||||
|
||||
refObjects.Add (obj, obj);
|
||||
TotalMemory += obj.Size;
|
||||
TotalMemory += map.GetObjectSize (obj);
|
||||
}
|
||||
|
||||
public bool HasReferences {
|
||||
|
@ -113,25 +128,28 @@ namespace HeapShot.Reader
|
|||
if (references != null)
|
||||
return references;
|
||||
|
||||
if (globalRefs) {
|
||||
RefsToParent = 0;
|
||||
RefCount = 0;
|
||||
TotalMemory = 0;
|
||||
foreach (int obj in map.GetObjectsByType (type))
|
||||
AddReference (obj);
|
||||
globalRefs = false;
|
||||
}
|
||||
|
||||
references = new ArrayList ();
|
||||
foreach (ObjectInfo obj in refObjects.Keys) {
|
||||
foreach (int obj in refObjects.Keys) {
|
||||
if (inverse) {
|
||||
if (obj.Referencers != null) {
|
||||
for (int n=0; n<obj.Referencers.Count; n++) {
|
||||
ObjectInfo oref = (ObjectInfo) obj.Referencers [n];
|
||||
ReferenceNode cnode = GetReferenceNode (oref.Type.Name);
|
||||
string fname = oref.GetReferencerField (obj);
|
||||
cnode.AddReference (obj, oref, fname);
|
||||
}
|
||||
foreach (int oref in map.GetReferencers (obj)) {
|
||||
ReferenceNode cnode = GetReferenceNode (oref);
|
||||
string fname = map.GetReferencerField (oref, obj);
|
||||
cnode.AddReference (obj, oref, fname);
|
||||
}
|
||||
} else {
|
||||
if (obj.References != null) {
|
||||
foreach (ObjectReference oref in obj.References) {
|
||||
if (oref.Object == null) continue;
|
||||
ReferenceNode cnode = GetReferenceNode (oref.Object.Type.Name);
|
||||
string fname = obj.GetReferencerField (oref.Object);
|
||||
cnode.AddReference (obj, oref.Object, fname);
|
||||
}
|
||||
foreach (int oref in map.GetReferences (obj)) {
|
||||
ReferenceNode cnode = GetReferenceNode (oref);
|
||||
string fname = map.GetReferencerField (obj, oref);
|
||||
cnode.AddReference (obj, oref, fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +157,6 @@ namespace HeapShot.Reader
|
|||
r.Flush ();
|
||||
|
||||
refObjects = null;
|
||||
references.Sort (new ReferenceSorter ());
|
||||
return references;
|
||||
}
|
||||
}
|
||||
|
@ -149,13 +166,14 @@ namespace HeapShot.Reader
|
|||
parentObjects = null;
|
||||
}
|
||||
|
||||
public ReferenceNode GetReferenceNode (string name)
|
||||
public ReferenceNode GetReferenceNode (int obj)
|
||||
{
|
||||
string name = map.GetObjectTypeName (obj);
|
||||
foreach (ReferenceNode cnode in references) {
|
||||
if (cnode.TypeName == name)
|
||||
return cnode;
|
||||
}
|
||||
ReferenceNode nod = new ReferenceNode (name, inverse);
|
||||
ReferenceNode nod = new ReferenceNode (map, map.GetObjectType (obj), inverse);
|
||||
references.Add (nod);
|
||||
return nod;
|
||||
}
|
||||
|
|
|
@ -27,48 +27,13 @@ using System.Text.RegularExpressions;
|
|||
|
||||
namespace HeapShot.Reader
|
||||
{
|
||||
public class TypeInfo
|
||||
public struct TypeInfo
|
||||
{
|
||||
uint code;
|
||||
string name;
|
||||
FieldInfo[] fields;
|
||||
|
||||
public ArrayList Objects = new ArrayList ();
|
||||
|
||||
internal TypeInfo (uint id, string name, FieldInfo[] fields)
|
||||
{
|
||||
this.code = id;
|
||||
this.name = name;
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public uint Code {
|
||||
get { return code; }
|
||||
}
|
||||
|
||||
public string Name {
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public FieldInfo[] Fields {
|
||||
get { return fields; }
|
||||
}
|
||||
|
||||
public uint TotalSize {
|
||||
get {
|
||||
uint s = 0;
|
||||
foreach (ObjectInfo oi in Objects)
|
||||
s += oi.Size;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFieldName (uint fieldCode)
|
||||
{
|
||||
foreach (FieldInfo f in Fields)
|
||||
if (f.Code == fieldCode)
|
||||
return f.Name;
|
||||
return null;
|
||||
}
|
||||
public uint Code;
|
||||
public string Name;
|
||||
public int FieldsIndex;
|
||||
public int FieldsCount;
|
||||
public int ObjectCount;
|
||||
public uint TotalSize;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using HeapShot.Reader;
|
||||
|
||||
|
@ -88,8 +89,8 @@ namespace HeapShot {
|
|||
PrintRoots (omap, type, maxlevels);
|
||||
} else {
|
||||
// Show the tree for a type
|
||||
ReferenceNode nod = new ReferenceNode (type, inverse);
|
||||
foreach (ObjectInfo obj in omap.GetObjectsByType (type)) {
|
||||
ReferenceNode nod = new ReferenceNode (omap, type, inverse);
|
||||
foreach (int obj in omap.GetObjectsByType (type)) {
|
||||
nod.AddReference (obj);
|
||||
}
|
||||
nod.Print (maxlevels);
|
||||
|
@ -97,9 +98,10 @@ namespace HeapShot {
|
|||
} else {
|
||||
// Show a summary
|
||||
int tot = 0;
|
||||
foreach (TypeInfo t in omap.GetTypes ()) {
|
||||
Console.WriteLine ("{0} {1} {2}", t.Objects.Count, t.TotalSize, t.Name);
|
||||
tot += t.Objects.Count;
|
||||
foreach (int t in omap.GetTypes ()) {
|
||||
int no = omap.GetObjectCountForType (t);
|
||||
Console.WriteLine ("{0} {1} {2}", no, omap.GetObjectSizeForType (t), omap.GetTypeName (t));
|
||||
tot += no;
|
||||
}
|
||||
Console.WriteLine ();
|
||||
Console.WriteLine ("Total: " + tot);
|
||||
|
@ -108,19 +110,19 @@ namespace HeapShot {
|
|||
|
||||
void PrintRoots (ObjectMapReader omap, string typeName, int maxlevels)
|
||||
{
|
||||
ArrayList path = new ArrayList ();
|
||||
Hashtable roots = new Hashtable ();
|
||||
Hashtable visited = new Hashtable ();
|
||||
List<int> path = new List<int> ();
|
||||
Dictionary<int,List<int>> roots = new Dictionary<int,List<int>> ();
|
||||
Dictionary<int,int> visited = new Dictionary<int,int> ();
|
||||
|
||||
foreach (ObjectInfo obj in omap.GetObjectsByType (typeName)) {
|
||||
FindRoot (visited, path, roots, obj);
|
||||
foreach (int obj in omap.GetObjectsByType (typeName)) {
|
||||
FindRoot (omap, visited, path, roots, obj);
|
||||
visited.Clear ();
|
||||
}
|
||||
|
||||
foreach (ArrayList ep in roots.Values) {
|
||||
foreach (List<int> ep in roots.Values) {
|
||||
for (int n=0; n < ep.Count && n < maxlevels; n++) {
|
||||
ObjectInfo ob = (ObjectInfo) ep [n];
|
||||
Console.WriteLine (n + ". " + ob.Type.Name);
|
||||
int ob = ep [n];
|
||||
Console.WriteLine (n + ". " + omap.GetObjectTypeName (ob));
|
||||
}
|
||||
if (maxlevels < ep.Count)
|
||||
Console.WriteLine ("...");
|
||||
|
@ -130,28 +132,31 @@ namespace HeapShot {
|
|||
}
|
||||
}
|
||||
|
||||
void FindRoot (Hashtable visited, ArrayList path, Hashtable roots, ObjectInfo obj)
|
||||
void FindRoot (ObjectMapReader omap, Dictionary<int,int> visited, List<int> path, Dictionary<int,List<int>> roots, int obj)
|
||||
{
|
||||
if (visited.Contains (obj))
|
||||
if (visited.ContainsKey (obj))
|
||||
return;
|
||||
visited [obj] = obj;
|
||||
path.Add (obj);
|
||||
if (obj.Referencers != null && obj.Referencers.Count > 0) {
|
||||
foreach (ObjectInfo oref in obj.Referencers) {
|
||||
FindRoot (visited, path, roots, oref);
|
||||
}
|
||||
} else {
|
||||
|
||||
bool hasrefs = false;
|
||||
foreach (int oref in omap.GetReferencers (obj)) {
|
||||
hasrefs = true;
|
||||
FindRoot (omap, visited, path, roots, oref);
|
||||
}
|
||||
|
||||
if (!hasrefs) {
|
||||
// A root
|
||||
ArrayList ep = (ArrayList) roots [obj];
|
||||
List<int> ep = roots [obj];
|
||||
if (ep == null) {
|
||||
roots [obj] = path.Clone ();
|
||||
roots [obj] = new List<int> (path);
|
||||
} else {
|
||||
if (ep.Count > path.Count)
|
||||
roots [obj] = path.Clone ();
|
||||
roots [obj] = new List<int> (path);
|
||||
}
|
||||
Console.WriteLine ("found root" + roots.Count + " " + path.Count + " " + obj.Type.Name);
|
||||
foreach (ObjectInfo o in path) {
|
||||
Console.Write (o.Type.Name + " / ");
|
||||
Console.WriteLine ("found root" + roots.Count + " " + path.Count + " " + omap.GetObjectTypeName (obj));
|
||||
foreach (int o in path) {
|
||||
Console.Write (omap.GetObjectTypeName (o) + " / ");
|
||||
}
|
||||
Console.WriteLine ();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ PKG_CHECK_MODULES(PROFILER, mono glib-2.0)
|
|||
|
||||
AC_PATH_PROG(MCS, mcs)
|
||||
AC_PATH_PROG(MONO, mono)
|
||||
pkglibdir=$prefix/lib/heapshot
|
||||
pkglibdir=$prefix/lib/heap-shot
|
||||
AC_SUBST(pkglibdir)
|
||||
|
||||
AC_CONFIG_FILES([HeapShot/heap-shot],[chmod +x HeapShot/heap-shot])
|
||||
|
|
|
@ -28,15 +28,11 @@
|
|||
|
||||
#include "outfile-writer.h"
|
||||
|
||||
#define MAGIC_NUMBER 0x4eabbdd1
|
||||
#define FILE_FORMAT_VERSION 5
|
||||
#define MAGIC_NUMBER 0x4eabfdd1
|
||||
#define FILE_FORMAT_VERSION 6
|
||||
#define FILE_LABEL "heap-shot logfile"
|
||||
#define TAG_TYPE 0x01
|
||||
#define TAG_METHOD 0x02
|
||||
#define TAG_CONTEXT 0x03
|
||||
#define TAG_GC 0x04
|
||||
#define TAG_RESIZE 0x05
|
||||
#define TAG_OBJECT 0x06
|
||||
#define TAG_OBJECT 0x02
|
||||
#define TAG_EOS 0xff
|
||||
|
||||
static void
|
||||
|
@ -98,6 +94,13 @@ outfile_writer_open_objectmap (const char *filename)
|
|||
write_uint32 (ofw->out, MAGIC_NUMBER);
|
||||
write_int32 (ofw->out, FILE_FORMAT_VERSION);
|
||||
write_string (ofw->out, FILE_LABEL);
|
||||
|
||||
ofw->saved_outfile_offset = ftell (ofw->out);
|
||||
// we update these after dumping all objects
|
||||
write_int32 (ofw->out, 0); // total # of types
|
||||
write_int32 (ofw->out, 0); // total # of objects
|
||||
write_int32 (ofw->out, 0); // total # of references
|
||||
write_int32 (ofw->out, 0); // total # of fields
|
||||
|
||||
return ofw;
|
||||
}
|
||||
|
@ -108,6 +111,13 @@ outfile_writer_close (OutfileWriter *ofw)
|
|||
// Write out the end-of-stream tag.
|
||||
write_byte (ofw->out, TAG_EOS);
|
||||
|
||||
// Seek back up to the right place in the header
|
||||
fseek (ofw->out, ofw->saved_outfile_offset, SEEK_SET);
|
||||
write_int32 (ofw->out, ofw->type_count);
|
||||
write_int32 (ofw->out, ofw->object_count);
|
||||
write_int32 (ofw->out, ofw->reference_count);
|
||||
write_int32 (ofw->out, ofw->field_count);
|
||||
|
||||
fclose (ofw->out);
|
||||
}
|
||||
|
||||
|
@ -135,6 +145,7 @@ outfile_writer_dump_object_begin (OutfileWriter *ofw,
|
|||
while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
|
||||
write_pointer (ofw->out, field);
|
||||
write_string (ofw->out, mono_field_get_name (field));
|
||||
ofw->field_count++;
|
||||
}
|
||||
write_pointer (ofw->out, NULL);
|
||||
}
|
||||
|
@ -149,6 +160,7 @@ outfile_writer_dump_object_begin (OutfileWriter *ofw,
|
|||
write_pointer (ofw->out, klass);
|
||||
write_int32 (ofw->out, (gint32)0);
|
||||
}
|
||||
ofw->object_count++;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -162,5 +174,6 @@ outfile_writer_dump_object_add_reference (OutfileWriter *ofw, gpointer ref, gpoi
|
|||
{
|
||||
write_pointer (ofw->out, ref);
|
||||
write_pointer (ofw->out, field);
|
||||
ofw->reference_count++;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ struct _OutfileWriter {
|
|||
FILE *out;
|
||||
GHashTable *seen_items;
|
||||
int type_count;
|
||||
int object_count;
|
||||
int reference_count;
|
||||
int field_count;
|
||||
long saved_outfile_offset;
|
||||
};
|
||||
|
||||
OutfileWriter *outfile_writer_open_objectmap (const char *filename);
|
||||
|
|
|
@ -21,6 +21,9 @@ namespace Application
|
|||
Aux ();
|
||||
Console.WriteLine ("Ready");
|
||||
Console.ReadLine ();
|
||||
EndType rt2 = new EndType ();
|
||||
Console.WriteLine ("Ready 2");
|
||||
Console.ReadLine ();
|
||||
}
|
||||
|
||||
static void Aux ()
|
||||
|
|
Загрузка…
Ссылка в новой задаче