diff --git a/analyzer/BacktracesReport.cs b/analyzer/BacktracesReport.cs new file mode 100644 index 0000000..baaa7e6 --- /dev/null +++ b/analyzer/BacktracesReport.cs @@ -0,0 +1,159 @@ +// +// BacktracesReport.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; + +namespace HeapBuddy { + + public class BacktracesReport : Report { + + public BacktracesReport () : base ("Backtraces") { } + + enum SortOrder { + Unsorted, + ByCount, + ByTotalBytes, + ByAverageBytes, + ByAverageAge + } + + static string BacktraceStringifier (Backtrace bt, int max_width) + { + StringBuilder sb = new StringBuilder (); + + sb.Append ("type="); + sb.Append (Util.Ellipsize (max_width-5, bt.Type.Name)); + foreach (Frame frame in bt.Frames) { + if (! frame.MethodName.StartsWith ("(wrapper")) { + sb.Append ('\n'); + sb.Append (Util.Ellipsize (max_width, frame.MethodName)); + } + } + + return sb.ToString (); + } + + static string BacktraceStringifier_Full (object obj) + { + return BacktraceStringifier ((Backtrace) obj, -1); + } + + static string BacktraceStringifier_Ellipsize (object obj) + { + const int max_width = 51; + return BacktraceStringifier ((Backtrace) obj, max_width); + } + + override public void Run (OutfileReader reader, string [] args) + { + SortOrder order = SortOrder.ByTotalBytes; + int max_rows = 25; + bool ellipsize_names = true; + + // Hacky free-form arg parser + + int i = 0; + while (i < args.Length) { + string arg = args [i].ToLower (); + + if (arg == "count") + order = SortOrder.ByCount; + else if (arg == "total") + order = SortOrder.ByTotalBytes; + else if (arg == "average") + order = SortOrder.ByAverageBytes; + else if (arg == "age") + order = SortOrder.ByAverageAge; + else if (arg == "all") + max_rows = -1; + else if (arg == "full" || arg == "long" || arg == "unellipsized") + ellipsize_names = false; + else { + int n = -1; + try { + n = Int32.Parse (arg); + } catch { } + if (n > 0) + max_rows = n; + } + + ++i; + } + + // Generate the table + + Table table; + table = new Table (); + + table.AddHeaders ("Backtrace", + "#", + "Total", + "AvSz", + "AvAge"); + + if (ellipsize_names) + table.SetStringify (0, BacktraceStringifier_Ellipsize); + else + table.SetStringify (0, BacktraceStringifier_Full); + table.SetStringify (2, Util.PrettySize_Obj); + table.SetStringify (3, "0.0"); + table.SetStringify (4, "0.0"); + + foreach (Backtrace bt in reader.Backtraces) { + table.AddRow (bt, + bt.LastObjectStats.AllocatedCount, + bt.LastObjectStats.AllocatedTotalBytes, + bt.LastObjectStats.AllocatedAverageBytes, + bt.LastObjectStats.AllocatedAverageAge); + } + + switch (order) { + case SortOrder.ByCount: + table.Sort (1, false); + break; + case SortOrder.ByTotalBytes: + table.Sort (2, false); + break; + case SortOrder.ByAverageBytes: + table.Sort (3, false); + break; + case SortOrder.ByAverageAge: + table.Sort (4, false); + break; + } + + table.SkipLines = true; + if (max_rows > 0) + table.MaxRows = max_rows; + + Console.WriteLine (table); + + if (table.RowCount > table.MaxRows) { + Console.WriteLine (); + Console.WriteLine ("(skipped {0} backtraces)", table.RowCount - table.MaxRows); + } + } + } +} diff --git a/analyzer/Frame.cs b/analyzer/Frame.cs index 8156b44..ff9092f 100644 --- a/analyzer/Frame.cs +++ b/analyzer/Frame.cs @@ -29,6 +29,7 @@ namespace HeapBuddy { public uint MethodCode; public string MethodName; + public string MethodArguments; public uint IlOffset; } diff --git a/analyzer/Gc.cs b/analyzer/Gc.cs index c8413c5..d7570b3 100644 --- a/analyzer/Gc.cs +++ b/analyzer/Gc.cs @@ -38,7 +38,9 @@ namespace HeapBuddy { public DateTime Timestamp; public long PreGcLiveBytes; + public int PreGcLiveObjects; public long PostGcLiveBytes; + public int PostGcLiveObjects; private GcData [] gc_data; OutfileReader reader; @@ -56,8 +58,16 @@ namespace HeapBuddy { get { return PreGcLiveBytes - PostGcLiveBytes; } } - public double FreedPercentage { - get { return 100.0 * FreedBytes / PreGcLiveBytes; } + public int FreedObjects { + get { return PreGcLiveObjects - PostGcLiveObjects; } + } + + public double FreedBytesPercentage { + get { return PreGcLiveBytes == 0 ? 0 : 100.0 * FreedBytes / PreGcLiveBytes; } + } + + public double FreedObjectsPercentage { + get { return PreGcLiveObjects == 0 ? 0 : 100.0 * FreedObjects / PreGcLiveObjects; } } public GcData [] GcData { diff --git a/analyzer/HeapBuddy.cs b/analyzer/HeapBuddy.cs index 81c3c88..8e1aa53 100644 --- a/analyzer/HeapBuddy.cs +++ b/analyzer/HeapBuddy.cs @@ -38,6 +38,11 @@ namespace HeapBuddy { ++args_i; } + if (! File.Exists (outfile_name)) { + Console.WriteLine ("Can't find outfile '{0}'", outfile_name); + return; + } + string report_name = "summary"; if (args_i < args.Length && Report.Exists (args [args_i])) { report_name = args [args_i]; diff --git a/analyzer/HistoryReport.cs b/analyzer/HistoryReport.cs index b98f9bf..0fd6515 100644 --- a/analyzer/HistoryReport.cs +++ b/analyzer/HistoryReport.cs @@ -32,6 +32,10 @@ namespace HeapBuddy { override public void Run (OutfileReader reader, string [] args) { + Table table; + table = new Table (); + table.Separator = " | "; + Resize [] resizes; resizes = reader.Resizes; @@ -40,6 +44,7 @@ namespace HeapBuddy { int i_resize = 0; int i_gc = 0; + long heap_size = 0; while (i_resize < resizes.Length || i_gc < gcs.Length) { @@ -51,26 +56,63 @@ namespace HeapBuddy { if (i_gc < gcs.Length) gc = gcs [i_gc]; + if (i_resize != 0 || i_gc != 0) + table.AddRow ("", "", ""); + + string timestamp, tag, message; + 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)); + timestamp = string.Format ("{0:HH:mm:ss}", r.Timestamp); + + if (r.PreviousSize == 0) { + tag = "Init"; + message = String.Format ("Initialized heap to {0}", + Util.PrettySize (r.NewSize)); + } else { + tag = "Resize"; + message = String.Format ("Grew heap from {0} to {1}\n" + + "{2} in live objects\n" + + "Heap went from {3:0.0}% to {4:0.0}% capacity", + Util.PrettySize (r.PreviousSize), + Util.PrettySize (r.NewSize), + Util.PrettySize (r.TotalLiveBytes), + r.PreResizeCapacity, r.PostResizeCapacity); + } + + heap_size = r.NewSize; ++i_resize; - } else if (gc != null) { + + } else { + timestamp = String.Format ("{0:HH:mm:ss}", gc.Timestamp); 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); + tag = "GC " + gc.Generation; + message = String.Format ("Collected {0} of {1} objects ({2:0.0}%)\n" + + "Collected {3} of {4} ({5:0.0}%)\n" + + "Heap went from {6:0.0}% to {7:0.0}% capacity", + gc.FreedObjects, + gc.PreGcLiveObjects, + gc.FreedObjectsPercentage, + Util.PrettySize (gc.FreedBytes), + Util.PrettySize (gc.PreGcLiveBytes), + gc.FreedBytesPercentage, + 100.0 * gc.PreGcLiveBytes / heap_size, + 100.0 * gc.PostGcLiveBytes / heap_size); + } else { + tag = "Exit"; + message = String.Format ("{0} live objects using {1}", + gc.PreGcLiveObjects, + Util.PrettySize (gc.PreGcLiveBytes)); } ++i_gc; } + + table.AddRow (timestamp, tag, message); } + + table.SetAlignment (1, Alignment.Left); + table.SetAlignment (2, Alignment.Left); + + Console.WriteLine (table); } } } diff --git a/analyzer/Makefile.am b/analyzer/Makefile.am index 485a212..1403a59 100644 --- a/analyzer/Makefile.am +++ b/analyzer/Makefile.am @@ -6,6 +6,7 @@ TARGET = HeapBuddy.exe WRAPPER = heap-buddy REPORT_CSFILES = \ + BacktracesReport.cs \ HistoryReport.cs \ SummaryReport.cs \ TypesReport.cs diff --git a/analyzer/OutfileReader.cs b/analyzer/OutfileReader.cs index 6541659..b3474f5 100644 --- a/analyzer/OutfileReader.cs +++ b/analyzer/OutfileReader.cs @@ -32,8 +32,8 @@ namespace HeapBuddy { public bool Debug = false; const uint magic_number = 0x4eabbdd1; - const int expected_log_version = 4; - const int expected_summary_version = 1; + const int expected_log_version = 5; + const int expected_summary_version = 2; const string log_file_label = "heap-buddy logfile"; const string summary_file_label = "heap-buddy summary"; @@ -50,6 +50,9 @@ namespace HeapBuddy { int n_backtraces; int n_resizes; + public long TotalAllocatedBytes; + public int TotalAllocatedObjects; + // Offsets in the summary file long type_name_data_offset = -1; long method_name_data_offset = -1; @@ -68,6 +71,7 @@ namespace HeapBuddy { private struct Method { public string Name; + public string Arguments; public long Position; // of the name in the summary file } @@ -177,8 +181,12 @@ namespace HeapBuddy { { uint this_magic; this_magic = reader.ReadUInt32 (); - if (this_magic != magic_number) - throw new Exception ("Bad magic number in heap-buddy outfile"); + if (this_magic != magic_number) { + string msg; + msg = String.Format ("Bad magic number: expected {0}, found {1}", + magic_number, this_magic); + throw new Exception (msg); + } int this_version; this_version = reader.ReadInt32 (); @@ -235,6 +243,9 @@ namespace HeapBuddy { n_backtraces = reader.ReadInt32 (); n_resizes = reader.ReadInt32 (); + TotalAllocatedBytes = reader.ReadInt64 (); + TotalAllocatedObjects = reader.ReadInt32 (); + Spew ("GCs = {0}", n_gcs); Spew ("Types = {0}", n_types); Spew ("Methods = {0}", n_methods); @@ -265,6 +276,8 @@ namespace HeapBuddy { writer.Write (n_methods); writer.Write (n_backtraces); writer.Write (n_resizes); + writer.Write (TotalAllocatedBytes); + writer.Write (TotalAllocatedObjects); Spew ("Finished writing header"); } @@ -447,6 +460,7 @@ namespace HeapBuddy { gc.TimeT = reader.ReadInt64 (); gc.Timestamp = Util.ConvertTimeT (gc.TimeT); gc.PreGcLiveBytes = reader.ReadInt64 (); + gc.PreGcLiveObjects = reader.ReadInt32 (); int n; n = reader.ReadInt32 (); @@ -460,6 +474,7 @@ namespace HeapBuddy { combsort_raw_gc_data (raw); gc.PostGcLiveBytes = reader.ReadInt64 (); + gc.PostGcLiveObjects = reader.ReadInt32 (); gcs [i_gc] = gc; raw_gc_data [i_gc] = raw; @@ -746,11 +761,15 @@ namespace HeapBuddy { // and replace the backtrace codes. for (int i = 0; i < backtraces.Length; ++i) { backtrace_type_codes [i] = TranslateTypeCode (backtrace_type_codes [i]); + backtraces [i].Type = types [backtrace_type_codes [i]]; 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; + GetMethod (code, + out backtraces [i].Frames [j].MethodName, + out backtraces [i].Frames [j].MethodArguments); } } @@ -928,7 +947,9 @@ namespace HeapBuddy { gc.TimeT = reader.ReadInt64 (); gc.Timestamp = Util.ConvertTimeT (gc.TimeT); gc.PreGcLiveBytes = reader.ReadInt64 (); + gc.PreGcLiveObjects = reader.ReadInt32 (); gc.PostGcLiveBytes = reader.ReadInt64 (); + gc.PostGcLiveObjects = reader.ReadInt32 (); gcs [i] = gc; gc_pos [i] = reader.ReadInt64 (); @@ -1031,7 +1052,9 @@ namespace HeapBuddy { writer.Write (gcs [i].Generation); writer.Write (gcs [i].TimeT); writer.Write (gcs [i].PreGcLiveBytes); + writer.Write (gcs [i].PreGcLiveObjects); writer.Write (gcs [i].PostGcLiveBytes); + writer.Write (gcs [i].PostGcLiveObjects); writer.Write (gc_pos [i]); } } @@ -1054,10 +1077,18 @@ namespace HeapBuddy { get { return resizes; } } + public Resize LastResize { + get { return resizes [resizes.Length-1]; } + } + public Gc [] Gcs { get { return gcs; } } + public Gc LastGc { + get { return gcs [gcs.Length-1]; } + } + public Backtrace [] Backtraces { get { return backtraces; } } @@ -1091,14 +1122,21 @@ namespace HeapBuddy { /////////////////////////////////////////////////////////////////// - private string GetMethodName (uint code) + private void GetMethod (uint code, out string name, out string args) { if (methods [code].Name == null) { lazy_reader.BaseStream.Seek (methods [code].Position, SeekOrigin.Begin); - methods [code].Name = lazy_reader.ReadString (); + + string method; + method = lazy_reader.ReadString (); + + int i = method.IndexOf (" ("); + methods [code].Name = method.Substring (0, i); + methods [code].Arguments = method.Substring (i+1); } - return methods [code].Name; + name = methods [code].Name; + args = methods [code].Arguments; } public Frame [] GetFrames (uint backtrace_code) @@ -1116,7 +1154,10 @@ namespace HeapBuddy { } for (int i = 0; i < length; ++i) - frames [i].MethodName = GetMethodName (frames [i].MethodCode); + GetMethod (frames [i].MethodCode, + out frames [i].MethodName, + out frames [i].MethodArguments); + return frames; } diff --git a/analyzer/Resize.cs b/analyzer/Resize.cs index bd819d9..4b33a0c 100644 --- a/analyzer/Resize.cs +++ b/analyzer/Resize.cs @@ -39,6 +39,15 @@ namespace HeapBuddy { public long TotalLiveBytes; + public double PreResizeCapacity { + get { return PreviousSize == 0 ? 0 : 100.0 * TotalLiveBytes / PreviousSize; } + } + + public double PostResizeCapacity { + get { return PreviousSize == 0 ? 0 : 100.0 * TotalLiveBytes / NewSize; } + } + + // You need to set PreviousSize by hand. public void Read (BinaryReader reader, int generation) { diff --git a/analyzer/SummaryReport.cs b/analyzer/SummaryReport.cs index 8de02ec..b49e696 100644 --- a/analyzer/SummaryReport.cs +++ b/analyzer/SummaryReport.cs @@ -39,13 +39,15 @@ namespace HeapBuddy { table.AddRow ("", ""); table.AddRow ("Filename:", reader.Filename); + table.AddRow ("Allocated Bytes:", Util.PrettySize (reader.TotalAllocatedBytes)); + table.AddRow ("Allocated Objects:", reader.TotalAllocatedObjects); table.AddRow ("GCs:", reader.Gcs.Length); table.AddRow ("Resizes:", reader.Resizes.Length); - table.AddRow ("Final heap size:", Util.PrettySize (reader.Resizes [reader.Resizes.Length-1].NewSize)); + table.AddRow ("Final heap size:", Util.PrettySize (reader.LastResize.NewSize)); table.AddRow ("", ""); - table.AddRow ("Allocated Types:", reader.Types.Length); + table.AddRow ("Distinct Types:", reader.Types.Length); table.AddRow ("Backtraces:", reader.Backtraces.Length); table.SetAlignment (1, Alignment.Left); diff --git a/analyzer/Table.cs b/analyzer/Table.cs index bfc4fbf..ae3b8d4 100644 --- a/analyzer/Table.cs +++ b/analyzer/Table.cs @@ -36,14 +36,15 @@ namespace HeapBuddy { public class Table { - private int cols = -1; + private int n_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 bool SkipLines = false; + public string Separator = " "; public int RowCount { get { return rows.Count; } @@ -51,22 +52,20 @@ namespace HeapBuddy { private void CheckColumns (ICollection whatever) { - if (cols == -1) { + if (n_cols == -1) { - cols = whatever.Count; - if (cols == 0) + n_cols = whatever.Count; + if (n_cols == 0) throw new Exception ("Can't have zero columns!"); - alignment = new Alignment [cols]; - for (int i = 0; i < cols; ++i) + alignment = new Alignment [n_cols]; + for (int i = 0; i < n_cols; ++i) alignment [i] = Alignment.Right; - max_length = new int [cols]; - - stringify = new Stringify [cols]; + stringify = new Stringify [n_cols]; - } else if (cols != whatever.Count) { - throw new Exception (String.Format ("Expected {0} columns, got {1}", cols, whatever.Count)); + } else if (n_cols != whatever.Count) { + throw new Exception (String.Format ("Expected {0} columns, got {1}", n_cols, whatever.Count)); } } @@ -74,24 +73,12 @@ namespace HeapBuddy { { 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) @@ -178,8 +165,8 @@ namespace HeapBuddy { private string GetColumnSeparator (int i) { - if (0 <= i && i < cols-1) - return " "; + if (0 <= i && i < n_cols-1) + return Separator; return ""; } @@ -201,33 +188,79 @@ namespace HeapBuddy { override public string ToString () { - StringBuilder sb; + int n_rows; + n_rows = rows.Count; + if (n_rows > MaxRows) + n_rows = MaxRows; + + int [] max_width; + max_width = new int [n_cols]; + + if (headers != null) + for (int i = 0; i < headers.Length; ++i) + max_width [i] = headers [i].Length; + + string [][][] grid; + grid = new string [n_rows] [][]; + + for (int r = 0; r < n_rows; ++r) { + object [] row = (object []) rows [r]; + grid [r] = new string [n_cols] []; + for (int c = 0; c < n_cols; ++c) { + string str; + str = stringify [c] != null ? stringify [c] (row [c]) : row [c].ToString (); + grid [r] [c] = str.Split ('\n'); + + foreach (string part in grid [r] [c]) + if (part.Length > max_width [c]) + max_width [c] = part.Length; + } + } + + StringBuilder sb, line; sb = new StringBuilder (); + line = 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])); + for (int i = 0; i < n_cols; ++i) { + sb.Append (Pad (max_width [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)); + for (int r = 0; r < n_rows; ++r) { + + bool did_something = true; + int i = 0; + + if (SkipLines && (r != 0 || headers != null)) + sb.Append ('\n'); + + while (did_something) { + did_something = false; + + line.Length = 0; + line.Append (GetColumnSeparator (-1)); + for (int c = 0; c < n_cols; ++c) { + string str = ""; + if (i < grid [r] [c].Length) { + str = grid [r][c][i]; + did_something = true; + } + str = Pad (max_width [c], alignment [c], str); + line.Append (str); + line.Append (GetColumnSeparator (c)); + } + + if (did_something) { + if (r != 0 || i != 0) + sb.Append ('\n'); + sb.Append (line); + } + ++i; } - ++count; - if (count >= MaxRows) - break; } return sb.ToString (); diff --git a/analyzer/Util.cs b/analyzer/Util.cs index 9f9f74d..a9abf43 100644 --- a/analyzer/Util.cs +++ b/analyzer/Util.cs @@ -34,9 +34,8 @@ namespace HeapBuddy { static public string Ellipsize (int max_length, string str) { - if (str.Length < max_length) + if (str.Length < max_length || max_length < 0) return str; - return str.Substring (0, max_length/2 - 2) + "..." + str.Substring (str.Length - max_length/2 + 2); } diff --git a/profiler/backtrace.c b/profiler/backtrace.c index 834a876..0560ea7 100644 --- a/profiler/backtrace.c +++ b/profiler/backtrace.c @@ -23,6 +23,7 @@ * USA. */ +#include #include "backtrace.h" struct HashAndCountInfo { @@ -64,7 +65,7 @@ stack_walk_build_frame_vector_fn (MonoMethod *method, gint32 native_offset, gint StackFrame *frame; frame = g_new0 (StackFrame, 1); - + frame->method = method; frame->native_offset = native_offset; frame->il_offset = il_offset; diff --git a/profiler/heap-buddy.c b/profiler/heap-buddy.c index 0ce1536..ae26dfa 100644 --- a/profiler/heap-buddy.c +++ b/profiler/heap-buddy.c @@ -44,6 +44,8 @@ struct _MonoProfiler { GHashTable *accountant_hash; gint64 total_allocated_bytes; gint64 total_live_bytes; + gint32 total_allocated_objects; + gint32 total_live_objects; gint32 n_dirty_accountants; OutfileWriter *outfile_writer; }; @@ -86,6 +88,8 @@ heap_buddy_alloc_func (MonoProfiler *p, MonoObject *obj, MonoClass *klass) accountant_register_object (acct, obj, size); p->total_allocated_bytes += size; p->total_live_bytes += size; + p->total_allocated_objects++; + p->total_live_objects++; mono_mutex_unlock (&p->lock); } @@ -98,6 +102,7 @@ post_gc_tallying_fn (gpointer key, gpointer value, gpointer user_data) accountant_post_gc_processing (acct); p->total_live_bytes += acct->n_live_bytes; + p->total_live_objects += acct->n_live_objects; if (acct->dirty) ++p->n_dirty_accountants; } @@ -120,6 +125,7 @@ static void heap_buddy_gc_func (MonoProfiler *p, MonoGCEvent e, int gen) { gint64 prev_total_live_bytes; + gint32 prev_total_live_objects; if (e != MONO_GC_EVENT_MARK_END) return; @@ -127,16 +133,24 @@ heap_buddy_gc_func (MonoProfiler *p, MonoGCEvent e, int gen) mono_mutex_lock (&p->lock); prev_total_live_bytes = p->total_live_bytes; + prev_total_live_objects = p->total_live_objects; p->total_live_bytes = 0; + p->total_live_objects = 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); + prev_total_live_bytes, + prev_total_live_objects, + 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); + outfile_writer_gc_end (p->outfile_writer, + p->total_allocated_bytes, + p->total_allocated_objects, + p->total_live_bytes, + p->total_live_objects); mono_mutex_unlock (&p->lock); } diff --git a/profiler/outfile-writer.c b/profiler/outfile-writer.c index e688a89..638298d 100644 --- a/profiler/outfile-writer.c +++ b/profiler/outfile-writer.c @@ -29,7 +29,7 @@ #include "outfile-writer.h" #define MAGIC_NUMBER 0x4eabbdd1 -#define FILE_FORMAT_VERSION 4 +#define FILE_FORMAT_VERSION 5 #define FILE_LABEL "heap-buddy logfile" #define TAG_TYPE 0x01 @@ -127,12 +127,16 @@ outfile_writer_open (const char *filename) 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 + write_int64 (ofw->out, -1); // total # of allocated bytes + write_int32 (ofw->out, -1); // total # of allocated objects return ofw; } static void -outfile_writer_update_totals (OutfileWriter *ofw) +outfile_writer_update_totals (OutfileWriter *ofw, + gint64 total_allocated_bytes, + gint32 total_allocated_objects) { // Seek back up to the right place in the header fseek (ofw->out, ofw->saved_outfile_offset, SEEK_SET); @@ -145,6 +149,11 @@ outfile_writer_update_totals (OutfileWriter *ofw) write_int32 (ofw->out, ofw->context_count); write_int32 (ofw->out, ofw->resize_count); + if (total_allocated_bytes >= 0) { + write_int64 (ofw->out, total_allocated_bytes); + write_int32 (ofw->out, total_allocated_objects); + } + // Seek back to the end of the outfile fseek (ofw->out, 0, SEEK_END); } @@ -216,7 +225,11 @@ outfile_writer_add_accountant (OutfileWriter *ofw, // 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) +outfile_writer_gc_begin (OutfileWriter *ofw, + gboolean is_final, + gint64 total_live_bytes, + gint32 total_live_objects, + gint32 n_accountants) { time_t timestamp; time (×tamp); @@ -225,6 +238,7 @@ outfile_writer_gc_begin (OutfileWriter *ofw, gboolean is_final, gint64 total_liv 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, total_live_objects); write_int32 (ofw->out, n_accountants); ++ofw->gc_count; @@ -248,15 +262,22 @@ outfile_writer_gc_log_stats (OutfileWriter *ofw, // 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) +outfile_writer_gc_end (OutfileWriter *ofw, + gint64 total_allocated_bytes, + gint32 total_allocated_objects, + gint64 total_live_bytes, + gint32 total_live_objects) { write_int64 (ofw->out, total_live_bytes); - outfile_writer_update_totals (ofw); + write_int32 (ofw->out, total_live_objects); + outfile_writer_update_totals (ofw, total_allocated_bytes, total_allocated_objects); fflush (ofw->out); } void -outfile_writer_resize (OutfileWriter *ofw, gint64 new_size, gint64 total_live_bytes) +outfile_writer_resize (OutfileWriter *ofw, + gint64 new_size, + gint64 total_live_bytes) { time_t timestamp; time (×tamp); @@ -266,7 +287,7 @@ outfile_writer_resize (OutfileWriter *ofw, gint64 new_size, gint64 total_live_by write_int64 (ofw->out, new_size); write_int64 (ofw->out, total_live_bytes); ++ofw->resize_count; - outfile_writer_update_totals (ofw); + outfile_writer_update_totals (ofw, -1, -1); fflush (ofw->out); } diff --git a/profiler/outfile-writer.h b/profiler/outfile-writer.h index 372c160..9494b40 100644 --- a/profiler/outfile-writer.h +++ b/profiler/outfile-writer.h @@ -47,11 +47,19 @@ 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_begin (OutfileWriter *ofw, + gboolean is_final, + gint64 total_live_bytes, + gint32 total_live_objects, + 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_gc_end (OutfileWriter *ofw, + gint64 total_allocated_bytes, + gint32 total_allocated_objects, + gint64 total_live_bytes, + gint32 total_live_objects); void outfile_writer_resize (OutfileWriter *ofw, gint64 new_size, gint64 total_live_bytes); diff --git a/test/Makefile.am b/test/Makefile.am index a89bd6f..0eea71b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -20,7 +20,7 @@ ALL_OUTFILES = $(ALL_CSFILES:%.cs=%.outfile) %.outfile: %.exe @echo "generating $@" - LD_LIBRARY_PATH=../profiler/.libs:$LD_LIBRARY_PATH $(RUNTIME) --profile=heap-buddy:$@ $^ + LD_LIBRARY_PATH=../profiler/.libs:$LD_LIBRARY_PATH $(RUNTIME) --debug --profile=heap-buddy:$@ $^ test: $(ALL_OUTFILES)