5

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

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


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

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

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

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


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

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

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

4

1 回答 1

4

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

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

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

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

如果 field.IsPrimitive() 为真:GetFieldValue() 将为您提供实际的原始值(即 Int32、Byte 或其他)

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

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

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

这有意义吗?

于 2014-03-06T15:57:17.347 回答