<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

namespace UnityHeapEx
{
    public class HeapDump
    {
        [MenuItem( "Tools/Memory/HeapDump" )]
        public static void DoStuff()
        {
            var h = new HeapDump();
            h.DumpToXml();
        }

        private readonly HashSet<object> seenObjects = new HashSet<object>();
        private StreamWriter writer;

        /* TODO
         * ignore cached delegates for lambdas(?)
         * better type names
         * deal with arrays of arrays
         * maybe special code for delegates? coroutines?
         */
		
		/// <summary>
		/// Collect all roots, i.e. static fields in all classes and all scripts; then dump all object 
		/// hierarchy reachable from these roots to file
		/// </summary>
        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 )
                {
                    writer.WriteLine( "  <type name=\"{0}\">", SecurityElement.Escape( type.GetFormattedName() ) );
                    if(type.IsEnum)
                    {
						// enums don't hold anything but their constants
                        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.
                        writer.WriteLine( "<ignored reason=\"IsGenericType\"/>" );
                    }
                    else
                    {
                        foreach( var fieldInfo in type.GetFields( BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic ) )
                        {
                            try
                            {
                                int size = ReportField( null, "    ", fieldInfo );
                                totalSize += size;
                            }
                            catch( Exception ex )
                            {
                                writer.WriteLine( "Exception: " + ex.Message + " on " + fieldInfo.FieldType.GetFormattedName() + " " +
                                                  fieldInfo.Name );
                            }
                        }
                    }
                    writer.WriteLine( "  </type>" );
                }<commit_msg>Added skipping of empty types<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

namespace UnityHeapEx
{
    public class HeapDump
    {
        [MenuItem( "Tools/Memory/HeapDump" )]
        public static void DoStuff()
        {
            var h = new HeapDump();
            h.DumpToXml();
        }

        private readonly HashSet<object> seenObjects = new HashSet<object>();
        private StreamWriter writer;

        // when true, types w/o any static field (and skipped types) are removed from output
        public static bool SkipEmptyTypes = true;

        /* TODO
         * ignore cached delegates for lambdas(?)
         * better type names
         * deal with arrays of arrays
         * maybe special code for delegates? coroutines?
         */
		
		/// <summary>
		/// Collect all roots, i.e. static fields in all classes and all scripts; then dump all object 
		/// hierarchy reachable from these roots to file
		/// </summary>
        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>" );
                }
{'fraction_non_alphanumeric': 0.08702550958134993, 'fraction_numerical': 0.0013426095447333088, 'mean_word_length': 1.412128348542832, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 2, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2580737', 'n_tokens_mistral': 2028, 'n_tokens_neox': 1834, 'n_words': 596}