Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 114 additions & 25 deletions ClrMD.Extensions/ClrDynamic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Dynamic;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.RegularExpressions;
using ClrMD.Extensions.LINQPad;
Expand Down Expand Up @@ -60,7 +61,8 @@ public ClrDynamic this[int arrayIndex]
if (!Type.IsArray)
throw new InvalidOperationException(string.Format("Type '{0}' is not an array", Type.Name));

int arrayLength = Type.GetArrayLength(Address);
//int arrayLength = Type.GetArrayLength(Address);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented code should be removed

int arrayLength = Heap.GetObject(Address).AsArray().Length;

if (arrayIndex >= arrayLength)
throw new IndexOutOfRangeException(string.Format("Array index '{0}' is not between 0 and '{1}'", arrayIndex, arrayLength));
Expand All @@ -78,7 +80,8 @@ public int ArrayLength
if (!Type.IsArray)
throw new InvalidOperationException(string.Format("Type '{0}' is not an array", Type.Name));

return Type.GetArrayLength(Address);
//return Type.GetArrayLength(Address);
return Heap.GetObject(Address).AsArray().Length; ;
}
}

Expand All @@ -91,12 +94,14 @@ public object SimpleDisplayValue
get
{
if (Type.IsEnum)
return Type.GetEnumName(SimpleValue) ?? SimpleValue.ToString();
{
return Type.AsEnum().EnumerateValues().FirstOrDefault(f => Convert.ToInt64(f.Value) == Convert.ToInt64(SimpleValue)).Name ?? SimpleValue.ToString();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably won't be a bottleneck, but Type.AsEnum().EnumerateValues().FirstOrDefault seems inefficient. That said I don't know how Type.GetEnumName used to work, maybe it was already implemented with something simliar.

}
return SimpleValue ?? "{null}";
}
}

public ulong Size => Type.GetSize(Address);
public ulong Size => Heap.GetObjectSize(Address, Type);

public dynamic Dynamic => this;

Expand Down Expand Up @@ -130,8 +135,11 @@ public bool IsUndefined()

private static ClrInstanceField FindField(ObfuscatedField oField)
{
TypeName delcaringType = ClrMDSession.Current.ObfuscateType(oField.DeclaringType);
var target = ClrMDSession.Current.Heap.GetTypeByName(delcaringType);
TypeName declaringType = ClrMDSession.Current.ObfuscateType(oField.DeclaringType);

var heapTypes = ClrMDSession.Current.Heap.EnumerateTypes();

var target = heapTypes.First(type => type.Name == declaringType);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will iterate all types to find the declaringType everytime ClrDynamic.FindField is called. This probably introduces a performance bottleneck.

If there isn't a way to lookup by type name, a cached map should be preprocessed to do this only once

return target.GetFieldByName(oField.ObfuscatedName);
}

Expand Down Expand Up @@ -239,10 +247,8 @@ public IEnumerable<ClrDynamic> EnumerateReferences()

public IEnumerable<ulong> EnumerateReferencesAddress()
{
List<ulong> references = new List<ulong>();

Type.EnumerateRefsOfObject(Address, (objRef, fieldOffset) => references.Add(objRef));
return references;
var refs = Heap.GetObject(Address).EnumerateReferences().Select(r => r.Address);
return refs;
}

public IEnumerable<ClrDynamic> EnumerateDictionaryValues()
Expand Down Expand Up @@ -274,8 +280,8 @@ private ClrDynamic GetInnerObject(ulong pointer, ClrType type)

if (type.IsObjectReference)
{
Type.Heap.ReadPointer(pointer, out fieldAddress);

Type.Heap.Runtime.DataTarget.DataReader.ReadPointer(pointer, out fieldAddress);
//Console.WriteLine(fieldAddress);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented line

if (!type.IsSealed && fieldAddress != NullAddress)
actualType = type.Heap.GetSafeObjectType(fieldAddress);
}
Expand All @@ -284,18 +290,19 @@ private ClrDynamic GetInnerObject(ulong pointer, ClrType type)
// Unfortunately, ClrType.GetValue for primitives assumes that the value is boxed,
// we decrement PointerSize because it will be added when calling ClrType.GetValue.
// ClrMD should be updated in a future version to include ClrType.GetValue(int interior).
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still true in ClrMD v2?

fieldAddress = pointer - (ulong)type.Heap.PointerSize;

fieldAddress = pointer - (ulong)type.Heap.Runtime.DataTarget.DataReader.PointerSize;
}
else if (type.IsValueClass)
else if (type.IsValueType)
{
fieldAddress = pointer;
}
else
{
throw new NotSupportedException(string.Format("Object type not supported '{0}'", type.Name));
throw new NotSupportedException($"Object type not supported '{type.Name}'");
}

return new ClrDynamic(fieldAddress, actualType, !type.IsObjectReference);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This space shouldn't have been removed

return new ClrDynamic(fieldAddress, actualType,!type.IsObjectReference);
}

