何时使用Windbg静态分析?何时使用Windbg动态调试?

news2024/11/27 0:21:29

目录

1、概述

2、使用Windbg静态分析dump文件

2.1、异常捕获模块自动生成dump文件

2.2、从Windows任务管理器中导出dump文件

2.3、从正在动态调试的Windbg中使用命令导出dump文件

2.4、使用Windbg静态分析dump文件的一般步骤

3、使用Windbg动态调试目标进程

3.1、程序发生异常,但异常捕获模块没有捕获到

3.2、异常捕获模块感知到了异常,但导出dump文件时产生了二次崩溃

3.3、程序并没有发生C++异常,只是发生卡死或死循环,异常捕获模块感知不到

3.4、程序运行过程中检测到不正常,调用abort函数强制结束进程,导致程序闪退

3.5、Visual Studio调试程序时产生异常,但看不到有效的函数调用堆栈,可以尝试使用Windbg进行动态调试

3.6、程序启动崩溃或失败时

3.7、程序弹出报错提示框时

3.8、使用Windbg动态调试目标进程的一般步骤

4、其他场景说明

5、在实际排查问题时,可能要尝试多种排查方法

6、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N6B9https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N6B9https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具案例集锦(专栏文章正在更新中...)icon-default.png?t=N6B9https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N6B9https://blog.csdn.net/chenlycly/category_11931267.html       使用Windbg分析C++软件问题时,可以静态分析,也可以动态调试。经常有朋友问,到底什么时候使用Windbg静态分析?什么时候Windbg进行动态调试?下面根据近几年来排查C++软件异常遇到的问题场景和项目实例,给大家做个详细的总结。

1、概述

       一般情况下,有dump文件时,使用Windbg静态分析dump文件即可;没有dump文件时,可能就需要使用Windbg去动态调试目标进程了。如果dump文件中的信息不足以分析出问题时,也可以尝试使用Windbg去动态调试。

       此外,如果使用Visual Studio调试代码时遇到异常,但在Visual Studio中分析不出来问题,比如刚启动调试时就产生异常或者报错,可以使用Windbg动态调试进行分析。 

2、使用Windbg静态分析dump文件

      Windbg静态分析的对象就是dump文件,必须要有dump文件。一般生成dump文件的方式主要有三种,分别是异常捕获模块生成、从Windows任务管理器中导出、从正在调试的Windbg中使用命令导出。

2.1、异常捕获模块自动生成dump文件

        为了让软件能自动捕获运行时的异常,一般会在软件中安装异常捕获模块。当软件发生异常时,异常捕获模块感知到异常,自动将包含异常上下文信息导出到dump文件中。有的软件甚至会弹出崩溃反馈提示框,比如如下所示的企业微信的提示框:

提示用户上传log日志和dump文件。为什么不自动上传呢?因为要上传用户机器上的文件,可能会涉及到用户隐私和用户信息安全,不能随意上传,必须经过用户授权后上传或者让用户上传。

       异常捕获库可以选用Google开源库CrashRpt,或者使用Chrome开源代码中的Breakpad或者Crashpad,对于QT程序则可以选用qBreakpad。程序发生异常时,异常捕获模块感知到,会将异常上下文信息导出到dump文件中。事后可以取来dump文件,使用Windbg进行静态分析了。

       异常捕获模块一般是调用系统API函数miniDumpWriteDump去生成dump文件,可以控制传给该API函数的参数去控制生成的dump文件的大小。此时一般生成的是比较小的mini dump文件,因为dump文件是保存在用户电脑上,不能生成全dump文件,因为全dump文件比较大(包含了所有内存信息,文件大小可能接近进程的总虚拟内存),会占用用户大量的磁盘空间。此外,dump文件可能要上传服务器,文件过大会给上传带来较大的网络负担,所以dump文件也不能太大。一般分析mini dump文件就能定位问题了。

2.2、从Windows任务管理器中导出dump文件

       如果程序中的线程出现卡死(可能是死循环或死锁引发的)或者程序运行弹出报错提示框,这些情况下程序的进程还在,可以直接到Windows任务管理器中手动导出dump文件。事后取来dump文件进行分析。

        从Windows任务管理器中导出dump文件的操作步骤是这样的。打开Windows任务管理器,找到出问题的目标进程,右键点击该进程,在弹出的右键菜单中点击“创建转储文件”菜单项:

即可导出包含进程上下文信息的到dump文件中。

       其实程序出现卡死或者运行报错时,还为时不晚的,可以直接将Windbg附加到进程上进行分析的。但有些客户比较敏感(比如安全级别比较高),不允许远程到机器上将Windbg附加上去,甚至都连Windbg都不让安装,这是就可以让客户手动从Windows任务管理器的进程列表中导出。这种方式导出的dump文件是全dump文件,文件会比较大,包含了进程的所有内存信息。

2.3、从正在动态调试的Windbg中使用命令导出dump文件

       可能正在使用Windbg调试目标进程,但一时半会又分析不出问题,不能长时间占用出问题的电脑,电脑主人还需要用。我们可以使用Windbg命令.dump导出当前正在调试的进程的dump文件,具体的命令样式可以这样:

.dump /ma D:\0606.dmp

事后取来dump文件,然后使用Windbg静态分析这个dump文件,继续分析问题。一般此时导出的也是全dump文件。

2.4、使用Windbg静态分析dump文件的一般步骤

       取来dump文件,使用Windbg打开,首先输入.excr命令切换到发生异常的上下文,然后使用kn命令查看发生异常时的函数调用堆栈,然后将函数调用堆栈与源码对应起来分析。一般此时需要根据函数调用堆栈中的模块,去找这些模块的pdb文件,然后再去查看函数调用堆栈,有了pdb文件中的函数及变量符号后,函数调用堆栈中可以看到具体的函数名称和代码行号:

甚至可以直接在Windbg中查看函数中局部变量的值或者函数所在类对象的成员变量的值。

       关于Windbg静态分析dump文件的详细步骤及相关要点,可以参见我之前写的文章:

使用Windbg静态分析dump文件的一般步骤及要点详解https://blog.csdn.net/chenlycly/article/details/130873143

3、使用Windbg动态调试目标进程

       如果没有生成dump文件,则没法使用Windbg进行静态分析,这时就需要使用Windbg进行动态调试了,即将Windbg动态附加到目标进程上进行调试。动态调试有两种方式:

1)直接使用Windbg启动目标程序;
2)先将目标程序启动起来,然后将Windbg附加到目标进程上。

当目标程序发生异常,Windbg就会感知到,会自动中断下来,这样使用Windbg命令就能查看当前发生的异常类型并查看此时的函数调用堆栈了。

       没有生成dump文件,有几种场景,这些场景需要使用Windbg的动态调试了,下面来详细介绍一下。

3.1、程序发生异常,但异常捕获模块没有捕获到

       程序中安装的异常捕获模块,并不能感知并捕获所有场景下的异常,即还是有少部分异常是捕获不到的。捕获不到的,自然就不会生成dump文件了。我们在项目中也时常会遇到,程序在使用过程中发生闪退或崩溃,异常捕获模块没捕获到,没有生成dump文件。一般遇到这类情况,都会让测试人员将Windbg附加到目标进程上,和目标进程一起跑,一旦目标进程运行过程中发生异常,Windbg会第一时间感知到并中断下来,此时就可以查看发生的异常类型和函数调用堆栈了。

3.2、异常捕获模块感知到了异常,但导出dump文件时产生了二次崩溃

       程序发生了异常,异常捕获模块感知到了,但在导出dump文件的过程中产生了二次崩溃,可能没生成dump文件,或者生成的dump文件大小为0,这种情况下也需要使用Windbg进行动态调试的。这个场景我们在项目中也遇到过多次。

3.3、程序并没有发生C++异常,只是发生卡死或死循环,异常捕获模块感知不到

       程序并没有发生C++异常,只是业务逻辑上产生异常(比如不能正常处理业务了),比如发生多线程死锁或死循环。因为并没有产生C++异常,异常捕获模块是感知不到的,所以也就无法生成dump文件。这个也需要将Windbg附加到进程上进行动态调试,进行详细分析的。

3.4、程序运行过程中检测到不正常,调用abort函数强制结束进程,导致程序闪退

       如果程序在运行过程中监测到不正常的情况,直接调用abort函数强制将进程终止了,这样给人一种程序发生崩溃闪退的感觉。这种情况也是没有发生C++异常,异常捕获模块也是感知不到的,所以也不会产生dump文件。比如在开源jsoncpp库中,如果解析的json节点的类型不匹配,会被jsoncpp内部检测到,会触发abort函数的调用,相关代码截图如下:

        再比如WebRTC开源库中在监测到malloc动态申请内存失败后,也会触发abort函数的调用,强行终止进程。估计是因为动态申请内存失败了,相关业务无法正常展开了,程序活着也没意思了,所以就强行终止进程了。相关代码截图如下:

       上述两类问题,我们在实际项目中都遇到过。遇到这类情况,也可以将Widnbg附加到目标进程上进行动态调试,尝试着去复现问题。如果程序调用abort函数,就会触发Windbg中断下来,然后查看函数调用堆栈,通过函数调用堆栈就知道是什么函数调用触发的abort函数的调用的。那为什么调用abort函数会让正在调试的调试器Windbg中断下来呢?因为abort函数内部会raise(产生)一个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 _DEBUG
    if (__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);
}

        关于调用abort函数强行终止进程场景的详细说明,可以参见我之前写的文章:

C++程序中执行abort等操作导致没有生成dump文件的问题案例分析https://blog.csdn.net/chenlycly/article/details/129003869

3.5、Visual Studio调试程序时产生异常,但看不到有效的函数调用堆栈,可以尝试使用Windbg进行动态调试

       如果使用Visual Studio调试代码的过程中产生了异常,但在Visual Studio中看不到完整的或者有效的函数调用堆栈,分析不出来问题,比如刚启动调试时就产生异常或者报错,可以尝试着使用Windbg动态调试进行分析。

3.6、程序启动崩溃或失败时

       当程序启动崩溃闪退,会因为某些原因启动失败时,可以尝试使用Windbg启动程序进行动态调试。可能Debug下不好复现,也可能即使复现用Visual Studio调试也很难排查。有可能问题只在客户机器上才会出现,没法到客户机器上使用Visual Studio调试代码。这些场景下的问题,使用Windbg去动态调试分析很方便。

3.7、程序弹出报错提示框时

       程序在运行过程中出现异常,弹出报错提示框,此时进程还在,此刻将Windbg附加到进程上还来的及,可以直接分析。可能问题很难复现,错过了就很难复现了,所以要及时地将Windbg附加到进程上分析。即使一时半会分析不出原因,也可以将进程上下文导出到dump文件中,事后再去分析。

3.8、使用Windbg动态调试目标进程的一般步骤

       将Windbg附加到目标进程上,一旦目标进程产生异常,Windbg就会感知到并中断下来,此时使用kn命令查看发生异常时的函数调用堆栈,然后将函数调用堆栈与源码对应起来分析。一般此时需要根据函数调用堆栈中的模块,去找这些模块的pdb文件,然后再去查看函数调用堆栈,有了pdb文件中的函数及变量符号后,函数调用堆栈中可以看到具体的函数名称和代码行号,甚至可以直接在Windbg中查看函数中局部变量的值或者函数所在类对象的成员变量的值。

       关于Windbg动态调试目标进程的详细步骤及相关要点,可以参见我之前写的文章:

使用Windbg动态调试目标进程的一般步骤及要点详解https://blog.csdn.net/chenlycly/article/details/131029795

4、其他场景说明

       产品发布后,问题是出在Release版本下的。如果使用Windbg静态分析dump文件分析不出来问题时,可以尝试使用Windbg动态调试的。如果使用Windbg分析不出来问题,并且问题是好复现的,则可以尝试使用Visual Studio去调试代码,进行Release下调试。如果问题出在底层的dll模块,则可以使用Visual Studio附加到进程调试的方式去单独调试这个dll模块,附加到进程调试还是很方便的。

       此外,我们有时甚至需要在Visual Studio中查看并单步调试汇编代码。CPU中执行的是一句一句汇编代码(或者叫二进制机器码,两者是等价的),看汇编代码才能看到代码的执行细节。一行C++源码可能对应多行汇编代码,有时我们要知道C++源码为何有问题,可以尝试在Visual Studio中转到汇编代码页面,去单步调试汇编代码,通过调试汇编代码去寻找真相

5、在实际排查问题时,可能要尝试多种排查方法

       在排查问题的过程中,可能需要使用多种排查方法,一个方法排查不出来,就换另一种方法;或者将两个或两个以上的方法结合起来使用,直到定位问题为止!此外,除了使用调试器,还可以考虑同时使用其他的软件分析工具去辅助定位,比如Process Explorer或Process Monitor等。

        之前使用多种方法去排查一个线程栈溢出的问题,相关问题排查实例记录文章如下:

线程栈溢出异常,程序崩溃在汇编代码test dword ptr [eax],eax上的问题排查https://blog.csdn.net/chenlycly/article/details/131743305在上述问题中先后使用了多种排查方法,从Windbg静态分析dump文件,到Windbg动态调试目标进程,再到使用Visual Studio调试源码,最后去单行调试汇编代码。最终调试汇编代码,才找出问题的原因。

6、最后

       本文根据近几年来排查C++软件异常遇到的问题场景和实例,给大家做个详细的总结,希望能给大家提供一个详细的借鉴或参考。此外,分析问题的方法是多样的,不是一成不变的,要根据具体情况去灵活选用一种或多种方法去排查。需要通过大量的实践去积累经验的,经验多了,处理问题的思路就多了,处理问题就更加得心应手了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/769995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Win10 配置NDK安装2023.7.19版本

NDK安装流程 1. 下载:2. 安装:3. 测试: 在大多数情况下,使用 Android SDK 管理器安装 NDK 会更轻松。本文单独安装NDK,但后续也可以使用管理器进行管理。 1. 下载: 地址 Fig.1 最新稳定版本 2. 安装&…

精益生产的五大管理工具:提升效率,降低成本!

在制造业的世界里,精益生产是一种以追求在制造过程的各个方面减少浪费为中心的方法。为了实现这一目标,有几个经常使用的管理工具。这些工具使制造商能够识别和消除生产过程中任何效率低下或浪费的资源。本文将讨论精益生产中使用的一些关键管理工具&…

Android 进程与进程之间的通信--Messager 详细教程,两个app实现

Messenger是一种轻量级的IPC方案,它的底层实现其实就是AIDL.跨进程通信使用Messenger时,Messenger会将所有服务调用加入队列,然后服务端那边一次处理一个调用,不会存在同时调用的情况.而AIDL则可能是多个调用同时执行,必须处理多线程问 步骤详情 一、服务端 public class MyM…

如何使用DiskPart命令行格式化分区?

想要格式化磁盘分区,您可以使用磁盘管理工具,或在Windows文件资源管理器中右键单击驱动器并选择“格式化”。如果您更想使用命令行来格式化磁盘,那么Windows自带的DiskPart将是首选。 DiskPart有很多优点,例如,如果您想…

《无畏契约》游戏分析

文章目录 介绍游戏继承性《守望先锋》游戏美术对比游戏机制对比 《CSGO》游戏美术对比游戏机制对比 《英雄联盟》游戏美术对比游戏机制对比 《无畏契约》的优点《无畏契约》的缺点该游戏值得学习之处总结 介绍 《无畏契约(VALORANT)》是一款由拳头游戏&…

Vue组件的基本使用

Vue中想用组件总共分几步: 1.创建组件 2.注册组件 3.使用组件 1.创建组件 //1.创建school组件,这里的school并不是组件名,只是一个中转变量名const school Vue.extend({// el:#root, //组件定时,一定不要写el配置项&#xff0…

[python][深度学习]diffusers加载模型每次都联网如何离线加载

diffusers模块很好用,唯一缺点就是没把离线加载模型做好。一般都是联网自动下载后,以后离线加载一下就行了,没想到每次都远程下载一堆东西而且经常容易断网。因此研究离线加载势在必行,经过N次下载之后终于成功下载模型 下载后发现…

【电路原理学习笔记】第5章:串联电路:5.1 电阻的串联

第5章:串联电路 5.1 电阻的串联 图5-1a展示了申联于A点和B点之间的2个电阻。图5-1b和图5-1c分别展示了3和4个电阻相串联的情况。当然,串联电路中可以有任意数量的电阻。 对于图51所示各电路,当电压源连接在A点和B点之间时,电流…

【数据结构】链表及无头单向非循环链表实现

目录 1.顺序表的问题 2.链表的概念、结构及分类 3.无头单向非循环链表实现 3.1创建节点 3.2头插数据 3.3头删数据 3.4尾插 3.5尾删 3.6链表销毁 3.7查找一个元素 3.8在pos之前插入 3.9在pos之后插入 3.10删除pos位置 3.11删除pos之后的位置 1.顺序表的问题 顺…

