Initial import of the new, improved, all-singing, all-dancing,

heap-buddy.

svn path=/trunk/heap-buddy/; revision=51438
This commit is contained in:
Jon Trowbridge 2005-10-07 21:02:33 +00:00
Коммит 429b0858d2
37 изменённых файлов: 4873 добавлений и 0 удалений

4
AUTHORS Normal file
Просмотреть файл

@ -0,0 +1,4 @@
Ben Maurer <bmaurer@ximian.com>
Jon Trowbridge <trow@novell.com>

0
ChangeLog Normal file
Просмотреть файл

3
Makefile.am Normal file
Просмотреть файл

@ -0,0 +1,3 @@
SUBDIRS = profiler analyzer test

0
NEWS Normal file
Просмотреть файл

17
README Normal file
Просмотреть файл

@ -0,0 +1,17 @@
To use heap-buddy:
(1) Compile heap-buddy.
(2) Install into a prefix that is in your LD_LIBRARY_PATH.
(3) mono --profile=heap-buddy foo.exe writes the alloc data to ./outfile
mono --profile=heap-buddy:/foo/bar foo.exe writes the alloc data to /foo/bar
(4) heap-buddy /path/to/outfile report_name
Otherwise, the only documentation is 'cat analyzer/*.cs'.

164
analyzer/Gc.cs Normal file
Просмотреть файл

@ -0,0 +1,164 @@
//
// Gc.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
using System.IO;
namespace HeapBuddy {
public struct GcData {
public uint BacktraceCode;
public ObjectStats ObjectStats;
}
public class Gc {
private int generation;
private long time_t;
private DateTime timestamp;
private long pre_gc_live_bytes;
private long post_gc_live_bytes;
private GcData [] gc_data;
public int Generation {
get { return generation; }
}
public DateTime Timestamp {
get { return timestamp; }
}
public long PreGcLiveBytes {
get { return pre_gc_live_bytes; }
}
public long PostGcLiveBytes {
get { return post_gc_live_bytes; }
}
public long FreedBytes {
get { return pre_gc_live_bytes - post_gc_live_bytes; }
}
public double FreedPercentage {
get { return 100.0 * FreedBytes / pre_gc_live_bytes; }
}
public GcData [] GcData {
get { return gc_data; }
}
/////////////////////////////////////////////////////////////////
private void ReadHead (BinaryReader reader)
{
generation = reader.ReadInt32 ();
time_t = reader.ReadInt64 ();
timestamp = Util.ConvertTimeT (time_t);
pre_gc_live_bytes = reader.ReadInt64 ();
}
public void ReadOnlyData (BinaryReader reader)
{
int n;
n = reader.ReadInt32 ();
gc_data = new GcData [n];
for (int i = 0; i < n; ++i) {
gc_data [i].BacktraceCode = reader.ReadUInt32 ();
gc_data [i].ObjectStats.Read (reader);
}
}
public void ReadWithData (BinaryReader reader)
{
ReadHead (reader);
ReadOnlyData (reader);
post_gc_live_bytes = reader.ReadInt64 ();
}
public void ReadWithoutData (BinaryReader reader)
{
ReadHead (reader);
post_gc_live_bytes = reader.ReadInt64 ();
}
public void WriteWithoutData (BinaryWriter writer)
{
writer.Write (generation);
writer.Write (time_t);
writer.Write (pre_gc_live_bytes);
writer.Write (post_gc_live_bytes);
}
public void WriteOnlyData (BinaryWriter writer)
{
combsort_gc_data ();
writer.Write (gc_data.Length);
for (int i = 0; i < gc_data.Length; ++i) {
writer.Write (gc_data [i].BacktraceCode);
gc_data [i].ObjectStats.Write (writer);
}
}
/////////////////////////////////////////////////////////////////
// This is copied from mono 1.1.8.3's implementation of System.Array
static int new_gap (int gap)
{
gap = (gap * 10) / 13;
if (gap == 9 || gap == 10)
return 11;
if (gap < 1)
return 1;
return gap;
}
void combsort_gc_data ()
{
int start = 0;
int size = gc_data.Length;
int gap = size;
while (true) {
gap = new_gap (gap);
bool swapped = false;
int end = start + size - gap;
for (int i = start; i < end; i++) {
int j = i + gap;
if (gc_data [i].BacktraceCode > gc_data [j].BacktraceCode) {
GcData tmp;
tmp = gc_data [i];
gc_data [i] = gc_data [j];
gc_data [j] = tmp;
swapped = true;
}
}
if (gap == 1 && !swapped)
break;
}
}
}
}

61
analyzer/HeapBuddy.cs Normal file
Просмотреть файл

@ -0,0 +1,61 @@
//
// HeapBuddy.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
using System.Collections;
using System.IO;
namespace HeapBuddy {
static public class HeapBuddyMain {
static public void Main (string [] args)
{
int args_i = 0;
string outfile_name = "outfile";
if (args_i < args.Length && File.Exists (args [args_i])) {
outfile_name = args [args_i];
++args_i;
}
string report_name = "types";
if (args_i < args.Length && Report.Exists (args [args_i])) {
report_name = args [args_i];
++args_i;
}
Report report;
report = Report.Get (report_name);
OutfileReader reader;
reader = new OutfileReader (outfile_name);
string [] remaining_args = new string [args.Length - args_i];
for (int i = args_i; i < args.Length; ++i)
remaining_args [i - args_i] = args [i];
report.Run (reader, remaining_args);
}
}
}

76
analyzer/HistoryReport.cs Normal file
Просмотреть файл

@ -0,0 +1,76 @@
//
// HistoryReport.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
using System.Collections;
using System.IO;
namespace HeapBuddy {
public class HistoryReport : Report {
public HistoryReport () : base ("History") { }
override public void Run (OutfileReader reader, string [] args)
{
Resize [] resizes;
resizes = reader.Resizes;
Gc [] gcs;
gcs = reader.Gcs;
int i_resize = 0;
int i_gc = 0;
while (i_resize < resizes.Length || i_gc < gcs.Length) {
Resize r = null;
if (i_resize < resizes.Length)
r = resizes [i_resize];
Gc gc = null;
if (i_gc < gcs.Length)
gc = gcs [i_gc];
if (r != null && (gc == null || r.Generation <= gc.Generation)) {
Console.WriteLine ("{0:HH:mm:ss} | Resize | {1} -> {2}, {3} in live objects",
r.Timestamp,
Util.PrettySize (r.PreviousSize),
Util.PrettySize (r.NewSize),
Util.PrettySize (r.TotalLiveBytes));
++i_resize;
} else if (gc != null) {
if (gc.Generation >= 0) {
Console.WriteLine ("{0:HH:mm:ss} | GC {1:000} | {2} -> {3}, freed {4} ({5:0.0}%)",
gc.Timestamp,
gc.Generation,
Util.PrettySize (gc.PreGcLiveBytes),
Util.PrettySize (gc.PostGcLiveBytes),
Util.PrettySize (gc.FreedBytes),
gc.FreedPercentage);
}
++i_gc;
}
}
}
}
}

