public static void Read(System.IO.BinaryReader reader, out PackedConnection[] value, out string stateString) { value = new PackedConnection[0]; stateString = ""; var version = reader.ReadInt32(); if (version >= 1) { var length = reader.ReadInt32(); //stateString = string.Format("Loading {0} Object Connections", length); value = new PackedConnection[length]; if (length == 0) { return; } var onePercent = Math.Max(1, value.Length / 100); for (int n = 0, nend = value.Length; n < nend; ++n) { if ((n % onePercent) == 0) { stateString = string.Format("Loading Object Connections\n{0}/{1}, {2:F0}% done", n + 1, length, ((n + 1) / (float)length) * 100); } value[n].from = reader.ReadInt32(); value[n].to = reader.ReadInt32(); } } }
/// <summary> /// Loads a memory snapshot from the specified 'filePath' and stores the result in 'snapshot'. /// </summary> /// <param name="filePath">Absolute file path</param> public bool LoadFromFile(string filePath) { busyString = "Loading"; using (var fileStream = new System.IO.FileStream(filePath, System.IO.FileMode.Open)) { using (var reader = new System.IO.BinaryReader(fileStream)) { try { PackedMemorySnapshotHeader.Read(reader, out header, out busyString); if (!header.isValid) { throw new Exception("Invalid header."); } PackedNativeType.Read(reader, out nativeTypes, out busyString); PackedNativeUnityEngineObject.Read(reader, out nativeObjects, out busyString); PackedGCHandle.Read(reader, out gcHandles, out busyString); PackedConnection.Read(reader, out connections, out busyString); PackedMemorySection.Read(reader, out managedHeapSections, out busyString); PackedManagedType.Read(reader, out managedTypes, out busyString); PackedVirtualMachineInformation.Read(reader, out virtualMachineInformation, out busyString); } catch (System.Exception e) { Debug.LogException(e); return(false); } } } return(true); }
/// <summary> /// Converts an Unity PackedMemorySnapshot to our own format. /// </summary> public static PackedMemorySnapshot FromMemoryProfiler(MemorySnapshotProcessingArgs args) { var source = args.source; var value = new PackedMemorySnapshot(); try { VerifyMemoryProfilerSnapshot(source); value.busyString = "Loading Header"; value.header = PackedMemorySnapshotHeader.FromMemoryProfiler(); value.busyString = string.Format("Loading {0} Native Types", source.nativeTypes.Length); value.nativeTypes = PackedNativeType.FromMemoryProfiler(source.nativeTypes); value.busyString = string.Format("Loading {0} Native Objects", source.nativeObjects.Length); value.nativeObjects = PackedNativeUnityEngineObject.FromMemoryProfiler(source.nativeObjects); value.busyString = string.Format("Loading {0} GC Handles", source.gcHandles.Length); value.gcHandles = PackedGCHandle.FromMemoryProfiler(source.gcHandles); value.busyString = string.Format("Loading {0} Object Connections", source.connections.Length); if (args.excludeNativeFromConnections) { value.connections = ConnectionsFromMemoryProfilerWithoutNativeHACK(value, source); } else { value.connections = PackedConnection.FromMemoryProfiler(source.connections); } value.busyString = string.Format("Loading {0} Managed Heap Sections", source.managedHeapSections.Length); value.managedHeapSections = PackedMemorySection.FromMemoryProfiler(source.managedHeapSections); value.busyString = string.Format("Loading {0} Managed Types", source.typeDescriptions.Length); value.managedTypes = PackedManagedType.FromMemoryProfiler(source.typeDescriptions); value.busyString = "Loading VM Information"; value.virtualMachineInformation = PackedVirtualMachineInformation.FromMemoryProfiler(source.virtualMachineInformation); } catch (System.Exception e) { Debug.LogException(e); value = null; throw; } return(value); }
/// <summary> /// Saves the specfified memory snapshot as a file, using the specified 'filePath'. /// </summary> public void SaveToFile(string filePath) { using (var fileStream = new System.IO.FileStream(filePath, System.IO.FileMode.OpenOrCreate)) { using (var writer = new System.IO.BinaryWriter(fileStream)) { PackedMemorySnapshotHeader.Write(writer, header); PackedNativeType.Write(writer, nativeTypes); PackedNativeUnityEngineObject.Write(writer, nativeObjects); PackedGCHandle.Write(writer, gcHandles); PackedConnection.Write(writer, connections); PackedMemorySection.Write(writer, managedHeapSections); PackedManagedType.Write(writer, managedTypes); PackedVirtualMachineInformation.Write(writer, virtualMachineInformation); } } }
// this method is a hack that excludes native object connections to workaround an unity bug. // https://forum.unity.com/threads/wip-heap-explorer-memory-profiler-debugger-and-analyzer-for-unity.527949/page-2#post-3615223 static PackedConnection[] ConnectionsFromMemoryProfilerWithoutNativeHACK(PackedMemorySnapshot snapshot, UnityEditor.MemoryProfiler.PackedMemorySnapshot source) { var managedStart = 0; var managedEnd = managedStart + source.gcHandles.Length; var nativeStart = managedStart + managedEnd; var nativeEnd = nativeStart + source.nativeObjects.Length; var output = new List <PackedConnection>(1024 * 1024); for (int n = 0, nend = source.connections.Length; n < nend; ++n) { if ((n % (nend / 100)) == 0) { var progress = ((n + 1.0f) / nend) * 100; snapshot.busyString = string.Format("Analyzing GCHandle Connections\n{0}/{1}, {2:F0}% done", n + 1, source.connections.Length, progress); } var connection = new PackedConnection { from = source.connections[n].from, to = source.connections[n].to, }; connection.fromKind = PackedConnection.Kind.GCHandle; if (connection.from >= nativeStart && connection.from < nativeEnd) { connection.from -= nativeStart; connection.fromKind = PackedConnection.Kind.Native; } connection.toKind = PackedConnection.Kind.GCHandle; if (connection.to >= nativeStart && connection.to < nativeEnd) { connection.to -= nativeStart; connection.toKind = PackedConnection.Kind.Native; } if (connection.fromKind != PackedConnection.Kind.Native) { output.Add(connection); } } snapshot.header.nativeObjectFromConnectionsExcluded = true; Debug.LogWarning("HeapExplorer: Native object 'from' connections are excluded due workaround an Unity bug. Thus the object connections in Heap Explorer show fewer connections than actually exist.\nhttps://forum.unity.com/threads/wip-heap-explorer-memory-profiler-debugger-and-analyzer-for-unity.527949/page-2#post-3615223"); return(output.ToArray()); }
/// <summary> /// Add a connection between two objects, such as a connection from a native object to its managed counter-part. /// </summary> /// <param name="fromKind">The connection kind, that is pointing to another object.</param> /// <param name="fromIndex">An index into a snapshot array, depending on specified fromKind. If the kind would be 'Native', then it must be an index into the snapshot.nativeObjects array.</param> /// <param name="toKind">The connection kind, to which the 'from' object is pointing to.</param> /// <param name="toIndex">An index into a snapshot array, depending on the specified toKind. If the kind would be 'Native', then it must be an index into the snapshot.nativeObjects array.</param> public void AddConnection(PackedConnection.Kind fromKind, int fromIndex, PackedConnection.Kind toKind, int toIndex) { var connection = new PackedConnection { fromKind = fromKind, from = fromIndex, toKind = toKind, to = toIndex }; if (connection.fromKind != PackedConnection.Kind.None && connection.from != -1) { var key = ComputeConnectionKey(connection.fromKind, connection.from); List <PackedConnection> list; if (!m_ConnectionsFrom.TryGetValue(key, out list)) { m_ConnectionsFrom[key] = list = new List <PackedConnection>(1); // Capacity=1 to reduce memory usage on HUGE memory snapshots } list.Add(connection); } if (connection.toKind != PackedConnection.Kind.None && connection.to != -1) { if (connection.to < 0) { connection.to = -connection.to; } var key = ComputeConnectionKey(connection.toKind, connection.to); List <PackedConnection> list; if (!m_ConnectionsTo.TryGetValue(key, out list)) { m_ConnectionsTo[key] = list = new List <PackedConnection>(1); // Capacity=1 to reduce memory usage on HUGE memory snapshots } list.Add(connection); } }
public static PackedConnection[] FromMemoryProfiler(UnityEditor.MemoryProfiler.Connection[] source) { PackedConnection[] value = null; try { value = new PackedConnection[source.Length]; } catch { Debug.LogErrorFormat("HeapExplorer: Failed to allocate 'new PackedConnection[{0}]'.", source.Length); throw; } for (int n = 0, nend = source.Length; n < nend; ++n) { value[n] = new PackedConnection { from = source[n].from, to = source[n].to, }; } return(value); }
public static PackedConnection[] FromMemoryProfiler(UnityEditor.Profiling.Memory.Experimental.PackedMemorySnapshot snapshot) { var result = new List <PackedConnection>(1024 * 1024); var invalidIndices = 0; // Get all NativeObject instanceIds var nativeObjects = snapshot.nativeObjects; var nativeObjectsCount = nativeObjects.GetNumEntries(); var nativeObjectsInstanceIds = new int[nativeObjects.instanceId.GetNumEntries()]; nativeObjects.instanceId.GetEntries(0, nativeObjects.instanceId.GetNumEntries(), ref nativeObjectsInstanceIds); // Create lookup table from instanceId to NativeObject arrayindex var instanceIdToNativeObjectIndex = new Dictionary <int, int>(nativeObjectsInstanceIds.Length); for (var n = 0; n < nativeObjectsInstanceIds.Length; ++n) { instanceIdToNativeObjectIndex.Add(nativeObjectsInstanceIds[n], n); } // Connect NativeObject with its GCHandle var nativeObjectsGCHandleIndices = new int[nativeObjects.gcHandleIndex.GetNumEntries()]; nativeObjects.gcHandleIndex.GetEntries(0, nativeObjects.gcHandleIndex.GetNumEntries(), ref nativeObjectsGCHandleIndices); var gcHandles = snapshot.gcHandles; var gcHandlesCount = gcHandles.GetNumEntries(); for (var n = 0; n < nativeObjectsGCHandleIndices.Length; ++n) { // Unity 2019.4.7f1 and earlier (maybe also newer) versions seem to have this issue: // (Case 1269293) PackedMemorySnapshot: nativeObjects.gcHandleIndex contains -1 always if (nativeObjectsGCHandleIndices[n] < 0 || nativeObjectsGCHandleIndices[n] >= gcHandlesCount) { if (nativeObjectsGCHandleIndices[n] != -1) { invalidIndices++; // I guess -1 means no connection to a gcHandle } continue; } var packed = new PackedConnection(); packed.from = n; // nativeObject index packed.fromKind = Kind.Native; packed.to = nativeObjectsGCHandleIndices[n]; // gcHandle index packed.toKind = Kind.GCHandle; result.Add(packed); } if (invalidIndices > 0) { Debug.LogErrorFormat("Reconstructing native to gchandle connections. Found {0} invalid indices into nativeObjectsGCHandleIndices[{1}] array.", invalidIndices, gcHandlesCount); } // Connect NativeObject with NativeObject var source = snapshot.connections; int[] sourceFrom = new int[source.from.GetNumEntries()]; source.from.GetEntries(0, source.from.GetNumEntries(), ref sourceFrom); int[] sourceTo = new int[source.to.GetNumEntries()]; source.to.GetEntries(0, source.to.GetNumEntries(), ref sourceTo); var invalidInstanceIDs = 0; invalidIndices = 0; for (int n = 0, nend = sourceFrom.Length; n < nend; ++n) { var packed = new PackedConnection(); if (!instanceIdToNativeObjectIndex.TryGetValue(sourceFrom[n], out packed.from)) { invalidInstanceIDs++; continue; // NativeObject InstanceID not found } if (!instanceIdToNativeObjectIndex.TryGetValue(sourceTo[n], out packed.to)) { invalidInstanceIDs++; continue; // NativeObject InstanceID not found } if (packed.from < 0 || packed.from >= nativeObjectsCount) { invalidIndices++; continue; // invalid index into array } if (packed.to < 0 || packed.to >= nativeObjectsCount) { invalidIndices++; continue; // invalid index into array } packed.fromKind = Kind.Native; packed.toKind = Kind.Native; result.Add(packed); } if (invalidIndices > 0) { Debug.LogErrorFormat("Reconstructing native to native object connections. Found {0} invalid indices into nativeObjectsCount[{1}] array.", invalidIndices, nativeObjectsCount); } if (invalidInstanceIDs > 0) { Debug.LogErrorFormat("Reconstructing native to native object connections. Found {0} invalid instanceIDs.", invalidInstanceIDs, nativeObjectsCount); } return(result.ToArray()); }