Skip to content

Commit 3829193

Browse files
author
fremag
committed
ClrDump: GetFieldNames can't find fields from interface type
Closes #140
1 parent fac66ca commit 3829193

File tree

5 files changed

+99
-39
lines changed

5 files changed

+99
-39
lines changed

MemoDummy/ComplexObject.cs

+35-6
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,24 @@ struct StructData
1313
public interface IMyInterface
1414
{
1515
int Id { get; }
16+
double MyDoubleValue { get; }
1617
}
1718

1819
public class MyInterfaceImpl_V1 : IMyInterface
1920
{
2021
private static int n = 0;
21-
public int Id { get; }
22-
22+
protected int backfield_id;
23+
public int Id {
24+
get {
25+
return backfield_id;
26+
}
27+
}
28+
public double MyDoubleValue { get; set; }
2329
public string name;
2430
public MyInterfaceImpl_V1()
2531
{
26-
Id = n++;
32+
backfield_id = n++;
33+
MyDoubleValue = backfield_id + 0.001;
2734
name = $"#{0:Id}";
2835
}
2936
}
@@ -33,6 +40,8 @@ public class MyInterfaceImpl_V2 : MyInterfaceImpl_V1
3340
public DateTime TimeStamp { get; set; }
3441
public MyInterfaceImpl_V2()
3542
{
43+
backfield_id *= -1;
44+
MyDoubleValue = backfield_id - 0.001;
3645
TimeStamp = DateTime.Now;
3746
}
3847
}
@@ -44,6 +53,26 @@ class InternalData
4453
public double X { get; set; }
4554
public double Y { get; set; }
4655
}
56+
public abstract class AnAbstractType
57+
{
58+
public abstract double AbstractDoubleProperty { get; }
59+
public double MyDoubleProperty { get; set; }
60+
}
61+
62+
public class AnAbstractTypeImpl : AnAbstractType
63+
{
64+
private static int n = 0;
65+
public new double MyDoubleProperty { get; }
66+
public override double AbstractDoubleProperty { get; }
67+
68+
public AnAbstractTypeImpl()
69+
{
70+
n++;
71+
AbstractDoubleProperty = -n - 0.0005;
72+
base.MyDoubleProperty = n + 0.0005;
73+
this.MyDoubleProperty = n + 0.0006;
74+
}
75+
}
4776

4877
internal class ComplexObject
4978
{
@@ -54,7 +83,7 @@ internal class ComplexObject
5483
internal StructData StructData => structData;
5584

5685
double value;
57-
InternalData data;
86+
InternalData data;
5887
bool isEven;
5988
DateTime date;
6089
TimeSpan time;
@@ -64,7 +93,7 @@ internal class ComplexObject
6493
int[] someInts;
6594
double[] someDoubles;
6695
IMyInterface myInterface;
67-
96+
AnAbstractType aFieldWithAbstractType;
6897
public ComplexObject()
6998
{
7099
id = n++;
@@ -102,7 +131,7 @@ public ComplexObject()
102131
someDoubles[i] = 2 * (n + i);
103132
}
104133
myInterface = id % 2 == 0 ? new MyInterfaceImpl_V1() : new MyInterfaceImpl_V2();
134+
aFieldWithAbstractType = new AnAbstractTypeImpl();
105135
}
106-
107136
}
108137
}

MemoScope/Core/ClrDump.cs

+58-27
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class ClrDump : IClrDump
3535

3636
public List<ClrHandle> Handles => Runtime.EnumerateHandles().ToList();
3737
public List<ulong> FinalizerQueueObjectAddresses => Runtime.EnumerateFinalizerQueueObjectAddresses().ToList();
38-
public IEnumerable<IGrouping<ClrType, ulong>> FinalizerQueueObjectAddressesByType => Runtime.EnumerateFinalizerQueueObjectAddresses().GroupBy( address => GetObjectType(address));
38+
public IEnumerable<IGrouping<ClrType, ulong>> FinalizerQueueObjectAddressesByType => Runtime.EnumerateFinalizerQueueObjectAddresses().GroupBy(address => GetObjectType(address));
3939
public IList<ClrThread> Threads => Runtime.Threads;
4040
public ClrThreadPool ThreadPool => Runtime.GetThreadPool();
4141
public List<ClrType> AllTypes => Heap.EnumerateTypes().ToList();
@@ -57,7 +57,7 @@ public Dictionary<int, ThreadProperty> ThreadProperties
5757
Dictionary<int, ThreadProperty> threadProperties;
5858
private readonly SingleThreadWorker worker;
5959
private ClrDumpCache cache;
60-
60+
6161
public ClrDump(DataTarget target, string dumpPath, MessageBus msgBus)
6262
{
6363
Id = n++;
@@ -94,7 +94,7 @@ private void InitRuntime()
9494

9595
public List<ClrType> GetTypes()
9696
{
97-
List<ClrType> t = worker.Eval( () => t = AllTypes);
97+
List<ClrType> t = worker.Eval(() => t = AllTypes);
9898
return t;
9999
}
100100

@@ -110,7 +110,7 @@ internal void Dispose()
110110
logger.Debug("Cache dispose");
111111
cache.Dispose();
112112
logger.Debug("Runtime.DataTarget.Dispose");
113-
Run( () => Runtime?.DataTarget?.Dispose());
113+
Run(() => Runtime?.DataTarget?.Dispose());
114114
logger.Debug("Worker.Dispose");
115115
worker.Dispose();
116116
}
@@ -205,7 +205,7 @@ private object GetSimpleValueImpl(ulong address, ClrType type)
205205
var value = SimpleValueHelper.GetSimpleValue(address, type, false);
206206
return value;
207207
}
208-
208+
209209
return address;
210210
}
211211

