如何使用ClrMD正确使用非原始ClrInstanceField值?

时间:2014-03-03 15:10:14

标签: c# .net windbg sos clrmd

我有一些非常大的托管进程内存转储,我试图从中获取大量的统计信息 - 以及能够呈现交互式视图 - 堆上相当深的对象图。在WinDbg中使用SOS设置与!do <address>类似的内容,您可以在其中不断点击属性并查看其值,只在更友好的UI中进行比较,以便比较多个对象。

我发现 Microsoft.Diagnostics.Runtime (ClrMD)特别适合这项任务,但我很难处理数组字段,我有点困惑关于对象领域,我工作得更好。


阵列: 如果我将一个地址直接放在堆上的数组,并使用prefer_dml 1ClrType.GetArrayLength一切正常,但是一旦我挖掘另一个对象上的字段,我不确定我有什么价值当ClrType.GetArrayElementValueClrInstanceField.GetValue时,我会从ClrInstanceField.ElementType获得(我还没有遇到ClrElementType.SZArray在我的对象图中挖掘,但我也想处理它)。

修改 我刚刚决定使用Array ClrType取消引用数组字段(使用System.UInt64来计算存储数组指针的地址),然后我可以像使用EnumerateObjects一样使用它。我现在遇到一些不支持parent address + offset of the array field属性的数组有些困难。我还没有使用Structs数组进行测试,所以我也想知道这是否是内联结构的C风格分配,就像ArrayComponentType一样,或者它是否是指向堆上结构的指针数组。 int[]是我遇到Guid[]时出现问题的类型之一。

对象已修复(逻辑错误) ArrayComponentType ClrInstanceField Type ClrElementType.Object我得到了更好的结果,但仍然需要更多。首先,在调用GetFieldValue之后,我得到一个ulong地址(?),我可以使用ClrInstanceField.Type.Fields反对就好了,所以我可以看到嵌套对象的字段名称和值。也就是说,我必须考虑多态性,所以我尝试在同一个地址上使用ClrHeap.GetObjectType并返回NULL或完全不正确的东西。这个地址在我的第一个用例中起作用似乎很奇怪,但不是第二个。

字符串已修复(找到解决方法) 因为我的真实项目已经使用了带有SOS的DbgEng,我有一种不同的方法可以轻松地通过地址获取字符串的值,但尝试使用ClrInstanceField.GetFieldValue成功返回字符串似乎很奇怪,但是完全不准确的结果(一堆奇怪的字符)。也许我这样做错了?


编辑:我从原始代码中提取了一个现在在LINQPad中运行的抽象。这里发帖时间有点长,但都是here in a gist。所有的复制/粘贴/重构仍然有点混乱,我将进一步清理它,可能会在我修复这些问题后在CodePlex或GitHub上发布最终源代码。

代码库相当大并且特定于项目,但如果绝对必要,我可以提取样本集。也就是说,对ClrMD对象的所有访问都非常简单。我从像!dumpheap -stat这样的SOS命令中获取初始地址(对于根对象可以正常工作),然后我使用ClrHeap.GetTypeByNameClrHeap.GetObjectType。之后,它完全依赖于ClrType.FieldsClrInstanceField成员TypeElementTypeGetFieldValue

作为额外的奖励,我确实找到了随NuGet包提供的browser friendly版本的XML Docs,尽管它与IntelliSense提供的文档相同。

1 个答案:

答案 0 :(得分:3)

如果没有看到你的代码看起来很难准确回答,但基本上就是这样:

为了能够调用GetFieldAddress / GetFieldValue,您需要知道的第一件事是,您拥有的对象地址是常规指针还是内部指针。也就是说,如果它直接指向堆上的对象,或指向实际对象中的内部结构(想想实际对象中的String与Struct字段)。

如果你从GetFieldAddress / GetFieldValue中获取了错误的值,通常意味着你没有指定你有一个内部指针(或者你认为你没有内部指针)。

第二部分是了解价值观的含义。

如果field.IsPrimitive()为true:GetFieldValue()将获得实际的原始值(即Int32,Byte或其他)

如果field.IsValueClass()为true,则GetFieldAddress()将为您提供指向结构的内部指针。因此,您在该地址上使用的任何GetFieldAddress / Value()调用都需要告诉它它是一个内部指针!

如果field.ElementType是一个ClrElementType.String,那么我似乎记得你需要调用GetFieldValue来获取实际的字符串内容(需要检查,但这应该是它)。

否则,您有一个对象引用,在这种情况下,GetFieldValue()将为您提供指向新引用对象的常规指针。

这有意义吗?