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

The*_*ide 5 .net c# windbg sos clrmd

我有一个托管进程的一些非常大的内存转储,我试图从中获取大量统计信息,并且能够呈现堆上相当深的对象图的交互式视图。想想!do <address>与prefer_dml 1在WinDbg中设置与SOS,在那里你可以不断地点击属性,并查看它们的值,只有在比较多的对象更友好的用户界面。

我发现Microsoft.Diagnostics.Runtime (ClrMD) 特别适合这项任务,但我很难处理数组字段,而且我对对象字段有点困惑,我已经做了一些工作更好的。


数组:如果我直接从堆中定位一个带有地址的数组并使用,ClrType.GetArrayLength并且ClrType.GetArrayElementValue一切正常,但是一旦我在另一个对象上挖掘字段,我不确定我从ClrInstanceField.GetValue什么时候得到的值ClrInstanceField.ElementType是ClrElementType.SZArray(我还没有Array在我的对象图中遇到过挖掘,但我也想处理它)。

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

对象:固定(逻辑错误) 随着ClrInstanceField具有Type的ClrElementType.Object我得到更好的结果,但仍需要更多一点。首先,在调用后GetFieldValue我得到一个ulong地址(?),我可以ClrInstanceField.Type.Fields很好地使用它,所以我可以看到嵌套对象的字段名称和值。也就是说,我必须考虑多态性,所以我尝试ClrHeap.GetObjectType在同一个地址上使用,它要么返回 NULL,要么返回完全不正确的东西。地址在我的第一个用例中有效,但在第二个用例中无效,这似乎很奇怪。

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


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

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

作为额外的好处,我确实找到了NuGet 包提供的 XML 文档的浏览器友好版本,尽管它与 IntelliSense 提供的文档相同。

tom*_*asr 4

如果不查看代码的样子,很难非常准确地回答,但基本上,它是这样的:

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

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

第二部分是理解这些值的含义。

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

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

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

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

这有道理吗?