Browse files

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

heap-buddy.

svn path=/trunk/heap-buddy/; revision=51438
  • Loading branch information...
0 parents commit 429b0858d28da491cb5ec7a4709b4dd76e02f53a Jon Trowbridge committed Oct 7, 2005
4 AUTHORS
@@ -0,0 +1,4 @@
+
+Ben Maurer <bmaurer@ximian.com>
+Jon Trowbridge <trow@novell.com>
+
0 ChangeLog
No changes.
3 Makefile.am
@@ -0,0 +1,3 @@
+
+SUBDIRS = profiler analyzer test
+
0 NEWS
No changes.
17 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'.
+
+
+
+
164 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;
+ }
+ }
+
+ }
+}
61 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);
+ }
+ }
+
+}
76 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;
+ }
+ }
+ }
+ }
+}
53 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)
154 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;
+ }
+ }
+}
1,020 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
+ }
+}
72 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 ());
+ }
+ }
+}
63 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);
+ }
+ }
+}
236 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 ();
+ }
+ }
+}
44 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;
+ }
+
+}
71 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);
+ }
+ }
+ }
+}
75 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);
+ }
+}
16 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 "$@"
149 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
1,563 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 <config-patches@gnu.org>. 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 <config-patches@gnu.org>."
+
+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
+ ;;