Skip to content

Commit c1551ba

Browse files
committed
Support reading arrays
1 parent b4c9f6e commit c1551ba

File tree

3 files changed

+89
-30
lines changed

3 files changed

+89
-30
lines changed

UMS.Analysis/SnapshotFile.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ public RawManagedObjectInfo ParseManagedObjectInfo(ulong address)
140140
throw new($"Failed to resolve type for object at {address:X}");
141141
}
142142

143-
info.Flags = ReadSingleValueType<TypeFlags>(EntryType.TypeDescriptions_Flags, info.TypeDescriptionIndex);
143+
var typeIndex = info.TypeDescriptionIndex;
144+
info.Flags = GetTypeFlagsByIndex(typeIndex);
144145
info.Size = SizeOfObjectInBytes(info, heap);
145146
info.Data = heap[..info.Size].ToArray();
146147
info.SelfAddress = address;
@@ -150,6 +151,14 @@ public RawManagedObjectInfo ParseManagedObjectInfo(ulong address)
150151
return info;
151152
}
152153

154+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
155+
public TypeFlags GetTypeFlagsByIndex(int typeIndex)
156+
=> ReadSingleValueType<TypeFlags>(EntryType.TypeDescriptions_Flags, typeIndex);
157+
158+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
159+
public int GetTypeDescriptionSizeBytes(int index)
160+
=> ReadSingleValueType<int>(EntryType.TypeDescriptions_Size, index);
161+
153162
public int SizeOfObjectInBytes(RawManagedObjectInfo info, Span<byte> heap)
154163
{
155164
if (info.Flags.HasFlag(TypeFlags.Array))
@@ -158,7 +167,7 @@ public int SizeOfObjectInBytes(RawManagedObjectInfo info, Span<byte> heap)
158167
if (info.TypeDescriptionIndex == WellKnownTypes.String)
159168
return GetObjectSizeFromStringInBytes(info, heap);
160169

161-
return ReadSingleValueType<int>(EntryType.TypeDescriptions_Size, info.TypeDescriptionIndex);
170+
return GetTypeDescriptionSizeBytes(info.TypeDescriptionIndex);
162171
}
163172

