我有一些非常大的托管进程内存转储,我试图从中获取大量的统计信息 - 以及能够呈现交互式视图 - 堆上相当深的对象图。在WinDbg中使用SOS设置与!do <address>
类似的内容,您可以在其中不断点击属性并查看其值,只在更友好的UI中进行比较,以便比较多个对象。
我发现 Microsoft.Diagnostics.Runtime (ClrMD)特别适合这项任务,但我很难处理数组字段,我有点困惑关于对象领域,我工作得更好。
阵列:
如果我将一个地址直接放在堆上的数组,并使用prefer_dml 1
和ClrType.GetArrayLength
一切正常,但是一旦我挖掘另一个对象上的字段,我不确定我有什么价值当ClrType.GetArrayElementValue
为ClrInstanceField.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.GetTypeByName
或ClrHeap.GetObjectType
。之后,它完全依赖于ClrType.Fields
和ClrInstanceField
成员Type
,ElementType
和GetFieldValue
作为额外的奖励,我确实找到了随NuGet包提供的browser friendly版本的XML Docs,尽管它与IntelliSense提供的文档相同。
答案 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()将为您提供指向新引用对象的常规指针。
这有意义吗?