【问题标题】:How can I "discover" the COM object behind the ref counted handle in WinDbg?如何在 WinDbg 中“发现”引用计数句柄后面的 COM 对象?
【发布时间】:2018-11-19 22:53:44
【问题描述】:

我正在通过 WinDbg DMP 文件调查内存泄漏。我发现堆上有很多AmazeType 的实例,虽然它是一个很好的类型,但存在的方式太多了。我想知道谁在囤积它们。

!gcroot-ing AmazeType 将我带到“引用计数句柄”。这是有道理的,因为令人敬畏的类型列表通过 COM 可调用包装器存储在 COM 对象实例的属性中。

0:047> !gcroot 00000001c093e448
HandleTable:
    00000000015041a0 (ref counted handle)
    -> 00000001c0932fd8 System.Collections.Generic.List`1[[AmazeType, AmazeAssembly]]
    -> 00000001c0927ef8 BaseAmazeType[]
    -> 00000001c093e448 AmazeType

如何使用 ref 计数句柄 00000000015041a0 来获取有关 RCW 及其相关的 COM 对象的信息?如果没有,如何交替解决问题?

【问题讨论】:

  • 您是否尝试过在转储中使用Debug Diagnostics?对于包括内存泄漏在内的许多问题,此工具可以更轻松地显示/突出潜在问题。
  • @Igor 是的,我已经用转储试过了。它表明终结器被阻止,我正在查看。不过感谢您的提示。
  • 你能!do poi(15041a0)看看它是否提供了一些有用的东西吗?我认为这是坚固且固定手柄的方法。
  • COM 对象也必须始终具有 RCW。您可以检查堆上的那些 RCW (System.__ComObjectiirc) 并使用 SOS !dumprcw 或 SOSEX !rcw
  • @ThomasWeller 它是一个 RefCount 句柄,!do poi(15041a0) 转储了AmazeType 的列表,所以似乎并没有让我更接近它所引用的 COM 类型。

标签: .net windbg


【解决方案1】:

我没有在 64 位转储中这样做,只有 32 位转储,但希望这会对您有所帮助。

(我意识到这对你来说可能为时已晚,但希望它会对其他人有所帮助。)

根据我的经验, COM 对象到 .NET 对象的运行方式如下。因此,从 .NET 对象到 COM 对象是一种逆向过程。我使用的是真实转储中的示例,但经过审查和重命名。

从 COM 到 .NET 对象。

您的 COM 对象中包含一个字段,可能如下所示:

+0x30 m_iTestObject    : ATL:CComPtr<ITestObject> { 0e645150 }

这是间接指向 clr!Unknown_QueryInterface 吗?如果不是,那就不是 CCW:

0:000> dds poi(0e645150) L1
0631c090  07466260 clr!Unknown_QueryInterface

好的,这是一个逆时针。我们回到原始地址 0e645150,此时我们必须检查一些偏移量,因为它们似乎在 .NET 运行时之间有所不同。到目前为止,我发现的三个偏移量是 -0x0c、-0x10 和 -0x14。显然,在 64 位下偏移量可能会有所不同,但您可以看到尝试不同的偏移量然后针对结果运行 !mdt 应该很容易:

0:000> dds poi(0e645150-0x0c) L1
2506a4c0  00000000
0:000> dds poi(0e645150-0x10) L1
01785bd8  08acc744
0:000> dds poi(0e645150-0x14) L1
00000000  ????????

我们现在可以对每个结果运行 !mdt (sosex) 或 !do。碰巧的是,其中只有一个实际上给了我们一个有效地址,即 08acc744:

0:000> !mdt 08acc744
08acc744 (TestAssembly.TestObject)
    requests:08accba8 (System.Collections.Generic.List`1[[TestAssembly.TestRequest, TestAssembly]])
    notifySink:08aee138 (TestAssembly.SinkWrapper)

回溯到指向这个 .NET 对象的 COM 对象:

我们从 08acc744 的同一个 .NET 对象开始并检查它的引用。 (您已经使用 !gcroot 来查找 RefCounted 句柄):

0:000> !refs 08acc744
Objects referenced by 08ACC744 
  NONE

Objects that reference 08ACC744 (TestAssembly.TestObject):
  RefCounted Handle at 01785BD8, AppDomain=0456F158

好的,所以我们有了句柄。这是什么意思?

0:000> s -d 0x00000000 L?0xffffffff 01785bd8 
0e645140  01785bd8 2506a4c0 0630ec60 00000000  .[x....%`.0.....

所以这个句柄是从 0e645140 指向的。我们知道,下一级不是直接指向这个地址,而是指向从这里开始的一个偏移量。我们能找到任何指向这些偏移地址的东西吗?

0:000> s -d 0x00000000 L?0xffffffff 0e645140+0x0c
0:000> s -d 0x00000000 L?0xffffffff 0e645140+0x10 
27f90d68  0e645150 25d100e8 27fab0f8 00000000  PQd....%...'....
0:000> s -d 0x00000000 L?0xffffffff 0e645140+0x14 

我们在 27f90d68 找到了一个指针,我们希望它是 COM 对象中的一个字段。让我们转储该地址周围的内存:

0:000> dds 27f90d68-0x40
27f90d28  0537f8b8
27f90d2c  00000000
27f90d30  559965bd
27f90d34  88000000
27f90d38  15ffe604 TestCore2!ATL::CComObject<CTestContainer>::`vftable'
27f90d3c  15ffe5e4 TestCore2!ATL::CComObject<CTestContainer>::`vftable'
27f90d40  00000002
27f90d44  0000000e
27f90d48  ffffffff
27f90d4c  0000000f
27f90d50  00000010
27f90d54  1dc721d8
27f90d58  00000000
27f90d5c  27d6ef80
27f90d60  262b1ec0
27f90d64  00000001
27f90d68  0e645150
27f90d6c  25d100e8
27f90d70  27fab0f8
27f90d74  00000000
27f90d78  559965b4

我们可以看到一个带有 vftable 的对象,该对象的起始地址稍早于我们的地址,因此希望我们的地址是该对象内部的一个字段。 (请注意,对象很可能在 vftable 条目之间存在间隙,ATL 接收器指针之类的东西可以驻留在其中,但碰巧这是对象的开始)。

0:000> dt 27f90d38  TestCore2!ATL::CComObject<CTestContainer>
   +0x000 __VFN_table : 0x15ffe604 
   =16010f38 _tih             : ATL::CComTypeInfoHolder
   +0x004 __VFN_table : 0x15ffe5e4 
   =16010f58 _tih             : ATL::CComTypeInfoHolder
   +0x008 m_dwRef          : 0n2
   +0x008 m_pOuterUnknown  : 0x00000002 IUnknown
   +0x00c m_nStateOrdinal  : 0n14
   +0x010 m_nKeyOrdinal    : 0n-1
   +0x014 m_nErrorCodeOrdinal : 0n15
   +0x018 m_nErrorTextOrdinal : 0n16
...
   +0x30 m_iTestObject    : ATL:CComPtr<ITestObject> { 0e645150 }

这样我们就一直从 .NET 对象回到指向它的 COM 对象。

希望对你有用。

【讨论】:

  • 谢谢彼得。正如您所猜测的那样,我早就不需要这个了,但我很感激您的回答,所以我接受了。我不知道它是否真的有效,但它看起来很合理,我想不出你为什么要编造一个 WinDBG 答案! :-) 祝你的 SO 旅程好运。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-05
  • 1970-01-01
  • 1970-01-01
  • 2015-03-15
  • 2014-12-18
  • 2017-01-21
相关资源
最近更新 更多