提问人:Dominique 提问时间:6/14/2023 更新时间:6/15/2023 访问量:245
Windbg C# dump 分析:如何找回对象所属的模块?
Windbg C# dump analysis: how to retrieve the module an object belongs to?
问:
我已经获取了正在运行的进程的内存转储(任务管理器,右键单击“创建转储文件”),现在我正在使用 Windbg 对其进行调查。
!Dumpheap -stat
揭示了大量的对象,这些对象似乎是 14 个条目的集合: 结尾看起来如下(前两列包含超链接):!Dumpheap -stat
3f62cc58 70b7d878 68
3f62cc9c 70b7d878 68
3f62cce0 70b7d878 68
3f62cd24 70b7d878 68
3f62cd68 70b7d878 68
单击此类对象将显示以下内容:
0:000> !DumpObj /d 3f62ebb0
Name: System.Object[]
MethodTable: 70b7d878
EEClass: 70754b80
Size: 68(0x44) bytes
Array: Rank 1, Number of elements 14, Type CLASS (Print Array)
Fields:
None
单击“打印数组”会得到这个:
0:000> !DumpArray /d 3f62ebb0
Name: System.Object[]
MethodTable: 70b7d878
EEClass: 70754b80
Size: 68(0x44) bytes
Array: Rank 1, Number of elements 14, Type CLASS
Element Methodtable: 70b7d824
[0] null
[1] 38f2cfd8
[2] null
[3] null
[4] null
[5] null
[6] null
[7] null
[8] null
[9] null
[10] null
[11] null
[12] null
[13] null
我想得到更多的信息,尤其是这些东西是什么样的收藏品。为此,我想加载调试符号并显示模块名称。
显示模块名称:我该怎么做?如何查看对象属于哪个模块?
加载调试符号:我该怎么做?我已经将一些“*.pdb”文件放在一个目录中,并填写了“文件”,“符号文件路径”,但在这两种情况下,模块都保持延迟:
C:\Temp_Folder\
C:\Temp_Folder\*
0:000> lm start end module name 00480000 004b8000 application_being_debugged (deferred) 1ce60000 1ce63000 security (deferred) 1d900000 1d91b000 opccomn_ps (deferred) 1d930000 1d956000 opcproxy (deferred)
我希望,设法配置调试符号路径,可能会透露很多信息。
有谁知道怎么做?
提前致谢
答:
我已经获取了正在运行的进程的内存转储 [...]
为了提供更好的指导,了解创建内存转储的原因非常重要。只是为了好玩,为了学习还是为了做真正的分析?如果是真正的分析,什么样的分析?您想了解高内存使用率、挂起、性能问题(CPU 峰值)还是您的应用程序是否崩溃?
[...](任务管理器,右键单击“创建转储文件”,现在我正在使用 Windbg 对其进行调查。
你没有报告转储的任何问题,但请注意,任务管理器不一定是最佳选择。任务管理器有两个版本,32 位版本和 64 位版本,每个版本都采用该位数的内存转储。查看用于创建故障转储的其他选项,这些选项可能更合适,具体取决于“原因”问题。
!Dumpheap -stat
揭示了大量的物体,[...]
这很正常。一个带有空窗口的简单 Hello World 样式的 WinForms 应用程序具有 5000 多个对象,而您可能认为您只有 1 个窗体。
当你说“巨大”时,很难把它放在上下文中。在调试方面,最好是精确的。说“!Dumpheap 列出了 123.000.468 个对象”。
结尾的样子如下
!Dumpheap -stat
不,对不起。要么您的应用程序中的某些内容最终被破坏了,要么这根本不是 的输出。 有 4 列:!dumpheap -stat
!dumpheap -stat
- 类型信息(MT = 方法表)
- 对象计数
- 该类型的所有对象的总大小
- 对象的名称
您发布的内容可能是 (without ) 输出的一部分。这通常不是很有用,因为事先不知道自己在寻找什么。!dumpheap
-stat
[...]这些东西是什么样的收藏品
嗯,a 是一个类型的数组,通常在 C# 中声明为 .关于这个系列,没什么好说的了。System.Object[]
System.Object
object[]
其他集合可能是 或 2D 数组,例如 或 C# 代码中的泛型。System.Windows.Forms.Control[]
System.Int32[][]
System.Collections.Generic.List'1[[System.Windows.Forms.Application+ParkingWindow, System.Windows.Forms]]
List<ParkingWindow>
我想过加载调试符号
这是个好主意。没有符号,调试是不值得的。
显示模块名称:我该怎么做?
您可以使用 显示模块。 为您提供版本号和日期信息。 为您提供完整的路径。您可以通过附加 .添加到之前的任何命令。lm
lmv
lmf
m modulename
如何查看对象属于哪个模块?
那是不可能的。为什么?我没有参考,所以让我们做一个有根据的猜测。
a) 跟踪每个小对象的 DLL 至少会增加指针大小开销。对于 8 字节,这可能是 4 个字节。
b) 你会跟踪哪个DLL?调用堆栈上始终有多个 DLL。一个模块调用另一个模块,另一个模块调用下一个模块。最后,你可能总是在Microsoft DLL的某个地方结束,因为这是字符串,整数和很多东西的来源。你不想要那样。在另一个极端,可执行文件是首先分配内存的主要原因。因此,要确定要跟踪哪些程序集以及不希望在该列表中显示哪些程序集并不简单。
c) 查看调用堆栈是一项非常昂贵的操作。这就是为什么抛出大量异常会使您的应用程序变慢的原因。int
总而言之,我想说的是,将一个模块记录到每个对象是不可行的。
我能想到的最接近的是 DLL 的堆标记,但它适用于 Windows 堆管理器,而 .NET 不使用。此外,您只知道哪个堆属于哪个 DLL,而不是对象。
加载调试符号:我该怎么做?
通常只需两个步骤即可:
- 使用 Microsoft 符号:
.symfix
- 添加符号:
.sympath+ C:\some\directory
有关详细信息,请参阅如何设置符号,其中详细介绍了更多详细信息。
一些“*.pdb”文件
不是一些。全部使用。
C:\Temp_Folder\*
无需使用通配符。只需指定目录即可。
模块保持延迟
这是想要的。加载符号的速度很慢。它们将在需要时加载。如果您现在想加载它们,请在等待时使用并给自己喝杯咖啡。ld *
可能会透露很多信息。
确定。通过调试,您可以获得大量信息。你可以看看一切。然而,当你面前有大海捞针时,知道你在寻找什么总是好的。我仍然不清楚。为什么要调试?你在找什么?
评论