53
analyzer/Makefile.am Normal file
Просмотреть файл

@ -0,0 +1,53 @@
CSC = mcs -debug
CSFLAGS = -target:exe
TARGET = HeapBuddy.exe
WRAPPER = heap-buddy
REPORT_CSFILES = \
HistoryReport.cs \
TypesReport.cs
CSFILES = \
HeapBuddy.cs \
Util.cs \
Table.cs \
ObjectStats.cs \
Type.cs \
Gc.cs \
Resize.cs \
OutfileReader.cs \
Report.cs \
$(REPORT_CSFILES)
bin_SCRIPTS = \
$(WRAPPER)
SRCDIR_CSFILES = $(CSFILES:%=$(srcdir)/%)
$(TARGET): $(SRCDIR_CSFILES)
$(CSC) -out:$@ $(CSFLAGS) $^
$(WRAPPER): $(srcdir)/$(WRAPPER).in
sed -e "s|\@pkglibdir\@|$(pkglibdir)|g" < $(srcdir)/$(WRAPPER).in > $@
chmod +x $(WRAPPER)
all: $(TARGET) $(WRAPPER)
install-data-local: $(TARGET)
$(mkinstalldirs) $(DESTDIR)$(pkglibdir)
$(INSTALL_DATA) $(TARGET) $(DESTDIR)$(pkglibdir)
uninstall-local:
cd $(DESTDIR)$(pkglibdir) && rm -f $(TARGET)
EXTRA_DIST = \
$(SRCDIR_CSFILES) \
$(WRAPPER).in
CLEANFILES = \
$(TARGET) \
$(TARGET).mdb \
$(WRAPPER)

154
analyzer/ObjectStats.cs Normal file
Просмотреть файл

@ -0,0 +1,154 @@
//
// ObjectStats.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// auint with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System.IO;
namespace HeapBuddy {
public struct ObjectStats {
public uint AllocatedCount;
public uint AllocatedTotalBytes;
public uint AllocatedTotalAge;
public uint AllocatedTotalWeight;
public double AllocatedAverageBytes {
get { return AllocatedCount != 0 ? AllocatedTotalBytes / (double) AllocatedCount : 0; }
}
public double AllocatedAverageAge {
get { return AllocatedCount != 0 ? AllocatedTotalAge / (double) AllocatedCount : 0; }
}
public uint LiveCount;
public uint LiveTotalBytes;
public uint LiveTotalAge;
public uint LiveTotalWeight;
public double LiveAverageBytes {
get { return LiveCount != 0 ? LiveTotalBytes / (double) LiveCount : 0; }
}
public double LiveAverageAge {
get { return LiveCount != 0 ? LiveTotalAge / (double) LiveCount : 0; }
}
public uint DeadCount {
get { return AllocatedCount - LiveCount; }
}
public uint DeadTotalBytes {
get { return AllocatedTotalBytes - LiveTotalBytes; }
}
public uint DeadTotalAge {
get { return AllocatedTotalAge - LiveTotalAge; }
}
public double DeadAverageBytes {
get { return DeadCount != 0 ? DeadTotalBytes / (double) DeadCount : 0; }
}
public double DeadAverageAge {
get { return DeadCount != 0 ? DeadTotalAge / (double) DeadCount : 0; }
}
public uint DeadTotalWeight {
get { return AllocatedTotalWeight - LiveTotalWeight; }
}
/////////////////////////////////////////////////////
public void Read (BinaryReader reader)
{
AllocatedCount = reader.ReadUInt32 ();
AllocatedTotalBytes = reader.ReadUInt32 ();
AllocatedTotalAge = reader.ReadUInt32 ();
AllocatedTotalWeight = reader.ReadUInt32 ();
LiveCount = reader.ReadUInt32 ();
LiveTotalBytes = reader.ReadUInt32 ();
LiveTotalAge = reader.ReadUInt32 ();
LiveTotalWeight = reader.ReadUInt32 ();
}
public void Write (BinaryWriter writer)
{
writer.Write (AllocatedCount);
writer.Write (AllocatedTotalBytes);
writer.Write (AllocatedTotalAge);
writer.Write (AllocatedTotalWeight);
writer.Write (LiveCount);
writer.Write (LiveTotalBytes);
writer.Write (LiveTotalAge);
writer.Write (LiveTotalWeight);
}
/////////////////////////////////////////////////////
public void AddAllocatedOnly (ObjectStats other)
{
this.AllocatedCount += other.AllocatedCount;
this.AllocatedTotalBytes += other.AllocatedTotalBytes;
this.AllocatedTotalAge += other.AllocatedTotalAge;
this.AllocatedTotalWeight += other.AllocatedTotalWeight;
}
/////////////////////////////////////////////////////
static public ObjectStats operator + (ObjectStats a, ObjectStats b)
{
ObjectStats c = new ObjectStats ();
c.AllocatedCount = a.AllocatedCount + b.AllocatedCount;
c.AllocatedTotalBytes = a.AllocatedTotalBytes + b.AllocatedTotalBytes;
c.AllocatedTotalAge = a.AllocatedTotalAge + b.AllocatedTotalAge;
c.AllocatedTotalWeight = a.AllocatedTotalWeight + b.AllocatedTotalWeight;
c.LiveCount = a.LiveCount + b.LiveCount;
c.LiveTotalBytes = a.LiveTotalBytes + b.LiveTotalBytes;
c.LiveTotalAge = a.LiveTotalAge + b.LiveTotalAge;
c.LiveTotalWeight = a.LiveTotalWeight + b.LiveTotalWeight;
return c;
}
static public ObjectStats operator - (ObjectStats a, ObjectStats b)
{
ObjectStats c = new ObjectStats ();
c.AllocatedCount = a.AllocatedCount - b.AllocatedCount;
c.AllocatedTotalBytes = a.AllocatedTotalBytes - b.AllocatedTotalBytes;
c.AllocatedTotalAge = a.AllocatedTotalAge - b.AllocatedTotalAge;
c.AllocatedTotalWeight = a.AllocatedTotalWeight - b.AllocatedTotalWeight;
c.LiveCount = a.LiveCount - b.LiveCount;
c.LiveTotalBytes = a.LiveTotalBytes - b.LiveTotalBytes;
c.LiveTotalAge = a.LiveTotalAge - b.LiveTotalAge;
c.LiveTotalWeight = a.LiveTotalWeight - b.LiveTotalWeight;
return c;
}
}
}

