1184 строки
32 KiB
C#
1184 строки
32 KiB
C#
//
|
|
// 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 = 6;
|
|
const int expected_summary_version = 3;
|
|
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;
|
|
|
|
public long TotalAllocatedBytes;
|
|
public int TotalAllocatedObjects;
|
|
|
|
// 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 string Arguments;
|
|
public long Position; // of the name in the summary file
|
|
}
|
|
|
|
private struct RawGcData {
|
|
public uint BacktraceCode;
|
|
public ObjectStats ObjectStats;
|
|
}
|
|
|
|
string filename;
|
|
|
|
Type [] types;
|
|
Method [] methods;
|
|
Backtrace [] backtraces;
|
|
long [] backtrace_pos;
|
|
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;
|
|
uint [] backtrace_type_codes;
|
|
RawGcData [] [] raw_gc_data;
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
public OutfileReader (string filename)
|
|
{
|
|
this.filename = 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];
|
|
backtrace_type_codes = new uint [n_backtraces];
|
|
raw_gc_data = new RawGcData [n_gcs] [];
|
|
|
|
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);
|
|
|
|
// Fix up the method names
|
|
for (int i = 0; i < methods.Length; ++i) {
|
|
int j = methods [i].Name.IndexOf (" (");
|
|
methods [i].Arguments = methods [i].Name.Substring (j+1);
|
|
methods [i].Name = methods [i].Name.Substring (0, j);
|
|
}
|
|
|
|
// Put the right method names in the frames
|
|
for (int i = 0; i < backtraces.Length; ++i)
|
|
for (int j = 0; j < backtraces [i].Frames.Length; ++j)
|
|
GetMethod (backtraces [i].Frames [j].MethodCode,
|
|
out backtraces [i].Frames [j].MethodName,
|
|
out backtraces [i].Frames [j].MethodArguments);
|
|
|
|
// Re-open the file for use as a lazy reader.
|
|
stream = new FileStream (filename, FileMode.Open, FileAccess.Read);
|
|
lazy_reader = new BinaryReader (stream);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
public string Filename {
|
|
get { return filename; }
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
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) {
|
|
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 ();
|
|
|
|
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 ();
|
|
|
|
TotalAllocatedBytes = reader.ReadInt64 ();
|
|
TotalAllocatedObjects = 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];
|
|
backtrace_pos = new long [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);
|
|
writer.Write (TotalAllocatedBytes);
|
|
writer.Write (TotalAllocatedObjects);
|
|
|
|
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 ();
|
|
|
|
Backtrace backtrace;
|
|
backtrace = new Backtrace (code, this);
|
|
|
|
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;
|
|
}
|
|
|
|
backtraces [i_backtrace] = backtrace;
|
|
|
|
backtrace_codes [i_backtrace] = code;
|
|
backtrace_type_codes [i_backtrace] = type_code;
|
|
|
|
Frame [] frames = new Frame [n_frames];
|
|
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 (this);
|
|
|
|
gc.Generation = reader.ReadInt32 ();
|
|
gc.TimeT = reader.ReadInt64 ();
|
|
gc.UTimeT = reader.ReadInt64 ();
|
|
gc.Timestamp = Util.ConvertTimeT (gc.TimeT);
|
|
gc.PreGcLiveBytes = reader.ReadInt64 ();
|
|
gc.PreGcLiveObjects = reader.ReadInt32 ();
|
|
|
|
int n;
|
|
n = reader.ReadInt32 ();
|
|
|
|
RawGcData [] raw;
|
|
raw = new RawGcData [n];
|
|
for (int i = 0; i < n; ++i) {
|
|
raw [i].BacktraceCode = reader.ReadUInt32 ();
|
|
raw [i].ObjectStats.Read (reader);
|
|
}
|
|
combsort_raw_gc_data (raw);
|
|
|
|
gc.PostGcLiveBytes = reader.ReadInt64 ();
|
|
gc.PostGcLiveObjects = reader.ReadInt32 ();
|
|
|
|
gcs [i_gc] = gc;
|
|
raw_gc_data [i_gc] = raw;
|
|
++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;
|
|
|
|
tmp_code = backtrace_type_codes [i];
|
|
backtrace_type_codes [i] = backtrace_type_codes [j];
|
|
backtrace_type_codes [j] = tmp_code;
|
|
|
|
long tmp_pos;
|
|
tmp_pos = backtrace_pos [i];
|
|
backtrace_pos [i] = backtrace_pos [j];
|
|
backtrace_pos [j] = tmp_pos;
|
|
|
|
Backtrace tmp;
|
|
tmp = backtraces [i];
|
|
backtraces [i] = backtraces [j];
|
|
backtraces [j] = tmp;
|
|
|
|
swapped = true;
|
|
}
|
|
}
|
|
if (gap == 1 && !swapped)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void combsort_raw_gc_data (RawGcData [] data)
|
|
{
|
|
int start = 0;
|
|
int size = 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 (data [i].BacktraceCode > data [j].BacktraceCode) {
|
|
RawGcData tmp;
|
|
tmp = data [i];
|
|
data [i] = data [j];
|
|
data [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 < raw_gc_data [i].Length; ++j) {
|
|
uint code;
|
|
code = raw_gc_data [i] [j].BacktraceCode;
|
|
code = TranslateBacktraceCode (code);
|
|
raw_gc_data [i] [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) {
|
|
backtrace_type_codes [i] = TranslateTypeCode (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;
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
|
|
// Populate the backtrace types and codes
|
|
for (int i = 0; i < backtraces.Length; ++i) {
|
|
backtraces [i].Code = TranslateBacktraceCode (backtraces [i].Code);
|
|
backtraces [i].Type = types [backtrace_type_codes [i]];
|
|
}
|
|
|
|
// 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 < raw_gc_data [i].Length; ++j) {
|
|
RawGcData raw;
|
|
raw = raw_gc_data [i] [j];
|
|
|
|
uint bt_code;
|
|
bt_code = raw.BacktraceCode;
|
|
if (backtraces [bt_code].LastGeneration == int.MaxValue) {
|
|
backtraces [bt_code].LastGeneration = gcs [i].Generation;
|
|
backtraces [bt_code].LastObjectStats = raw.ObjectStats;
|
|
--count;
|
|
|
|
// Add this backtrace to our per-type totals
|
|
uint type_code;
|
|
type_code = backtrace_type_codes [bt_code];
|
|
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 ReadSummary_TableOfContents (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 WriteSummary_TableOfContents (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)
|
|
{
|
|
ReadSummary_TableOfContents (reader);
|
|
ReadSummary_Methods (reader);
|
|
ReadSummary_Types (reader);
|
|
ReadSummary_Backtraces (reader);
|
|
ReadSummary_Resizes (reader);
|
|
ReadSummary_Gcs (reader);
|
|
}
|
|
|
|
private void ReadSummary_Methods (BinaryReader reader)
|
|
{
|
|
reader.BaseStream.Seek (methods_by_code_offset, SeekOrigin.Begin);
|
|
for (int i = 0; i < methods.Length; ++i)
|
|
methods [i].Position = reader.ReadInt64 ();
|
|
|
|
}
|
|
|
|
private void ReadSummary_Types (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 ReadSummary_Backtraces (BinaryReader reader)
|
|
{
|
|
reader.BaseStream.Seek (backtrace_index_offset, SeekOrigin.Begin);
|
|
for (int i = 0; i < backtraces.Length; ++i) {
|
|
Backtrace backtrace;
|
|
backtrace = new Backtrace ((uint) i, this);
|
|
backtraces [i] = backtrace;
|
|
|
|
uint type_code;
|
|
type_code = reader.ReadUInt32 ();
|
|
backtrace.Type = types [type_code];
|
|
backtrace.LastGeneration = reader.ReadInt32 ();
|
|
backtrace.LastObjectStats.Read (reader);
|
|
backtrace_pos [i] = reader.ReadInt64 ();
|
|
}
|
|
|
|
}
|
|
|
|
private void ReadSummary_Resizes (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 ReadSummary_Gcs (BinaryReader reader)
|
|
{
|
|
reader.BaseStream.Seek (gc_index_offset, SeekOrigin.Begin);
|
|
for (int i = 0; i < gcs.Length; ++i) {
|
|
Gc gc;
|
|
gc = new Gc (this);
|
|
|
|
gc.Generation = reader.ReadInt32 ();
|
|
gc.TimeT = reader.ReadInt64 ();
|
|
gc.UTimeT = 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 ();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Summary file writer
|
|
//
|
|
|
|
private void WriteSummaryFile (BinaryWriter writer)
|
|
{
|
|
WritePreamble (writer);
|
|
WriteHeader (writer);
|
|
|
|
long toc_offset;
|
|
toc_offset = writer.BaseStream.Position;
|
|
WriteSummary_TableOfContents (writer); // writes placeholder data
|
|
|
|
WriteSummary_Data (writer);
|
|
|
|
WriteSummary_Indexes (writer);
|
|
|
|
WriteSummary_Types (writer);
|
|
|
|
writer.BaseStream.Seek (toc_offset, SeekOrigin.Begin);
|
|
WriteSummary_TableOfContents (writer); // writes the actual data
|
|
}
|
|
|
|
private void WriteSummary_Data (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) {
|
|
backtrace_pos [i] = 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;
|
|
|
|
writer.Write (raw_gc_data [i].Length);
|
|
for (int j = 0; j < raw_gc_data [i].Length; ++j) {
|
|
writer.Write (raw_gc_data [i] [j].BacktraceCode);
|
|
raw_gc_data [i] [j].ObjectStats.Write (writer);
|
|
}
|
|
}
|
|
raw_gc_data = null; // We don't need these anymore
|
|
|
|
|
|
// 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 WriteSummary_Indexes (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 (backtrace_type_codes [i]);
|
|
writer.Write (backtraces [i].LastGeneration);
|
|
backtraces [i].LastObjectStats.Write (writer);
|
|
writer.Write (backtrace_pos [i]);
|
|
}
|
|
|
|
gc_index_offset = writer.BaseStream.Position;
|
|
for (int i = 0; i < gcs.Length; ++i) {
|
|
writer.Write (gcs [i].Generation);
|
|
writer.Write (gcs [i].TimeT);
|
|
writer.Write (gcs [i].UTimeT);
|
|
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]);
|
|
}
|
|
}
|
|
|
|
private void WriteSummary_Types (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 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; }
|
|
}
|
|
|
|
public Type [] Types {
|
|
get { return types; }
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
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);
|
|
|
|
string method;
|
|
method = lazy_reader.ReadString ();
|
|
|
|
int i = method.IndexOf (" (");
|
|
methods [code].Name = method.Substring (0, i);
|
|
methods [code].Arguments = method.Substring (i+1);
|
|
}
|
|
|
|
name = methods [code].Name;
|
|
args = methods [code].Arguments;
|
|
}
|
|
|
|
public Frame [] GetFrames (uint backtrace_code)
|
|
{
|
|
lazy_reader.BaseStream.Seek (backtrace_pos [backtrace_code], 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 ();
|
|
}
|
|
|
|
for (int i = 0; i < length; ++i)
|
|
GetMethod (frames [i].MethodCode,
|
|
out frames [i].MethodName,
|
|
out frames [i].MethodArguments);
|
|
|
|
|
|
return frames;
|
|
}
|
|
|
|
public GcData [] GetGcData (int generation)
|
|
{
|
|
try {
|
|
lazy_reader.BaseStream.Seek (gc_pos [generation], SeekOrigin.Begin);
|
|
} catch (Exception e) { }
|
|
|
|
int length;
|
|
length = lazy_reader.ReadInt32 ();
|
|
|
|
GcData [] gc_data;
|
|
gc_data = new GcData [length];
|
|
for (int i = 0; i < length; ++i) {
|
|
uint bt_code;
|
|
bt_code = lazy_reader.ReadUInt32 ();
|
|
gc_data [i].Backtrace = backtraces [bt_code];
|
|
gc_data [i].ObjectStats.Read (lazy_reader);
|
|
}
|
|
|
|
return gc_data;
|
|
}
|
|
}
|
|
}
|