Finding a type's instance data in .net heap

ぃ、小莉子 提交于 2021-02-07 10:22:24

问题


Let's say I have two class Foo and Bar as follows

public class Foo
{
    private Bar _bar;
    private string _whatever = "whatever";
    public Foo()
    {
        _bar = new Bar();
    }

    public Bar TheBar
        {
            get
            {
                return _bar;
            }

        }
}

public class Bar
{
    public string Name { get; set; }
}

I have an application that attaches to a process that is using these classes. I would like to see all instances of Foo type in .NET heap and inspect the TheBar.Name property or _whatever field of all Foo instances present in .NET heap. I can find the type but I am not sure how to get the instance and see its properties. Any ideas how?

using (DataTarget target = DataTarget.AttachToProcess(processId, 30000))
{
    string dacLocation = target.ClrVersions[0].TryGetDacLocation();
    ClrRuntime runtime = target.CreateRuntime(dacLocation);

    if (runtime != null)
    {
        ClrHeap heap = runtime.GetHeap();
        foreach (ulong obj in heap.EnumerateObjects())
        {
            ClrType type = heap.GetObjectType(obj);
            if (type.Name.Compare("Foo") == 0 )
            {
                // I would like to see value of TheBar.Name property or _whatever field of all instances of Foo type in the heap. How can I do it?
            }
        }
    }
}

回答1:


I don't think you can get property values directly because that would require you to run code and the target might not even be a process but a dump file.

You can definitely get an object's fields and their values. ClrType has a Fields property which you can use to loop through fields. Then you can call GetFieldValue for fields where HasSimpleValue is true.

A simple example:

private static void PrintFieldsForType(ClrRuntime runtime, string targetType)
{
    ClrHeap heap = runtime.GetHeap();
    foreach (var ptr in heap.EnumerateObjects())
    {
        ClrType type = heap.GetObjectType(ptr);
        if (type.Name == targetType)
        {
            foreach(var field in type.Fields)
            {
                if (field.HasSimpleValue)
                {
                    object value = field.GetFieldValue(ptr);
                    Console.WriteLine("{0} ({1}) = {2}", field.Name, field.Type.Name, value);
                }
                else
                {
                    Console.WriteLine("{0} ({1})", field.Name, field.Type.Name);
                }
            }
        }
    }
}

So you could look for a field that has "Name", "_name", or something similar in it. If it is an auto-implemented property, the name will be something like <Name>k__BackingField.

Your scenario is a little more complicated in that you want to go into another object. To do that we can recursively inspect the fields. However note that in the general case you would want to keep track of which objects you've visited so you don't recurse indefinitely.

Here is an example that is more appropriate for this:

private static void PrintFieldsForType(ClrRuntime runtime, TextWriter writer, string targetType)
{
    ClrHeap heap = runtime.GetHeap();
    foreach (var ptr in heap.EnumerateObjects())
    {
        ClrType type = heap.GetObjectType(ptr);
        if (type.Name == targetType)
        {
            writer.WriteLine("{0}:", targetType);
            PrintFields(type, writer, ptr, 0);
        }
    }
}

private static void PrintFields(ClrType type, TextWriter writer, ulong ptr, int indentLevel)
{
    string indent = new string(' ', indentLevel * 4);
    foreach (var field in type.Fields)
    {
        writer.Write(indent);
        if (field.IsObjectReference() && field.Type.Name != "System.String")
        {
            writer.WriteLine("{0} ({1})", field.Name, field.Type.Name);
            ulong nextPtr = (ulong)field.GetFieldValue(ptr);
            PrintFields(field.Type, writer, nextPtr, indentLevel + 1);
        }
        else if (field.HasSimpleValue)
        {
            object value = field.GetFieldValue(ptr);
            writer.WriteLine("{0} ({1}) = {2}", field.Name, field.Type.Name, value);
        }
        else
        {
            writer.WriteLine("{0} ({1})", field.Name, field.Type.Name);
        }
    }
}



回答2:


Here is how you could do it in LINQPad with ClrMD.Extensions:

var session = ClrMDSession.AttachToProcess(processId);
session.EnumerateClrObjects("*Foo").Dump(depth:3);



回答3:


I don't know if it's possible to query the heap in such a way . but an easy soluotion is to do something like this :

public class Foo
{
    public static List<WeakReference<Foo>> allInstances = new List<WeakReference<Foo>>();

    public Foo()
    {
        allInstances.Add(new WeakReference<Foo>(this));
    }
}

Make sure to wrap then in a WeakReference so your collection won't keep them in the heap until the process ends .



来源:https://stackoverflow.com/questions/30062852/finding-a-types-instance-data-in-net-heap

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!