在 .net 堆中查找类型的实例数据
Finding a type's instance data in .net heap
假设我有两个 class Foo 和 Bar,如下所示
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; }
}
我有一个附加到使用这些 classes 的进程的应用程序。我想在 .NET 堆中查看 Foo 类型的所有实例并检查所有的 TheBar.Name 属性 或 _whatever 字段
.NET 堆中存在 Foo 实例。我可以找到类型,但不确定如何获取实例并查看其属性。有什么想法吗?
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?
}
}
}
}
不知道这样查询堆是否可行。
但一个简单的解决方案是做这样的事情:
public class Foo
{
public static List<WeakReference<Foo>> allInstances = new List<WeakReference<Foo>>();
public Foo()
{
allInstances.Add(new WeakReference<Foo>(this));
}
}
确保将 then 包装在 WeakReference 中,这样您的集合就不会在进程结束之前将它们保留在堆中。
我认为您不能直接获取 属性 值,因为这需要您 运行 编码,目标甚至可能不是进程而是转储文件。
你绝对可以获得对象的字段及其值。 ClrType 有一个 Fields 属性 ,您可以使用它来遍历字段。然后您可以为 HasSimpleValue 为 true 的字段调用 GetFieldValue。
一个简单的例子:
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);
}
}
}
}
}
因此您可以查找包含 "Name"、“_name”或类似内容的字段。如果它是自动实现的 属性,名称将类似于 <Name>k__BackingField
.
你的场景有点复杂,因为你想进入另一个对象。为此,我们可以递归地检查字段。但是请注意,在一般情况下,您会希望跟踪您访问过的对象,这样您就不会无限期地递归。
这里有一个更适合这个的例子:
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);
}
}
}
以下是在 LINQPad 中使用 ClrMD.Extensions 的方法:
var session = ClrMDSession.AttachToProcess(processId);
session.EnumerateClrObjects("*Foo").Dump(depth:3);
假设我有两个 class Foo 和 Bar,如下所示
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; }
}
我有一个附加到使用这些 classes 的进程的应用程序。我想在 .NET 堆中查看 Foo 类型的所有实例并检查所有的 TheBar.Name 属性 或 _whatever 字段 .NET 堆中存在 Foo 实例。我可以找到类型,但不确定如何获取实例并查看其属性。有什么想法吗?
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?
}
}
}
}
不知道这样查询堆是否可行。 但一个简单的解决方案是做这样的事情:
public class Foo
{
public static List<WeakReference<Foo>> allInstances = new List<WeakReference<Foo>>();
public Foo()
{
allInstances.Add(new WeakReference<Foo>(this));
}
}
确保将 then 包装在 WeakReference 中,这样您的集合就不会在进程结束之前将它们保留在堆中。
我认为您不能直接获取 属性 值,因为这需要您 运行 编码,目标甚至可能不是进程而是转储文件。
你绝对可以获得对象的字段及其值。 ClrType 有一个 Fields 属性 ,您可以使用它来遍历字段。然后您可以为 HasSimpleValue 为 true 的字段调用 GetFieldValue。
一个简单的例子:
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);
}
}
}
}
}
因此您可以查找包含 "Name"、“_name”或类似内容的字段。如果它是自动实现的 属性,名称将类似于 <Name>k__BackingField
.
你的场景有点复杂,因为你想进入另一个对象。为此,我们可以递归地检查字段。但是请注意,在一般情况下,您会希望跟踪您访问过的对象,这样您就不会无限期地递归。
这里有一个更适合这个的例子:
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);
}
}
}
以下是在 LINQPad 中使用 ClrMD.Extensions 的方法:
var session = ClrMDSession.AttachToProcess(processId);
session.EnumerateClrObjects("*Foo").Dump(depth:3);