@@ -225,28 +225,30 @@ public object GetFieldValue(ulong address, ClrType type, ClrInstanceField field)
225225
return obj;
226226
}
227227

228-
internal Dictionary<string, ClrType> GetFieldNames(ClrType type)
228+
internal List<FieldInfo> GetFieldInfos(ClrType type)
229229
{
230-
Dictionary<string, ClrType> fieldNames = Eval(() => GetFieldNamesImpl(type));
230+
List<FieldInfo> fieldNames = Eval(() => GetFieldNamesImpl(type));
231231
return fieldNames;
232232
}
233233

234-
private Dictionary<string, ClrType> GetFieldNamesImpl(ClrType type)
234+
private List<FieldInfo> GetFieldNamesImpl(ClrType type)
235235
{
236-
var fieldNames = type.Fields.ToDictionary(f => f.Name, f => f.Type);
237-
if( type.IsInterface)
236+
var fieldNames = type.Fields.Select(f => new FieldInfo(f.Name, f.Type)).ToList();
237+
if (type.IsInterface || type.IsAbstract)
238238
{
239-
var properties = type.Methods.Where(meth => meth.Name.StartsWith("get_") && meth.IsVirtual);
240-
foreach (var meth in properties)
239+
foreach (var someType in Heap.EnumerateTypes())
241240
{
242-
var propName = meth.Name.Substring("get_".Length);
243-
// BUG: this is not the real type of the field !
244-
// TODO: get the return type from the method
245-
fieldNames[propName] = meth.Type;
241+
if (type.IsInterface && someType.Interfaces.Any(interf => interf.Name == type.Name))
242+
{
243+
fieldNames.AddRange(GetFieldNamesImpl(someType));
244+
}
245+
if (type.IsAbstract && someType.BaseType == type)
246+
{
247+
fieldNames.AddRange(GetFieldNamesImpl(someType));
248+
}
246249
}
247-
248250
}
249-
return fieldNames;
251+
return fieldNames.Distinct().ToList();
250252
}
251253

