Lots of hacking: file format tweaks, new reports,
better formatting, saner behavior, bug fixes, etc. etc. svn path=/trunk/heap-buddy/; revision=51500
This commit is contained in:
Родитель
4a454b5010
Коммит
d293ab81e0
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ namespace HeapBuddy {
|
|||
|
||||
public uint MethodCode;
|
||||
public string MethodName;
|
||||
public string MethodArguments;
|
||||
public uint IlOffset;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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,
|
||||
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));
|
||||
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),
|
||||
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),
|
||||
gc.FreedPercentage);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ TARGET = HeapBuddy.exe
|
|||
WRAPPER = heap-buddy
|
||||
|
||||
REPORT_CSFILES = \
|
||||
BacktracesReport.cs \
|
||||
HistoryReport.cs \
|
||||
SummaryReport.cs \
|
||||
TypesReport.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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 [n_cols];
|
||||
|
||||
stringify = new Stringify [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)
|
||||
for (int r = 0; r < n_rows; ++r) {
|
||||
|
||||
bool did_something = true;
|
||||
int i = 0;
|
||||
|
||||
if (SkipLines && (r != 0 || headers != null))
|
||||
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));
|
||||
|
||||
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 ();
|
||||
|
|
|
@ -34,10 +34,9 @@ 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* USA.
|
||||
*/
|
||||
|
||||
#include <mono/metadata/mono-debug.h>
|
||||
#include "backtrace.h"
|
||||
|
||||
struct HashAndCountInfo {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче