程序发生闪退且没有生成dump文件问题的排查经验总结与分享
目录
1、概述
2、程序发生闪退且没有生成dump文件的两类场景
3、程序发生了异常崩溃,但异常捕获模块没有捕获到异常
3.1、可以尝试到系统应用程序日志中查看系统是否自动生成了dump文件
3.2、将Windbg附加到进程上进行动态调试,尝试去复现问题
3.3、复现问题时要尝试找到复现问题的方法与步骤
3.4、尝试在dump文件中查看相关的变量值,有些变量的值可能是排查问题的关键线索
4、程序中并没有发生异常崩溃,可能是代码中检测到不正常,直接调用abort或exit强行将进程终止了
4.1、调用abort或exit强行终止进程,并没有发生异常崩溃,并没有生成dump文件
4.2、WebRTC开源库内部malloc申请内存失败,强行调用abort终止进程
4.3、程序在强行终止进程之前,可能会调用DebugBreak让调试器中断下来,abort函数内部也会让调试器中断下来,正是利用这一点,将Windbg附加到进程上动态调试,感知到这个问题
5、最后
C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++实战进阶(已更新到450多篇,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/140824370VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/124272585Windows C++ 软件开发从入门到精通(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_12695902.htmlC++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_2276111.html 最近技术群中有几个朋友问,程序发生了崩溃闪退,但没有生成dump文件,像这类问题该如何排查。今天就来讲讲发生此类问题的场景以及常用排查方法,并给出对应的实战问题排查案例,供大家借鉴或参考。
1、概述
为了有效地排查C++软件异常崩溃问题,我们一般会在程序中安装异常捕获模块。当程序运行过程中发生异常时,异常捕获模块感知到,自动生成包含异常上下文的dump文件。事后取来dump文件,用Windbg打开分析,这是分析C++软件异常的常用且有效的方式。
对于没有生成dump文件的场景,则需要将Windbg附加到目标进程上进行动态调试,然后去复现异常崩溃。当异常崩溃复现时,正在调试的Windbg会立即感知到并中断下来,此时去查看函数调用堆栈,对照着C++源码,进行排查和定位。
2、程序发生闪退且没有生成dump文件的两类场景
对于程序闪退没有生成dump文件的问题,主要包含两大场景:
1)场景1:程序运行过程中发生了异常崩溃,但安装的异常捕获模块没有感知到。异常捕获模块没法捕获到所有的异常。
2)场景2:程序中没有发生异常崩溃,程序代码在执行过程中检测到不正常的行为,直接调用abort或exit强行将进程终止掉。
下面对这两大场景进行展开,分别进行详细地说明。
3、程序发生了异常崩溃,但异常捕获模块没有捕获到异常
程序中安装的异常捕获模块没法捕获到所有的异常,有少部分的异常是捕获不到的。我们在日常项目中会时常遇到。对于这类没有生成dump文件的场景,我们可以先尝试到系统应用程序日志中查看系统是否自动生成了dump文件。如果系统没有生成dump文件,则需要重启程序,然后将Windbg附加到进程上进行动态调试,然后尝试去复现问题。
3.1、可以尝试到系统应用程序日志中查看系统是否自动生成了dump文件
虽然程序中安装的异常捕获模块没有感知到异常,没有生成dump文件,但操作系统可能感知到了(应用程序日志中会有程序crash的记录),系统可能自动生成了包含异常上下文的dump文件。可以尝试到系统的应用程序日志中查看日志记录,如果系统自动生成了dump文件,日志记录中会有dump文件的完整路径,比如:
根据路径就可以拿到dump文件,然后就可以用Windbg进行分析了。
有些崩溃闪退只在特定环境或者特定条件下才会复现,事后可能很难复现,这时更要尝试去看系统有没有自动生成dump文件。我在项目中就遇到过几次!
我们在项目中多次使用该方法,通过系统生成的dump文件,去排查一些小概率偶现的问题。对于如何去查看系统是否生成了dump文件,可以查看我之前写的专题案例分析文章:
使用Windbg分析从系统应用程序日志中找到的系统自动生成的dump文件去排查程序崩溃问题https://blog.csdn.net/chenlycly/article/details/132024253
在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)
专栏1:(该精品技术专栏的订阅量已达到520多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!欢迎订阅!)
C++软件调试与异常排查从入门到精通系列文章汇总https://blog.csdn.net/chenlycly/article/details/125529931
本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!
考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!
专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!
专栏2:(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达160多个,专栏文章已经更新到450多篇,持续更新中...)
C/C++实战进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。
专栏3:
C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/131405795
常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!
专栏4:
VC++常用功能开发汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/124272585
将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。
专栏5:
C++ 软件开发从入门到精通(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12695902.html
根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。
3.2、将Windbg附加到进程上进行动态调试,尝试去复现问题
如果问题发生在测试同事那边,可以让测试同事每次运行程序时都手动将Windbg附加到程序进程上。如果问题发生在客户环境中,则要麻烦客户每次运行程序时手动将Windbg附加到程序进程上,和程序一起运行。一旦复现异常崩溃,动态调试的Windbg会立即感知到并中断下来,我们可以到测试那边去查看分析,或者远程到出问题的机器上查看。
如果问题复现,但一时半会分析不出问题,或者客户那边不方便直接查看(不方便远程),可以在Windbg中使用.dump命令导出包含异常上下文的dump文件,然后将dump文件发给我们,我们事后再去详细分析。具体的.dump命令为:
.dump /ma D:\20241010.dmp
关于.dump等命令及支持参数的说明,可以查看Windbg的帮助文档。点击Windbg菜单栏中的Help -> Index,打开如下的chm格式的帮助文档:
关于使用Windbg动态调试目标进程的一般步骤与要点,可以查看我的文章:
使用Windbg调试目标进程的一般步骤及要点详解https://blog.csdn.net/chenlycly/article/details/131029795 关于使用Windbg分析dump文件的一般步骤与要点,可以查看我的文章:
使用Windbg分析dump文件的一般步骤及要点详解https://blog.csdn.net/chenlycly/article/details/130873143
3.3、复现问题时要尝试找到复现问题的方法与步骤
在公司环境中排查软件问题,很多时候我们希望测试人员能够找到复现问题的方法与步骤。如果能找到复现问题的方法和步骤(比如找到问题必现的办法),可以根据复现问题时执行的一些操作,判断出问题和哪些业务代码相关,这样排查起来更有针对性,效率更高。
有些测试人员在复现问题时很有一手,回想最初出问题的场景和操作步骤,然后结合自己的猜测和想象力,估计问题可能与哪些操作有关,尽力找到复现问题的办法与步骤!
对于偶现的程序崩溃问题,我们依然要尝试去找到复现问题的办法,这不仅方便用动态调试的Windbg去抓到程序中发生的异常,也方便修改代码后进行验证。
有时客户环境中出现的问题,我们为了排查,会尝试在我们公司环境内复现,这样排查起来也方便很多。但有的问题可能只在客户环境中才有(问题与客户环境有紧密的关系),公司测试环境无法复现,则需要客户或者派遣技术支持人员到客户现场协助排查,让他们取来软件运行日志及dump文件、抓取网络包等。
3.4、尝试在dump文件中查看相关的变量值,有些变量的值可能是排查问题的关键线索
在分析dump文件时,将函数调用堆栈与C++源码结合起来看,可以去尝试查看函数调用堆栈中函数中相关变量的值(在Windbg中查看相关变量的值),这些变量的值可能是排查问题的关键线索。比如我们在排查某个崩溃问题时查看到的相关变量的值,如下所示:
我们正是通过这个变量值,结合着C++源码,快读地定位了问题。
我们在项目中多次使用这个方法去排查问题,查看变量的值去排查问题的项目实战案例,可以查看我的文章:
通过查看Windbg中变量值去定位C++软件异常问题https://blog.csdn.net/chenlycly/article/details/125731044通过查看Windbg中变量值去定位C++软件异常的又一典型案例分享
https://blog.csdn.net/chenlycly/article/details/125793532 需要注意的是,在用dump文件分析问题时,有些变量的值可能查看不了,只能查看到部分变量的值。能否看到变量的值(变量内存中的内容),和dump文件的类型有关系:
1)如果是只有几MB大小的mini dump文件,则包含很少的内存信息,只能查看到少部分变量的值。
2)如果是全dump文件,则保存了程序进程所有的内存信息,可以查看到所有变量的值。
关于dump文件的类型、生成dump文件的方式以及dump文件的详细介绍,可以查看我之前写的文章:
【C++软件调试技术】dump文件类型与dump文件生成方法详解https://blog.csdn.net/chenlycly/article/details/127991002
4、程序中并没有发生异常崩溃,可能是代码中检测到不正常,直接调用abort或exit强行将进程终止了
程序发生闪退,不一定是异常崩溃导致的,可能程序压根就没发生异常崩溃。可能程序代码在执行的过程中发现了不正常,然后调用abort或exit接口强行将进程终止了。从现象上看,好像是程序发生崩溃闪退了,实则是程序自动终止进程的。
对于这类问题,也需要重启程序,然后将Windbg附加到程序进程上进行动态调试。那既然程序中没有发生与异常崩溃,是如何让调试器感知到的呢?是因为abort函数内部产生了一个特殊的异常(下面会讲到),让正在调试的调试器Windbg中断下来,这样Windbg就感知到了,查看此时的函数调用堆栈就可以进行分析了。
4.1、调用abort或exit强行终止进程,并没有发生异常崩溃,并没有生成dump文件
因为程序内部检测到不正常,直接调用abort或exit强行终止进程,程序并没有发生异常崩溃,所以程序中安装的异常捕获模块没有感知到,所以就不会生成dump文件。
所以,后面再遇到程序发生闪退,且没有生成dump文件,可能是程序内部调用abort或exit强行终止进程引起的(我们在项目中多次用过这种方法),直接重新启动程序,然后将Winbg附加到进程上一起跑,一旦调用abort函数,abort函数内部会产生一个特殊的异常让Windbg中断下来,这样查看此时的函数调用堆栈,就知道是什么原因导致程序调用abort接口了。
4.2、WebRTC开源库内部malloc申请内存失败,强行调用abort终止进程
比如我们在32位程序中使用开源WebRTC库就遇到过这类问题,因为内存不足导致WebRTC内部在用malloc申请内存时申请失败,malloc返回NULL,WebRTC内部检测了malloc的返回值,发现malloc返回NULL,得知内存申请失败,导致相关需要内存的业务没法继续执行,认为这是Fatal致命的,于是直接调用abort强行终止了进程,程序闪退了。之前遇到过两个问题场景导致WebRTC内部用malloc申请内存失败:(都是内存不足导致malloc申请内存失败的)
1)程序占用的用户态虚拟内存接近进程的用户态内存上限
我们的程序是32位的,系统在程序启动时会给进程分配4GB的虚拟内存,默认情况下,用户态虚拟内存占2GB、内核态虚拟内存占2GB:
程序中使用了比较占内存的大型开源库WebRTC,该库在初始化时就会占用大量的内存,当我们执行比较耗内存的操作时(比如加入视频会议,要处理大量的音视频码流,从解码到显示,会占用很多内存),导致程序占用的总的用户态虚拟内存接近进程用户态虚拟内存上限,导致内存不足,再调用malloc申请内存可能就会失败了。
后来的解决办法是,在VS工程配置中开启主程序的大地址选项,将程序的用户态虚拟内存从2GB扩充到3GB,将用户态虚拟内存不够用的问题给规避掉。
2)程序中发生了内存泄漏,导致内存不足
虽然已经将程序的用户态虚拟内存扩充到3GB,但还是出现了内存不够用的问题。经排查分析得知,程序中发生了内存泄漏,持续的内存泄漏导致内存不足,所以又出现malloc申请内存失败的问题。找到内存泄漏的代码,解决内存泄漏,就解决问题了。
4.3、程序在强行终止进程之前,可能会调用DebugBreak让调试器中断下来,abort函数内部也会让调试器中断下来,正是利用这一点,将Windbg附加到进程上动态调试,感知到这个问题
在WebRTC开源代码中,在调用abort强行终止进程之前,会调用系统API函数DebugBreak,让正在调试的调试器感知到。系统API函数DebugBreak,从函数名称也可以看出来,就是让正在调试的调试器(Debug)中断下来(Break),这样调试器就有机会查看此时的函数调用堆栈去看当前发生了什么,这给我们使用Windbg动态调试目标程序排查问题带来了极大的便利。
其实abort函数内部也会产生一个特殊的SIGABRT信号终止异常,从abort的内部实现源码可以看到:
/***
*void abort() - abort the current program by raising SIGABRT
*
*Purpose:
* print out an abort message and raise the SIGABRT signal. If the user
* hasn't defined an abort handler routine, terminate the program
* with exit status of 3 without cleaning up.
*
* Multi-thread version does not raise SIGABRT -- this isn't supported
* under multi-thread.
*******************************************************************************/
void __cdecl abort (void)
{_PHNDLR sigabrt_act = SIG_DFL;#ifdef _DEBUGif (__abort_behavior & _WRITE_ABORT_MSG){/* write the abort message */_NMSG_WRITE(_RT_ABORT);}
#endif /* _DEBUG *//* Check if the user installed a handler for SIGABRT.* We need to read the user handler atomically in the case* another thread is aborting while we change the signal* handler.*/sigabrt_act = __get_sigabrt();if (sigabrt_act != SIG_DFL){raise(SIGABRT);}/* If there is no user handler for SIGABRT or if the user* handler returns, then exit from the program anyway*/if (__abort_behavior & _CALL_REPORTFAULT){_call_reportfault(_CRT_DEBUGGER_ABORT, STATUS_FATAL_APP_EXIT, EXCEPTION_NONCONTINUABLE);}/* If we don't want to call ReportFault, then we call _exit(3), which is the* same as invoking the default handler for SIGABRT*/_exit(3);
}
这个异常也会让调试器中断下来,所以此时会让调试器中断两次,即调用DebugBreak会让Windbg中断一次,调用abort也会让Windbg中断一次。
这类问题排查很简单,只要将Windbg附加到进程,复现问题,Windbg就会中断下来,查看此时的函数调用堆栈即可进行分析了。如果问题很难复现,则要像我上面说到的那样,每次运行软件时都要手动将Windbg附加到进程上,直到复现出问题为止。
关于这类问题的实战问题排查实例,可以查看我之前写的文章:
C++程序中执行abort等操作导致没有生成dump文件的问题案例分析https://blog.csdn.net/chenlycly/article/details/129003869WebRTC开源库内部调用abort函数引发C++程序发生闪退问题的详细排查
https://blog.csdn.net/chenlycly/article/details/129460580
5、最后
本文详细讲述了程序发生闪退且没有生成dump这类问题的场景及排查方法,并给出了相关实战问题分析实例,有一定的实战参考价值,希望能给大家提供一定的帮助。