少年修仙传客户端基础资源
client_Wu Xijin
2018-08-14 31c2cebbccba45a00a755981dd6d0cdd0a0459e9
Assets/Editor/MemoryProfiler/Crawler.cs
@@ -1,357 +1,357 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.MemoryProfiler;
namespace MemoryProfilerWindow
{
    enum PointerType
    {
        Reference,
        RawPointer
    }
    struct ThingToProfile
    {
        public readonly PointerType type;
        public readonly BytesAndOffset bytesAndOffset;
        public readonly ulong objectPointer;
        public readonly TypeDescription typeDescription;
        public readonly bool useStaticFields;
        public readonly int indexOfFrom;
        public ThingToProfile(ulong objectPtr, int refIndexOfFrom)
        {
            type = PointerType.Reference;
            objectPointer = objectPtr;
            indexOfFrom = refIndexOfFrom;
            useStaticFields = true;
            typeDescription = new TypeDescription();
            bytesAndOffset = new BytesAndOffset();
        }
        public ThingToProfile(TypeDescription typeDesc, BytesAndOffset inBytesAndOffset, bool inUseStaticFields, int inIndexOfFrom)
        {
            type = PointerType.RawPointer;
            typeDescription = typeDesc;
            bytesAndOffset = inBytesAndOffset;
            useStaticFields = inUseStaticFields;
            indexOfFrom = inIndexOfFrom;
            objectPointer = 0;
        }
    }
    internal class Crawler
    {
        private Dictionary<UInt64, TypeDescription> _typeInfoToTypeDescription;
        private Dictionary<int, UInt64> _pointer2Backups = new Dictionary<int, ulong>();
        private VirtualMachineInformation _virtualMachineInformation;
        private TypeDescription[] _typeDescriptions;
        private FieldDescription[][] _instanceFields;
        private FieldDescription[][] _staticFields;
        public PackedCrawlerData Crawl(PackedMemorySnapshot input)
        {
            _typeInfoToTypeDescription = input.typeDescriptions.ToDictionary(td => td.typeInfoAddress, td => td);
            _virtualMachineInformation = input.virtualMachineInformation;
            _typeDescriptions = input.typeDescriptions;
            _instanceFields = new FieldDescription[_typeDescriptions.Length][];
            _staticFields = new FieldDescription[_typeDescriptions.Length][];
            foreach (var type in _typeDescriptions)
            {
                _instanceFields[type.typeIndex] = TypeTools.AllFieldsOf(type, _typeDescriptions, TypeTools.FieldFindOptions.OnlyInstance).ToArray();
                _staticFields[type.typeIndex] = TypeTools.AllFieldsOf(type, _typeDescriptions, TypeTools.FieldFindOptions.OnlyStatic).ToArray();
            }
            var result = new PackedCrawlerData(input);
            var managedObjects = new List<PackedManagedObject>(result.startIndices.OfFirstManagedObject * 3);
            var connections = new List<Connection>(managedObjects.Capacity * 3);
            //we will be adding a lot of connections, but the input format also already had connections. (nativeobject->nativeobject and nativeobject->gchandle). we'll add ours to the ones already there.
            connections.AddRange(input.connections);
            Stack<ThingToProfile> thingsToProfile = new Stack<ThingToProfile>();
            for(int i = 0; i < input.gcHandles.Length; ++i)
            {
                thingsToProfile.Push(new ThingToProfile(input.gcHandles[i].target, result.startIndices.OfFirstGCHandle + i));
            }
            for (int i = 0; i < result.typesWithStaticFields.Length; i++)
            {
                var typeDescription = result.typesWithStaticFields[i];
                thingsToProfile.Push(new ThingToProfile(typeDescription, new BytesAndOffset { bytes = typeDescription.staticFieldBytes, offset = 0, pointerSize = _virtualMachineInformation.pointerSize }, true, result.startIndices.OfFirstStaticFields + i));
            }
            while (thingsToProfile.Count > 0)
            {
                var thingToProfile = thingsToProfile.Pop();
                if(thingToProfile.type == PointerType.Reference)
                {
                    CrawlPointerNonRecursive(input, result.startIndices, thingToProfile.objectPointer, thingToProfile.indexOfFrom, connections, managedObjects, thingsToProfile);
                }
                else
                {
                    CrawlRawObjectDataNonRecursive(input, result.startIndices, thingToProfile.bytesAndOffset, thingToProfile.typeDescription, thingToProfile.useStaticFields, thingToProfile.indexOfFrom, connections, managedObjects, thingsToProfile);
                }
            }
            result.managedObjects = managedObjects.ToArray();
            connections.AddRange(AddManagedToNativeConnectionsAndRestoreObjectHeaders(input, result.startIndices, result));
            result.connections = connections.ToArray();
            return result;
        }
        private IEnumerable<Connection> AddManagedToNativeConnectionsAndRestoreObjectHeaders(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, PackedCrawlerData packedCrawlerData)
        {
            if (packedMemorySnapshot.typeDescriptions.Length == 0)
                yield break;
            var unityEngineObjectTypeDescription = packedMemorySnapshot.typeDescriptions.First(td => td.name == "UnityEngine.Object");
            bool unityEngineObjectHasInstanceIDField = unityEngineObjectTypeDescription.fields.Any(f => f.name == "m_InstanceID");
            int instanceIDOffset = -1;
            if (unityEngineObjectHasInstanceIDField)
                instanceIDOffset = unityEngineObjectTypeDescription.fields.Single(f => f.name == "m_InstanceID").offset;
#if UNITY_5_4_OR_NEWER
            var cachedPtrOffset = unityEngineObjectTypeDescription.fields.Single(f => f.name == "m_CachedPtr").offset;
#endif
            for (int i = 0; i != packedCrawlerData.managedObjects.Length; i++)
            {
                var managedObjectIndex = i + startIndices.OfFirstManagedObject;
                var address = packedCrawlerData.managedObjects[i].address;
                var typeDescription = RestoreObjectHeader(packedMemorySnapshot.managedHeapSections, address, managedObjectIndex);
                if (!DerivesFrom(packedMemorySnapshot.typeDescriptions, typeDescription.typeIndex, unityEngineObjectTypeDescription.typeIndex))
                    continue;
                int indexOfNativeObject = -1;
                if (unityEngineObjectHasInstanceIDField)
                {
                    var instanceID = packedMemorySnapshot.managedHeapSections.Find(address + (UInt64)instanceIDOffset, packedMemorySnapshot.virtualMachineInformation).ReadInt32();
                    indexOfNativeObject = Array.FindIndex(packedMemorySnapshot.nativeObjects, no => no.instanceId == instanceID);
                }
#if UNITY_5_4_OR_NEWER // Since Unity 5.4, UnityEngine.Object no longer stores instance id inside when running in the player. Use cached ptr instead to find the index of native object
                else
                {
                    // If you get a compilation error on the following 2 lines, update to Unity 5.4b14.
                    var cachedPtr = packedMemorySnapshot.managedHeapSections.Find(address + (UInt64)cachedPtrOffset, packedMemorySnapshot.virtualMachineInformation).ReadPointer();
                    indexOfNativeObject = Array.FindIndex(packedMemorySnapshot.nativeObjects, no => (ulong)no.nativeObjectAddress == cachedPtr);
                }
#endif
                if (indexOfNativeObject != -1)
                    yield return new Connection { @from = managedObjectIndex, to = indexOfNativeObject + startIndices.OfFirstNativeObject };
            }
        }
        private bool DerivesFrom(TypeDescription[] typeDescriptions, int typeIndex, int potentialBase)
        {
            if (typeIndex == potentialBase)
                return true;
            var baseIndex = typeDescriptions[typeIndex].baseOrElementTypeIndex;
            if (baseIndex == -1)
                return false;
            return DerivesFrom(typeDescriptions, baseIndex, potentialBase);
        }
        private TypeDescription GetTypeDescription(MemorySection[] heap, ulong objectAddress)
        {
            TypeDescription typeDescription;
            // IL2CPP has the class pointer as the first member of the object.
            if (!_typeInfoToTypeDescription.TryGetValue(objectAddress, out typeDescription))
            {
                // Mono has a vtable pointer as the first member of the object.
                // The first member of the vtable is the class pointer.
                var vtable = heap.Find(objectAddress, _virtualMachineInformation);
                var vtableClassPointer = vtable.ReadPointer();
                typeDescription = _typeInfoToTypeDescription[vtableClassPointer];
            }
            return typeDescription;
        }
        private TypeDescription RestoreObjectHeader(MemorySection[] heaps, ulong address, int managedObjectIndex)
        {
            var bo = heaps.Find(address, _virtualMachineInformation);
            var mask = this._virtualMachineInformation.pointerSize == 8 ? System.UInt64.MaxValue - 1 : System.UInt32.MaxValue - 1;
            var pointer = bo.ReadPointer();
            var typeInfoAddress = pointer & mask;
            bo.WritePointer(typeInfoAddress);
            UInt64 restoreValue = 0;
            _pointer2Backups.TryGetValue(managedObjectIndex, out restoreValue);
            bo.NextPointer().WritePointer(restoreValue);
            return GetTypeDescription(heaps, typeInfoAddress);
        }
        private void CrawlRawObjectDataNonRecursive(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, BytesAndOffset bytesAndOffset, TypeDescription typeDescription, bool useStaticFields, int indexOfFrom,
                                                        List<Connection> out_connections, List<PackedManagedObject> out_managedObjects, Stack<ThingToProfile> out_thingsToProfile)
        {
            // Do not crawl MemoryProfilerWindow objects
            if (typeDescription.name.StartsWith("MemoryProfilerWindow."))
                return;
            var fields = useStaticFields ? _staticFields[typeDescription.typeIndex] : _instanceFields[typeDescription.typeIndex];
            for(int i = 0; i < fields.Length; ++i)
            {
                var field = fields[i];
                var fieldType = packedMemorySnapshot.typeDescriptions[field.typeIndex];
                var fieldLocation = bytesAndOffset.Add(field.offset - (useStaticFields ? 0 : _virtualMachineInformation.objectHeaderSize));
                if(fieldType.isValueType)
                {
                    out_thingsToProfile.Push(new ThingToProfile(fieldType, fieldLocation, false, indexOfFrom));
                    continue;
                }
                else
                {
                    //temporary workaround for a bug in 5.3b4 and earlier where we would get literals returned as fields with offset 0. soon we'll be able to remove this code.
                    bool gotException = false;
                    try
                    {
                        fieldLocation.ReadPointer();
                    }
                    catch (ArgumentException)
                    {
                        UnityEngine.Debug.LogWarningFormat("Skipping field {0} on type {1}", field.name, typeDescription.name);
                        UnityEngine.Debug.LogWarningFormat("FieldType.name: {0}", fieldType.name);
                        gotException = true;
                    }
                    if (!gotException)
                    {
                        out_thingsToProfile.Push(new ThingToProfile(fieldLocation.ReadPointer(), indexOfFrom));
                    }
                }
            }
        }
        private void CrawlPointerNonRecursive(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, ulong pointer, int indexOfFrom, List<Connection> out_connections, List<PackedManagedObject> out_managedObjects, Stack<ThingToProfile> out_thingsToProfile)
        {
            var bo = packedMemorySnapshot.managedHeapSections.Find(pointer, _virtualMachineInformation);
            if (!bo.IsValid)
                return;
            UInt64 typeInfoAddress;
            int indexOfObject;
            bool wasAlreadyCrawled;
            try
            {
                ParseObjectHeader(startIndices, packedMemorySnapshot.managedHeapSections, pointer, out typeInfoAddress, out indexOfObject, out wasAlreadyCrawled, out_managedObjects);
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogWarningFormat("Exception parsing object header. Skipping. {0}", e);
                return;
            }
            out_connections.Add(new Connection() { from = indexOfFrom, to = indexOfObject });
            if (wasAlreadyCrawled)
                return;
            var typeDescription = _typeInfoToTypeDescription[typeInfoAddress];
            if (!typeDescription.isArray)
            {
                out_thingsToProfile.Push(new ThingToProfile(typeDescription, bo.Add(_virtualMachineInformation.objectHeaderSize), false, indexOfObject));
                return;
            }
            var arrayLength = ArrayTools.ReadArrayLength(packedMemorySnapshot.managedHeapSections, pointer, typeDescription, _virtualMachineInformation);
            var elementType = packedMemorySnapshot.typeDescriptions[typeDescription.baseOrElementTypeIndex];
            var cursor = bo.Add(_virtualMachineInformation.arrayHeaderSize);
            for (int i = 0; i != arrayLength; i++)
            {
                if (elementType.isValueType)
                {
                    out_thingsToProfile.Push(new ThingToProfile(elementType, cursor, false, indexOfObject));
                    cursor = cursor.Add(elementType.size);
                }
                else
                {
                    out_thingsToProfile.Push(new ThingToProfile(cursor.ReadPointer(), indexOfObject));
                    cursor = cursor.NextPointer();
                }
            }
        }
        int SizeOfObjectInBytes(TypeDescription typeDescription, BytesAndOffset bo, MemorySection[] heap, ulong address)
        {
            if (typeDescription.isArray)
                return ArrayTools.ReadArrayObjectSizeInBytes(heap, address, typeDescription, _typeDescriptions, _virtualMachineInformation);
            if (typeDescription.name == "System.String")
                return StringTools.ReadStringObjectSizeInBytes(bo, _virtualMachineInformation);
            //array and string are the only types that are special, all other types just have one size, which is stored in the typedescription
            return typeDescription.size;
        }
        private void ParseObjectHeader(StartIndices startIndices, MemorySection[] heap, ulong originalHeapAddress, out ulong typeInfoAddress, out int indexOfObject, out bool wasAlreadyCrawled, List<PackedManagedObject> outManagedObjects)
        {
            var bo = heap.Find(originalHeapAddress, _virtualMachineInformation);
            var pointer1 = bo.ReadPointer();
            var pointer2 = bo.NextPointer();
            if (HasMarkBit(pointer1) == 0)
            {
                TypeDescription typeDescription = GetTypeDescription(heap, pointer1);
                wasAlreadyCrawled = false;
                indexOfObject = outManagedObjects.Count + startIndices.OfFirstManagedObject;
                typeInfoAddress = typeDescription.typeInfoAddress;
                var size = SizeOfObjectInBytes(typeDescription, bo, heap, originalHeapAddress);
                outManagedObjects.Add(new PackedManagedObject() { address = originalHeapAddress, size = size, typeIndex = typeDescription.typeIndex });
                //okay, we gathered all information, now lets set the mark bit, and store the index for this object in the 2nd pointer of the header, which is rarely used.
                bo.WritePointer(pointer1 | 1);
                //test writepointer implementation
                ulong magic = bo.pointerSize == 8 ? 0x12345678deadbeefUL : 0xdeadbeef;
                pointer2.WritePointer(magic);
                var check = pointer2.ReadPointer();
                if (check != magic)
                    throw new Exception("writepointer broken");
                pointer2.WritePointer((ulong)indexOfObject);
                return;
            }
            //give typeinfo address back without the markbit
            typeInfoAddress = ClearMarkBit(pointer1);
            wasAlreadyCrawled = true;
            //read the index for this object that we stored in the 2ndpointer field of the header
            indexOfObject = (int)pointer2.ReadPointer();
        }
        private static ulong HasMarkBit(ulong pointer1)
        {
            return pointer1 & 1;
        }
        private static ulong ClearMarkBit(ulong pointer1)
        {
            return pointer1 & ~(1UL);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.MemoryProfiler;
namespace MemoryProfilerWindow
{
    enum PointerType
    {
        Reference,
        RawPointer
    }
    struct ThingToProfile
    {
        public readonly PointerType type;
        public readonly BytesAndOffset bytesAndOffset;
        public readonly ulong objectPointer;
        public readonly TypeDescription typeDescription;
        public readonly bool useStaticFields;
        public readonly int indexOfFrom;
        public ThingToProfile(ulong objectPtr, int refIndexOfFrom)
        {
            type = PointerType.Reference;
            objectPointer = objectPtr;
            indexOfFrom = refIndexOfFrom;
            useStaticFields = true;
            typeDescription = new TypeDescription();
            bytesAndOffset = new BytesAndOffset();
        }
        public ThingToProfile(TypeDescription typeDesc, BytesAndOffset inBytesAndOffset, bool inUseStaticFields, int inIndexOfFrom)
        {
            type = PointerType.RawPointer;
            typeDescription = typeDesc;
            bytesAndOffset = inBytesAndOffset;
            useStaticFields = inUseStaticFields;
            indexOfFrom = inIndexOfFrom;
            objectPointer = 0;
        }
    }
    internal class Crawler
    {
        private Dictionary<UInt64, TypeDescription> _typeInfoToTypeDescription;
        private Dictionary<int, UInt64> _pointer2Backups = new Dictionary<int, ulong>();
        private VirtualMachineInformation _virtualMachineInformation;
        private TypeDescription[] _typeDescriptions;
        private FieldDescription[][] _instanceFields;
        private FieldDescription[][] _staticFields;
        public PackedCrawlerData Crawl(PackedMemorySnapshot input)
        {
            _typeInfoToTypeDescription = input.typeDescriptions.ToDictionary(td => td.typeInfoAddress, td => td);
            _virtualMachineInformation = input.virtualMachineInformation;
            _typeDescriptions = input.typeDescriptions;
            _instanceFields = new FieldDescription[_typeDescriptions.Length][];
            _staticFields = new FieldDescription[_typeDescriptions.Length][];
            foreach (var type in _typeDescriptions)
            {
                _instanceFields[type.typeIndex] = TypeTools.AllFieldsOf(type, _typeDescriptions, TypeTools.FieldFindOptions.OnlyInstance).ToArray();
                _staticFields[type.typeIndex] = TypeTools.AllFieldsOf(type, _typeDescriptions, TypeTools.FieldFindOptions.OnlyStatic).ToArray();
            }
            var result = new PackedCrawlerData(input);
            var managedObjects = new List<PackedManagedObject>(result.startIndices.OfFirstManagedObject * 3);
            var connections = new List<Connection>(managedObjects.Capacity * 3);
            //we will be adding a lot of connections, but the input format also already had connections. (nativeobject->nativeobject and nativeobject->gchandle). we'll add ours to the ones already there.
            connections.AddRange(input.connections);
            Stack<ThingToProfile> thingsToProfile = new Stack<ThingToProfile>();
            for(int i = 0; i < input.gcHandles.Length; ++i)
            {
                thingsToProfile.Push(new ThingToProfile(input.gcHandles[i].target, result.startIndices.OfFirstGCHandle + i));
            }
            for (int i = 0; i < result.typesWithStaticFields.Length; i++)
            {
                var typeDescription = result.typesWithStaticFields[i];
                thingsToProfile.Push(new ThingToProfile(typeDescription, new BytesAndOffset { bytes = typeDescription.staticFieldBytes, offset = 0, pointerSize = _virtualMachineInformation.pointerSize }, true, result.startIndices.OfFirstStaticFields + i));
            }
            while (thingsToProfile.Count > 0)
            {
                var thingToProfile = thingsToProfile.Pop();
                if(thingToProfile.type == PointerType.Reference)
                {
                    CrawlPointerNonRecursive(input, result.startIndices, thingToProfile.objectPointer, thingToProfile.indexOfFrom, connections, managedObjects, thingsToProfile);
                }
                else
                {
                    CrawlRawObjectDataNonRecursive(input, result.startIndices, thingToProfile.bytesAndOffset, thingToProfile.typeDescription, thingToProfile.useStaticFields, thingToProfile.indexOfFrom, connections, managedObjects, thingsToProfile);
                }
            }
            result.managedObjects = managedObjects.ToArray();
            connections.AddRange(AddManagedToNativeConnectionsAndRestoreObjectHeaders(input, result.startIndices, result));
            result.connections = connections.ToArray();
            return result;
        }
        private IEnumerable<Connection> AddManagedToNativeConnectionsAndRestoreObjectHeaders(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, PackedCrawlerData packedCrawlerData)
        {
            if (packedMemorySnapshot.typeDescriptions.Length == 0)
                yield break;
            var unityEngineObjectTypeDescription = packedMemorySnapshot.typeDescriptions.First(td => td.name == "UnityEngine.Object");
            bool unityEngineObjectHasInstanceIDField = unityEngineObjectTypeDescription.fields.Any(f => f.name == "m_InstanceID");
            int instanceIDOffset = -1;
            if (unityEngineObjectHasInstanceIDField)
                instanceIDOffset = unityEngineObjectTypeDescription.fields.Single(f => f.name == "m_InstanceID").offset;
#if UNITY_5_4_OR_NEWER
            var cachedPtrOffset = unityEngineObjectTypeDescription.fields.Single(f => f.name == "m_CachedPtr").offset;
#endif
            for (int i = 0; i != packedCrawlerData.managedObjects.Length; i++)
            {
                var managedObjectIndex = i + startIndices.OfFirstManagedObject;
                var address = packedCrawlerData.managedObjects[i].address;
                var typeDescription = RestoreObjectHeader(packedMemorySnapshot.managedHeapSections, address, managedObjectIndex);
                if (!DerivesFrom(packedMemorySnapshot.typeDescriptions, typeDescription.typeIndex, unityEngineObjectTypeDescription.typeIndex))
                    continue;
                int indexOfNativeObject = -1;
                if (unityEngineObjectHasInstanceIDField)
                {
                    var instanceID = packedMemorySnapshot.managedHeapSections.Find(address + (UInt64)instanceIDOffset, packedMemorySnapshot.virtualMachineInformation).ReadInt32();
                    indexOfNativeObject = Array.FindIndex(packedMemorySnapshot.nativeObjects, no => no.instanceId == instanceID);
                }
#if UNITY_5_4_OR_NEWER // Since Unity 5.4, UnityEngine.Object no longer stores instance id inside when running in the player. Use cached ptr instead to find the index of native object
                else
                {
                    // If you get a compilation error on the following 2 lines, update to Unity 5.4b14.
                    var cachedPtr = packedMemorySnapshot.managedHeapSections.Find(address + (UInt64)cachedPtrOffset, packedMemorySnapshot.virtualMachineInformation).ReadPointer();
                    indexOfNativeObject = Array.FindIndex(packedMemorySnapshot.nativeObjects, no => (ulong)no.nativeObjectAddress == cachedPtr);
                }
#endif
                if (indexOfNativeObject != -1)
                    yield return new Connection { @from = managedObjectIndex, to = indexOfNativeObject + startIndices.OfFirstNativeObject };
            }
        }
        private bool DerivesFrom(TypeDescription[] typeDescriptions, int typeIndex, int potentialBase)
        {
            if (typeIndex == potentialBase)
                return true;
            var baseIndex = typeDescriptions[typeIndex].baseOrElementTypeIndex;
            if (baseIndex == -1)
                return false;
            return DerivesFrom(typeDescriptions, baseIndex, potentialBase);
        }
        private TypeDescription GetTypeDescription(MemorySection[] heap, ulong objectAddress)
        {
            TypeDescription typeDescription;
            // IL2CPP has the class pointer as the first member of the object.
            if (!_typeInfoToTypeDescription.TryGetValue(objectAddress, out typeDescription))
            {
                // Mono has a vtable pointer as the first member of the object.
                // The first member of the vtable is the class pointer.
                var vtable = heap.Find(objectAddress, _virtualMachineInformation);
                var vtableClassPointer = vtable.ReadPointer();
                typeDescription = _typeInfoToTypeDescription[vtableClassPointer];
            }
            return typeDescription;
        }
        private TypeDescription RestoreObjectHeader(MemorySection[] heaps, ulong address, int managedObjectIndex)
        {
            var bo = heaps.Find(address, _virtualMachineInformation);
            var mask = this._virtualMachineInformation.pointerSize == 8 ? System.UInt64.MaxValue - 1 : System.UInt32.MaxValue - 1;
            var pointer = bo.ReadPointer();
            var typeInfoAddress = pointer & mask;
            bo.WritePointer(typeInfoAddress);
            UInt64 restoreValue = 0;
            _pointer2Backups.TryGetValue(managedObjectIndex, out restoreValue);
            bo.NextPointer().WritePointer(restoreValue);
            return GetTypeDescription(heaps, typeInfoAddress);
        }
        private void CrawlRawObjectDataNonRecursive(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, BytesAndOffset bytesAndOffset, TypeDescription typeDescription, bool useStaticFields, int indexOfFrom,
                                                        List<Connection> out_connections, List<PackedManagedObject> out_managedObjects, Stack<ThingToProfile> out_thingsToProfile)
        {
            // Do not crawl MemoryProfilerWindow objects
            if (typeDescription.name.StartsWith("MemoryProfilerWindow."))
                return;
            var fields = useStaticFields ? _staticFields[typeDescription.typeIndex] : _instanceFields[typeDescription.typeIndex];
            for(int i = 0; i < fields.Length; ++i)
            {
                var field = fields[i];
                var fieldType = packedMemorySnapshot.typeDescriptions[field.typeIndex];
                var fieldLocation = bytesAndOffset.Add(field.offset - (useStaticFields ? 0 : _virtualMachineInformation.objectHeaderSize));
                if(fieldType.isValueType)
                {
                    out_thingsToProfile.Push(new ThingToProfile(fieldType, fieldLocation, false, indexOfFrom));
                    continue;
                }
                else
                {
                    //temporary workaround for a bug in 5.3b4 and earlier where we would get literals returned as fields with offset 0. soon we'll be able to remove this code.
                    bool gotException = false;
                    try
                    {
                        fieldLocation.ReadPointer();
                    }
                    catch (ArgumentException)
                    {
                        UnityEngine.Debug.LogWarningFormat("Skipping field {0} on type {1}", field.name, typeDescription.name);
                        UnityEngine.Debug.LogWarningFormat("FieldType.name: {0}", fieldType.name);
                        gotException = true;
                    }
                    if (!gotException)
                    {
                        out_thingsToProfile.Push(new ThingToProfile(fieldLocation.ReadPointer(), indexOfFrom));
                    }
                }
            }
        }
        private void CrawlPointerNonRecursive(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, ulong pointer, int indexOfFrom, List<Connection> out_connections, List<PackedManagedObject> out_managedObjects, Stack<ThingToProfile> out_thingsToProfile)
        {
            var bo = packedMemorySnapshot.managedHeapSections.Find(pointer, _virtualMachineInformation);
            if (!bo.IsValid)
                return;
            UInt64 typeInfoAddress;
            int indexOfObject;
            bool wasAlreadyCrawled;
            try
            {
                ParseObjectHeader(startIndices, packedMemorySnapshot.managedHeapSections, pointer, out typeInfoAddress, out indexOfObject, out wasAlreadyCrawled, out_managedObjects);
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogWarningFormat("Exception parsing object header. Skipping. {0}", e);
                return;
            }
            out_connections.Add(new Connection() { from = indexOfFrom, to = indexOfObject });
            if (wasAlreadyCrawled)
                return;
            var typeDescription = _typeInfoToTypeDescription[typeInfoAddress];
            if (!typeDescription.isArray)
            {
                out_thingsToProfile.Push(new ThingToProfile(typeDescription, bo.Add(_virtualMachineInformation.objectHeaderSize), false, indexOfObject));
                return;
            }
            var arrayLength = ArrayTools.ReadArrayLength(packedMemorySnapshot.managedHeapSections, pointer, typeDescription, _virtualMachineInformation);
            var elementType = packedMemorySnapshot.typeDescriptions[typeDescription.baseOrElementTypeIndex];
            var cursor = bo.Add(_virtualMachineInformation.arrayHeaderSize);
            for (int i = 0; i != arrayLength; i++)
            {
                if (elementType.isValueType)
                {
                    out_thingsToProfile.Push(new ThingToProfile(elementType, cursor, false, indexOfObject));
                    cursor = cursor.Add(elementType.size);
                }
                else
                {
                    out_thingsToProfile.Push(new ThingToProfile(cursor.ReadPointer(), indexOfObject));
                    cursor = cursor.NextPointer();
                }
            }
        }
        int SizeOfObjectInBytes(TypeDescription typeDescription, BytesAndOffset bo, MemorySection[] heap, ulong address)
        {
            if (typeDescription.isArray)
                return ArrayTools.ReadArrayObjectSizeInBytes(heap, address, typeDescription, _typeDescriptions, _virtualMachineInformation);
            if (typeDescription.name == "System.String")
                return StringTools.ReadStringObjectSizeInBytes(bo, _virtualMachineInformation);
            //array and string are the only types that are special, all other types just have one size, which is stored in the typedescription
            return typeDescription.size;
        }
        private void ParseObjectHeader(StartIndices startIndices, MemorySection[] heap, ulong originalHeapAddress, out ulong typeInfoAddress, out int indexOfObject, out bool wasAlreadyCrawled, List<PackedManagedObject> outManagedObjects)
        {
            var bo = heap.Find(originalHeapAddress, _virtualMachineInformation);
            var pointer1 = bo.ReadPointer();
            var pointer2 = bo.NextPointer();
            if (HasMarkBit(pointer1) == 0)
            {
                TypeDescription typeDescription = GetTypeDescription(heap, pointer1);
                wasAlreadyCrawled = false;
                indexOfObject = outManagedObjects.Count + startIndices.OfFirstManagedObject;
                typeInfoAddress = typeDescription.typeInfoAddress;
                var size = SizeOfObjectInBytes(typeDescription, bo, heap, originalHeapAddress);
                outManagedObjects.Add(new PackedManagedObject() { address = originalHeapAddress, size = size, typeIndex = typeDescription.typeIndex });
                //okay, we gathered all information, now lets set the mark bit, and store the index for this object in the 2nd pointer of the header, which is rarely used.
                bo.WritePointer(pointer1 | 1);
                //test writepointer implementation
                ulong magic = bo.pointerSize == 8 ? 0x12345678deadbeefUL : 0xdeadbeef;
                pointer2.WritePointer(magic);
                var check = pointer2.ReadPointer();
                if (check != magic)
                    throw new Exception("writepointer broken");
                pointer2.WritePointer((ulong)indexOfObject);
                return;
            }
            //give typeinfo address back without the markbit
            typeInfoAddress = ClearMarkBit(pointer1);
            wasAlreadyCrawled = true;
            //read the index for this object that we stored in the 2ndpointer field of the header
            indexOfObject = (int)pointer2.ReadPointer();
        }
        private static ulong HasMarkBit(ulong pointer1)
        {
            return pointer1 & 1;
        }
        private static ulong ClearMarkBit(ulong pointer1)
        {
            return pointer1 & ~(1UL);
        }
    }
}