public void DumpToXml()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// assembles contain all referenced system assemblies, as well as UnityEngine, UnityEditor and editor
// code. To make root list smaller, we only select main game assembly from the list, although it might
// concievably miss some statics
var gameAssembly = assemblies.Single( a => a.FullName.Contains( "Assembly-CSharp," ) );
var allTypes = gameAssembly.GetTypes();
var allScripts = UnityEngine.Object.FindObjectsOfType( typeof( MonoBehaviour ) );
seenObjects.Clear(); // used to prevent going through same object twice
using( writer = new StreamWriter( "heapdump.xml" ) )
{
writer.WriteLine( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" );
int totalSize = 0;
writer.WriteLine( "<statics>" );
// enumerate all static fields
foreach( var type in allTypes )
{
bool tagWritten = false;
if(!SkipEmptyTypes)
writer.WriteLine( " <type name=\"{0}\">", SecurityElement.Escape( type.GetFormattedName() ) );
if(type.IsEnum)
{
// enums don't hold anything but their constants
if( !SkipEmptyTypes )
writer.WriteLine( "<ignored reason=\"IsEnum\"/>" );
}
else if(type.IsGenericType)
{
// generic types are ignored, because we can't access static fields unless we
// know actual type parameters of the class containing generics, and we have no way
// of knowing these - they depend on what concrete type were ever instantiated.
// This may miss a lot of stuff if generics are heavily used.
if( !SkipEmptyTypes )
writer.WriteLine( "<ignored reason=\"IsGenericType\"/>" );
}
else
{
foreach( var fieldInfo in type.GetFields( BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic ) )
{
try
{
if(SkipEmptyTypes && !tagWritten)
{
writer.WriteLine( " <type name=\"{0}\">", SecurityElement.Escape( type.GetFormattedName() ) );
tagWritten = true;
}
int size = ReportField( null, " ", fieldInfo );
totalSize += size;
}
catch( Exception ex )
{
writer.WriteLine( "Exception: " + ex.Message + " on " + fieldInfo.FieldType.GetFormattedName() + " " +
fieldInfo.Name );
}
}
}
if( !SkipEmptyTypes || tagWritten )
writer.WriteLine( " </type>" );
}
writer.WriteLine( "</statics>" );
// enumerate all MonoBehaviours - that is, all user scripts on all existing objects.
// TODO this maybe misses objects with active==false.
writer.WriteLine( "<scripts>" );
foreach( MonoBehaviour mb in allScripts )
{
writer.WriteLine( " <object type=\"{0}\" name=\"{1}\">", SecurityElement.Escape( mb.GetType().GetFormattedName() ), SecurityElement.Escape( mb.name ) );
var type = mb.GetType();
foreach( var fieldInfo in type.EnumerateAllFields() )
{
try
{
int size = ReportField( mb, " ", fieldInfo );
totalSize += size;
}
catch( Exception ex )
{
writer.WriteLine( "Exception: " + ex.Message + " on " + fieldInfo.FieldType.GetFormattedName() + " " +
fieldInfo.Name );
}
}
writer.WriteLine( " </object>" );
}
writer.WriteLine( "</scripts>" );
// writer.WriteLine( "Total size: " + totalSize );
}
Debug.Log( "OK" );
}