internal static int ReadArrayLength(MemorySection[] heap, UInt64 address, TypeDescription arrayType, VirtualMachineInformation virtualMachineInformation) { BytesAndOffset bo = heap.Find(address, virtualMachineInformation); UInt64 bounds = bo.Add(virtualMachineInformation.arrayBoundsOffsetInHeader).ReadPointer(); if (bounds == 0) #if UNITY_2017_2_OR_NEWER { return((int)bo.Add(virtualMachineInformation.arraySizeOffsetInHeader).ReadPointer()); } #else { return(bo.Add(virtualMachineInformation.arraySizeOffsetInHeader).ReadInt32()); } #endif BytesAndOffset cursor = heap.Find(bounds, virtualMachineInformation); int length = 1; for (int i = 0; i != arrayType.arrayRank; i++) { #if UNITY_2017_2_OR_NEWER length *= (int)cursor.ReadPointer(); cursor = cursor.Add(virtualMachineInformation.pointerSize == 4 ? 8 : 16); #else length *= cursor.ReadInt32(); cursor = cursor.Add(8); #endif } return(length); }
public static int ReadStringObjectSizeInBytes(BytesAndOffset bo, VirtualMachineInformation virtualMachineInformation) { var lengthPointer = bo.Add(virtualMachineInformation.objectHeaderSize); var length = lengthPointer.ReadInt32(); return(virtualMachineInformation.objectHeaderSize + /*lengthfield*/ 1 + (length * /*utf16=2bytes per char*/ 2) + /*2 zero terminators*/ 2); }
private void ParseObjectHeader(StartIndices startIndices, MemorySection[] heap, ulong originalHeapAddress, out ulong typeInfoAddress, out int indexOfObject, out bool wasAlreadyCrawled, List <PackedManagedObject> outManagedObjects) { BytesAndOffset bo = heap.Find(originalHeapAddress, _virtualMachineInformation); UInt64 pointer1 = bo.ReadPointer(); BytesAndOffset pointer2 = bo.NextPointer(); if (HasMarkBit(pointer1) == 0) { TypeDescription typeDescription = GetTypeDescription(heap, pointer1); wasAlreadyCrawled = false; indexOfObject = outManagedObjects.Count + startIndices.OfFirstManagedObject; typeInfoAddress = typeDescription.typeInfoAddress; int 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); 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 void CrawlPointerNonRecursive(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, ulong pointer, int indexOfFrom, List <Connection> out_connections, List <PackedManagedObject> out_managedObjects, Stack <ThingToProfile> out_thingsToProfile) { BytesAndOffset 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; } TypeDescription typeDescription = _typeInfoToTypeDescription[typeInfoAddress]; if (!typeDescription.isArray) { out_thingsToProfile.Push(new ThingToProfile(typeDescription, bo.Add(_virtualMachineInformation.objectHeaderSize), false, indexOfObject)); return; } int arrayLength = ArrayTools.ReadArrayLength(packedMemorySnapshot.managedHeapSections, pointer, typeDescription, _virtualMachineInformation); TypeDescription elementType = packedMemorySnapshot.typeDescriptions[typeDescription.baseOrElementTypeIndex]; BytesAndOffset 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(); } } }
public static string ReadString(BytesAndOffset bo, VirtualMachineInformation virtualMachineInformation) { var lengthPointer = bo.Add(virtualMachineInformation.objectHeaderSize); var length = lengthPointer.ReadInt32(); var firstChar = lengthPointer.Add(4); return(System.Text.Encoding.Unicode.GetString(firstChar.bytes, firstChar.offset, length * 2)); }
internal ThingToProfile(TypeDescription typeDesc, BytesAndOffset inBytesAndOffset, bool inUseStaticFields, int inIndexOfFrom) { type = PointerType.RawPointer; typeDescription = typeDesc; bytesAndOffset = inBytesAndOffset; useStaticFields = inUseStaticFields; indexOfFrom = inIndexOfFrom; objectPointer = 0; }
public ThingToProfile(TypeDescription typeDesc, BytesAndOffset inBytesAndOffset, bool inUseStaticFields, int inIndexOfFrom) { type = PointerType.Reference; typeDescription = typeDesc; bytesAndOffset = inBytesAndOffset; useStaticFields = inUseStaticFields; indexOfFrom = inIndexOfFrom; object_offset = 0; }
internal ThingToProfile(ulong objectPtr, int refIndexOfFrom) { type = PointerType.Reference; objectPointer = objectPtr; indexOfFrom = refIndexOfFrom; useStaticFields = true; typeDescription = new TypeDescription(); bytesAndOffset = new BytesAndOffset(); }
public ThingToProfile(ulong refOffset, int refIndexOfFrom) { type = PointerType.Reference; object_offset = refOffset; indexOfFrom = refIndexOfFrom; useStaticFields = true; typeDescription = new TypeDescription(); bytesAndOffset = new BytesAndOffset(); }
public UInt64 ReadPointer(BytesAndOffset bo) { if (_virtualMachineInformation.pointerSize == 4) { return(ReadUInt32(bo)); } else { return(ReadUInt64(bo)); } }
private string DumpFields(TypeDescription typeDescription, BytesAndOffset bytesAndOffset, bool useStatics = false) { var str = new StringBuilder(); foreach (var field in TypeTools.AllFieldsOf(typeDescription, _unpackedCrawl.typeDescriptions, useStatics ? TypeTools.FieldFindOptions.OnlyStatic : TypeTools.FieldFindOptions.OnlyInstance)) { str.AppendFormat("\t\t\t{0}\n", field.name); // str.Append(DumpValueFor(field, bytesAndOffset.Add(field.offset))); } return(str.ToString()); }
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 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. BytesAndOffset vtable = heap.Find(objectAddress, _virtualMachineInformation); UInt64 vtableClassPointer = vtable.ReadPointer(); typeDescription = _typeInfoToTypeDescription[vtableClassPointer]; } return(typeDescription); }
private TypeDescription RestoreObjectHeader(MemorySection[] heaps, ulong address, int managedObjectIndex) { BytesAndOffset bo = heaps.Find(address, _virtualMachineInformation); UInt64 mask = this._virtualMachineInformation.pointerSize == 8 ? System.UInt64.MaxValue - 1 : System.UInt32.MaxValue - 1; UInt64 pointer = bo.ReadPointer(); UInt64 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; } FieldDescription[] fields = useStaticFields ? _staticFields[typeDescription.typeIndex] : _instanceFields[typeDescription.typeIndex]; for (int i = 0; i < fields.Length; ++i) { FieldDescription field = fields[i]; TypeDescription fieldType = packedMemorySnapshot.typeDescriptions[field.typeIndex]; BytesAndOffset 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 DrawFields(TypeDescription typeDescription, BytesAndOffset bytesAndOffset, bool useStatics = false) { int counter = 0; foreach (var field in TypeTools.AllFieldsOf(typeDescription, _unpackedCrawl.typeDescriptions, useStatics ? TypeTools.FieldFindOptions.OnlyStatic : TypeTools.FieldFindOptions.OnlyInstance)) { counter++; var gUIStyle = counter % 2 == 0 ? Styles.entryEven : Styles.entryOdd; gUIStyle.margin = new RectOffset(0, 0, 0, 0); gUIStyle.overflow = new RectOffset(0, 0, 0, 0); gUIStyle.padding = EditorStyles.label.padding; GUILayout.BeginHorizontal(gUIStyle); GUILayout.Label(field.name, labelWidth); GUILayout.BeginVertical(); DrawValueFor(field, bytesAndOffset.Add(field.offset)); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } }
private void CrawlRawObjectData(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, BytesAndOffset bytesAndOffset, TypeDescription typeDescription, bool useStaticFields, int indexOfFrom, List <Connection> out_connections, List <PackedManagedObject> out_managedObjects) { var fields = useStaticFields ? _staticFields[typeDescription.typeIndex] : _instanceFields[typeDescription.typeIndex]; foreach (var field in fields) { var fieldType = packedMemorySnapshot.typeDescriptions[field.typeIndex]; var fieldLocation = bytesAndOffset.Add(field.offset - (useStaticFields ? 0 : _virtualMachineInformation.objectHeaderSize)); if (fieldType.isValueType) { CrawlRawObjectData(packedMemorySnapshot, startIndices, fieldLocation, fieldType, false, indexOfFrom, out_connections, out_managedObjects); continue; } //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) { CrawlPointer(packedMemorySnapshot, startIndices, fieldLocation.ReadPointer(), indexOfFrom, out_connections, out_managedObjects); } } }
public System.Int64 ReadInt64(BytesAndOffset bo) { return(BitConverter.ToInt64(bo.bytes, bo.offset)); }
public System.UInt16 ReadUInt16(BytesAndOffset bo) { return(BitConverter.ToUInt16(bo.bytes, bo.offset)); }
private void DrawValueFor(FieldDescription field, BytesAndOffset bytesAndOffset) { var typeDescription = _unpackedCrawl.typeDescriptions[field.typeIndex]; try { switch (typeDescription.name) { case "System.Int32": GUILayout.Label(_primitiveValueReader.ReadInt32(bytesAndOffset).ToString()); break; case "System.Int64": GUILayout.Label(_primitiveValueReader.ReadInt64(bytesAndOffset).ToString()); break; case "System.UInt32": GUILayout.Label(_primitiveValueReader.ReadUInt32(bytesAndOffset).ToString()); break; case "System.UInt64": GUILayout.Label(_primitiveValueReader.ReadUInt64(bytesAndOffset).ToString()); break; case "System.Int16": GUILayout.Label(_primitiveValueReader.ReadInt16(bytesAndOffset).ToString()); break; case "System.UInt16": GUILayout.Label(_primitiveValueReader.ReadUInt16(bytesAndOffset).ToString()); break; case "System.Byte": GUILayout.Label(_primitiveValueReader.ReadByte(bytesAndOffset).ToString()); break; case "System.SByte": GUILayout.Label(_primitiveValueReader.ReadSByte(bytesAndOffset).ToString()); break; case "System.Char": GUILayout.Label(_primitiveValueReader.ReadChar(bytesAndOffset).ToString()); break; case "System.Boolean": GUILayout.Label(_primitiveValueReader.ReadBool(bytesAndOffset).ToString()); break; case "System.Single": GUILayout.Label(_primitiveValueReader.ReadSingle(bytesAndOffset).ToString()); break; case "System.Double": GUILayout.Label(_primitiveValueReader.ReadDouble(bytesAndOffset).ToString()); break; case "System.IntPtr": GUILayout.Label(_primitiveValueReader.ReadPointer(bytesAndOffset).ToString("X")); break; default: if (!typeDescription.isValueType) { ThingInMemory item = GetThingAt(bytesAndOffset.ReadPointer()); if (item == null) { EditorGUI.BeginDisabledGroup(true); GUILayout.Button("Null"); EditorGUI.EndDisabledGroup(); } else { DrawLinks(new ThingInMemory[] { item }); } } else { DrawFields(typeDescription, bytesAndOffset); } break; } } catch (Exception ex) { GUILayout.Label(string.Format("<bad_entry> type: {0}, len: {1}, offset: {2}, ex: {3}", typeDescription.name, bytesAndOffset.bytes.Length, bytesAndOffset.offset, ex.GetType().Name)); Debug.LogFormat("<bad_entry> type: {0}, len: {1}, offset: {2}, ex: {3}", typeDescription.name, bytesAndOffset.bytes.Length, bytesAndOffset.offset, ex.GetType().Name); } }
public System.Byte ReadByte(BytesAndOffset bo) { return(bo.bytes[bo.offset]); }
private void CrawlRawObjectData(PackedMemorySnapshot packedMemorySnapshot, StartIndices startIndices, BytesAndOffset bytesAndOffset, TypeDescription typeDescription, bool useStaticFields, int indexOfFrom, List <Connection> out_connections, List <PackedManagedObject> out_managedObjects) { foreach (var field in TypeTools.AllFieldsOf(typeDescription, _typeDescriptions, useStaticFields ? TypeTools.FieldFindOptions.OnlyStatic : TypeTools.FieldFindOptions.OnlyInstance)) { if (field.typeIndex == typeDescription.typeIndex && typeDescription.isValueType) { //this happens in System.Single, which is a weird type that has a field of its own type. continue; } if (field.offset == -1) { //this is how we encode TLS fields. todo: actually treat TLS fields as roots continue; } var fieldType = packedMemorySnapshot.typeDescriptions[field.typeIndex]; var fieldLocation = bytesAndOffset.Add(field.offset - (useStaticFields ? 0 : _virtualMachineInformation.objectHeaderSize)); if (fieldType.isValueType) { CrawlRawObjectData(packedMemorySnapshot, startIndices, fieldLocation, fieldType, false, indexOfFrom, out_connections, out_managedObjects); continue; } //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) { CrawlPointer(packedMemorySnapshot, startIndices, fieldLocation.ReadPointer(), indexOfFrom, out_connections, out_managedObjects); } } }
public System.SByte ReadSByte(BytesAndOffset bo) { return((System.SByte)bo.bytes[bo.offset]); }
public System.Boolean ReadBool(BytesAndOffset bo) { return(ReadByte(bo) != 0); }
public Char ReadChar(BytesAndOffset bytesAndOffset) { return(System.Text.Encoding.Unicode.GetChars(bytesAndOffset.bytes, bytesAndOffset.offset, 2)[0]); }
public System.Single ReadSingle(BytesAndOffset bytesAndOffset) { return(BitConverter.ToSingle(bytesAndOffset.bytes, bytesAndOffset.offset)); }
internal System.Byte ReadByte(BytesAndOffset bo) { return(bo.bytes[bo.offset]); }
public System.Double ReadDouble(BytesAndOffset bytesAndOffset) { return(BitConverter.ToDouble(bytesAndOffset.bytes, bytesAndOffset.offset)); }
private void DrawValueFor(FieldDescription field, BytesAndOffset bytesAndOffset) { var typeDescription = _unpackedCrawl.typeDescriptions[field.typeIndex]; switch (typeDescription.name) { case "System.Int32": GUILayout.Label(_primitiveValueReader.ReadInt32(bytesAndOffset).ToString()); break; case "System.Int64": GUILayout.Label(_primitiveValueReader.ReadInt64(bytesAndOffset).ToString()); break; case "System.UInt32": GUILayout.Label(_primitiveValueReader.ReadUInt32(bytesAndOffset).ToString()); break; case "System.UInt64": GUILayout.Label(_primitiveValueReader.ReadUInt64(bytesAndOffset).ToString()); break; case "System.Int16": GUILayout.Label(_primitiveValueReader.ReadInt16(bytesAndOffset).ToString()); break; case "System.UInt16": GUILayout.Label(_primitiveValueReader.ReadUInt16(bytesAndOffset).ToString()); break; case "System.Byte": GUILayout.Label(_primitiveValueReader.ReadByte(bytesAndOffset).ToString()); break; case "System.SByte": GUILayout.Label(_primitiveValueReader.ReadSByte(bytesAndOffset).ToString()); break; case "System.Char": GUILayout.Label(_primitiveValueReader.ReadChar(bytesAndOffset).ToString()); break; case "System.Boolean": GUILayout.Label(_primitiveValueReader.ReadBool(bytesAndOffset).ToString()); break; case "System.Single": GUILayout.Label(_primitiveValueReader.ReadSingle(bytesAndOffset).ToString()); break; case "System.Double": GUILayout.Label(_primitiveValueReader.ReadDouble(bytesAndOffset).ToString()); break; case "System.IntPtr": GUILayout.Label(_primitiveValueReader.ReadPointer(bytesAndOffset).ToString("X")); break; default: if (!typeDescription.isValueType) { ThingInMemory item = GetThingAt(bytesAndOffset.ReadPointer()); if (item == null) { EditorGUI.BeginDisabledGroup(true); GUILayout.Button("Null"); EditorGUI.EndDisabledGroup(); } else { DrawLinks(new ThingInMemory[] { item }); } } else { DrawFields(typeDescription, bytesAndOffset); } break; } }
internal System.Int16 ReadInt16(BytesAndOffset bo) { return(BitConverter.ToInt16(bo.bytes, bo.offset)); }