commit 429b0858d28da491cb5ec7a4709b4dd76e02f53a Author: Jon Trowbridge Date: Fri Oct 7 21:02:33 2005 +0000 Initial import of the new, improved, all-singing, all-dancing, heap-buddy. svn path=/trunk/heap-buddy/; revision=51438 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..ee471cd --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ + +Ben Maurer +Jon Trowbridge + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3d9bcee --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS = profiler analyzer test + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..e82cc5e --- /dev/null +++ b/README @@ -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'. + + + + diff --git a/analyzer/Gc.cs b/analyzer/Gc.cs new file mode 100644 index 0000000..916edb8 --- /dev/null +++ b/analyzer/Gc.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; + } + } + + } +} diff --git a/analyzer/HeapBuddy.cs b/analyzer/HeapBuddy.cs new file mode 100644 index 0000000..21d4044 --- /dev/null +++ b/analyzer/HeapBuddy.cs @@ -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); + } + } + +} diff --git a/analyzer/HistoryReport.cs b/analyzer/HistoryReport.cs new file mode 100644 index 0000000..b98f9bf --- /dev/null +++ b/analyzer/HistoryReport.cs @@ -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; + } + } + } + } +} diff --git a/analyzer/Makefile.am b/analyzer/Makefile.am new file mode 100644 index 0000000..999e1b9 --- /dev/null +++ b/analyzer/Makefile.am @@ -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) \ No newline at end of file diff --git a/analyzer/ObjectStats.cs b/analyzer/ObjectStats.cs new file mode 100644 index 0000000..3496b73 --- /dev/null +++ b/analyzer/ObjectStats.cs @@ -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; + } + } +} diff --git a/analyzer/OutfileReader.cs b/analyzer/OutfileReader.cs new file mode 100644 index 0000000..12710f3 --- /dev/null +++ b/analyzer/OutfileReader.cs @@ -0,0 +1,1020 @@ +// +// OutfileReader.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; +using System.Text.RegularExpressions; + +namespace HeapBuddy { + + public class OutfileReader { + + public bool Debug = false; + + const uint magic_number = 0x4eabbdd1; + const int expected_log_version = 4; + const int expected_summary_version = 1; + const string log_file_label = "heap-buddy logfile"; + const string summary_file_label = "heap-buddy summary"; + + // + // Data from the outfile's header + // + + bool terminated_normally; + + // Object counts + int n_gcs; + int n_types; + int n_methods; + int n_backtraces; + int n_resizes; + + // Offsets in the summary file + long type_name_data_offset = -1; + long method_name_data_offset = -1; + long backtrace_data_offset = -1; + long gc_data_offset = -1; + long resize_data_offset = -1; + long types_by_code_offset = -1; + long methods_by_code_offset = -1; + long backtrace_index_offset = -1; + long gc_index_offset = -1; + + // The reader to use for lazy lookups of names, etc. + BinaryReader lazy_reader = null; + + /////////////////////////////////////////////////////////////////// + + private struct Method { + public string Name; + public long Position; // of the name in the summary file + } + + private struct Frame { + public uint MethodCode; + public uint IlOffset; + } + + private struct Backtrace { + public uint TypeCode; + public Frame [] Frames; + public int LastGeneration; + public ObjectStats LastObjectStats; + public long Position; + } + + Type [] types; + Method [] methods; + Backtrace [] backtraces; + Gc [] gcs; + long [] gc_pos; + Resize [] resizes; + + // These are only needed for log files + uint [] type_codes_old; + uint [] type_codes_new; + uint [] method_codes_old; + uint [] method_codes_new; + uint [] backtrace_codes; + + /////////////////////////////////////////////////////////////////// + + public OutfileReader (string filename) + { + Stream stream; + stream = new FileStream (filename, FileMode.Open, FileAccess.Read); + + BinaryReader reader; + reader = new BinaryReader (stream); + + bool is_summary; + is_summary = ReadPreamble (reader); + + Spew ("This is a {0} file", is_summary ? "summary" : "log"); + + ReadHeader (reader); + + if (is_summary) { + lazy_reader = reader; + ReadSummaryFile (reader); + } else { + + type_codes_old = new uint [n_types]; + type_codes_new = new uint [n_types]; + method_codes_old = new uint [n_methods]; + method_codes_new = new uint [n_methods]; + backtrace_codes = new uint [n_backtraces]; + + ReadLogFile (reader); + reader.Close (); + + RemapAllCodes (); + + CollectFinalBacktraceAndTypeStats (); + + string tmp_filename; + tmp_filename = Path.GetTempFileName (); + stream = new FileStream (tmp_filename, FileMode.Open, FileAccess.Write); + + BinaryWriter writer; + writer = new BinaryWriter (stream); + WriteSummaryFile (writer); + writer.Close (); + + // Replace the log file with the summary file. + File.Copy (tmp_filename, filename, true /* allow overwrite */); + File.Delete (tmp_filename); + + // We don't need to set lazy_reader, since all + // of the data will already be in memory. + } + } + + /////////////////////////////////////////////////////////////////// + + private void Spew (string format, params object [] args) + { + if (Debug) { + string message; + message = String.Format (format, args); + Console.WriteLine (message); + } + } + + /////////////////////////////////////////////////////////////////// + + // Return true if this is a summary file, false if it is a log file. + private bool ReadPreamble (BinaryReader reader) + { + uint this_magic; + this_magic = reader.ReadUInt32 (); + if (this_magic != magic_number) + throw new Exception ("Bad magic number in heap-buddy outfile"); + + int this_version; + this_version = reader.ReadInt32 (); + + string this_label; + bool is_summary; + int expected_version; + + this_label = reader.ReadString (); + if (this_label == log_file_label) { + is_summary = false; + expected_version = expected_log_version; + } else if (this_label == summary_file_label) { + is_summary = true; + expected_version = expected_summary_version; + } else + throw new Exception ("Unknown file label in heap-buddy outfile"); + + if (this_version != expected_version) { + string msg; + msg = String.Format ("Version error in {0}: expected {1}, found {2}", + this_label, expected_version, this_version); + throw new Exception (msg); + } + + return is_summary; + } + + private void WritePreamble (BinaryWriter writer) + { + writer.Write (magic_number); + writer.Write (expected_summary_version); + writer.Write (summary_file_label); // we only write summary files from C# + } + + /////////////////////////////////////////////////////////////////// + + private void ReadHeader (BinaryReader reader) + { + Spew ("Reading header"); + + byte termination_byte; + termination_byte = reader.ReadByte (); + if (termination_byte == 1) + terminated_normally = true; + else if (termination_byte != 0) + throw new Exception ("Unexpected termination status byte: " + termination_byte); + + Spew ("Log is {0}", terminated_normally ? "complete" : "truncated"); + + n_gcs = reader.ReadInt32 (); + n_types = reader.ReadInt32 (); + n_methods = reader.ReadInt32 (); + n_backtraces = reader.ReadInt32 (); + n_resizes = reader.ReadInt32 (); + + Spew ("GCs = {0}", n_gcs); + Spew ("Types = {0}", n_types); + Spew ("Methods = {0}", n_methods); + Spew ("Backtraces = {0}", n_backtraces); + Spew ("Resizes = {0}", n_resizes); + + types = new Type [n_types]; + methods = new Method [n_methods]; + backtraces = new Backtrace [n_backtraces]; + gcs = new Gc [n_gcs]; + gc_pos = new long [n_gcs]; + resizes = new Resize [n_resizes]; + + Spew ("Finished reading header"); + } + + private void WriteHeader (BinaryWriter writer) + { + Spew ("Writing header"); + + // We only write out summary files, which + // are never truncated. + writer.Write ((byte) 1); + + writer.Write (n_gcs); + writer.Write (n_types); + writer.Write (n_methods); + writer.Write (n_backtraces); + writer.Write (n_resizes); + + Spew ("Finished writing header"); + } + + /////////////////////////////////////////////////////////////////// + + // + // Code to read the log files generated at runtime + // + + // These need to agree w/ the definitions in outfile-writer.c + const byte TAG_TYPE = 0x01; + const byte TAG_METHOD = 0x02; + const byte TAG_BACKTRACE = 0x03; + const byte TAG_GC = 0x04; + const byte TAG_RESIZE = 0x05; + const byte TAG_EOS = 0xff; + + int i_type = 0, i_method = 0, i_backtrace = 0, i_gc = 0, i_resize = 0; + + private void ReadLogFile (BinaryReader reader) + { + int chunk_count = 0; + + try { + while (ReadLogFileChunk (reader)) + ++chunk_count; + + } catch (System.IO.EndOfStreamException) { + // This means that the outfile was truncated. + // In that case, just do nothing --- except if the file + // claimed that things terminated normally. + if (terminated_normally) + throw new Exception ("The heap log did not contain TAG_EOS, " + + "but the outfile was marked as having been terminated normally, so " + + "something must be terribly wrong."); + } + Spew ("Processed {0} chunks", chunk_count); + + if (i_type != n_types) + throw new Exception (String.Format ("Found {0} types, expected {1}", i_type, n_types)); + + if (i_method != n_methods) + throw new Exception (String.Format ("Found {0} methods, expected {1}", i_method, n_methods)); + + if (i_backtrace != n_backtraces) + throw new Exception (String.Format ("Found {0} backtraces, expected {1}", i_backtrace, n_backtraces)); + + if (i_gc != n_gcs) + throw new Exception (String.Format ("Found {0} GCs, expected {1}", i_gc, n_gcs)); + + if (i_resize != n_resizes) + throw new Exception (String.Format ("Found {0} resizes, expected {1}", i_resize, n_resizes)); + } + + private bool ReadLogFileChunk (BinaryReader reader) + { + + // FIXME: This will fail on truncated outfiles + + byte tag = reader.ReadByte (); + + switch (tag) { + case TAG_TYPE: + ReadLogFileChunk_Type (reader); + break; + + case TAG_METHOD: + ReadLogFileChunk_Method (reader); + break; + + case TAG_BACKTRACE: + ReadLogFileChunk_Backtrace (reader); + break; + + case TAG_GC: + ReadLogFileChunk_Gc (reader); + break; + + case TAG_RESIZE: + ReadLogFileChunk_Resize (reader); + break; + + case TAG_EOS: + //Spew ("Found EOS"); + return false; + + default: + throw new Exception ("Unknown tag! " + tag); + } + + return true; + } + + private void ReadLogFileChunk_Type (BinaryReader reader) + { + uint code; + code = reader.ReadUInt32 (); + + string name; + name = reader.ReadString (); + + if (i_type >= n_types) + return; + + //Spew ("Found type '{0}'", name); + + type_codes_old [i_type] = code; + + Type type; + type = new Type (); + type.Name = name; + types [i_type] = type; + + ++i_type; + } + + private void ReadLogFileChunk_Method (BinaryReader reader) + { + uint code; + code = reader.ReadUInt32 (); + + string name; + name = reader.ReadString (); + + if (i_method >= n_methods) + return; + + //Spew ("Found method '{0}' with code {1}", name, code); + + method_codes_old [i_method] = code; + methods [i_method].Name = name; + + ++i_method; + } + + private void ReadLogFileChunk_Backtrace (BinaryReader reader) + { + uint code; + code = reader.ReadUInt32 (); + + uint type_code; + type_code = reader.ReadUInt32 (); + + int n_frames; + n_frames = reader.ReadInt16 (); + + if (i_backtrace >= n_backtraces) { + for (int i = 0; i < n_frames; ++i) { + reader.ReadUInt32 (); // skip method code + reader.ReadUInt32 (); // skip native offset + } + return; + } + + backtrace_codes [i_backtrace] = code; + backtraces [i_backtrace].TypeCode = type_code; + + Frame [] frames = new Frame [n_frames]; + backtraces [i_backtrace].Frames = frames; + + for (int i = 0; i < n_frames; ++i) { + frames [i].MethodCode = reader.ReadUInt32 (); + frames [i].IlOffset = reader.ReadUInt32 (); + } + + ++i_backtrace; + } + + private void ReadLogFileChunk_Gc (BinaryReader reader) + { + Gc gc; + gc = new Gc (); + gc.ReadWithData (reader); + + gcs [i_gc] = gc; + ++i_gc; + + if (gc.Generation >= 0) + Spew ("GC {0}: collected {1} bytes, {2} to {3}", + gc.Generation, + gc.FreedBytes, + gc.PreGcLiveBytes, + gc.PostGcLiveBytes); + } + + private void ReadLogFileChunk_Resize (BinaryReader reader) + { + Resize r; + r = new Resize (); + r.Read (reader, i_gc); + if (i_resize > 0) + r.PreviousSize = resizes [i_resize-1].NewSize; + Spew ("Resize to {0}, {1} live bytes", r.NewSize, r.TotalLiveBytes); + resizes [i_resize] = r; + ++i_resize; + } + + /////////////////////////////////////////////////////////////////// + + // 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; + } + + private enum SortOrder { + ByCode, + ByName + } + + void combsort_types (SortOrder order) + { + int start = 0; + int size = types.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; + + bool out_of_order; + if (order == SortOrder.ByCode) + out_of_order = type_codes_old [i] > type_codes_old [j]; + else + out_of_order = String.Compare (types [i].Name, types [j].Name) > 0; + + if (out_of_order) { + + uint tmp_code; + Type tmp; + + tmp_code = type_codes_old [i]; + type_codes_old [i] = type_codes_old [j]; + type_codes_old [j] = tmp_code; + + tmp_code = type_codes_new [i]; + type_codes_new [i] = type_codes_new [j]; + type_codes_new [j] = tmp_code; + + tmp = types [i]; + types [i] = types [j]; + types [j] = tmp; + + swapped = true; + } + } + if (gap == 1 && !swapped) + break; + } + } + + void combsort_methods (SortOrder order) + { + int start = 0; + int size = methods.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; + + bool out_of_order; + if (order == SortOrder.ByCode) + out_of_order = method_codes_old [i] > method_codes_old [j]; + else + out_of_order = String.Compare (methods [i].Name, methods [j].Name) > 0; + + if (out_of_order) { + + uint tmp_code; + Method tmp; + + tmp_code = method_codes_old [i]; + method_codes_old [i] = method_codes_old [j]; + method_codes_old [j] = tmp_code; + + tmp_code = method_codes_new [i]; + method_codes_new [i] = method_codes_new [j]; + method_codes_new [j] = tmp_code; + + tmp = methods [i]; + methods [i] = methods [j]; + methods [j] = tmp; + + swapped = true; + } + } + if (gap == 1 && !swapped) + break; + } + } + + void combsort_backtraces () + { + int start = 0; + int size = backtraces.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 (backtrace_codes [i] > backtrace_codes [j]) { + + uint tmp_code; + tmp_code = backtrace_codes [i]; + backtrace_codes [i] = backtrace_codes [j]; + backtrace_codes [j] = tmp_code; + + Backtrace tmp; + tmp = backtraces [i]; + backtraces [i] = backtraces [j]; + backtraces [j] = tmp; + + swapped = true; + } + } + if (gap == 1 && !swapped) + break; + } + } + + /////////////////////////////////////////////////////////////////// + + private uint TranslateTypeCode (uint code) + { + int i, i0, i1; + i0 = 0; + i1 = types.Length-1; + + while (i0 <= i1) { + i = (i0 + i1) / 2; + if (type_codes_old [i] == code) + return type_codes_new [i]; + else if (type_codes_old [i] < code) + i0 = i+1; + else + i1 = i-1; + } + + throw new Exception ("Couldn't resolve type code " + code); + } + + private uint TranslateMethodCode (uint code) + { + int i, i0, i1; + i0 = 0; + i1 = methods.Length-1; + + while (i0 <= i1) { + i = (i0 + i1) / 2; + if (method_codes_old [i] == code) + return method_codes_new [i]; + else if (method_codes_old [i] < code) + i0 = i+1; + else + i1 = i-1; + } + + throw new Exception ("Couldn't resolve method code " + code); + } + + private uint TranslateBacktraceCode (uint code) + { + int i, i0, i1; + i0 = 0; + i1 = backtraces.Length-1; + + while (i0 <= i1) { + i = (i0 + i1) / 2; + if (backtrace_codes [i] == code) + return (uint) i; + else if (backtrace_codes [i] < code) + i0 = i+1; + else + i1 = i-1; + } + + throw new Exception ("Couldn't resolve backtrace code " + code); + } + + private void RemapAllCodes () + { + combsort_types (SortOrder.ByName); + for (int i = 0; i < type_codes_new.Length; ++i) + type_codes_new [i] = (uint) i; + combsort_types (SortOrder.ByCode); // this sorts by the old codes + + combsort_methods (SortOrder.ByName); + for (int i = 0; i < method_codes_new.Length; ++i) + method_codes_new [i] = (uint) i; + combsort_methods (SortOrder.ByCode); // again, this sorts by the old codes + + combsort_backtraces (); + + // Remap the backtrace codes in the GCs + for (int i = 0; i < gcs.Length; ++i) { + for (int j = 0; j < gcs [i].GcData.Length; ++j) { + uint code; + code = gcs [i].GcData [j].BacktraceCode; + code = TranslateBacktraceCode (code); + gcs [i].GcData [j].BacktraceCode = code; + } + } + + // Remap the type and method codes in the backtrace, + // and replace the backtrace codes. + for (int i = 0; i < backtraces.Length; ++i) { + backtraces [i].TypeCode = TranslateTypeCode (backtraces [i].TypeCode); + for (int j = 0; j < backtraces [i].Frames.Length; ++j) { + uint code; + code = backtraces [i].Frames [j].MethodCode; + code = TranslateMethodCode (code); + backtraces [i].Frames [j].MethodCode = code; + } + } + + // Re-sort them back into name order, which is the same as sorting by the new + // codes. This puts everything into the correct order for when we write + // them out to the summary file. + combsort_types (SortOrder.ByName); + combsort_methods (SortOrder.ByName); + + // After remapping the codes, we don't need these any more. + type_codes_old = null; + type_codes_new = null; + method_codes_old = null; + method_codes_new = null; + backtrace_codes = null; + } + + /////////////////////////////////////////////////////////////////// + + private void CollectFinalBacktraceAndTypeStats () + { + for (int i = 0; i < backtraces.Length; ++i) + backtraces [i].LastGeneration = int.MaxValue; + + for (int i = 0; i < types.Length; ++i) + types [i].LastGeneration = int.MaxValue; + + int count; + count = backtraces.Length; + + for (int i = gcs.Length - 1; i >= 0; --i) { + for (int j = 0; j < gcs [i].GcData.Length; ++j) { + uint bt_code; + bt_code = gcs [i].GcData [j].BacktraceCode; + if (backtraces [bt_code].LastGeneration == int.MaxValue) { + backtraces [bt_code].LastGeneration = gcs [i].Generation; + backtraces [bt_code].LastObjectStats = gcs [i].GcData [j].ObjectStats; + --count; + + // Add this backtrace to our per-type totals + uint type_code; + type_code = backtraces [bt_code].TypeCode; + types [type_code].BacktraceCount++; + if (types [type_code].LastGeneration == int.MaxValue) { + types [type_code].LastGeneration = backtraces [bt_code].LastGeneration; + types [type_code].LastObjectStats = backtraces [bt_code].LastObjectStats; + } else if (types [type_code].LastGeneration == backtraces [bt_code].LastGeneration) { + types [type_code].LastObjectStats += backtraces [bt_code].LastObjectStats; + } else { + types [type_code].LastObjectStats.AddAllocatedOnly (backtraces [bt_code].LastObjectStats); + } + } + } + + // If we've found stats for every backtrace, bail out of the loop early. + if (count == 0) + break; + } + } + + /////////////////////////////////////////////////////////////////// + + private void ReadSummaryTableOfContents (BinaryReader reader) + { + type_name_data_offset = reader.ReadInt64 (); + method_name_data_offset = reader.ReadInt64 (); + backtrace_data_offset = reader.ReadInt64 (); + gc_data_offset = reader.ReadInt64 (); + resize_data_offset = reader.ReadInt64 (); + types_by_code_offset = reader.ReadInt64 (); + methods_by_code_offset = reader.ReadInt64 (); + backtrace_index_offset = reader.ReadInt64 (); + gc_index_offset = reader.ReadInt64 (); + } + + private void WriteSummaryTableOfContents (BinaryWriter writer) + { + writer.Write (type_name_data_offset); + writer.Write (method_name_data_offset); + writer.Write (backtrace_data_offset); + writer.Write (gc_data_offset); + writer.Write (resize_data_offset); + writer.Write (types_by_code_offset); + writer.Write (methods_by_code_offset); + writer.Write (backtrace_index_offset); + writer.Write (gc_index_offset); + } + + /////////////////////////////////////////////////////////////////// + + // + // Summary file reader + // + + private void ReadSummaryFile (BinaryReader reader) + { + ReadSummaryTableOfContents (reader); + ReadSummaryIndexes (reader); + ReadSummaryTypes (reader); + ReadSummaryResizes (reader); + ReadSummaryGcs (reader); + } + + private void ReadSummaryIndexes (BinaryReader reader) + { + reader.BaseStream.Seek (methods_by_code_offset, SeekOrigin.Begin); + for (int i = 0; i < methods.Length; ++i) + methods [i].Position = reader.ReadInt64 (); + + reader.BaseStream.Seek (backtrace_index_offset, SeekOrigin.Begin); + for (int i = 0; i < backtraces.Length; ++i) { + backtraces [i].TypeCode = reader.ReadUInt32 (); + backtraces [i].LastGeneration = reader.ReadInt32 (); + backtraces [i].LastObjectStats.Read (reader); + backtraces [i].Position = reader.ReadInt64 (); + } + } + + private void ReadSummaryTypes (BinaryReader reader) + { + reader.BaseStream.Seek (type_name_data_offset, SeekOrigin.Begin); + for (int i = 0; i < types.Length; ++i) { + Type type; + type = new Type (); + type.Name = reader.ReadString (); + types [i] = type; + } + + reader.BaseStream.Seek (types_by_code_offset, SeekOrigin.Begin); + for (int i = 0; i < types.Length; ++i) { + Type type; + type = types [i]; + type.BacktraceCount = reader.ReadInt32 (); + type.LastGeneration = reader.ReadInt32 (); + type.LastObjectStats.Read (reader); + } + } + + private void ReadSummaryResizes (BinaryReader reader) + { + reader.BaseStream.Seek (resize_data_offset, SeekOrigin.Begin); + for (int i = 0; i < resizes.Length; ++i) { + Resize r; + r = new Resize (); + r.Read (reader, -1); + if (i > 0) + r.PreviousSize = resizes [i-1].NewSize; + resizes [i] = r; + } + } + + private void ReadSummaryGcs (BinaryReader reader) + { + reader.BaseStream.Seek (gc_index_offset, SeekOrigin.Begin); + for (int i = 0; i < gcs.Length; ++i) { + Gc gc; + gc = new Gc (); + gc.ReadWithoutData (reader); + gcs [i] = gc; + gc_pos [i] = reader.ReadInt64 (); + } + } + + /////////////////////////////////////////////////////////////////// + + // + // Summary file writer + // + + private void WriteSummaryFile (BinaryWriter writer) + { + WritePreamble (writer); + WriteHeader (writer); + + long toc_offset; + toc_offset = writer.BaseStream.Position; + WriteSummaryTableOfContents (writer); // writes placeholder data + + WriteSummaryData (writer); + + WriteSummaryIndexes (writer); + + WriteSummaryTypes (writer); + + writer.BaseStream.Seek (toc_offset, SeekOrigin.Begin); + WriteSummaryTableOfContents (writer); // writes the actual data + } + + private void WriteSummaryData (BinaryWriter writer) + { + // Write out the name strings. + type_name_data_offset = writer.BaseStream.Position; + for (int i = 0; i < types.Length; ++i) + writer.Write (types [i].Name); + + + // Write out the method names, and remember the position + // of each in the file. + method_name_data_offset = writer.BaseStream.Position; + for (int i = 0; i < methods.Length; ++i) { + methods [i].Position = writer.BaseStream.Position; + writer.Write (methods [i].Name); + } + + + // Write out all of the backtrace frame data, and remember the position + // of each in the file. + backtrace_data_offset = writer.BaseStream.Position; + for (int i = 0; i < backtraces.Length; ++i) { + backtraces [i].Position = writer.BaseStream.Position; + writer.Write (backtraces [i].Frames.Length); + for (int j = 0; j < backtraces [i].Frames.Length; ++j) { + writer.Write (backtraces [i].Frames [j].MethodCode); + writer.Write (backtraces [i].Frames [j].IlOffset); + } + } + + + // Write out all of the GC data, and remember the position of + // each in the file. + gc_data_offset = writer.BaseStream.Position; + for (int i = 0; i < gcs.Length; ++i) { + gc_pos [i] = writer.BaseStream.Position; + gcs [i].WriteOnlyData (writer); + } + + + // Write out all the resizes. + resize_data_offset = writer.BaseStream.Position; + for (int i = 0; i < resizes.Length; ++i) + resizes [i].Write (writer); + } + + private void WriteSummaryIndexes (BinaryWriter writer) + { + methods_by_code_offset = writer.BaseStream.Position; + for (int i = 0; i < methods.Length; ++i) + writer.Write (methods [i].Position); + + // backtraces were sorted in WriteSummary + backtrace_index_offset = writer.BaseStream.Position; + for (int i = 0; i < backtraces.Length; ++i) { + writer.Write (backtraces [i].TypeCode); + writer.Write (backtraces [i].LastGeneration); + backtraces [i].LastObjectStats.Write (writer); + writer.Write (backtraces [i].Position); + } + + gc_index_offset = writer.BaseStream.Position; + for (int i = 0; i < gcs.Length; ++i) { + gcs [i].WriteWithoutData (writer); + writer.Write (gc_pos [i]); + } + } + + private void WriteSummaryTypes (BinaryWriter writer) + { + types_by_code_offset = writer.BaseStream.Position; + for (int i = 0; i < types.Length; ++i) { + Type type; + type = types [i]; + writer.Write (type.BacktraceCount); + writer.Write (type.LastGeneration); + type.LastObjectStats.Write (writer); + } + } + + /////////////////////////////////////////////////////////////////// + + public Resize [] Resizes { + get { return resizes; } + } + + public Gc [] Gcs { + get { return gcs; } + } + + /////////////////////////////////////////////////////////////////// + + public Type [] Types { + get { return types; } + } + + public ArrayList GetTypesMatching (Regex regex) + { + ArrayList matches; + matches = new ArrayList (); + for (int i = 0; i < types.Length; ++i) + if (regex.Match (types [i].Name).Success) + matches.Add (types [i]); + return matches; + } + + public ArrayList GetTypesMatching (string regex) + { + return GetTypesMatching (new Regex (regex)); + } + + /////////////////////////////////////////////////////////////////// + + public string GetMethodName (uint code) + { + if (methods [code].Name == null) { + lazy_reader.BaseStream.Seek (methods [code].Position, SeekOrigin.Begin); + methods [code].Name = lazy_reader.ReadString (); + } + + return methods [code].Name; + } + +#if false + private void PopulateBacktrace (uint code) + { + if (backtraces [code].Frames != null) + return; + + lazy_reader.BaseStream.Seek (backtraces [code].Position, SeekOrigin.Begin); + + int length; + length = lazy_reader.ReadInt32 (); + + Frame [] frames; + frames = new Frame [length]; + for (int i = 0; i < length; ++i) { + frames [i].MethodCode = lazy_reader.ReadUInt32 (); + frames [i].IlOffset = lazy_reader.ReadUInt32 (); + } + + backtraces [code].Frames = frames; + } +#endif + } +} diff --git a/analyzer/Report.cs b/analyzer/Report.cs new file mode 100644 index 0000000..e964dc2 --- /dev/null +++ b/analyzer/Report.cs @@ -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 ()); + } + } +} diff --git a/analyzer/Resize.cs b/analyzer/Resize.cs new file mode 100644 index 0000000..bd819d9 --- /dev/null +++ b/analyzer/Resize.cs @@ -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); + } + } +} diff --git a/analyzer/Table.cs b/analyzer/Table.cs new file mode 100644 index 0000000..bfc4fbf --- /dev/null +++ b/analyzer/Table.cs @@ -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 (); + } + } +} diff --git a/analyzer/Type.cs b/analyzer/Type.cs new file mode 100644 index 0000000..64b1a05 --- /dev/null +++ b/analyzer/Type.cs @@ -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; + } + +} diff --git a/analyzer/TypesReport.cs b/analyzer/TypesReport.cs new file mode 100644 index 0000000..e14452a --- /dev/null +++ b/analyzer/TypesReport.cs @@ -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); + } + } + } +} diff --git a/analyzer/Util.cs b/analyzer/Util.cs new file mode 100644 index 0000000..9f9f74d --- /dev/null +++ b/analyzer/Util.cs @@ -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); + } +} diff --git a/analyzer/heap-buddy.in b/analyzer/heap-buddy.in new file mode 100644 index 0000000..c7bb9c8 --- /dev/null +++ b/analyzer/heap-buddy.in @@ -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 "$@" diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..704dc9d --- /dev/null +++ b/autogen.sh @@ -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 diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..c884ad4 --- /dev/null +++ b/config.sub @@ -0,0 +1,1563 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +timestamp='2005-02-10' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ + kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | msp430-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..30832c5 --- /dev/null +++ b/configure.in @@ -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 +]) + + diff --git a/doc/outfile-format b/doc/outfile-format new file mode 100644 index 0000000..d4d9b63 --- /dev/null +++ b/doc/outfile-format @@ -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 + + + + + + + + diff --git a/profiler/Makefile.am b/profiler/Makefile.am new file mode 100644 index 0000000..7ed9cd9 --- /dev/null +++ b/profiler/Makefile.am @@ -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 \ No newline at end of file diff --git a/profiler/accountant.c b/profiler/accountant.c new file mode 100644 index 0000000..8e28dd4 --- /dev/null +++ b/profiler/accountant.c @@ -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 +#include + +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; + } +} diff --git a/profiler/accountant.h b/profiler/accountant.h new file mode 100644 index 0000000..a0a2c63 --- /dev/null +++ b/profiler/accountant.h @@ -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 +#include +#include + +#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__ */ + diff --git a/profiler/backtrace.c b/profiler/backtrace.c new file mode 100644 index 0000000..834a876 --- /dev/null +++ b/profiler/backtrace.c @@ -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; +} diff --git a/profiler/backtrace.h b/profiler/backtrace.h new file mode 100644 index 0000000..ffcdbec --- /dev/null +++ b/profiler/backtrace.h @@ -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 +#include + +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__ */ + diff --git a/profiler/heap-buddy.c b/profiler/heap-buddy.c new file mode 100644 index 0000000..0ce1536 --- /dev/null +++ b/profiler/heap-buddy.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/profiler/outfile-writer.c b/profiler/outfile-writer.c new file mode 100644 index 0000000..fd3feb0 --- /dev/null +++ b/profiler/outfile-writer.c @@ -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 +#include + +#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); +} + diff --git a/profiler/outfile-writer.h b/profiler/outfile-writer.h new file mode 100644 index 0000000..372c160 --- /dev/null +++ b/profiler/outfile-writer.h @@ -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 +#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__ */ + diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..a89bd6f --- /dev/null +++ b/test/Makefile.am @@ -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) diff --git a/test/test-01.cs b/test/test-01.cs new file mode 100644 index 0000000..3e83b69 --- /dev/null +++ b/test/test-01.cs @@ -0,0 +1,9 @@ + +class Test { + + static void Main () + { + + } + +} diff --git a/test/test-02.cs b/test/test-02.cs new file mode 100644 index 0000000..010d7b3 --- /dev/null +++ b/test/test-02.cs @@ -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; + } + } + +} diff --git a/test/test-03.cs b/test/test-03.cs new file mode 100644 index 0000000..f4399b3 --- /dev/null +++ b/test/test-03.cs @@ -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); + } + +} diff --git a/test/test-04.cs b/test/test-04.cs new file mode 100644 index 0000000..caca208 --- /dev/null +++ b/test/test-04.cs @@ -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)); + } + +} diff --git a/test/test-05.cs b/test/test-05.cs new file mode 100644 index 0000000..0cffe96 --- /dev/null +++ b/test/test-05.cs @@ -0,0 +1,10 @@ +using System; + +class Test { + + static void Main () + { + throw new Exception ("Foo"); + } + +} diff --git a/test/test-06.cs b/test/test-06.cs new file mode 100644 index 0000000..c019b81 --- /dev/null +++ b/test/test-06.cs @@ -0,0 +1,10 @@ +using System; + +class Test { + + static void Main () + { + Console.WriteLine ("foo"); + } + +}