第一百一十天学习记录:C++实战:自我设计用单链表、多态和文件操作写一个公会人员管理系统

实现程序界面展示: 主界面: 程序输入非正常字符情况保护 添加会员信息 删除会员信息 查找会员信息 变更会员会阶 显示所有会员 排序会员信息 查看种族职业 保存信息的txt文件 工程文件目录 main.cpp代码 #include "allmember.h" #include &q…

2023年7月广州/深圳软考中级系统集成项目管理工程师招生

系统集成项目管理工程师是全国计算机技术与软件专业技术资格(水平)考试(简称软考)项目之一,是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试,既属于国家职业资格考试,又是职…

FFmpeg 命令行实现居中高清上下模糊播放效果

FFmpeg 命令行实现居中高清上下模糊播放效果。 1、16:9 的横屏原视频,以 16:9 竖屏上下模糊播放 以该效果播放视频的命令如下: ffplay -i horizontal_test_video_169.mp4 -vf \ "split[a][b]; \ [a]crop(ih/16*9):ih,scaleiw/10:-1,gblursigma5…

GreatSQL通过错误日志信息判断数据库实例是如何关闭的

背景概述 在一次客户的数据库实例连接不上了,需要我们排查一下原因,通过查看数据库实例进程已经不存在了,在错误日志中没有发现其他报错信息,发现有shutdown的字样出现,怀疑是某个用户手动关闭了实例。我们通过以下测…

华为认证的题库,不仅能考试,还能帮你提升技能

1、OSPF协议在哪种状态下确定DD报文的主从关系? A. 2-way B.Exchange C. ExStart D. Full 2、在VRP操作系统中,如何进入OSPF区域0的视图?A. [Huawei-ospf-1]area 0 B.[Huawei]ospf area 0 C. [Huawei-ospf-1]area 0 enable D. [Huawe…

珀莱雅、华熙生物、贝泰妮、丸美股份一季报PK,谁是“卷王”?

国货美妆有多“卷”? 618落幕,各大电商平台公布了美妆销售数据。据统计,618期间天猫、京东、抖音、快手四大平台美妆销售总额超过610亿元。 近日,四家国货美妆企业,珀莱雅、华熙生物、贝泰妮、丸美股份分别公布了202…

经济和行政手段使双高企业降低能耗总量和能耗强度,提高能源利用效率-安科瑞黄安南

摘要 2022年6月29日工信部、发改委、财政部、生态环境部、国资委、市场监管总局六部门联合下发《关于印发工业能效提升行动计划的通知》(工信部联节〔2022〕76号,以下简称《行动计划》),主要目的是为了提高工业领域能源利用效率&…

【5】Vite+Vue3 JsonPath的使用

在当今前端开发的领域里,快速、高效的项目构建工具以及使用最新技术栈是非常关键的。ViteVue3 组合为一体的项目实战示例专栏将带领你深入了解和掌握这一最新的前端开发工具和框架。 作为下一代前端构建工具,Vite 在开发中的启动速度和热重载方面具有突…

PMP证书怎么考?来自前辈的经验之谈

虽然我成功通过了 PMP 考试,这是我第一季度所做的事情的成果,但是考试的兴奋感很快就过去了。在交流群里,大家都在讨论证书的存续条件以及获得证书后带来的实质性收益,例如补贴、城市定居加分和职业晋升等。与这些相比&#xff0c…

Android ViewGroup onDraw为什么没调用

ViewGroup,它本身并没有任何可画的东西,它是一个透明的控件,因些并不会触发onDraw,但是你现在给LinearLayout设置一个背景色,其实这个背景色不管你设置成什么颜色,系统会认为,这个LinearLayout上…

【Redis】底层探析 I - Redis 有序集合(ZSet)是如何实现的?

目录 ZSet的编码方式 什么是跳跃列表(skiplist)? ZSet的底层结构 跳跃列表的查询过程 ZSet的编码方式 Redis中的有序集合zset底层实现采用了两种编码方式: REDIS_ENCODING_SKIPLIST 跳跃列表REDIS_ENCODING_ZIPLIST 压缩列表 对于不同编码的触发方式…