诊断工具
Dump
dump在计算机科学中是一个广泛运用的动词、名词。
作为动词:一般指将数据导出、转存成文件或静态形式。比如可以理解成:把内存某一时刻的内容,dump(转存,导出,保存)成文件。
作为名词:一般特指上述过程中所得到的文件或者静态形式。
1、为什么要dump(dump的目的)?
因为程序在计算机中运行时,在内存、CPU、I/O等设备上的数据都是动态的(或者说是易失的),也就是说数据使用完或者发生异常就会丢掉。如果我想得到某些时刻的数据(有可能是调试程序Bug或者收集某些信息),就要把他转储(dump)为静态(如文件)的形式。否则,这些数据你永远都拿不到。
2、dump转储的是什么内容(dump的对象)?
其实上边已经提到了,就是将动态(易失)的数据,保存为静态的数据(持久数据)。像程序这种本来就保存在存储介质(如硬盘)中的数据,也就没有必要dump。
WinDbg调式
1、理论学习
WinDbg:Windows 调试工具(WinDbg、KD、CDB、NTSD)
win10上安装后目录:C:\Program Files (x86)\Windows Kits\10\Debuggers\x64
Windbg是Microsoft公司免费调试器调试集合中的GUI的调试器,支持Source和Assembly两种模式的调试。Windbg不仅可以调试应用程序,还可以进行Kernel Debug。结合Microsoft的Symbol Server,可以获取系统符号文件,便于应用程序和内核的调试。Windbg支持的平台包括X86、IA64、AMD64。虽然windbg也提供图形界面操作,但它最强大的地方还是有着强大的调试命令,一般情况会结合GUI和命令行进行操作,常用的视图有:局部变量、全局变量、调用栈、线程、命令、寄存器、白板等。其中“命令”视图是默认打开的。
Windbg在用户态和内核态下,都支持两种调试模式,即“实时调试模式(Living)”和“事后调试模式(Postmortem)”。
- 所谓实时模式,是被调试的目标对象(Target)当前正在运行当中,调试器可以实时分析、修改被调试目标的状态,如寄存器、内存、变量,调试exe可执行程序或双机实时调试都属于这种模式;
- 所谓事后模式,是被调试的目标对象(Target)已经结束了,现在只是事后对它保留的快照进行分析,这个快照称为转储文件(Dump文件)。
Windbg另一个重大优点,还在于它支持源码级的调试,就像VC自带的调试器一样。虽然提供了用户界面,但Windbg归根结底还是需要用户一个个地输入命令来指挥其行动。这就是他的Command窗口。每个调试命令都各有使用范围,有些命令只能用于内核调试,有些命令只能用于用户调试,有些命令只能用于活动调试。但用户也不必记得这许多,一旦在某个环境下,使用了不被支持的命令,都会显示“No export XXX found”的字样。
其他参考:
WinDbg的安装、配置和功能 (系列博文)使用WinDbg调试入门(用户模式)
Windbg命令相关
WinDbg 命令三部曲:(二)WinDbg SOS 扩展命令手册
!DumpHeap 将遍历 GC 堆对对象进行分析。通过指定不同的选项,可以查看特定的类型、数组和锁。
!GCRoot [-nostacks] <Object address> 查询一个对象的所有引用根。
!syncblk
看看有没有 lock 的情况
!DumpStack Objects 显示当前调用栈上的所有托管对象的信息,可配合 k 或 CLRStack 命令使用
!clrstack 看调用栈
!eeheap -gc
命令,看下托管堆大小【即遍历进程内存中的 CLR 数据结构】
!eeheap -loader
命令,看下 loader 堆大小(非托管)
WinDbg 命令三部曲:(三)WinDbg SOSEX 扩展命令手册
用 sosex
扩展的 !dlk
命令可以自动检索是否有死锁
2、概念原理之:什么是SOS、mscordacwks
mscorwks:通用语言运行时 (CLR) 是执行托管代码的 Microsoft .NET 框架的核心引擎。mscorwks.dll
是CLR 2.0实现的主要文件。此引擎在本机代码中实现。
SOS:SOS.DLL
可以提供关于CLR的信息,帮助我们在vs和windbg调试托管程序。例如,可以显示有关托管堆的信息、查找堆损坏情况、显示运行时使用的内部数据类型以及查看有关运行时内运行的所有托管代码的信息。
mscorwks使用本机代码实现了CLR,SOS可以提供托管的CLR信息,而mscordacwks即为连接本机代码和托管代码之间的桥。SOS无需了解CLR底层细节。
官网:SOS.dll (SOS debugging extension)
3、Windbg话题
几个分析思路:
内存泄漏:首先就要排查到底是 托管堆
还是 非托管堆
的问题 ,参考:记一次 .NET 某HIS系统后端服务 内存泄漏分析
实践
如何在 .NET 程序万种死法中有效的生成 Dump
用Windbg打开生成的Dump文件【用vs也可以打开dmp文件进行分析】
执行命令如下:
!address -summary 查看当前 process 的内存占用量
!dumpheap -stat -min 1024 寻找大对象(单位Byte),在托管堆中
!dumpheap -type System.String -min 10240 用 -type
属性筛选出 >10k
的字符串。
!gcroot 4a855060 【参数是 MT对应的任意一个address】
以下是一些说明:
ProcDump生成Dump文件
procdump ConsoleApp2 -m 1024 -ma E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug
如果你的机器有多个 ConsoleApp2 ,可以将其替换成 pid(进程id)
报错error opening
是需要使用管理员命令提示符才能成功抓取dump
应用场景:主要用于高CPU占用率的性能分析优化,程序停止响应的调试,First chance异常捕获等,还可以监视内存使用、结合系统性能计数器使用。
在命令提示符下procdump -?可以查看全部参数的说明,这里列举几个常用的:
-c/-cl: 监视CPU占用率阀值,-c为当CPU占用率高于该值时创建dump,-cl则在低于该值时创建dump。
-u: 监视单核心的CPU占用率,与-c一起使用
-s: 时间,以秒为单位,结合-c使用实现当连续N秒CPU占用高于某值时保存dump。
-n: 设置数量,保存多个dump后才退出该程序。
-h: 当进程中存在挂起窗口(不响应窗口消息)时创建dump。
!dumpheap -stat
报错1:no export dumpheap found
则执行.load sos 继续报错:系统找不到指定的文件
参考:加载扩展DLL:load 需要跟完整路径:0:000> .load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SOS.dll
继续执行 !dumpheap -stat
报错2:Failed to find runtime DLL (clr.dll),0x80004005
再加载一个其他版本的sos:C:\Windows\Microsoft.NET\Framework64\v2.0.50727\SOS.dll
继续报错:Failed to find runtime DLL (mscorwks.dll),0x80004005
改为加载.netcore 2.1 的sos 【因为.netcore3.1下没有sos,这就为后续调试.netcore3.1的程序时埋了个坑】:.load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.28\sos
!gcroot -all报错:The version of SOS does not match the version of CLR you are debugging
步骤:!dumpheap -type System.String -min 10240
正确输出了,单击最下面一个。结果显示 crl版本 与SOS版本不匹配。。所以要下载一个netcore版本下面的SOS。
可是.nercore3.1里面没有sos.dll,只有一个rm
打开可以看到,进到 Installing SOS on Windows
然后执行 .load C:\Users\huy\.dotnet\sos\sos 就好了。
注意:2和3 综合起来,下次分析dump时,分别执行:
.loadby sos crl
.load C:\Users\huy\.dotnet\sos\sos
!gcroot 返回 Found 0 unique roots
参考:WinDbg not telling me where my string is rooted
这是因为,WinDbg告诉您的是正确的-这些没有根,它们是垃圾,但是因为它们在LOH上,它们可能不会很快被清除(如果有的话)。您肯定需要重新考虑如何处理XML,将数据流输入/输出,而不是预先在内存中加载/创建数据。
修改代码为:
public static void TestMemory() { List<string> list = new List<string>(); for (int i = 0; i < int.MaxValue; i++) { string temp = string.Join(",", Enumerable.Range(0, 10000)); list.Add(temp); if (i % 30 == 0) SY.Filer.FileHelper.AppendAllText(temp, "temp.txt"); } Console.ReadLine(); }
重新生成dump去分析