static void CrawlRawObjectData(IntermediateCrawlData crawlData, BytesAndOffset bytesAndOffset, int iTypeDescription, bool useStaticFields, ulong ptrFrom, int indexOfFrom) { var snapshot = crawlData.CachedMemorySnapshot; var fields = useStaticFields ? snapshot.typeDescriptions.fieldIndicesOwned_static[iTypeDescription] : snapshot.typeDescriptions.fieldIndices_instance[iTypeDescription]; foreach (var iField in fields) { int iField_TypeDescription_TypeIndex = snapshot.fieldDescriptions.typeIndex[iField]; int iField_TypeDescription_ArrayIndex = snapshot.typeDescriptions.TypeIndex2ArrayIndex(iField_TypeDescription_TypeIndex); var fieldLocation = bytesAndOffset.Add(snapshot.fieldDescriptions.offset[iField] - (useStaticFields ? 0 : snapshot.virtualMachineInformation.objectHeaderSize)); if (snapshot.typeDescriptions.HasFlag(iField_TypeDescription_ArrayIndex, TypeFlags.kValueType)) { CrawlRawObjectData(crawlData, fieldLocation, iField_TypeDescription_ArrayIndex, useStaticFields, ptrFrom, indexOfFrom); continue; } ulong fieldAddr; if (fieldLocation.TryReadPointer(out fieldAddr) == BytesAndOffset.PtrReadError.Success) { crawlData.CrawlDataStack.Push(new StackCrawlData() { ptr = fieldAddr, ptrFrom = ptrFrom, typeFrom = iTypeDescription, indexOfFrom = indexOfFrom, fieldFrom = iField, fromArrayIndex = -1 }); } } }
static void GatherIntermediateCrawlData(CachedSnapshot snapshot, IntermediateCrawlData crawlData) { unsafe { var uniqueHandlesPtr = (ulong *)UnsafeUtility.Malloc(UnsafeUtility.SizeOf <ulong>() * snapshot.gcHandles.Count, UnsafeUtility.AlignOf <ulong>(), Collections.Allocator.Temp); ulong *uniqueHandlesBegin = uniqueHandlesPtr; ulong *uniqueHandlesEnd = uniqueHandlesPtr + snapshot.gcHandles.Count; // Parse all handles for (int i = 0; i != snapshot.gcHandles.Count; i++) { var moi = new ManagedObjectInfo(); var target = snapshot.gcHandles.target[i]; if (target == 0) { #if SNAPSHOT_CRAWLER_DIAG Debuging.DebugUtility.LogWarning("null object in gc handles " + i); #endif moi.ManagedObjectIndex = i; crawlData.ManagedObjectInfos.Add(moi); } else if (snapshot.CrawledData.ManagedObjectByAddress.ContainsKey(target)) { #if SNAPSHOT_CRAWLER_DIAG Debuging.DebugUtility.LogWarning("Duplicate gc handles " + i + " addr:" + snapshot.gcHandles.target[i]); #endif moi.ManagedObjectIndex = i; moi.PtrObject = target; crawlData.ManagedObjectInfos.Add(moi); crawlData.DuplicatedGCHandlesStack.Push(i); } else { moi.ManagedObjectIndex = i; crawlData.ManagedObjectInfos.Add(moi); snapshot.CrawledData.ManagedObjectByAddress.Add(target, moi); UnsafeUtility.CopyStructureToPtr(ref target, uniqueHandlesBegin++); } } uniqueHandlesBegin = uniqueHandlesPtr; //reset iterator //add handles for processing while (uniqueHandlesBegin != uniqueHandlesEnd) { crawlData.CrawlDataStack.Push(new StackCrawlData { ptr = UnsafeUtility.ReadArrayElement <ulong>(uniqueHandlesBegin++, 0), ptrFrom = 0, typeFrom = -1, indexOfFrom = -1, fieldFrom = -1, fromArrayIndex = -1 }); } UnsafeUtility.Free(uniqueHandlesPtr, Collections.Allocator.Temp); } }
static void GatherIntermediateCrawlData(CachedSnapshot snapshot, IntermediateCrawlData crawlData) { unsafe { var uniqueHandlesPtr = (ulong *)UnsafeUtility.Malloc(UnsafeUtility.SizeOf <ulong>() * snapshot.gcHandles.Count, UnsafeUtility.AlignOf <ulong>(), Collections.Allocator.Temp); ulong *uniqueHandlesBegin = uniqueHandlesPtr; int writtenRange = 0; // Parse all handles for (int i = 0; i != snapshot.gcHandles.Count; i++) { var moi = new ManagedObjectInfo(); var target = snapshot.gcHandles.target[i]; moi.ManagedObjectIndex = i; //this can only happen pre 19.3 scripting snapshot implementations where we dumped all handle targets but not the handles. //Eg: multiple handles can have the same target. Future facing we need to start adding that as we move forward if (snapshot.CrawledData.MangedObjectIndexByAddress.ContainsKey(target)) { moi.PtrObject = target; crawlData.DuplicatedGCHandleTargetsStack.Push(i); } else { snapshot.CrawledData.MangedObjectIndexByAddress.Add(target, moi.ManagedObjectIndex); *(uniqueHandlesBegin++) = target; ++writtenRange; } crawlData.ManagedObjectInfos.Add(moi); } uniqueHandlesBegin = uniqueHandlesPtr; //reset iterator ulong *uniqueHandlesEnd = uniqueHandlesPtr + writtenRange; //add handles for processing while (uniqueHandlesBegin != uniqueHandlesEnd) { crawlData.CrawlDataStack.Push(new StackCrawlData { ptr = UnsafeUtility.ReadArrayElement <ulong>(uniqueHandlesBegin++, 0), ptrFrom = 0, typeFrom = -1, indexOfFrom = -1, fieldFrom = -1, fromArrayIndex = -1 }); } UnsafeUtility.Free(uniqueHandlesPtr, Collections.Allocator.Temp); } }
static void CrawlRawObjectData(IntermediateCrawlData crawlData, BytesAndOffset bytesAndOffset, int iTypeDescription, bool useStaticFields, ulong ptrFrom, int indexOfFrom) { var snapshot = crawlData.CachedMemorySnapshot; var fields = useStaticFields ? snapshot.typeDescriptions.fieldIndicesOwned_static[iTypeDescription] : snapshot.typeDescriptions.fieldIndices_instance[iTypeDescription]; foreach (var iField in fields) { int iField_TypeDescription_TypeIndex = snapshot.fieldDescriptions.typeIndex[iField]; int iField_TypeDescription_ArrayIndex = snapshot.typeDescriptions.TypeIndex2ArrayIndex(iField_TypeDescription_TypeIndex); var fieldLocation = bytesAndOffset.Add(snapshot.fieldDescriptions.offset[iField] - (useStaticFields ? 0 : snapshot.virtualMachineInformation.objectHeaderSize)); if (snapshot.typeDescriptions.HasFlag(iField_TypeDescription_ArrayIndex, TypeFlags.kValueType)) { CrawlRawObjectData(crawlData, fieldLocation, iField_TypeDescription_ArrayIndex, useStaticFields, ptrFrom, indexOfFrom); continue; } //Workaround that was done to not error out when trying to read an array where the remaining length is less than that pointer size. bool gotException = false; try { ulong ptr = fieldLocation.ReadPointer(); if (ptr == 0) { gotException = true; } } catch (ArgumentException) { gotException = true; } if (!gotException) { crawlData.CrawlDataStack.Push(new StackCrawlData() { ptr = fieldLocation.ReadPointer(), ptrFrom = ptrFrom, typeFrom = iTypeDescription, indexOfFrom = indexOfFrom, fieldFrom = iField, fromArrayIndex = -1 }); } } }
static bool CrawlPointer(IntermediateCrawlData dataStack) { UnityEngine.Debug.Assert(dataStack.CrawlDataStack.Count > 0); var snapshot = dataStack.CachedMemorySnapshot; var typeDescriptions = snapshot.typeDescriptions; var data = dataStack.CrawlDataStack.Pop(); var virtualMachineInformation = snapshot.virtualMachineInformation; var managedHeapSections = snapshot.managedHeapSections; var byteOffset = managedHeapSections.Find(data.ptr, virtualMachineInformation); if (!byteOffset.IsValid) { return(false); } ManagedObjectInfo obj; bool wasAlreadyCrawled; obj = ParseObjectHeader(snapshot, data.ptr, out wasAlreadyCrawled, false); ++obj.RefCount; snapshot.CrawledData.ManagedObjects[obj.ManagedObjectIndex] = obj; snapshot.CrawledData.ManagedObjectByAddress[obj.PtrObject] = obj; dataStack.ManagedConnections.Add(ManagedConnection.MakeConnection(snapshot, data.indexOfFrom, data.ptrFrom, obj.ManagedObjectIndex, data.ptr, data.typeFrom, data.fieldFrom, data.fromArrayIndex)); if (!obj.IsKnownType()) { return(false); } if (wasAlreadyCrawled) { return(true); } if (!typeDescriptions.HasFlag(obj.ITypeDescription, TypeFlags.kArray)) { CrawlRawObjectData(dataStack, byteOffset.Add(snapshot.virtualMachineInformation.objectHeaderSize), obj.ITypeDescription, false, data.ptr, obj.ManagedObjectIndex); return(true); } var arrayLength = ArrayTools.ReadArrayLength(snapshot, data.ptr, obj.ITypeDescription); int iElementTypeDescription = typeDescriptions.baseOrElementTypeIndex[obj.ITypeDescription]; if (iElementTypeDescription == -1) { return(false); //do not crawl uninitialized object types, as we currently don't have proper handling for these } var arrayData = byteOffset.Add(virtualMachineInformation.arrayHeaderSize); for (int i = 0; i != arrayLength; i++) { if (typeDescriptions.HasFlag(iElementTypeDescription, TypeFlags.kValueType)) { CrawlRawObjectData(dataStack, arrayData, iElementTypeDescription, false, data.ptr, obj.ManagedObjectIndex); arrayData = arrayData.Add(typeDescriptions.size[iElementTypeDescription]); } else { dataStack.CrawlDataStack.Push(new StackCrawlData() { ptr = arrayData.ReadPointer(), ptrFrom = data.ptr, typeFrom = obj.ITypeDescription, indexOfFrom = obj.ManagedObjectIndex, fieldFrom = -1, fromArrayIndex = i }); arrayData = arrayData.NextPointer(); } } return(true); }
static void ConnectNativeToManageObject(IntermediateCrawlData crawlData) { var snapshot = crawlData.CachedMemorySnapshot; var objectInfos = crawlData.ManagedObjectInfos; if (snapshot.typeDescriptions.Count == 0) { return; } // Get UnityEngine.Object int iTypeDescription_UnityEngineObject = snapshot.typeDescriptions.typeDescriptionName.FindIndex(x => x == "UnityEngine.Object"); if (iTypeDescription_UnityEngineObject < 0) { //No Unity Object ? return; } //Get UnityEngine.Object.m_InstanceID field int iField_UnityEngineObject_m_InstanceID = Array.FindIndex( snapshot.typeDescriptions.fieldIndices[iTypeDescription_UnityEngineObject] , iField => snapshot.fieldDescriptions.fieldDescriptionName[iField] == "m_InstanceID"); int instanceIDOffset = -1; int cachedPtrOffset = -1; if (iField_UnityEngineObject_m_InstanceID >= 0) { var fieldIndex = snapshot.typeDescriptions.fieldIndices[iTypeDescription_UnityEngineObject][iField_UnityEngineObject_m_InstanceID]; instanceIDOffset = snapshot.fieldDescriptions.offset[fieldIndex]; } if (instanceIDOffset < 0) { // on UNITY_5_4_OR_NEWER, there is the member m_CachedPtr we can use to identify the connection //Since Unity 5.4, UnityEngine.Object no longer stores instance id inside when running in the player. Use cached ptr instead to find the instanceID of native object int iField_UnityEngineObject_m_CachedPtr = Array.FindIndex( snapshot.typeDescriptions.fieldIndices[iTypeDescription_UnityEngineObject] , iField => snapshot.fieldDescriptions.fieldDescriptionName[iField] == "m_CachedPtr"); if (iField_UnityEngineObject_m_CachedPtr >= 0) { cachedPtrOffset = snapshot.fieldDescriptions.offset[iField_UnityEngineObject_m_CachedPtr]; } } if (instanceIDOffset < 0 && cachedPtrOffset < 0) { Debug.LogWarning("Could not find unity object instance id field or m_CachedPtr"); return; } for (int i = 0; i != objectInfos.Count; i++) { //Must derive of unity Object var objectInfo = objectInfos[i]; objectInfo.NativeObjectIndex = -1; int instanceID = CachedSnapshot.NativeObjectEntriesCache.InstanceID_None; if (DerivesFrom(snapshot.typeDescriptions, objectInfo.ITypeDescription, iTypeDescription_UnityEngineObject)) { //Find object instance id if (iField_UnityEngineObject_m_InstanceID >= 0) { var h = snapshot.managedHeapSections.Find(objectInfo.PtrObject + (UInt64)instanceIDOffset, snapshot.virtualMachineInformation); if (h.IsValid) { instanceID = h.ReadInt32(); } else { Debug.LogWarning("Managed object missing head (addr:" + objectInfo.PtrObject + ", index:" + objectInfo.ManagedObjectIndex + ")"); } } else if (cachedPtrOffset >= 0) { // If you get a compilation error on the following 2 lines, update to Unity 5.4b14. var heapSection = snapshot.managedHeapSections.Find(objectInfo.PtrObject + (UInt64)cachedPtrOffset, snapshot.virtualMachineInformation); if (!heapSection.IsValid) { Debug.LogWarning("Managed object (addr:" + objectInfo.PtrObject + ", index:" + objectInfo.ManagedObjectIndex + ") does not have data at cachedPtr offset(" + cachedPtrOffset + ")"); } else { var cachedPtr = heapSection.ReadPointer(); var indexOfNativeObject = snapshot.nativeObjects.nativeObjectAddress.FindIndex(no => no == cachedPtr); if (indexOfNativeObject >= 0) { instanceID = snapshot.nativeObjects.instanceId[indexOfNativeObject]; } } } if (instanceID != CachedSnapshot.NativeObjectEntriesCache.InstanceID_None && snapshot.nativeObjects.instanceId2Index.TryGetValue(instanceID, out objectInfo.NativeObjectIndex)) { snapshot.nativeObjects.managedObjectIndex[objectInfo.NativeObjectIndex] = i; } } objectInfos[i] = objectInfo; if (snapshot.HasConnectionOverhaul && instanceID != CachedSnapshot.NativeObjectEntriesCache.InstanceID_None) { snapshot.CrawledData.Connections.Add(ManagedConnection.MakeUnityEngineObjectConnection(objectInfo.NativeObjectIndex, objectInfo.ManagedObjectIndex)); ++snapshot.nativeObjects.refcount[objectInfo.NativeObjectIndex]; } snapshot.CrawledData.ManagedObjectByAddress[objectInfo.PtrObject] = objectInfo; } }
public static IEnumerator Crawl(CachedSnapshot snapshot) { const int stepCount = 5; var status = new EnumerationUtilities.EnumerationStatus(stepCount); IntermediateCrawlData crawlData = new IntermediateCrawlData(snapshot); crawlData.ManagedObjectInfos.Capacity = (int)snapshot.gcHandles.Count * 3; crawlData.ManagedConnections.Capacity = (int)snapshot.gcHandles.Count * 6; //Gather handles and duplicates status.StepStatus = "Gathering snapshot managed data."; yield return(status); GatherIntermediateCrawlData(snapshot, crawlData); //crawl handle data status.IncrementStep(); status.StepStatus = "Crawling GC handles."; yield return(status); while (crawlData.CrawlDataStack.Count > 0) { CrawlPointer(crawlData); } //crawl data pertaining to types with static fields and enqueue any heap objects status.IncrementStep(); status.StepStatus = "Crawling data types with static fields"; yield return(status); for (int i = 0; i < crawlData.TypesWithStaticFields.Count; i++) { var iTypeDescription = crawlData.TypesWithStaticFields[i]; var bytesOffset = new BytesAndOffset { bytes = snapshot.typeDescriptions.staticFieldBytes[iTypeDescription], offset = 0, pointerSize = snapshot.virtualMachineInformation.pointerSize }; CrawlRawObjectData(crawlData, bytesOffset, iTypeDescription, true, 0, -1); } //crawl handles belonging to static instances status.IncrementStep(); status.StepStatus = "Crawling static instances heap data."; yield return(status); while (crawlData.CrawlDataStack.Count > 0) { CrawlPointer(crawlData); } //copy crawled object source data for duplicate objects foreach (var i in crawlData.DuplicatedGCHandlesStack) { var ptr = snapshot.CrawledData.ManagedObjects[i].PtrObject; snapshot.CrawledData.ManagedObjects[i] = snapshot.CrawledData.ManagedObjectByAddress[ptr]; } //crawl connection data status.IncrementStep(); status.StepStatus = "Crawling connection data"; yield return(status); ConnectNativeToManageObject(crawlData); AddupRawRefCount(crawlData.CachedMemorySnapshot); }
static void ConnectNativeToManageObject(IntermediateCrawlData crawlData) { var snapshot = crawlData.CachedMemorySnapshot; var objectInfos = crawlData.ManagedObjectInfos; if (snapshot.typeDescriptions.Count == 0) { return; } // Get UnityEngine.Object int iTypeDescription_UnityEngineObject = snapshot.typeDescriptions.typeDescriptionName.FindIndex(x => x == "UnityEngine.Object"); #if DEBUG_VALIDATION //This shouldn't really happen if (iTypeDescription_UnityEngineObject < 0) { throw new Exception("Unable to find UnityEngine.Object"); } #endif int cachedPtrOffset = -1; int iField_UnityEngineObject_m_CachedPtr = Array.FindIndex( snapshot.typeDescriptions.fieldIndices[iTypeDescription_UnityEngineObject] , iField => snapshot.fieldDescriptions.fieldDescriptionName[iField] == "m_CachedPtr"); if (iField_UnityEngineObject_m_CachedPtr >= 0) { cachedPtrOffset = snapshot.fieldDescriptions.offset[iField_UnityEngineObject_m_CachedPtr]; } #if DEBUG_VALIDATION if (cachedPtrOffset < 0) { Debug.LogWarning("Could not find unity object instance id field or m_CachedPtr"); return; } #endif for (int i = 0; i != objectInfos.Count; i++) { //Must derive of unity Object var objectInfo = objectInfos[i]; objectInfo.NativeObjectIndex = -1; int instanceID = CachedSnapshot.NativeObjectEntriesCache.k_InstanceIDNone; if (DerivesFrom(snapshot.typeDescriptions, objectInfo.ITypeDescription, iTypeDescription_UnityEngineObject)) { var heapSection = snapshot.managedHeapSections.Find(objectInfo.PtrObject + (ulong)cachedPtrOffset, snapshot.virtualMachineInformation); if (!heapSection.IsValid) { Debug.LogWarning("Managed object (addr:" + objectInfo.PtrObject + ", index:" + objectInfo.ManagedObjectIndex + ") does not have data at cachedPtr offset(" + cachedPtrOffset + ")"); } else { ulong cachedPtr; heapSection.TryReadPointer(out cachedPtr); if (!snapshot.nativeObjects.nativeObjectAddressToInstanceId.TryGetValue(cachedPtr, out instanceID)) { instanceID = CachedSnapshot.NativeObjectEntriesCache.k_InstanceIDNone; } } if (instanceID != CachedSnapshot.NativeObjectEntriesCache.k_InstanceIDNone && snapshot.nativeObjects.instanceId2Index.TryGetValue(instanceID, out objectInfo.NativeObjectIndex)) { snapshot.nativeObjects.managedObjectIndex[objectInfo.NativeObjectIndex] = i; } } objectInfos[i] = objectInfo; if (snapshot.HasConnectionOverhaul && instanceID != CachedSnapshot.NativeObjectEntriesCache.k_InstanceIDNone) { snapshot.CrawledData.Connections.Add(ManagedConnection.MakeUnityEngineObjectConnection(objectInfo.NativeObjectIndex, objectInfo.ManagedObjectIndex)); ++snapshot.nativeObjects.refcount[objectInfo.NativeObjectIndex]; } } }