1020
analyzer/OutfileReader.cs Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

72
analyzer/Report.cs Normal file
Просмотреть файл

@ -0,0 +1,72 @@
//
// Report.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
using System.Collections;
using System.Reflection;
namespace HeapBuddy {
abstract public class Report {
private string name;
protected Report (string name)
{
this.name = name;
}
public string Name {
get { return name; }
}
abstract public void Run (OutfileReader reader, string [] command_line_args);
///////////////////////////////////////////////////////////
static Hashtable by_name = new Hashtable ();
static Report ()
{
Assembly assembly;
assembly = Assembly.GetExecutingAssembly ();
foreach (System.Type t in assembly.GetTypes ()) {
if (t.IsSubclassOf (typeof (Report)) && ! t.IsAbstract) {
Report report;
report = (Report) Activator.CreateInstance (t);
by_name [report.Name.ToLower ()] = report;
}
}
}
static public Report Get (string name)
{
return (Report) by_name [name.ToLower ()];
}
static public bool Exists (string name)
{
return by_name.Contains (name.ToLower ());
}
}
}

63
analyzer/Resize.cs Normal file
Просмотреть файл

@ -0,0 +1,63 @@
//
// HeapResize.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
using System.IO;
namespace HeapBuddy {
public class Resize {
// The GC Generation during which the resize happened
public int Generation;
private long time_t;
public DateTime Timestamp;
public long PreviousSize;
public long NewSize;
public long TotalLiveBytes;
// You need to set PreviousSize by hand.
public void Read (BinaryReader reader, int generation)
{
if (generation < 0)
Generation = reader.ReadInt32 ();
else
Generation = generation;
time_t = reader.ReadInt64 ();
Timestamp = Util.ConvertTimeT (time_t);
NewSize = reader.ReadInt64 ();
TotalLiveBytes = reader.ReadInt64 ();
}
public void Write (BinaryWriter writer)
{
writer.Write (Generation);
writer.Write (time_t);
writer.Write (NewSize);
writer.Write (TotalLiveBytes);
}
}
}

236
analyzer/Table.cs Normal file
Просмотреть файл

@ -0,0 +1,236 @@
//
// Table.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
using System.Collections;
using System.Text;
namespace HeapBuddy {
public enum Alignment {
Right,
Left,
Center
}
public delegate string Stringify (object obj);
public class Table {
private int cols = -1;
private string [] headers;
private Alignment [] alignment;
private int [] max_length;
private Stringify [] stringify;
private ArrayList rows = new ArrayList ();
public int MaxRows = int.MaxValue;
public int RowCount {
get { return rows.Count; }
}
private void CheckColumns (ICollection whatever)
{
if (cols == -1) {
cols = whatever.Count;
if (cols == 0)
throw new Exception ("Can't have zero columns!");
alignment = new Alignment [cols];
for (int i = 0; i < cols; ++i)
alignment [i] = Alignment.Right;
max_length = new int [cols];
stringify = new Stringify [cols];
} else if (cols != whatever.Count) {
throw new Exception (String.Format ("Expected {0} columns, got {1}", cols, whatever.Count));
}
}
public void AddHeaders (params string [] args)
{
CheckColumns (args);
headers = args;
for (int i = 0; i < cols; ++i) {
int len = args [i].Length;
if (len > max_length [i])
max_length [i] = len;
}
}
public void AddRow (params object [] row)
{
CheckColumns (row);
rows.Add (row);
for (int i = 0; i < cols; ++i) {
string str;
str = stringify [i] != null ? stringify [i] (row [i]) : row [i].ToString ();
int len = str.Length;
if (len > max_length [i])
max_length [i] = len;
}
}
public void SetAlignment (int i, Alignment align)
{
alignment [i] = align;
}
/////////////////////////////////////////////////////////////////////////////////////
public void SetStringify (int i, Stringify s)
{
stringify [i] = s;
}
private class FormatClosure {
string format;
public FormatClosure (string format)
{
this.format = format;
}
public string Stringify (object obj)
{
return ((IFormattable) obj).ToString (format, null);
}
}
public void SetStringify (int i, string format)
{
FormatClosure fc = new FormatClosure (format);
SetStringify (i, new Stringify (fc.Stringify));
}
/////////////////////////////////////////////////////////////////////////////////////
private class SortClosure : IComparer {
int col;
IComparer comparer;
bool ascending;
public SortClosure (int col, IComparer comparer, bool ascending)
{
this.col = col;
this.comparer = comparer;
this.ascending = ascending;
}
public int Compare (object x, object y)
{
int cmp;
object [] row_x = (object []) x;
object [] row_y = (object []) y;
if (comparer == null)
cmp = ((IComparable) row_x [col]).CompareTo (row_y [col]);
else
cmp = comparer.Compare (row_x [col], row_y [col]);
if (! ascending)
cmp = -cmp;
return cmp;
}
}
public void Sort (int i, IComparer comparer, bool ascending)
{
SortClosure sc;
sc = new SortClosure (i, comparer, ascending);
rows.Sort (sc);
}
public void Sort (int i)
{
Sort (i, null, true);
}
public void Sort (int i, bool ascending)
{
Sort (i, null, ascending);
}
/////////////////////////////////////////////////////////////////////////////////////
private string GetColumnSeparator (int i)
{
if (0 <= i && i < cols-1)
return " ";
return "";
}
private string Pad (int length, Alignment alignment, string str)
{
switch (alignment) {
case Alignment.Right:
str = str.PadLeft (length);
break;
case Alignment.Left:
str = str.PadRight (length);
break;
case Alignment.Center:
str = str.PadLeft ((length + str.Length+1)/2).PadRight (length);
break;
}
return str;
}
override public string ToString ()
{
StringBuilder sb;
sb = new StringBuilder ();
if (headers != null) {
sb.Append (GetColumnSeparator (-1));
for (int i = 0; i < cols; ++i) {
sb.Append (Pad (max_length [i], Alignment.Center, headers [i]));
sb.Append (GetColumnSeparator (i));
}
sb.Append ('\n');
}
int count = 0;
foreach (object [] row in rows) {
if (count != 0)
sb.Append ('\n');
sb.Append (GetColumnSeparator (-1));
for (int i = 0; i < cols; ++i) {
string str;
str = stringify [i] != null ? stringify [i] (row [i]) : row [i].ToString ();
str = Pad (max_length [i], alignment [i], str);
sb.Append (str);
sb.Append (GetColumnSeparator (i));
}
++count;
if (count >= MaxRows)
break;
}
return sb.ToString ();
}
}
}

