454 строки
10 KiB
C#
454 строки
10 KiB
C#
//
|
|
// MemlogReport.cs
|
|
// based on BacktracesReport.cs
|
|
//
|
|
|
|
//
|
|
// 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 MemlogReport : Report {
|
|
Hashtable types;
|
|
Array typeArray;
|
|
int max_rows = 10;
|
|
ulong prog_bytes = 0;
|
|
TypeZone CurrentZone;
|
|
|
|
SortedList CurrentList = null;
|
|
string CurrentPath = "/";
|
|
const string PS1 = ">> ";
|
|
|
|
enum Operand {
|
|
OP_NONE,
|
|
OP_HELP,
|
|
OP_LIST,
|
|
OP_SHOW,
|
|
OP_SELECT
|
|
}
|
|
|
|
public MemlogReport () : base ("Memlog") { }
|
|
|
|
public class TypeZone {
|
|
public uint count;
|
|
public uint bytes;
|
|
public string name;
|
|
public Hashtable callers;
|
|
|
|
public TypeZone ()
|
|
{
|
|
callers = new Hashtable ();
|
|
count = 0;
|
|
bytes = 0;
|
|
}
|
|
|
|
public TypeZone (string n)
|
|
{
|
|
name = n;
|
|
callers = new Hashtable ();
|
|
count = 0;
|
|
bytes = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make sure that all the methods in the
|
|
* call trace exist in the backtrace data
|
|
* structure. Add it if it doesn't, do
|
|
* nothing if it already exists.
|
|
*/
|
|
public void MergeBacktrace (Backtrace bt, TypeZone tz)
|
|
{
|
|
Hashtable callers = tz.callers;
|
|
|
|
foreach (Frame f in bt.Frames) {
|
|
if (f.MethodName.StartsWith ("(wrapper"))
|
|
continue;
|
|
|
|
if (!callers.ContainsKey (f.MethodName))
|
|
callers.Add (f.MethodName, new TypeZone (f.MethodName));
|
|
|
|
callers = ((TypeZone)callers[f.MethodName]).callers;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add in the number of allocations and the number
|
|
* of bytes in each method of this calltrace in the
|
|
* backtrace data structure.
|
|
*/
|
|
public void UpdateBacktrace (Backtrace bt, TypeZone tz)
|
|
{
|
|
Hashtable callers = tz.callers;
|
|
|
|
tz.count += bt.LastObjectStats.AllocatedCount;
|
|
tz.bytes += bt.LastObjectStats.AllocatedTotalBytes;
|
|
|
|
foreach (Frame f in bt.Frames) {
|
|
if (f.MethodName.StartsWith ("(wrapper"))
|
|
continue;
|
|
|
|
// Do nothing for now if we can't
|
|
// find our method, but throw a fit
|
|
if (!callers.ContainsKey (f.MethodName)) {
|
|
Console.WriteLine ("Cannot find {0}", f.MethodName);
|
|
continue;
|
|
}
|
|
|
|
((TypeZone)callers[f.MethodName]).count += bt.LastObjectStats.AllocatedCount;
|
|
((TypeZone)callers[f.MethodName]).bytes += bt.LastObjectStats.AllocatedTotalBytes;
|
|
|
|
callers = ((TypeZone)callers[f.MethodName]).callers;
|
|
}
|
|
}
|
|
|
|
public void AddBacktrace (Backtrace bt, TypeZone tz)
|
|
{
|
|
// Make sure the calls are in the data structures
|
|
MergeBacktrace (bt, tz);
|
|
|
|
// Add the new values to the calls
|
|
UpdateBacktrace (bt, tz);
|
|
}
|
|
|
|
public void GetZoneData (OutfileReader reader)
|
|
{
|
|
types = new Hashtable ();
|
|
TypeZone zone;
|
|
|
|
foreach (Backtrace bt in reader.Backtraces) {
|
|
if (types.ContainsKey (bt.Type.Name)) {
|
|
zone = (TypeZone)types[bt.Type.Name];
|
|
} else {
|
|
zone = new TypeZone (bt.Type.Name);
|
|
types.Add (bt.Type.Name, zone);
|
|
}
|
|
|
|
AddBacktrace (bt, zone);
|
|
prog_bytes += zone.bytes;
|
|
}
|
|
|
|
uint [] sizes = new uint [types.Count];
|
|
TypeZone [] list = new TypeZone [types.Count];
|
|
uint i = 0;
|
|
|
|
foreach (DictionaryEntry de in types) {
|
|
TypeZone z = (TypeZone)de.Value;
|
|
//Console.WriteLine ("{0} of type {1}", Util.PrettySize (z.bytes), z.type);
|
|
|
|
sizes[i] = z.bytes;
|
|
list[i] = z;
|
|
i++;
|
|
}
|
|
|
|
Array.Sort (sizes, list);
|
|
Array.Reverse (list);
|
|
|
|
typeArray = (Array)list.Clone ();
|
|
|
|
/*
|
|
foreach (TypeZone z in list) {
|
|
Console.WriteLine ("{0} of type {1}", Util.PrettySize (z.bytes), z.type);
|
|
PrintCallTrace (z, " ");
|
|
//if (++i > 0)
|
|
//break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Set's the path given a path builder string
|
|
*/
|
|
public void SetPath (string path) {
|
|
CurrentPath = path;
|
|
|
|
// Set the leading slash
|
|
if (!CurrentPath.StartsWith ("/"))
|
|
CurrentPath = "/" + CurrentPath;
|
|
|
|
CurrentPath = CurrentPath.Replace ("//", "/");
|
|
}
|
|
|
|
/*
|
|
* Searches the data tree and returns a
|
|
* TypeZone that meets the path's spec
|
|
*/
|
|
public TypeZone GetTypeZoneFromPath (string path)
|
|
{
|
|
string [] segments = path.Split ('/');
|
|
Hashtable ht = types;
|
|
TypeZone tz = null;
|
|
|
|
if (path == "/")
|
|
return null;
|
|
|
|
foreach (string s in segments) {
|
|
if (s == "")
|
|
continue;
|
|
|
|
tz = (TypeZone)ht[s];
|
|
if (tz == null) {
|
|
Console.WriteLine ("No Match for {0}", s);
|
|
return null;
|
|
}
|
|
|
|
ht = tz.callers;
|
|
}
|
|
|
|
CurrentPath = path;
|
|
Console.WriteLine ("{0} with {1}", tz.name, Util.PrettySize (tz.bytes));
|
|
|
|
return tz;
|
|
}
|
|
|
|
public void PrintCallTrace (TypeZone z, string indent)
|
|
{
|
|
TypeZone zone;
|
|
|
|
foreach (DictionaryEntry de in z.callers) {
|
|
zone = (TypeZone)de.Value;
|
|
|
|
Console.Write (indent);
|
|
Console.WriteLine ("{0} from {1} allocations - {2}", Util.PrettySize (zone.bytes), zone.count, de.Key);
|
|
|
|
PrintCallTrace ((TypeZone)de.Value, indent + " ");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Prints a numbered list of the
|
|
* calling functions for the given
|
|
* TypeZone
|
|
*/
|
|
public void PrintCallers (TypeZone z)
|
|
{
|
|
Table table = new Table ();
|
|
table.AddHeaders (" # ", "Size", " % ", "Method");
|
|
int i = 0;
|
|
|
|
TypeZone tz;
|
|
CurrentList = new SortedList ();
|
|
|
|
foreach (DictionaryEntry de in z.callers) {
|
|
tz = (TypeZone)de.Value;
|
|
CurrentList.Add (i, tz.name);
|
|
table.AddRow (i++ + " :", Util.PrettySize (tz.bytes), String.Format ("{0:#0.0}", (float)tz.bytes / (float)z.bytes * 100), de.Key);
|
|
}
|
|
|
|
table.Sort (2, false);
|
|
|
|
Console.WriteLine (table);
|
|
}
|
|
|
|
/*
|
|
* THE BIG LIST FUNCTION
|
|
*/
|
|
public void List () {
|
|
// Are we dealing with a method or a type?
|
|
// TODO: Add the method and type distinction...
|
|
|
|
if (CurrentPath == "/") {
|
|
ListTypes ();
|
|
return;
|
|
}
|
|
|
|
if (CurrentZone == null)
|
|
CurrentZone = GetTypeZoneFromPath (CurrentPath);
|
|
if (CurrentZone == null)
|
|
return;
|
|
|
|
ListTypeZone (CurrentZone);
|
|
}
|
|
|
|
public void ListTypeZone (TypeZone tz) {
|
|
Table table = new Table ();
|
|
int i = 0;
|
|
|
|
table.AddHeaders (" # ", "Size", "Caller");
|
|
|
|
foreach (DictionaryEntry de in tz.callers) {
|
|
TypeZone z = (TypeZone)de.Value;
|
|
|
|
if (max_rows >= 0 && i >= max_rows)
|
|
break;
|
|
|
|
table.AddRow (i++ + " :", Util.PrettySize (z.bytes), z.name);
|
|
}
|
|
|
|
Console.WriteLine (table);
|
|
}
|
|
|
|
public void ListTypes ()
|
|
{
|
|
Table table = new Table ();
|
|
int i = 0;
|
|
|
|
table.AddHeaders (" # ", "Size", " % ", "Type");
|
|
|
|
foreach (TypeZone z in typeArray) {
|
|
if (max_rows >= 0 && i >= max_rows)
|
|
break;
|
|
|
|
table.AddRow (i++ + " :", Util.PrettySize (z.bytes), String.Format ("{0:#0.0}", (float)z.bytes / (float)prog_bytes * 100), z.name);
|
|
}
|
|
|
|
Console.WriteLine (table);
|
|
}
|
|
|
|
public void ShowType (string f)
|
|
{
|
|
if (!types.ContainsKey (f))
|
|
return;
|
|
|
|
TypeZone t = (TypeZone)types[f];
|
|
|
|
Console.WriteLine ("{0} of {1}", Util.PrettySize (t.bytes), t.name);
|
|
PrintCallers (t);
|
|
//PrintCallTrace (t, " ");
|
|
}
|
|
|
|
public void ShowPath () {
|
|
Console.WriteLine (CurrentPath);
|
|
}
|
|
|
|
public void PrintHelpInfo ()
|
|
{
|
|
Console.WriteLine ("Memlog commands:");
|
|
Console.WriteLine (" list: list the items in the current path");
|
|
Console.WriteLine (" show: preview the items in a listed item");
|
|
Console.WriteLine (" help: show this screen");
|
|
Console.WriteLine (" quit: quit");
|
|
}
|
|
|
|
override public void Run (OutfileReader reader, string [] args)
|
|
{
|
|
string cmd = "";
|
|
string [] cmds;
|
|
|
|
GetZoneData (reader);
|
|
|
|
PrintHelpInfo ();
|
|
|
|
while (String.Compare (cmd, "quit") != 0 && String.Compare (cmd, "q") != 0) {
|
|
Console.Write ("{0}", PS1);
|
|
|
|
cmd = Console.ReadLine ();
|
|
if (cmd == null)
|
|
cmd = "q";
|
|
if (cmd == "")
|
|
continue;
|
|
|
|
Console.WriteLine ();
|
|
int val = new int ();
|
|
int n = new int ();
|
|
|
|
cmds = cmd.Split (null);
|
|
Operand op = Operand.OP_NONE;
|
|
|
|
int i = 0;
|
|
max_rows = 10;
|
|
while (i < cmds.Length) {
|
|
string arg = cmds [i].ToLower ();
|
|
|
|
if (arg == "list" || arg == "lsit")
|
|
op = Operand.OP_LIST;
|
|
|
|
else if (arg == "path" || arg == "pth") {
|
|
if (i + 1 >= cmds.Length)
|
|
ShowPath ();
|
|
else {
|
|
if (cmds[i+1].Split ('/').Length == 1 && !cmds[i+1].StartsWith ("/"))
|
|
cmds[i+1] = CurrentPath + "/" + cmds[i+1];
|
|
|
|
CurrentZone = GetTypeZoneFromPath (cmds[++i]);
|
|
SetPath (cmds[i]);
|
|
}
|
|
}
|
|
|
|
else if (arg == "all")
|
|
max_rows = -1;
|
|
|
|
else if (arg == "show" || arg == "shwo" || arg == "shw") {
|
|
if (cmds[i+1] == "path") {
|
|
ShowPath ();
|
|
i++;
|
|
} else
|
|
op = Operand.OP_SHOW;
|
|
}
|
|
else if (arg == "select" || arg == "slct" || arg == "slc" || arg == "slect")
|
|
op = Operand.OP_SELECT;
|
|
|
|
else {
|
|
n = -1;
|
|
try {
|
|
n = Int32.Parse (arg);
|
|
} catch { }
|
|
if (n > 0)
|
|
val = n;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
switch (op) {
|
|
|
|
case Operand.OP_LIST:
|
|
if (n > 0)
|
|
max_rows = val;
|
|
List ();
|
|
break;
|
|
|
|
case Operand.OP_SHOW:
|
|
if (val > typeArray.Length || val < 0)
|
|
break;
|
|
if (max_rows == -1) {
|
|
foreach (TypeZone t in typeArray) {
|
|
ShowType (t.name);
|
|
}
|
|
} else {
|
|
ShowType (((TypeZone)typeArray.GetValue(val)).name);
|
|
}
|
|
break;
|
|
|
|
case Operand.OP_SELECT:
|
|
if (val > CurrentList.Count || val < 0)
|
|
break;
|
|
SetPath (CurrentPath + "/" + CurrentList[val]);
|
|
Console.WriteLine (CurrentPath);
|
|
break;
|
|
|
|
case Operand.OP_HELP:
|
|
PrintHelpInfo ();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|