164173
private int GetObjectSizeFromStringInBytes(RawManagedObjectInfo info, Span<byte> heap)
@@ -177,7 +186,7 @@ private int GetObjectSizeFromStringInBytes(RawManagedObjectInfo info, Span<byte>
177186

178187
private int GetObjectSizeFromArrayInBytes(RawManagedObjectInfo info, Span<byte> heap)
179188
{
180-
var arrayLength = ReadArrayLength(info, heap);
189+
var arrayLength = ReadArrayLength(info.Flags, heap);
181190

182191
if (arrayLength > heap.Length)
183192
{
@@ -200,7 +209,7 @@ private int GetObjectSizeFromArrayInBytes(RawManagedObjectInfo info, Span<byte>
200209
return VirtualMachineInformation.ArrayHeaderSize + elementSize * arrayLength;
201210
}
202211

203-
private int ReadArrayLength(RawManagedObjectInfo info, Span<byte> heap)
212+
public int ReadArrayLength(TypeFlags flags, Span<byte> heap)
204213
{
205214
var heapTemp = heap[VirtualMachineInformation.ArrayBoundsOffsetInHeader..]; //Seek to the bounds offset
206215
var bounds = ReadPointer(heapTemp);
@@ -213,7 +222,7 @@ private int ReadArrayLength(RawManagedObjectInfo info, Span<byte> heap)
213222
return 0;
214223

215224
var length = 1;
216-
var rank = (int)(info.Flags & TypeFlags.ArrayRankMask) >> 16;
225+
var rank = (int)(flags & TypeFlags.ArrayRankMask) >> 16;
217226
for (var i = 0; i < rank; i++)
218227
{
219228
length *= MemoryMarshal.Read<int>(boundsHeap);

UMS.Analysis/Structures/Objects/ComplexFieldValue.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public struct ComplexFieldValue : IFieldValue
1010

1111
public ManagedClassInstance? Value { get; }
1212

13-
public ComplexFieldValue(SnapshotFile file, BasicFieldInfoCache info, ManagedClassInstance parent, Span<byte> data, int depth)
13+
public ComplexFieldValue(SnapshotFile file, BasicFieldInfoCache info, ManagedClassInstance parent, Span<byte> data, int depth, bool array)
1414
{
1515
IsNull = false;
1616
FailedToParse = false;
@@ -22,7 +22,7 @@ public ComplexFieldValue(SnapshotFile file, BasicFieldInfoCache info, ManagedCla
2222
var size = info.FieldTypeSize;
2323
if (size > 0) //Have observed a negative size (-8), MIGHT be for pointers to value types, so we'll fall back to the below logic.
2424
{
25-
var vtInst = new ManagedClassInstance(file, info.TypeDescriptionIndex, info.Flags, size, data, parent, depth, LoadedReason.InstanceField, info.FieldIndex);
25+
var vtInst = new ManagedClassInstance(file, info.TypeDescriptionIndex, info.Flags, size, data, parent, depth, array ? LoadedReason.ArrayElement : LoadedReason.InstanceField, info.FieldIndex);
2626
Value = vtInst;
2727
return;
2828
}
@@ -38,7 +38,7 @@ public ComplexFieldValue(SnapshotFile file, BasicFieldInfoCache info, ManagedCla
3838
return;
3939
}
4040

41-
var mci = file.GetOrCreateManagedClassInstance(ptr, parent, depth, LoadedReason.InstanceField, info.FieldIndex);
41+
var mci = file.GetOrCreateManagedClassInstance(ptr, parent, depth, array ? LoadedReason.ArrayElement : LoadedReason.InstanceField, info.FieldIndex);
4242

4343
if (mci == null)
4444
{

UMS.Analysis/Structures/Objects/ManagedClassInstance.cs

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,35 @@ public ManagedClassInstance(SnapshotFile file, RawManagedObjectInfo info, Manage
7878

7979
if (IsArray)
8080
{
81-
//TODO array items
82-
Fields = Array.Empty<IFieldValue>();
81+
var arrayElementCount = file.ReadArrayLength(TypeDescriptionFlags, data);
82+
83+
if (arrayElementCount == 0)
84+
{
85+
Fields = Array.Empty<IFieldValue>();
86+
return;
87+
}
88+
89+
var typeInfo = file.GetTypeInfo(info.TypeDescriptionIndex);
90+
91+
if (typeInfo.BaseTypeIndex < 0)
92+
{
93+
Console.WriteLine("WARNING: Skipping uninitialized array type");
94+
Fields = Array.Empty<IFieldValue>();
95+
return;
96+
}
97+
98+
var elementType = file.GetTypeInfo(typeInfo.BaseTypeIndex);
99+
var elementFlags = file.GetTypeFlagsByIndex(elementType.TypeIndex);
100+
var elementTypeSize = (elementFlags & TypeFlags.ValueType) != 0 ? file.GetTypeDescriptionSizeBytes(elementType.TypeIndex) : 8;
101+
var arrayData = info.Data.AsSpan(file.VirtualMachineInformation.ArrayHeaderSize..);
102+
103+
Fields = new IFieldValue[arrayElementCount];
104+
for (var i = 0; i < arrayElementCount; i++)
105+
{
106+
var elementData = arrayData[(i * elementTypeSize)..];
107+
Fields[i] = ReadFieldValue(file, elementData, depth, elementFlags, elementTypeSize, elementType.TypeIndex, i);
108+
}
109+
83110
return;
84111
}
85112

@@ -111,30 +138,48 @@ private IFieldValue[] ReadFields(SnapshotFile file, Span<byte> data, int depth)
111138
if (isValueType)
112139
fieldOffset -= file.VirtualMachineInformation.ObjectHeaderSize;
113140

114-
var fieldPtr = data[fieldOffset..];
115-
116-
//For all integer types, we just handle unsigned as signed
117-
if (info.TypeDescriptionIndex == file.WellKnownTypes.String)
118-
fields[index] = new StringFieldValue(file, fieldPtr);
119-
else if (info.TypeDescriptionIndex == file.WellKnownTypes.Boolean || info.TypeDescriptionIndex == file.WellKnownTypes.Byte)
120-
fields[index] = new IntegerFieldValue(fieldPtr[0]);
121-
else if (info.TypeDescriptionIndex == file.WellKnownTypes.Int16 || info.TypeDescriptionIndex == file.WellKnownTypes.UInt16 || info.TypeDescriptionIndex == file.WellKnownTypes.Char)
122-
fields[index] = new IntegerFieldValue(BitConverter.ToInt16(fieldPtr));
123-
else if (info.TypeDescriptionIndex == file.WellKnownTypes.Int32 || info.TypeDescriptionIndex == file.WellKnownTypes.UInt32)
124-
fields[index] = new IntegerFieldValue(BitConverter.ToInt32(fieldPtr));
125-
else if (info.TypeDescriptionIndex == file.WellKnownTypes.Int64 || info.TypeDescriptionIndex == file.WellKnownTypes.UInt64 || info.TypeDescriptionIndex == file.WellKnownTypes.IntPtr)
126-
fields[index] = new IntegerFieldValue(BitConverter.ToInt64(fieldPtr));
127-
else if (info.TypeDescriptionIndex == file.WellKnownTypes.Single)
128-
fields[index] = new FloatingPointFieldValue(BitConverter.ToSingle(fieldPtr));
129-
else if (info.TypeDescriptionIndex == file.WellKnownTypes.Double)
130-
fields[index] = new FloatingPointFieldValue(BitConverter.ToDouble(fieldPtr));
131-
else
132-
fields[index] = new ComplexFieldValue(file, info, this, fieldPtr, depth + 1);
141+
var fieldData = data[fieldOffset..];
142+
143+
fields[index] = ReadFieldValue(file, info, fieldData, depth, false);
133144
}
134145

135146
return fields;
136147
}
137148

149+
private IFieldValue ReadFieldValue(SnapshotFile file, BasicFieldInfoCache info, Span<byte> fieldData, int depth, bool array)
150+
{
151+
//For all integer types, we just handle unsigned as signed
152+
if (info.TypeDescriptionIndex == file.WellKnownTypes.String)
153+
return new StringFieldValue(file, fieldData);
154+
if (info.TypeDescriptionIndex == file.WellKnownTypes.Boolean || info.TypeDescriptionIndex == file.WellKnownTypes.Byte)
155+
return new IntegerFieldValue(fieldData[0]);
156+
if (info.TypeDescriptionIndex == file.WellKnownTypes.Int16 || info.TypeDescriptionIndex == file.WellKnownTypes.UInt16 || info.TypeDescriptionIndex == file.WellKnownTypes.Char)
157+
return new IntegerFieldValue(BitConverter.ToInt16(fieldData));
158+
if (info.TypeDescriptionIndex == file.WellKnownTypes.Int32 || info.TypeDescriptionIndex == file.WellKnownTypes.UInt32)
159+
return new IntegerFieldValue(BitConverter.ToInt32(fieldData));
160+
if (info.TypeDescriptionIndex == file.WellKnownTypes.Int64 || info.TypeDescriptionIndex == file.WellKnownTypes.UInt64 || info.TypeDescriptionIndex == file.WellKnownTypes.IntPtr)
161+
return new IntegerFieldValue(BitConverter.ToInt64(fieldData));
162+
if (info.TypeDescriptionIndex == file.WellKnownTypes.Single)
163+
return new FloatingPointFieldValue(BitConverter.ToSingle(fieldData));
164+
if (info.TypeDescriptionIndex == file.WellKnownTypes.Double)
165+
return new FloatingPointFieldValue(BitConverter.ToDouble(fieldData));
166+
167+
return new ComplexFieldValue(file, info, this, fieldData, depth + 1, array);
168+
}
169+
170+
private IFieldValue ReadFieldValue(SnapshotFile file, Span<byte> fieldData, int depth, TypeFlags fieldTypeFlags, int fieldTypeSize, int fieldTypeIndex, int arrayOffset)
171+
{
172+
BasicFieldInfoCache info = new()
173+
{
174+
Flags = fieldTypeFlags,
175+
FieldIndex = arrayOffset,
176+
TypeDescriptionIndex = fieldTypeIndex,
177+
FieldTypeSize = fieldTypeSize,
178+
};
179+
180+
return ReadFieldValue(file, info, fieldData, depth, true);
181+
}
182+
138183
private bool IsEnumType(SnapshotFile file)
139184
=> TypeInfo.BaseTypeIndex == file.WellKnownTypes.Enum;
140185

@@ -221,8 +266,13 @@ private void AppendRetentionReason(StringBuilder sb, SnapshotFile file, ManagedC
221266
break;
222267
}
223268
case LoadedReason.ArrayElement:
224-
//TODO
269+
{
270+
var parentName = file.GetTypeName(parent.TypeInfo.TypeIndex);
271+
sb.Append("Array Element ").Append(child.FieldIndexOrArrayOffset).Append(" of ");
272+
sb.Append(parentName).Append(" at 0x").Append(parent.ObjectAddress.ToString("X"));
273+
sb.Append(" <- ");
225274
break;
275+
}
226276
default:
227277
throw new ArgumentOutOfRangeException(nameof(child), "Invalid LoadedReason");
228278
}

0 commit comments

Comments
 (0)