#region Operators
Expand All @@ -305,7 +312,7 @@ public override bool Equals(object other)
if (HasSimpleValue)
{
if (other is string && Type.IsEnum)
return Equals(other, Type.GetEnumName(SimpleValue));
return Equals(other, Type.AsEnum().EnumerateValues().FirstOrDefault(f => Convert.ToInt64(f.Value) == Convert.ToInt64(SimpleValue)).Name);

return Equals(other, SimpleValue);
}
Expand Down Expand Up @@ -460,7 +467,7 @@ public static explicit operator double(ClrDynamic obj)
public static explicit operator string(ClrDynamic obj)
{
if (obj.Type.IsEnum)
return obj.Type.GetEnumName(obj.SimpleValue);
return obj.Type.AsEnum().EnumerateValues().FirstOrDefault(f => (int)f.Value == (int)obj.SimpleValue).Name;

return (string)obj.SimpleValue;
}
Expand Down Expand Up @@ -670,10 +677,92 @@ public static object GetSimpleValue(ClrDynamic obj)
ClrType type = obj.Type;
ClrHeap heap = type.Heap;

if (type.IsPrimitive || type.IsString)
return type.GetValue(obj.Address);
ulong unboxedAddress = obj.Address + (ulong)heap.Runtime.DataTarget.DataReader.PointerSize;

switch (type.ElementType)
{
case ClrElementType.String:
{
return heap.GetObject(obj.Address).AsString();
}

case ClrElementType.Boolean:
{
return heap.Runtime.DataTarget.DataReader.Read<bool>(unboxedAddress);
}

case ClrElementType.Int8:
{
return heap.Runtime.DataTarget.DataReader.Read<byte>(unboxedAddress);
}

case ClrElementType.Int16:
{
return heap.Runtime.DataTarget.DataReader.Read<short>(unboxedAddress);
}

case ClrElementType.Int32:
{
return heap.Runtime.DataTarget.DataReader.Read<int>(unboxedAddress);
}

case ClrElementType.Int64:
{
return heap.Runtime.DataTarget.DataReader.Read<long>(unboxedAddress);
}

case ClrElementType.UInt8:
{
return heap.Runtime.DataTarget.DataReader.Read<sbyte>(unboxedAddress);
}
case ClrElementType.UInt16:
{
return heap.Runtime.DataTarget.DataReader.Read<ushort>(unboxedAddress);
}

case ClrElementType.UInt32:
{
return heap.Runtime.DataTarget.DataReader.Read<uint>(unboxedAddress);
}

case ClrElementType.UInt64:
{
return heap.Runtime.DataTarget.DataReader.Read<ulong>(unboxedAddress);
}

case ClrElementType.NativeInt:
{
heap.Runtime.DataTarget.DataReader.ReadPointer(unboxedAddress, out var value);
return (long)value;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why the (long) cast is needed here, it looks like this case should be included with the others just below.

}

case ClrElementType.FunctionPointer:
case ClrElementType.Pointer:
case ClrElementType.NativeUInt:
{
heap.Runtime.DataTarget.DataReader.ReadPointer(unboxedAddress, out var value);
return value;
}

case ClrElementType.Float:
{
return heap.Runtime.DataTarget.DataReader.Read<float>(unboxedAddress);
}

case ClrElementType.Double:
{
return heap.Runtime.DataTarget.DataReader.Read<double>(unboxedAddress);
}

case ClrElementType.Char:
{
return heap.Runtime.DataTarget.DataReader.Read<char>(unboxedAddress);
}


}

ulong address = obj.IsInterior ? obj.Address : obj.Address + (ulong)heap.PointerSize;
ulong address = obj.IsInterior ? obj.Address : obj.Address + (ulong)heap.Runtime.DataTarget.DataReader.PointerSize;

switch (type.Name)
{
Expand Down Expand Up @@ -715,7 +804,7 @@ public static string GetSimpleValueString(ClrDynamic obj)

ClrType type = obj.Type;
if (type != null && type.IsEnum)
return type.GetEnumName(value) ?? value.ToString();
return type.AsEnum().EnumerateValues().FirstOrDefault(f => Convert.ToInt64(value) == Convert.ToInt64(f.Value)).Name ?? value.ToString();

DateTime? dateTime = value as DateTime?;
if (dateTime != null)
Expand All @@ -726,13 +815,13 @@ public static string GetSimpleValueString(ClrDynamic obj)

private static byte[] ReadBuffer(ClrHeap heap, ulong address, int length)
{
byte[] buffer = new byte[length];
int byteRead = heap.ReadMemory(address, buffer, 0, buffer.Length);
Span<byte> buffer = new byte[length];
int byteRead = heap.Runtime.DataTarget.DataReader.Read(address, buffer);

if (byteRead != length)
throw new InvalidOperationException(string.Format("Expected to read {0} bytes and actually read {1}", length, byteRead));

return buffer;
return buffer.ToArray();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is creating a new array, it would be better to declare buffer as byte[], there will be an implicit conversion to Span when calling DataReader.Read

}

private static DateTime GetDateTime(ulong dateData)
Expand Down
2 changes: 1 addition & 1 deletion ClrMD.Extensions/ClrMD.Extensions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="1.0.2" />
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.0.145301" />
</ItemGroup>

</Project>
Expand Down
Loading