本文将和大家介绍一个简单且实际用途不大的使用 windbg 配合脚本的方式,进行自动化的大批量对 dotnet 系应用的 dump 进行自动化分析调试处理,可以自动根据调试需求输出 dump 文件的一些信息

利用 windbg 执行调试脚本的能力,可以实现自动化调试 dump 文件,将调试 dump 文件获取的信息进行输出,方便进行统一处理

在开始之前先说一下我的需求点是什么。我有大量的用户,我也有大量的不同的软件,这些软件对接进了 dump 平台。所谓 dump 平台实际上就是软件崩了的时候,创建一个 dump 文件,然后不差钱的将 dump 文件传送到我的服务器上。我每天都会堆积几千个 dump 文件,压根调试不过来。我期望能够有一个工具可以辅助我提前预处理一些 dump 文件,比如说将堆栈打印出来等等

打印出来堆栈,我可以导入到聚类算法里面,找到其中崩溃堆栈最多的,重点对其进行处理。或者进行一些时间段异常监控

尽管我在开始时说实际用途不大,但用途不大不代表没有用途。整套玩下来,还是能够减少一点工作量的,且能够带来一丁点的收益的。比如说通过本文介绍的方式,我将海量的 dump 文件的堆栈打印出来,通过聚类算法获取到了,大量的 dump 都是无用的 dump 文件。因为大量的崩溃都是事后现场,即进入了最终崩溃点,非发生异常的点,或者异常发生点是属于类似空异常的类型,没有可用信息。或者是内存溢出,内存不足等通用问题。其他的有用的占比不到百分之一,在这里面就基本归类于用户环境问题占了大半,另一半就是调用的其他团队提供的 C++ 库。用户环境问题中,显卡驱动问题占比居多,这部分带来了确实有用的信息。其次的环境问题是比较杂项的,如第三方注入问题、输入法带崩问题、某些系统组件坏掉。用户环境问题的输出监控还是有用的,但调用的其他团队提供的 C++ 库这个基本上就凉凉了,因为很多团队开发完成就解散了,人和代码都找不到,这些就只能用数据来和产品大佬砍需求了,或者申请资源给他用 C# 代码重写了,或者是考虑跨进程调用了

通过分析用户问题,根据时间加入分析,可以了解到趋势信息。比如说最近某段时间内发现用户环境问题中,在崩溃堆栈里面,关于 Intel 显卡驱动的上升。可以进一步关注是否最近 Intel 的最新显卡驱动更新出现问题,取其中的 dump 文件,通过内部合作渠道反馈给到厂商,请厂商尽快修复问题。再比如最近某段时间内发现某个系统组件出现较高的崩溃率,可进一步关注微软系统更新,如果发现系统更新投毒了,那再给微软反馈一下,让微软修修。通过了解趋势信息可以辅助定位第三方影响问题,不仅包括直接厂商,如 Intel 和微软的,也包括第三方厂商,如搜狗拼音输入法还有上回给我投毒的展盟网络科技等等

尽管上文提到说内存溢出问题用途不大,但是如果带上软件版本号,可能通过趋势分析也是有点用途的。比如说发现了某个版本的内存溢出问题比其他版本高很多,再经一步调查,也许可以看到某个版本引入了某些奇特的逻辑,确实是软件自身的问题,而不是用户环境带来的内存不足问题

内存溢出问题如果发现是某段时间内有很多内存溢出问题,且所有软件版本都升高,那就可能也是第三方影响问题。比如之前调查到的 Intel 或微软的 D3D9On12 部分存在内存溢出问题,导致了大量应用都会多占用更多内存,从而出现内存溢出

但无论如何,本文介绍的这个自动化分析方法都无法百分百减少工作量,只能是辅助作用。本文介绍的自动化分析方法比较适合用在有大量的 dump 文件,人工调试不过来的情况,如果本身团队规模比较小,那就只能用于满足领导们的汇报需求了,比如用于吹质量设计

在 windbg 工具中,可以使用 -c 参数带上脚本文件,大概的命令行格式如下

windbg.exe -z [DUMP文件] -logo [日志输出文件] -c "$<[脚本文件]"