44
analyzer/Type.cs Normal file
Просмотреть файл

@ -0,0 +1,44 @@
//
// Type.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
using System.IO;
namespace HeapBuddy {
public class Type {
public string Name;
// How many different backtraces allocate this type?
public int BacktraceCount;
// What is the last generation in which there are
// live objects of this type?
public int LastGeneration;
// Total allocation stats for this type as of the
// last generation in which there were live objects.
public ObjectStats LastObjectStats;
}
}

71
analyzer/TypesReport.cs Normal file
Просмотреть файл

@ -0,0 +1,71 @@
//
// TypesReport.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
using System.Collections;
using System.IO;
namespace HeapBuddy {
public class TypesReport : Report {
public TypesReport () : base ("Types") { }
override public void Run (OutfileReader reader, string [] args)
{
Table table;
table = new Table ();
table.AddHeaders ("Type",
"#",
"Total",
"AvSz",
"AvAge",
"BT#");
table.SetStringify (0, Util.Ellipsize);
table.SetStringify (2, Util.PrettySize_Obj);
table.SetStringify (3, "0.0");
table.SetStringify (4, "0.0");
foreach (Type type in reader.Types) {
table.AddRow (type.Name,
type.LastObjectStats.AllocatedCount,
type.LastObjectStats.AllocatedTotalBytes,
type.LastObjectStats.AllocatedAverageBytes,
type.LastObjectStats.AllocatedAverageAge,
type.BacktraceCount);
}
table.Sort (2, false);
table.MaxRows = 25;
Console.WriteLine (table);
if (table.RowCount > table.MaxRows) {
Console.WriteLine ();
Console.WriteLine ("(skipped {0} types)", table.RowCount - table.MaxRows);
}
}
}
}

75
analyzer/Util.cs Normal file
Просмотреть файл

@ -0,0 +1,75 @@
//
// Util.cs
//
// Copyright (C) 2005 Novell, Inc.
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
using System;
namespace HeapBuddy {
static public class Util {
static DateTime base_time = new DateTime (1970, 1, 1, 0, 0, 0);
static public DateTime ConvertTimeT (long time_t)
{
return base_time.AddSeconds (time_t);
}
static public string Ellipsize (int max_length, string str)
{
if (str.Length < max_length)
return str;
return str.Substring (0, max_length/2 - 2) + "..." + str.Substring (str.Length - max_length/2 + 2);
}
static public string Ellipsize (object str)
{
return Ellipsize (40, (string) str);
}
static public string PrettySize (uint num_bytes)
{
if (num_bytes < 1024)
return String.Format ("{0}b", num_bytes);
if (num_bytes < 1024*10)
return String.Format ("{0:0.0}k", num_bytes / 1024.0);
if (num_bytes < 1024*1024)
return String.Format ("{0}k", num_bytes / 1024);
return String.Format ("{0:0.0}M", num_bytes / (1024 * 1024.0));
}
static public string PrettySize (long num_bytes)
{
return PrettySize ((uint) num_bytes);
}
static public string PrettySize_Obj (object obj)
{
return PrettySize ((uint) obj);
}
static public Stringify PrettySize_Stringify = new Stringify (PrettySize_Obj);
}
}

16
analyzer/heap-buddy.in Normal file
Просмотреть файл

@ -0,0 +1,16 @@
#!/bin/sh
prefix=@prefix@
exec_prefix=@exec_prefix@
pkglibdir=@pkglibdir@
exe_name="HeapBuddy.exe"
if [ -e ./$exe_name ] && [ -e ./Makefile ] && [ -e ./HeapBuddy.cs ]; then
#echo "*** Running uninstalled heap-buddy ***"
EXE_TO_RUN="./$exe_name"
else
EXE_TO_RUN="$pkglibdir/$exe_name"
fi
mono --debug $MONO_EXTRA_ARGS $EXE_TO_RUN "$@"

149
autogen.sh Executable file
Просмотреть файл