252254
public object GetFieldValueImpl(ulong address, ClrType type, List<ClrInstanceField> fields)
@@ -257,7 +259,7 @@ public object GetFieldValueImpl(ulong address, ClrType type, List<ClrInstanceFie
257259
{
258260
var field = fields[i];
259261
obj = obj[field];
260-
if( obj.IsNull )
262+
if (obj.IsNull)
261263
{
262264
return null;
263265
}
@@ -272,7 +274,13 @@ public object GetFieldValueImpl(ulong address, ClrType type, List<string> fieldN
272274
for (int i = 0; i < fieldNames.Count; i++)
273275
{
274276
var fieldName = fieldNames[i];
275-
obj = obj[fieldName];
277+
ClrInstanceField field = obj.GetField(fieldName);
278+
if( field == null)
279+
{
280+
return null;
281+
}
282+
283+
obj = obj[field];
276284
if (obj.IsNull)
277285
{
278286
return null;
@@ -313,7 +321,7 @@ public bool HasReferers(ulong address)
313321

314322
public int CountReferers(ulong address)
315323
{
316-
var count = cache.CountReferers(address) ;
324+
var count = cache.CountReferers(address);
317325
return count;
318326
}
319327

@@ -384,15 +392,15 @@ public ClrMethod GetMethodByHandle(ulong methodDescriptorPtr)
384392
}
385393

386394
// Find the field in instance at address that references refAddress
387-
public string GetFieldNameReference(ulong refAddress, ulong address, bool prefixWithType=false)
395+
public string GetFieldNameReference(ulong refAddress, ulong address, bool prefixWithType = false)
388396
{
389397
return Eval(() => GetFieldNameReferenceImpl(refAddress, address, prefixWithType));
390398
}
391399

392400
public string GetFieldNameReferenceImpl(ulong refAddress, ulong address, bool prefixWithType)
393-
{
401+
{
394402
ClrType type = GetObjectTypeImpl(address);
395-
if( type == null)
403+
if (type == null)
396404
{
397405
return "Unknown";
398406
}
@@ -504,16 +512,16 @@ public List<ClrRoot> GetClrRoots()
504512

505513
public bool HasField(ClrType clrType)
506514
{
507-
if( clrType.IsPrimitive)
515+
if (clrType.IsPrimitive)
508516
{
509517
return false;
510518
}
511-
if ( clrType.Fields.Any())
519+
if (clrType.Fields.Any())
512520
{
513521
return true;
514522
}
515523

516-
if( clrType.IsInterface && clrType.Methods.Any(meth => meth.Name.StartsWith("get_")))
524+
if (clrType.IsInterface && clrType.Methods.Any(meth => meth.Name.StartsWith("get_")))
517525
{
518526
return true;
519527
}
@@ -531,4 +539,27 @@ public class ThreadProperty
531539
public int Priority { get; set; }
532540
public int ManagedId { get; set; }
533541
}
542+
543+
public class FieldInfo : IEquatable<FieldInfo>
544+
{
545+
public string Name { get; }
546+
public ClrType FieldType { get; }
547+
public FieldInfo(string name, ClrType fieldType)
548+
{
549+
Name = name;
550+
FieldType = fieldType;
551+
}
552+
public bool Equals(FieldInfo fieldInfo)
553+
{
554+
return fieldInfo.Name == Name && fieldInfo.FieldType.Name == FieldType.Name;
555+
}
556+
public override bool Equals(object o )
557+
{
558+
return ((IEquatable<FieldInfo>)this).Equals((FieldInfo)o);
559+
}
560+
public override int GetHashCode()
561+
{
562+
return Name.GetHashCode() * 37 + FieldType.Name.GetHashCode();
563+
}
564+
}
534565
}

MemoScope/Core/Data/ClrObject.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public ClrObject this[string fieldName]
2222
ClrInstanceField field = GetField(fieldName);
2323

2424
if (field == null)
25-
throw new ArgumentException(string.Format("Field '{0}' not found in Type '{1}'", fieldName, Type.Name));
25+
throw new ArgumentException($"Field '{fieldName}' not found in Type '{Type.Name}'");
2626

2727
return this[field];
2828
}

MemoScope/Modules/Instances/FieldNode.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public List<FieldNode> Children
4040
{
4141
get
4242
{
43-
var fieldTypesByName = ClrDump.GetFieldNames(ClrType);
44-
var fieldNodes = fieldTypesByName.Select(kvp => new FieldNode(kvp.Key, kvp.Value, ClrDump, this));
43+
var fieldInfos = ClrDump.GetFieldInfos(ClrType);
44+
var fieldNodes = fieldInfos.Select(fieldInfo => new FieldNode(fieldInfo.Name, fieldInfo.FieldType, ClrDump, this));
4545
return fieldNodes.ToList();
4646
}
4747
}

MemoScope/Modules/Instances/InstancesModule.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace MemoScope.Modules.Instances
1919
{
2020
public partial class InstancesModule : UIClrDumpModule, UIDataProvider<ClrDumpType>, UIDataProvider<ClrDumpObject>, IModelFilter
2121
{
22-
private Dictionary<string, ClrType> fieldTypesByNames;
22+
private List<FieldInfo> fieldInfos;
2323
private AddressList AddressList { get; set; }
2424
private List<Func<bool>> filters;
2525
private FieldAccessor myFieldAccessor;
@@ -193,13 +193,13 @@ public override void Init()
193193
{
194194
var dump = AddressList.ClrDump;
195195
var type = AddressList.ClrType;
196-
fieldTypesByNames = dump.GetFieldNames(type);
196+
fieldInfos = dump.GetFieldInfos(type);
197197
}
198198

199199
// Gui thread
200200
public override void PostInit()
201201
{
202-
var fieldNodes = fieldTypesByNames.Select(kvp => new FieldNode(kvp.Key, kvp.Value, AddressList.ClrDump));
202+
var fieldNodes = fieldInfos.Select(fieldInfo=> new FieldNode(fieldInfo.Name, fieldInfo.FieldType, AddressList.ClrDump));
203203
dtlvFields.Roots = fieldNodes;
204204
dlvAdresses.VirtualListDataSource = new InstanceVirtualSource(dlvAdresses, AddressList, filteredAddresses);
205205
Summary = $"{AddressList.Addresses.Count:###,###,###,##0} instances";

0 commit comments

Comments
 (0)