一般来说会先组织工作文件夹,将 dump 文件、脚本文件,以及将要被输出的日志文件都放在一个文件夹里面,将这个文件夹当成工作文件夹。如此即可简化命令行,如在 cmd 里通过 cd 命令进入到工作文件夹里面,再执行命令。如以下命令将分析 lindexi.dmp 文件,输出到 log.txt 里面,采用 script.txt 文件作为脚本

windbg.exe -z lindexi.dmp -logo log.txt -c "$<script.txt"

如在 C# 代码里面,可以在 Process 时指定工作路径,例子的代码如下

        Process.Start(new ProcessStartInfo("windbg.exe")
        {
            WorkingDirectory = @"C:\lindexi\Works",
            Arguments = "-z lindexi.dmp -logo log.txt -c \"$<script.txt\""
        });

上述代码的 -c 参数后面可以带上执行命令,也可以带上放入执行命令的脚本文件。对于比较复杂的,比较多条的命令,推荐放在脚本文件里面。在 windbg 里面,通过 $< 等前缀识别传入的是脚本文件而不是执行命令

接下来我将告诉大家如何编写这个脚本文件

脚本文件的格式非常简单,就是一行一句命令

根据分析 dotnet 应用的知识,对于 .NET Core 系框架,包括 dotnet 6 和 dotnet 7 和 dotnet 8 和 dotnet 9 等版本,第一步咱应该加载 sos.dll 文件。加载 sos.dll 的方法请参阅 WinDbg 加载 dotnet core 的 sos.dll 辅助调试方法

值得特别说明的是,默认的 dotnet-sos 工具存放的是 x64 的 sos.dll 文件,如果将要分析 dump 的应用是 x86 版本的,还需要更改 .load 的路径。比如我这里是放在 C:\Users\lindexi\.dotnet\tools\.store\dotnet-sos\8.0.510501\dotnet-sos\8.0.510501\tools\net6.0\any\win-x86\sos.dll 路径的

完成加载之后,即可使用 !analyze -v 强大的命令进行自动的分析,这一步能够输出异常等信息出来,非常好用。缺点只是分析时间比较长。如果是自动分析的话,挂着让其慢慢分析就不怕分析速度太慢了

接着使用 !clrstack 命令打印出来 dotnet 的托管堆栈,有时候可以在这里看到具体是哪个模块调用的。以及带上 ~*k 输出更多线程堆栈信息和可选加上 ~*e!clrstack 输出所有线程的托管调用堆栈

最后加上 qq 命令,让 windbg 自行退出。于是执行脚本的时候就可以在分析完成之后自动退出

根据上文编写的简单脚本代码如下

.load C:\Users\lindexi\.dotnet\tools\.store\dotnet-sos\8.0.510501\dotnet-sos\8.0.510501\tools\net6.0\any\win-x86\sos.dll
.echo ==============start=============
!analyze -v
!clrstack
~*k
.echo ==============end=============
qq

可以看到以上脚本带上了 .echo ==============start============= 等命令,这些命令只是为了在日志文件里面输入一些标识,方便后续咱编写代码读取日志文件,获取到一些 !analyze -v!clrstack 命令输出的信息。完成以上步骤,接下来就是按照自己的喜好,编写一些 C# 逻辑,让 WinDbg 跑起来,自动分析 dump 文件。然后读取分析结果的日志文件,分析日志文件里面的内容。如此就完成了自动编写 DUMP 分析工具平台了。额外的,在现实使用中,可能还会带上 -y 参数,用于指定符号文件夹,减少加载符号时,拉取符号的耗时,参数是 -y [符号文件夹] 的格式


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/dotnet-%E4%BD%BF%E7%94%A8-windbg-%E8%BF%90%E8%A1%8C%E8%84%9A%E6%9C%AC%E6%96%B9%E5%BC%8F%E8%87%AA%E5%8A%A8%E6%89%B9%E9%87%8F%E8%B0%83%E8%AF%95%E5%A4%84%E7%90%86-dump-%E6%96%87%E4%BB%B6.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者收藏我的博客导航

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

微软最具价值专家


无盈利,不卖课,做纯粹的技术博客

以下是广告时间

推荐关注 Edi.Wang 的公众号

欢迎进入 Eleven 老师组建的 .NET 社区

以上广告全是友情推广,无盈利