Initial import of the new, improved, all-singing, all-dancing,
heap-buddy. svn path=/trunk/heap-buddy/; revision=51438
This commit is contained in:
Коммит
429b0858d2
|
@ -0,0 +1,4 @@
|
|||
|
||||
Ben Maurer <bmaurer@ximian.com>
|
||||
Jon Trowbridge <trow@novell.com>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
SUBDIRS = profiler analyzer test
|
||||
|
|
@ -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'.
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 ());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 "$@"
|
|
@ -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
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
||||
])
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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__ */
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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 (×tamp);
|
||||
|
||||
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 (×tamp);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -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__ */
|
||||
|
|
@ -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)
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
class Test {
|
||||
|
||||
static void Main ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
class Test {
|
||||
|
||||
static void Main ()
|
||||
{
|
||||
throw new Exception ("Foo");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
class Test {
|
||||
|
||||
static void Main ()
|
||||
{
|
||||
Console.WriteLine ("foo");
|
||||
}
|
||||
|
||||
}
|
Загрузка…
Ссылка в новой задаче