Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
// logdump.cs -- a little utility program that dumps summary statistics for CLR profiler logs
// © 2005 Microsoft Corporation
// csc /o+ logdump.cs is all you need to do to build it.
// this program is offered as is with no warranty implied and confers no rights .
using System;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
class LogDumper
{
// this tells us the current number of allocations and total bytes at a specific stack for a specific type
class MemCharge
{
public int count;
public int bytes;
};
// this is used to help us find the top N of any kind of id by size
struct TopCalc
{
public int id;
public int bytes;
}
// the log file we will be reading
private String strLogFile = null;
// the filter file if we are going to only analyze specific types
private String strFilterFile = null;
// the number of stacks and types we will be dumping
private int cTop = 10;
private TopCalc[] topStacks = null;
private TopCalc[] topTypes = null;
private int bytesTotal = 0;
private int allocsTotal = 0;
private Char[] space = { ' ' };
// storage is in these dictionaries
private Dictionary<String, bool> dictTypenameFilterSet = null;
private Dictionary<String, int> dictFuncNameToFuncId = new Dictionary<String, int>();
private Dictionary<int, String> dictFuncIdToFuncName = new Dictionary<int, String>();
private Dictionary<String, int> dictTypeNameToTypeId = new Dictionary<String, int>();
private Dictionary<int, String> dictTypeIdToTypeName = new Dictionary<int, String>();
private Dictionary<int, String> dictStackIdToStackstring = new Dictionary<int, String>();
private Dictionary<String, int> dictStackstringToStackId = new Dictionary<String, int>();
private Dictionary<int, Dictionary<int, MemCharge>> dictStackIdToCharges =
new Dictionary<int, Dictionary<int, MemCharge>>();
private Dictionary<int, int> dictStackIdToTypeId = new Dictionary<int, int>();
private Dictionary<int, int> dictStackIdToSize = new Dictionary<int, int>();
private Dictionary<int, MemCharge> dictTypeIdToCharges = new Dictionary<int, MemCharge>();
public static void Main(String[] args)
{
new LogDumper().InitializeAndRun(args);
}
private void InitializeAndRun(String[] args)
{
if (args.Length == 0)
goto UsageMessage;
for (int iarg = 0; iarg < args.Length; iarg++)
{
if (args[iarg].Length >= 2 && args[iarg][0] == '-' || args[iarg][0] == '/')
{
switch (args[iarg][1])
{
case 'n':
if (iarg + 1 >= args.Length)
goto UsageMessage;
iarg++;
cTop = Int32.Parse(args[iarg]);
break;
case 'f':
if (iarg + 1 >= args.Length)
goto UsageMessage;
iarg++;
strFilterFile = args[iarg];
break;
default:
goto UsageMessage;
}
continue;
}
strLogFile = args[iarg];
break;
}
// establish the filter set if there is to be one
if (strFilterFile != null)
{
ReadFilterStream(strFilterFile);
}
if (strLogFile == null)
goto UsageMessage;
// read the "new" file building up the full cost of the allocs
ReadLogFile(strLogFile);
WriteSummary();
return;
UsageMessage:
Console.WriteLine("Usage: logdump [-n num] [-f filter] file");
Console.WriteLine("-f filter : filter file indicates types to consider");
Console.WriteLine("-n num : top num types and stacks displayed in summary");
Console.WriteLine("file : the input file generated by clrprofiler for beta2 or later");
return;
}
private void ReadLogFile(String strFile)
{
String line;
int id, code;
String s;
int typeid = -1;
int typesize = -1;
int referredStackId = -1;
String[] a = null;
Char[] space = { ' ' };
using (StreamReader stmIn = new StreamReader(strFile))
{
while ((line = stmIn.ReadLine()) != null)
{
switch (line[0])
{
// defines a function
case 'f':
// we'll use the trailing ')' character to find the full function name definition
{
int i1, i2, i3;
i1 = line.IndexOf(' ');
if (i1 < 0) break;
i1++;
i2 = line.IndexOf(' ', i1);
if (i2 < 0) break;
id = Int32.Parse(line.Substring(i1, i2 - i1 + 1));
i2++;
i3 = line.IndexOf(')', i2);
if (i3 < 0) break;
s = line.Substring(i2, i3 - i2 + 1);
}
// function names are not unique so we have to de-dupe, we'll use the first one
// for the canonical mapping
if (!dictFuncNameToFuncId.ContainsKey(s))
dictFuncNameToFuncId.Add(s, id);
if (!dictFuncIdToFuncName.ContainsKey(id))
{
dictFuncIdToFuncName.Add(id, s);
}
else
{
Console.Error.WriteLine("Warning: function id {0} duplicated", id);
Console.Error.WriteLine("Warning: 1st def: {0}", dictFuncIdToFuncName[id]);
Console.Error.WriteLine("Warning: 2nd def: {0}", s);
Console.Error.WriteLine();
}
break;
// an allocation
case '!':
a = line.Split(space);
if (a.Length != 4)
{
Console.Error.WriteLine("Corrupt allocation line: {0}", line);
break;
}
ProcessAlloc(Int32.Parse(a[3]));
break;
// a new stack
case 'n':
a = line.Split(space);
id = Int32.Parse(a[1]);
code = Int32.Parse(a[2]);
int bit0 = code & 1;
code >>= 1;
int bit1 = code & 1;
code >>= 1;
int next = 3;
// indicates type and size of allocation present
if (bit0 != 0)
{
typeid = Int32.Parse(a[next]);
typesize = Int32.Parse(a[next + 1]);
next += 2;
}
referredStackId = -1;
if (code != 0)
{
referredStackId = Int32.Parse(a[next]);
next++;
}
// we're going to make the full stack string for the stack
// including any part which was done by reference to a previous stack
StringBuilder sb = new StringBuilder();
if (referredStackId != -1)
{
String t = dictStackIdToStackstring[referredStackId];
int cch = 0;
for (int i = 0; i < code; i++)
{
cch = t.IndexOf(' ', cch + 1);
if (cch < 0)
{
cch = t.Length;
break;
}
}
if (cch == t.Length)
sb.Append(t);
else
sb.Append(t, 0, cch);
}
while (next < a.Length)
{
if (sb.Length != 0)
sb.Append(" ");
int funcId = Int32.Parse(a[next++]);
funcId = dictFuncNameToFuncId[dictFuncIdToFuncName[funcId]];
sb.Append(funcId.ToString());
}
s = sb.ToString();
dictStackIdToStackstring.Add(id, s);
if (bit0 != 0)
{
dictStackIdToTypeId.Add(id, typeid);
dictStackIdToSize.Add(id, typesize);
}
break;
// type definition
case 't':
{
int i1, i2;
i1 = line.IndexOf(' ');
if (i1 < 0) break;
i1++;
i2 = line.IndexOf(' ', i1);
if (i2 < 0) break;
id = Int32.Parse(line.Substring(i1, i2 - i1 + 1));
i2++;
if (i2 + 1 >= line.Length)
break;
// try to be compatible with formats with an extra number before the type or not
while (line[i2] >= '0' && line[i2] <= '9')
{
i2 = line.IndexOf(' ', i2);
i2++;
if (i2 + 1 >= line.Length)
break;
}
s = line.Substring(i2);
if (dictTypenameFilterSet != null && !dictTypenameFilterSet.ContainsKey(s))
break;
if (!dictTypeNameToTypeId.ContainsKey(s))
dictTypeNameToTypeId.Add(s, id);
dictTypeIdToTypeName.Add(id, s);
}
break;
default:
// the rest we can ignore for this dumper
break;
}
}
}
}
private void WriteSummary()
{
Dictionary<int, MemCharge> dictTypeIdToCharges = new Dictionary<int, MemCharge>();
topStacks = new TopCalc[cTop];
topTypes = new TopCalc[cTop];
Console.WriteLine("This report shows allocations in {0}", strLogFile);
if (strFilterFile != null)
{
Console.WriteLine("This report only considers object types found in {0}", strFilterFile);
}
Array.Clear(topStacks, 0, cTop);
Array.Clear(topTypes, 0, cTop);
ComputeTopStacks();
ComputeTopTypes();
Console.WriteLine();
Console.WriteLine("Total Allocations {0} Objects {1} Bytes", allocsTotal, bytesTotal);
Console.WriteLine();
PrintTypes();
PrintStacks();
}
private void ComputeTopStacks()
{
foreach (KeyValuePair<int, Dictionary<int, MemCharge>> kvStack in dictStackIdToCharges)
{
Dictionary<int, MemCharge> d = kvStack.Value;
int stackbytes = 0;
foreach (KeyValuePair<int, MemCharge> kv in d)
{
if (kv.Value.count == 0 || kv.Value.bytes == 0)
continue;
bytesTotal += kv.Value.bytes;
allocsTotal += kv.Value.count;
stackbytes += kv.Value.bytes;
MemCharge charge = null;
if (dictTypeIdToCharges.ContainsKey(kv.Key))
{
charge = dictTypeIdToCharges[kv.Key];
charge.bytes += kv.Value.bytes;
charge.count += kv.Value.count;
}
else
{
charge = new MemCharge();
dictTypeIdToCharges.Add(kv.Key, charge);
charge.bytes = kv.Value.bytes;
charge.count = kv.Value.count;
}
}
int i;
for (i = cTop; --i >= 0; )
{
if (topStacks[i].bytes > stackbytes)
break;
}
int iNew = i + 1;
if (iNew < cTop)
{
for (i = cTop - 1; --i >= iNew; )
{
topStacks[i + 1] = topStacks[i];
}
topStacks[iNew].bytes = stackbytes;
topStacks[iNew].id = kvStack.Key;
}
}
}
private void ComputeTopTypes()
{
foreach (KeyValuePair<int, MemCharge> tmem in dictTypeIdToCharges)
{
int i;
for (i = cTop; --i >= 0; )
{
if (topTypes[i].bytes > tmem.Value.bytes)
break;
}
int iNew = i + 1;
if (iNew < cTop)
{
for (i = cTop - 1; --i >= iNew; )
{
topTypes[i + 1] = topTypes[i];
}
topTypes[iNew].bytes = tmem.Value.bytes;
topTypes[iNew].id = tmem.Key;
}
}
}
private void PrintTypes()
{
Console.WriteLine("Top {0} Allocated Types", cTop);
Console.WriteLine();
Console.WriteLine("{0,8:s} {1,8:s} {2}", "Count", "Bytes", "Type");
for (int i = 0; i < cTop; i++)
{
int id = topTypes[i].id;
if (id == 0)
break;
MemCharge charge = dictTypeIdToCharges[id];
Console.WriteLine("{0,8:d} {1,8:d} {2}", charge.count, charge.bytes, dictTypeIdToTypeName[id]);
}
}
private void PrintStacks()
{
Console.WriteLine();
Console.WriteLine("Top {0} Allocating Stacks", cTop);
for (int i = 0; i < cTop; i++)
{
int id = topStacks[i].id;
if (id == 0 || topStacks[i].bytes == 0)
break;
Console.WriteLine();
Console.WriteLine("Stack {0} allocates {1} bytes", i + 1, topStacks[i].bytes);
String[] a = dictStackIdToStackstring[id].Split(space);
for (int j = 0; j < a.Length; j++)
{
if (a[j].Length == 0)
break;
int fid = Int32.Parse(a[j]);
Console.WriteLine("{0}", dictFuncIdToFuncName[fid]);
}
Dictionary<int, MemCharge> d = dictStackIdToCharges[id];
foreach (KeyValuePair<int, MemCharge> kv in d)
{
if (kv.Value.count == 0 || kv.Value.bytes == 0)
continue;
Console.WriteLine("{0,8:d} {1,8:d} {2}", kv.Value.count, kv.Value.bytes, dictTypeIdToTypeName[kv.Key]);
}
}
}
private void ReadFilterStream(String ffilter)
{
using (StreamReader sr = new StreamReader(ffilter))
{
dictTypenameFilterSet = new Dictionary<String, bool>();
String line;
while ((line = sr.ReadLine()) != null)
{
dictTypenameFilterSet.Add(line, true);
}
}
}
private void ProcessAlloc(int stackid)
{
int typeid = dictStackIdToTypeId[stackid];
int size = dictStackIdToSize[stackid];
String s;
if (!dictStackIdToStackstring.ContainsKey(stackid))
return;
s = dictStackIdToStackstring[stackid];
// We want to report the results as though there was one unique
// stackids for each callstack. So we choose a standard stack
// to charge here. The other stacks will have no allocations
if (dictStackstringToStackId.ContainsKey(s))
stackid = dictStackstringToStackId[s];
else
dictStackstringToStackId.Add(s, stackid);
// don't count allocations against types we filtered out
if (!dictTypeIdToTypeName.ContainsKey(typeid))
return;
s = dictTypeIdToTypeName[typeid];
if (!dictTypeNameToTypeId.ContainsKey(s))
return;
typeid = dictTypeNameToTypeId[s];
if (!dictStackIdToCharges.ContainsKey(stackid))
dictStackIdToCharges.Add(stackid, new Dictionary<int, MemCharge>());
Dictionary<int, MemCharge> dictCharges = dictStackIdToCharges[stackid];
MemCharge charge = null;
if (dictCharges.ContainsKey(typeid))
{
charge = dictCharges[typeid];
charge.count++;
charge.bytes += size;
}
else
{
charge = new MemCharge();
charge.count = 1;
charge.bytes = size;
dictCharges.Add(typeid, charge);
}
}
}
Comments
- Anonymous
August 08, 2005
Ever wonder how I produce nice textual allocation summaries like this one?
This report shows allocations... - Anonymous
August 08, 2005
Oops. Thing like the following entries won't compile here...
private Dictionary<String, bool> dictTypenameFilterSet
^^^^^^^^^^^^^^ - Anonymous
August 09, 2005
You'll need the Whidbey beta to compile this -- it's using generics Dictionaries for the main storage.