@ -0,0 +1,149 @@
#!/bin/sh
# Run this to generate all the initial makefiles, etc.
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
ORIGDIR=`pwd`
cd $srcdir
PROJECT=heap-buddy
TEST_TYPE=-f
FILE=profiler/heap-buddy.c
DIE=0
(autoconf --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "You must have autoconf installed to compile $PROJECT."
echo "Download the appropriate package for your distribution,"
echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
DIE=1
}
AUTOMAKE=automake-1.8
ACLOCAL=aclocal-1.8
($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
AUTOMAKE=automake
ACLOCAL=aclocal
}
($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "You must have automake installed to compile $PROJECT."
echo "Get ftp://sourceware.cygnus.com/pub/automake/automake-1.4.tar.gz"
echo "(or a newer version if it is available)"
DIE=1
}
(grep "^AM_PROG_LIBTOOL" configure.in >/dev/null) && {
(libtool --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "**Error**: You must have \`libtool' installed to compile $PROJECT."
echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz"
echo "(or a newer version if it is available)"
DIE=1
}
}
grep "^AM_GLIB_GNU_GETTEXT" configure.in >/dev/null && {
grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \
(glib-gettextize --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "**Error**: You must have \`glib' installed to compile $PROJECT."
DIE=1
}
}
if test "$DIE" -eq 1; then
exit 1
fi
test $TEST_TYPE $FILE || {
echo "You must run this script in the top-level $PROJECT directory"
exit 1
}
if test -z "$*"; then
echo "I am going to run ./configure with no arguments - if you wish "
echo "to pass any to it, please specify them on the $0 command line."
fi
case $CC in
*xlc | *xlc\ * | *lcc | *lcc\ *) am_opt=--include-deps;;
esac
for coin in `find $srcdir -name configure.in -print`
do
dr=`dirname $coin`
if test -f $dr/NO-AUTO-GEN; then
echo skipping $dr -- flagged as no auto-gen
else
echo processing $dr
macrodirs=`sed -n -e 's,AM_ACLOCAL_INCLUDE(\(.*\)),\1,gp' < $coin`
( cd $dr
aclocalinclude="$ACLOCAL_FLAGS"
for k in $macrodirs; do
if test -d $k; then
aclocalinclude="$aclocalinclude -I $k"
##else
## echo "**Warning**: No such directory \`$k'. Ignored."
fi
done
if grep "^AM_GNU_GETTEXT" configure.in >/dev/null; then
if grep "sed.*POTFILES" configure.in >/dev/null; then
: do nothing -- we still have an old unmodified configure.in
else
echo "Creating $dr/aclocal.m4 ..."
test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
echo "Running gettextize... Ignore non-fatal messages."
echo "no" | gettextize --force --copy
echo "Making $dr/aclocal.m4 writable ..."
test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
fi
fi
if grep "^AM_GNOME_GETTEXT" configure.in >/dev/null; then
echo "Creating $dr/aclocal.m4 ..."
test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
echo "Running gettextize... Ignore non-fatal messages."
echo "no" | gettextize --force --copy
echo "Making $dr/aclocal.m4 writable ..."
test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
fi
if grep "^AM_GLIB_GNU_GETTEXT" configure.in >/dev/null; then
echo "Creating $dr/aclocal.m4 ..."
test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
echo "Running gettextize... Ignore non-fatal messages."
echo "no" | glib-gettextize --force --copy
echo "Making $dr/aclocal.m4 writable ..."
test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
fi
if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
echo "Running libtoolize..."
libtoolize --force --copy
fi
echo "Running $ACLOCAL $aclocalinclude ..."
$ACLOCAL $aclocalinclude
if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
echo "Running autoheader..."
autoheader
fi
echo "Running $AUTOMAKE --gnu $am_opt ..."
$AUTOMAKE --add-missing --gnu $am_opt
echo "Running autoconf ..."
autoconf
)
fi
done
conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
cd "$ORIGDIR"
if test x$NOCONFIGURE = x; then
echo Running $srcdir/configure $conf_flags "$@" ...
$srcdir/configure $conf_flags "$@" \
&& echo Now type \`make\' to compile $PROJECT || exit 1
else
echo Skipping configure process.
fi

1563
config.sub поставляемый Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

18
configure.in Normal file
Просмотреть файл

@ -0,0 +1,18 @@
AC_INIT(profiler/heap-buddy.c)
AM_INIT_AUTOMAKE(heap-buddy, 0.1)
AC_PROG_CC
AM_PROG_LIBTOOL
PKG_CHECK_MODULES(PROFILER, mono glib-2.0)
AC_PATH_PROG(MCS, mcs)
AC_PATH_PROG(MONO, mono)
AC_OUTPUT([
Makefile
profiler/Makefile
analyzer/Makefile
test/Makefile
])

40
doc/outfile-format Normal file
Просмотреть файл

@ -0,0 +1,40 @@
*** HEADER ***
1b: 10 (the length of the string "HEAP-BUDDY")
10b: HEAP-BUDDY
4b: int32 containing the version number (4 in this case)
1b: 1 if the run terminated normally, 0 otherwise
8b: int64 total number of allocated bytes
4b: int32 total number of GCs, or -1 if unknown
4b: int32 total number of types, or -1
4b: int32 total number of methods, or -1
4b: int32 total number of contexts, or -1
8b: int64 offset to GC index, or 0
8b: int64 offset to type index, or 0
8b: int64 offset to method index, or 0
8b: int64 offset to context index, or 0
Maybe some padding to leave room for future expansion?
*** TAG STREAM ***
Tag types:
0x01 Type
0x02 Method
0x03 Context
0x04 GC
0x05 Resize
0xff EOS

15
profiler/Makefile.am Normal file
Просмотреть файл

@ -0,0 +1,15 @@
lib_LTLIBRARIES = libmono-profiler-heap-buddy.la
libmono_profiler_heap_buddy_la_SOURCES = \
accountant.h \
accountant.c \
backtrace.h \
backtrace.c \
outfile-writer.h \
outfile-writer.c \
heap-buddy.c
libmono_profiler_heap_buddy_la_LIBADD = @PROFILER_LIBS@
INCLUDES = @PROFILER_CFLAGS@ -Wall

109
profiler/accountant.c Normal file
Просмотреть файл

@ -0,0 +1,109 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
* accountant.c
*
* Copyright (C) 2005 Novell, Inc.
*
*/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
#include "accountant.h"
#include <mono/metadata/mono-gc.h>
#include <mono/metadata/debug-helpers.h>
typedef struct _LiveObject LiveObject;
struct _LiveObject {
MonoObject *obj;
int size;
int age;
};
Accountant *
accountant_new (MonoClass *klass, StackFrame **backtrace)
{
Accountant *acct = g_new0 (Accountant, 1);
acct->klass = klass;
acct->backtrace = backtrace;
return acct;
}
void
accountant_register_object (Accountant *acct, MonoObject *obj, int obj_size)
{
LiveObject *live = g_new0 (LiveObject, 1);
GSList *iter;
live->obj = obj;
live->size = obj_size;
live->age = 0;
// Clean up any dead objects
if (acct->dead_objects != NULL) {
for (iter = acct->dead_objects; iter != NULL; iter = iter->next)
g_free (iter->data);
g_slist_free (acct->dead_objects);
acct->dead_objects = NULL;
}
acct->live_objects = g_list_prepend (acct->live_objects, live);
acct->n_allocated_objects++;
acct->n_allocated_bytes += live->size;
acct->n_live_objects++;
acct->n_live_bytes += live->size;
acct->dirty = TRUE;
}
void
accountant_post_gc_processing (Accountant *acct)
{
GList *iter;
iter = acct->live_objects;
if (iter != NULL)
acct->dirty = TRUE;
while (iter != NULL) {
LiveObject *live = iter->data;
GList *next_iter = iter->next;
if (mono_object_is_alive (live->obj)) {
acct->allocated_total_age++;
live->age++;
acct->live_total_age++;
acct->allocated_total_weight += live->size;
acct->live_total_weight += live->size;
} else {
acct->n_live_objects--;
acct->n_live_bytes -= live->size;
acct->live_total_age -= live->age;
acct->live_total_weight -= live->size * live->age;
acct->live_objects = g_list_delete_link (acct->live_objects, iter);
// Calling g_free at this point can cause deadlocks, so we put the
// dead objects into a list to be g_freed later.
acct->dead_objects = g_slist_prepend (acct->dead_objects, live);
}
iter = next_iter;
}
}

64
profiler/accountant.h Normal file
Просмотреть файл

@ -0,0 +1,64 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
* accountant.h
*
* Copyright (C) 2005 Novell, Inc.
*
*/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
#ifndef __ACCOUNTANT_H__
#define __ACCOUNTANT_H__
#include <glib.h>
#include <mono/metadata/class.h>
#include <mono/metadata/object.h>
#include "backtrace.h"
typedef struct _Accountant Accountant;
struct _Accountant {
MonoClass *klass;
StackFrame **backtrace;
guint32 n_allocated_objects;
guint32 n_allocated_bytes;
guint32 allocated_total_age;
guint32 allocated_total_weight;
guint32 n_live_objects;
guint32 n_live_bytes;
guint32 live_total_age;
guint32 live_total_weight;
GList *live_objects;
GSList *dead_objects;
gboolean dirty;
};
Accountant *accountant_new (MonoClass *klass, StackFrame **backtrace);
void accountant_register_object (Accountant *acct, MonoObject *obj, int obj_size);
void accountant_post_gc_processing (Accountant *acct);
#endif /* __ACCOUNTANT_H__ */

108
profiler/backtrace.c Normal file
Просмотреть файл

@ -0,0 +1,108 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
* backtrace.c
*
* Copyright (C) 2005 Novell, Inc.
*
*/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
#include "backtrace.h"
struct HashAndCountInfo {
gint32 hash;
int count;
};
static gboolean
stack_walk_hash_and_count_fn (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
{
struct HashAndCountInfo *info = data;
const guint32 full_c = 2909;
const guint32 method_c = 277;
const guint32 native_c = 163;
const guint32 il_c = 47;
guint32 method_hash = GPOINTER_TO_UINT (method);
info->hash = full_c * info->hash
+ method_c * method_hash
+ native_c * native_offset
+ il_c * il_offset
+ (managed ? 1 : 0);
info->count++;
return FALSE;
}
struct FrameInfo {
int pos;
StackFrame **vec;
};
static gboolean
stack_walk_build_frame_vector_fn (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
{
struct FrameInfo *info = data;
StackFrame *frame;
frame = g_new0 (StackFrame, 1);
frame->method = method;
frame->native_offset = native_offset;
frame->il_offset = il_offset;
frame->managed = managed;
info->vec [info->pos] = frame;
++info->pos;
return FALSE;
}
StackFrame **
backtrace_get_current ()
{
static GHashTable *backtrace_cache = NULL;
struct HashAndCountInfo hc_info;
struct FrameInfo frame_info;
if (backtrace_cache == NULL)
backtrace_cache = g_hash_table_new (NULL, NULL);
hc_info.hash = 0;
hc_info.count = 0;
mono_stack_walk_no_il (stack_walk_hash_and_count_fn, &hc_info);
StackFrame **frame_vec;
frame_vec = g_hash_table_lookup (backtrace_cache, GUINT_TO_POINTER (hc_info.hash));
if (frame_vec != NULL)
return frame_vec;
// FIXME: we need to deal with hash collisions
frame_vec = g_new0 (StackFrame *, hc_info.count + 1);
frame_info.pos = 0;
frame_info.vec = frame_vec;
mono_stack_walk_no_il (stack_walk_build_frame_vector_fn, &frame_info);
g_hash_table_insert (backtrace_cache, GUINT_TO_POINTER (hc_info.hash), frame_vec);
return frame_vec;
}

44
profiler/backtrace.h Normal file
Просмотреть файл

@ -0,0 +1,44 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
* backtrace.h
*
* Copyright (C) 2005 Novell, Inc.
*
*/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
#ifndef __BACKTRACE_H__
#define __BACKTRACE_H__
#include <glib.h>
#include <mono/metadata/debug-helpers.h>
typedef struct _StackFrame StackFrame;
struct _StackFrame {
MonoMethod *method;
gint32 native_offset;
gint32 il_offset;
gboolean managed;
};
/* Returns a NULL-terminated vector of StackFrames */
StackFrame **backtrace_get_current ();
#endif /* __BACKTRACE_H__ */

191
profiler/heap-buddy.c Normal file
Просмотреть файл

@ -0,0 +1,191 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* heap-buddy.c
*
* Copyright (C) 2005 Novell, Inc.
*
*/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
#include <string.h>
#include <glib.h>
#include <mono/metadata/assembly.h>
#include <mono/io-layer/mono-mutex.h>
#include <mono/metadata/class.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/object.h>
#include <mono/metadata/profiler.h>
#include <mono/metadata/mono-gc.h>
#include <unistd.h>
#include "accountant.h"
#include "backtrace.h"
#include "outfile-writer.h"
struct _MonoProfiler {
mono_mutex_t lock;
GHashTable *accountant_hash;
gint64 total_allocated_bytes;
gint64 total_live_bytes;
gint32 n_dirty_accountants;
OutfileWriter *outfile_writer;
};
static MonoProfiler *
create_mono_profiler (const char *outfilename)
{
MonoProfiler *p = g_new0 (MonoProfiler, 1);
mono_mutex_init (&p->lock, NULL);
p->accountant_hash = g_hash_table_new (NULL, NULL);
p->total_live_bytes = 0;
p->outfile_writer = outfile_writer_open (outfilename);
return p;
}
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
static void
heap_buddy_alloc_func (MonoProfiler *p, MonoObject *obj, MonoClass *klass)
{
Accountant *acct;
StackFrame **backtrace;
int size;
backtrace = backtrace_get_current ();
mono_mutex_lock (&p->lock);
acct = g_hash_table_lookup (p->accountant_hash, backtrace);
if (acct == NULL) {
acct = accountant_new (klass, backtrace);
g_hash_table_insert (p->accountant_hash, backtrace, acct);
outfile_writer_add_accountant (p->outfile_writer, acct);
}
size = mono_object_get_size (obj);
accountant_register_object (acct, obj, size);
p->total_allocated_bytes += size;
p->total_live_bytes += size;
mono_mutex_unlock (&p->lock);
}
static void
post_gc_tallying_fn (gpointer key, gpointer value, gpointer user_data)
{
MonoProfiler *p = user_data;
Accountant *acct = value;
accountant_post_gc_processing (acct);
p->total_live_bytes += acct->n_live_bytes;
if (acct->dirty)
++p->n_dirty_accountants;
}
static void
post_gc_logging_fn (gpointer key, gpointer value, gpointer user_data)
{
MonoProfiler *p = user_data;
Accountant *acct = value;
// Only log the accountant's stats to the outfile if
// something has changed since the last time it was logged.
if (acct->dirty) {
outfile_writer_gc_log_stats (p->outfile_writer, acct);
acct->dirty = FALSE;
}
}
static void
heap_buddy_gc_func (MonoProfiler *p, MonoGCEvent e, int gen)
{
gint64 prev_total_live_bytes;
if (e != MONO_GC_EVENT_MARK_END)
return;
mono_mutex_lock (&p->lock);
prev_total_live_bytes = p->total_live_bytes;
p->total_live_bytes = 0;
p->n_dirty_accountants = 0;
g_hash_table_foreach (p->accountant_hash, post_gc_tallying_fn, p);
outfile_writer_gc_begin (p->outfile_writer,
gen < 0, // negative gen == this is final
prev_total_live_bytes, p->n_dirty_accountants);
g_hash_table_foreach (p->accountant_hash, post_gc_logging_fn, p);
outfile_writer_gc_end (p->outfile_writer, p->total_live_bytes);
mono_mutex_unlock (&p->lock);
}
static void
heap_buddy_gc_resize_func (MonoProfiler *p, gint64 new_size)
{
mono_mutex_lock (&p->lock);
outfile_writer_resize (p->outfile_writer, new_size, p->total_live_bytes);
mono_mutex_unlock (&p->lock);
}
static void
heap_buddy_shutdown (MonoProfiler *p)
{
// Do a final, synthetic GC
heap_buddy_gc_func (p, MONO_GC_EVENT_MARK_END, -1);
outfile_writer_close (p->outfile_writer);
}
void
mono_profiler_startup (const char *desc)
{
MonoProfiler *p;
const char *outfilename;
g_assert (! strncmp (desc, "heap-buddy", 10));
outfilename = strchr (desc, ':');
if (outfilename == NULL)
outfilename = "outfile";
else {
// Advance past the : and use the rest as the name.
++outfilename;
}
g_print ("*** Running with heap-buddy ***\n");
mono_profiler_install_allocation (heap_buddy_alloc_func);
mono_profiler_install_gc (heap_buddy_gc_func, heap_buddy_gc_resize_func);
mono_profiler_set_events (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_GC);
p = create_mono_profiler (outfilename);
mono_profiler_install (p, heap_buddy_shutdown);
}

272
profiler/outfile-writer.c Normal file
Просмотреть файл

@ -0,0 +1,272 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
* outfile-writer.c
*
* Copyright (C) 2005 Novell, Inc.
*
*/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
#include <string.h>
#include <time.h>
#include "outfile-writer.h"
#define MAGIC_NUMBER 0x4eabbdd1
#define FILE_FORMAT_VERSION 4
#define FILE_LABEL "heap-buddy logfile"
#define TAG_TYPE 0x01
#define TAG_METHOD 0x02
#define TAG_CONTEXT 0x03
#define TAG_GC 0x04
#define TAG_RESIZE 0x05
#define TAG_EOS 0xff
static void
write_byte (FILE *out, guint8 x)
{
fwrite (&x, sizeof (guint8), 1, out);
}
static void
write_pointer (FILE *out, gpointer x)
{
guint32 y = GPOINTER_TO_UINT (x);
fwrite (&y, sizeof (guint32), 1, out);
}
static void
write_int16 (FILE *out, gint16 x)
{
fwrite (&x, sizeof (gint16), 1, out);
}
static void
write_uint16 (FILE *out, guint16 x)
{
fwrite (&x, sizeof (guint16), 1, out);
}
static void
write_int32 (FILE *out, gint32 x)
{
fwrite (&x, sizeof (gint32), 1, out);
}
static void
write_uint32 (FILE *out, guint32 x)
{
fwrite (&x, sizeof (guint32), 1, out);
}
static void
write_int64 (FILE *out, gint64 x)
{
fwrite (&x, sizeof (gint64), 1, out);
}
static void
write_vint (FILE *out, guint32 x)
{
guint8 y;
do {
y = (guint8) (x & 0x7f);
x = x >> 7;
if (x != 0)
y |= 0x80;
write_byte (out, y);
} while (x != 0);
}
static void
write_string (FILE *out, const char *str)
{
int len = strlen (str);
write_vint (out, (guint32) len);
fwrite (str, sizeof (char), len, out);
}
OutfileWriter *
outfile_writer_open (const char *filename)
{
OutfileWriter *ofw;
ofw = g_new0 (OutfileWriter, 1);
ofw->out = fopen (filename, "w");
ofw->seen_items = g_hash_table_new (NULL, NULL);
ofw->gc_count = 0;
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 every GC
write_byte (ofw->out, 0); // is the log fully written out?
write_int32 (ofw->out, -1); // total # of GCs
write_int32 (ofw->out, -1); // total # of types
write_int32 (ofw->out, -1); // total # of methods
write_int32 (ofw->out, -1); // total # of contexts/backtraces
write_int32 (ofw->out, -1); // total # of resizes
return ofw;
}
static void
outfile_writer_update_totals (OutfileWriter *ofw)
{
// Seek back up to the right place in the header
fseek (ofw->out, ofw->saved_outfile_offset, SEEK_SET);
// Update our counts and totals
write_byte (ofw->out, 0); // we still might terminate abnormally
write_int32 (ofw->out, ofw->gc_count);
write_int32 (ofw->out, ofw->type_count);
write_int32 (ofw->out, ofw->method_count);
write_int32 (ofw->out, ofw->context_count);
write_int32 (ofw->out, ofw->resize_count);
// Seek back to the end of the outfile
fseek (ofw->out, 0, SEEK_END);
}
void
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);
// Indicate that we terminated normally.
write_byte (ofw->out, 1);
fclose (ofw->out);
}
void
outfile_writer_add_accountant (OutfileWriter *ofw,
Accountant *acct)
{
int i, frame_count;
char *name;
/* First, add this type if we haven't seen it before. */
if (g_hash_table_lookup (ofw->seen_items, acct->klass) == NULL) {
name = mono_type_get_full_name (mono_class_get_type (acct->klass));
write_byte (ofw->out, TAG_TYPE);
write_pointer (ofw->out, acct->klass);
write_string (ofw->out, name);
g_free (name);
g_hash_table_insert (ofw->seen_items, acct->klass, acct->klass);
++ofw->type_count;
}
/* Next, walk across the backtrace and add any previously-unseen
methods. */
frame_count = 0;
for (i = 0; acct->backtrace [i] != NULL; ++i) {
++frame_count;
MonoMethod *method = acct->backtrace [i]->method;
if (g_hash_table_lookup (ofw->seen_items, method) == NULL) {
char *name = mono_method_full_name (method, TRUE);
write_byte (ofw->out, TAG_METHOD);
write_pointer (ofw->out, method);
write_string (ofw->out, name);
g_free (name);
g_hash_table_insert (ofw->seen_items, method, method);
++ofw->method_count;
}
}
/* Now we can spew out the accountant's context */
write_byte (ofw->out, TAG_CONTEXT);
write_pointer (ofw->out, acct);
write_pointer (ofw->out, acct->klass);
write_int16 (ofw->out, frame_count);
for (i = 0; acct->backtrace [i] != NULL; ++i) {
write_pointer (ofw->out, acct->backtrace [i]->method);
write_uint32 (ofw->out, acct->backtrace [i]->il_offset);
}
++ofw->context_count;
fflush (ofw->out);
}
// total_live_bytes is the total size of all of the live objects
// before the GC
void
outfile_writer_gc_begin (OutfileWriter *ofw, gboolean is_final, gint64 total_live_bytes, gint32 n_accountants)
{
time_t timestamp;
time (&timestamp);
write_byte (ofw->out, TAG_GC);
write_int32 (ofw->out, is_final ? -1 : ofw->gc_count);
write_int64 (ofw->out, (gint64) timestamp);
write_int64 (ofw->out, total_live_bytes);
write_int32 (ofw->out, n_accountants);
++ofw->gc_count;
}
void
outfile_writer_gc_log_stats (OutfileWriter *ofw,
Accountant *acct)
{
write_pointer (ofw->out, acct);
write_uint32 (ofw->out, acct->n_allocated_objects);
write_uint32 (ofw->out, acct->n_allocated_bytes);
write_uint32 (ofw->out, acct->allocated_total_age);
write_uint32 (ofw->out, acct->allocated_total_weight);
write_uint32 (ofw->out, acct->n_live_objects);
write_uint32 (ofw->out, acct->n_live_bytes);
write_uint32 (ofw->out, acct->live_total_age);
write_uint32 (ofw->out, acct->live_total_weight);
}
// total_live_bytes is the total size of all live objects
// after the GC is finished
void
outfile_writer_gc_end (OutfileWriter *ofw, gint64 total_live_bytes)
{
write_int64 (ofw->out, total_live_bytes);
outfile_writer_update_totals (ofw);
fflush (ofw->out);
}
void
outfile_writer_resize (OutfileWriter *ofw, gint64 new_size, gint64 total_live_bytes)
{
time_t timestamp;
time (&timestamp);
write_byte (ofw->out, TAG_RESIZE);
write_int64 (ofw->out, (gint64) timestamp);
write_int64 (ofw->out, new_size);
write_int64 (ofw->out, total_live_bytes);
++ofw->resize_count;
outfile_writer_update_totals (ofw);
fflush (ofw->out);
}

59
profiler/outfile-writer.h Normal file
Просмотреть файл

@ -0,0 +1,59 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
* outfile-writer.h
*
* Copyright (C) 2005 Novell, Inc.
*
*/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
#ifndef __OUTFILE_WRITER_H__
#define __OUTFILE_WRITER_H__
#include <glib.h>
#include "accountant.h"
typedef struct _OutfileWriter OutfileWriter;
struct _OutfileWriter {
FILE *out;
GHashTable *seen_items;
int gc_count;
int type_count;
int method_count;
int context_count;
int resize_count;
long saved_outfile_offset;
};
OutfileWriter *outfile_writer_open (const char *filename);
void outfile_writer_close (OutfileWriter *ofw);
void outfile_writer_add_accountant (OutfileWriter *ofw, Accountant *acct);
void outfile_writer_gc_begin (OutfileWriter *ofw, gboolean is_final, gint64 total_live_bytes, gint32 n_accountants);
void outfile_writer_gc_log_stats (OutfileWriter *ofw, Accountant *acct);
void outfile_writer_gc_end (OutfileWriter *ofw, gint64 total_live_bytes);
void outfile_writer_resize (OutfileWriter *ofw, gint64 new_size, gint64 total_live_bytes);
#endif /* __OUTFILE_WRITER_H__ */

33
test/Makefile.am Normal file
Просмотреть файл

@ -0,0 +1,33 @@
CSC = mcs -debug
RUNTIME = mono
CSFLAGS =
ALL_CSFILES = \
test-01.cs \
test-02.cs \
test-03.cs \
test-04.cs \
test-05.cs \
test-06.cs
ALL_EXE = $(ALL_CSFILES:%.cs=%.exe)
ALL_OUTFILES = $(ALL_CSFILES:%.cs=%.outfile)
%.exe: $(srcdir)/%.cs
$(CSC) -out:$@ $(CSFLAGS) $^
%.outfile: %.exe
@echo "generating $@"
LD_LIBRARY_PATH=../profiler/.libs:$LD_LIBRARY_PATH $(RUNTIME) --profile=heap-buddy:$@ $^
test: $(ALL_OUTFILES)
EXTRA_DIST = \
$(ALL_CSFILES)
CLEANFILES = \
$(ALL_EXE) \
$(ALL_EXE:%=%.mdb) \
$(ALL_OUTFILES)

9
test/test-01.cs Normal file
Просмотреть файл

@ -0,0 +1,9 @@
class Test {
static void Main ()
{
}
}

13
test/test-02.cs Normal file
Просмотреть файл

@ -0,0 +1,13 @@
class Test {
static void Main ()
{
for (int i = 0; i < 10; ++i) {
int [] foo;
foo = new int [20];
foo [0] = i;
}
}
}

19
test/test-03.cs Normal file
Просмотреть файл

@ -0,0 +1,19 @@
class Test {
static void Recursive (int i)
{
if (i > 0) {
int [] foo;
foo = new int [20];
foo [0] = i;
Recursive (i-1);
}
}
static void Main ()
{
Recursive (2000);
}
}

18
test/test-04.cs Normal file
Просмотреть файл

@ -0,0 +1,18 @@
using System;
class Test {
static int Fib (int n)
{
if (n < 2)
return 1;
return Fib (n-1) + Fib (n-2);
}
static void Main ()
{
Console.WriteLine (Fib (20));
}
}

10
test/test-05.cs Normal file
Просмотреть файл

@ -0,0 +1,10 @@
using System;
class Test {
static void Main ()
{
throw new Exception ("Foo");
}
}

10
test/test-06.cs Normal file
Просмотреть файл

@ -0,0 +1,10 @@
using System;
class Test {
static void Main ()
{
Console.WriteLine ("foo");
}
}