使用Windbg动态调试目标程序去分析异常的两实战案例分享

目录

1、前言

2、案例1:程序退出时弹出报错提示框

2.1、问题说明

2.2、到系统应用程序日志中看系统有没有自动生成dump文件

2.3、将Windbg附加到目标程序上进行动态调试

3、案例2:程序在运行过程中弹出ASSERT断言提示框

3.1、问题说明

3.2、将Windbg附加到进程上调试

3.3、Windbg是如何找到pdb文件的?

4、使用Windbg静态分析dump文件以及动态调试目标进程的一般步骤

5、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       有时我们需要将Windbg附加到目标程序进程上动态调试,去排查软件运行过程中出现的异常。今天以最近项目中遇到的两个问题实例,给大家详细讲解一下使用Windbg动态调试去分析问题的完整过程,以供大家借鉴或参考。

1、前言

       Windbg是微软提供的一款强大的调试器工具,是分析C++软件异常问题的必备利器。使用Windbg分析软件异常主要有两种方式,一种是使用Windbg静态分析dump文件,一种是将Windbg附加到目标进程上进行动态调试。在日常工作中,主要使用静态分析dump文件的方式,但在少部分场景下可能没有生成dump文件或者生成的dump不足以排查出问题,这就需要使用Windbg进行动态调试了。

       本文主要来讲述使用Windbg动态调试的相关主题内容。在以往排查的多个问题实例的基础上,对需要使用Windbg进行动态调试的场景进行了总结,主要有以下几种场景:(主要看没有生成dump文件的场景)

1)程序发生死循环或死锁时
程序并没有发生异常闪退或崩溃,仅仅是发生死循环或死锁,程序还在持续运行。因为没有产生异常,程序中安装的异常捕获模块是无法感知到的,所以无法生成dump文件。分析这类问题,需要将Windbg附加到出问题的进程上进行动态调试,去查看线程的函数调用堆栈等信息。
2)程序发生异常,但异常捕获模块没有捕获到
程序中虽然安装了异常捕获模块,但异常捕获模块只能捕获到大部分异常崩溃,还有一小部分异常是感知不到的,感知不到的情况下是没法生成dump文件的。比如程序运行过程中发生的一些闪退问题,异常捕获模块没有感知到,可以尝试使用Windbg去动态调试,去尝试复现问题。Windbg在动态调试时,一旦程序发生异常,Windbg会第一时间感知到并中断下来,就可以去查看函数调用堆栈等信息去分析了。
3)异常捕获模块感知到了异常,但导出dump文件时产生了二次崩溃,dump文件生成失败
异常捕获模块感知到了异常,但在导出dump文件时异常捕获模块产生了崩溃(即产生了二次崩溃),dump文件生成失败。

4)程序运行过程中检测到不正常,直接调用abort函数强制结束进程,导致程序闪退
程序闪退,主要有两种原因,一种是运行过程遇到了异常直接崩溃退出,一种是程序中监测到不正场直接调用abort直接将进程终止。

代码中根据变量的值或者函数的返回值检测到不正常,可能会因为程序相关的业务没法正常执行认为程序没有继续存活下去的必要了,直接调用abort函数强行将当前进程终止掉,程序直接闪退。这种情况下的闪退,并不是因为执行汇编代码产生了异常崩溃,而是直接主动调用abort函数导致的,所以异常捕获模块感知不到,也就无法生成dump文件。
对于这种调用abort导致程序闪退的情况,可以将Windbg附加到进程上动态调试,abort函数内部会发出一个特殊的异常信号让调试器Windbg中断下来,这样Windbg就能感知到,通过查看此时的函数调用堆栈进行分析了。
5)用IDE调试程序时产生异常,但看不到有效的函数调用堆栈,可以尝试使用Windbg进行动态调试
有时我们在用IDE调试代码遇到异常崩溃时,可能看不到有效的函数调用堆栈(比如只能看到一两行函数调用记录,或者只能看到底层模块的模块名,看不到具体的函数),可以尝试使用Windbg动态调试去查看到问题发生时的完整函数调用堆栈。
6)程序启动崩溃或失败时
程序启动崩溃或启动失败时,可以尝试用Windbg去启动程序,程序发生异常时Windbg就会中断下来,就可以进行分析了。
7)程序弹出报错提示框时
程序弹出报错提示框,此时进程还在的,可以将Windbg附加到进程上进行分析。当然也可以直接打开Windows任务管理器直接导出dump文件供事后分析。

       关于使用Windbg动态调试场景的详细说明及相关的问题案例,在此就不再赘述了,感兴趣的,可以查看我之前写的长篇文章:

使用 Windbg 分析软件异常时的诸多细节与技巧总结icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/135517926        最近项目中遇到了两个使用Windbg动态调试分析问题的实例,在这里给大家大概地分享一下。


         在这里,给大家重点推荐一下我的几个热门畅销专栏:

专栏1:(该专栏订阅量接近400个,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据近几年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的实战问题分析实例,带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

专栏中的文章均是通过项目实战总结出来的(通过项目实战积累了大量的异常排查素材和案例),有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2: 

C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域的多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!

专栏3: 

开源组件及数据库技术icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html

以多年的开发实战为基础,分享一些开源组件及数据库技术!


2、案例1:程序退出时弹出报错提示框

2.1、问题说明

       测试人员反馈,在其Win7的电脑上关闭我们的软件(退出软件)时,每次都会弹出应用程序已经停止运行的提示,如下所示:

并且这个问题在他的这台Win7电脑上是必现的。对比了一下,在Win10的电脑上运行程序,并没有这个问题,即不同的系统中表现是有差异的。但这个问题在Win7的系统中是必现的,会严重影响用户的体验,必须要解决掉。

       看到这个提示框,初步怀疑可能是程序退出时发生了异常(崩溃),所以系统弹出这个异常提示框。

2.2、到系统应用程序日志中看系统有没有自动生成dump文件

       既然怀疑是程序退出时产生了崩溃,所以尝试到系统应用程序日志中看看有没有程序的异常记录以及自动生成dump文件。具体的操作步骤是,右键点击“此电脑”,在弹出的右键菜单中点击“管理”菜单项,如下所示:

在打开的计算机管理窗口中,在路径 系统工具->事件查看器->Windows 日志->应用程序中点击“应用程序”节点:

右侧就会显示系统记录的应用程序事件。确实看到了程序发生崩溃的记录,但并没有生成dump文件的记录。如果系统自动生成了dump文件,则会在记录文字中给出dump文件的完整路径等信息。

       之前在客户那边遇到的一个程序崩溃的问题,没有生成dump文件,将Windbg直接挂载到目标程序进程上调试运行,也没感知到异常。后来在操作系统的应用程序日志中找到了程序的异常记录,并查看到了系统自动生成的dump文件路径,后来正是用这个dump文件定位了问题。之前我在文章中对这个问题进行了记录与总结,可以查看对应的文章:

使用Windbg分析从系统应用程序日志中找到的系统自动生成的dump文件去排查问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/132024253

2.3、将Windbg附加到目标程序上进行动态调试

       这个问题在测试同事的Win7电脑上是必现的,所以很适合使用Windbg动态调试去分析排查。

       于是重新启动程序,然后将Windbg附加到程序进程上(附加到进程上时会自动中断下来,输入g命令,让程序继续运行),然后退出程序复现问题。程序退出时果然发生了异常,Windbg感知到了并中断了下来,于是输入kn命令查看此时的函数调用堆栈:

       因为没有加载pdb符号文件,所以堆栈中看不到具体的函数名和行号,只能看到相关的模块名。于是使用lm命令查看堆栈中的模块的时间戳,找到对应时间点的pdb文件,然后将pdb文件的路径设置到Windbg中,然后再使用kn命令查看函数调用堆栈,如下所示:

堆栈中看到了具体的函数名和代码行号。堆栈中出问题的模块是协议开发组维护的,于是将函数调用堆栈等信息发给协议组的同事,让他们对照着C++源码去排查。后来他们排查出了原因,是对一块堆内存释放了两次,在第二次释放时产生了崩溃

3、案例2:程序在运行过程中弹出ASSERT断言提示框

3.1、问题说明

       为了排查某个问题,直接运行Debug版本的exe程序(不是使用Visual Studio调试运行程序),在执行某个操作时,弹出了如下的ASSERT断言提示框:

即代码中出现了断言错误,可能是代码运行过程中遇到了异常的变量值。

       这种断言其实排查起来很简单,如果直接使用Visual Studio调试运行的话,直接点击重试按钮,就会中断在发生异常的地方,然后查看函数调用堆栈就能很快定位问题。当前是直接运行Debug版本的exe程序,不是使用Visual Studio调试的,此时进程和问题都还在的,可以将Windbg附加到进程上,然后的点击ASSERT断言对话框中的重试按钮,然后Windbg就会中断下来。

3.2、将Windbg附加到进程上调试

       将Windbg附加到弹出ASSERT断言的进程上,然后点击ASSERT断言提示框中的重试按钮,然后Windbg直接跳转到如下图的STL vector列表的数组下标操作符重载函数处:

vector subscript out of range,vector下标超过范围了。于是输入kn命令查看此时的函数调用堆栈:

堆栈中的第一行位于STL源码中,第二条位于我们的业务库中,所以要看第二条记录中的函数。

细心的朋友可能会有疑惑,我们并没有找pdb文件,为啥输出的函数调用堆栈信息中能看到具体的函数名和行号呢?这个问题接下来会详细讲!

根据第二条记录中显示的函数名和行号,找到源码中的位置,如下:

代码中使用数组下标的方式访问了vector列表vtUserDomainlist中的第一个元素,按照上面的指示,下标超过范围了。

       读第一个元素时就超过范围了,说明列表中没有元素,第一个位置的元素是不存在的。在访问列表元素时没有判断这个列表是否为空,就使用下标直接访问第1个元素了!

3.3、Windbg是如何找到pdb文件的?

       上面我们提到了,有人可能会有疑问,我们并没有去找pdb文件,为啥Windbg显示的函数调用堆栈中能显示出具体的函数名和行号呢?那是因为编译代码时生成的二进制文件中会自动写入默认的pdb文件路径(就是编译时生成的pdb文件路径)。以一个测试程序TestDlg.exe为例,我们直接使用Notepad++、记事本或者UltraEdit打开exe文件(可以先启动这些工具,然后将exe文件拖入到这些工具中即可以查看),然后以pdb关键字搜索,就能找到编译时写入的pdb文件的绝对路径,如下:

pdb文件的绝对路径为:

D:\VSProjects\TestDlg\Debug\TestDlg.pdb

而在本问题中,运行的Debug版本程序就是我机器上编译的程序也是在我机器上运行的,通过写入的pdb文件的绝对路径,在我机器上能找到这个pdb文件,所以Windbg就自动加载了,所以堆栈中能看到详细的函数名及代码行号了。

       如果将我机器上编译的exe主程序拷贝到其他机器上运行,在弹出上述的ASSERT断言提示框时将Windbg附加上去,在运行机器上就找不到pdb文件了,也就看不到详细的堆栈信息了。

4、使用Windbg静态分析dump文件以及动态调试目标进程的一般步骤

       为了让大家能够顺利地将Windbg用起来,有所参考,我之前详细总结了使用Windbg静态分析dump文件的一般步骤以及将Windbg附加到目标进程上进行动态调试的一般步骤,分别详细介绍了分析问题的详细步骤,并以具体的问题实例进行详细地演示,很有指导意义。

       使用Windbg静态分析dump文件的一般步骤,可以参考我之前写的文章:

使用Windbg静态分析dump文件的一般步骤及要点详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/130873143      将Windbg附加到目标进程上进行动态调试的一般步骤,可以参考我之前写的文章:

使用Windbg动态调试目标进程的一般步骤及要点详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131029795

5、最后

       本文给出了两个将Windbg附加到程序进程上动态调试分析的实例,虽然不复杂,但都有一定的代表性,可以给大家提供一定的借鉴或参考。

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

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

相关文章

获取discord上自己创建的服务器的服务器ID、频道ID以及discord的登录token(用于第三方登录)

在服务器图标上右键点击-》复制服务器ID 在频道上右键点击-》复制频道ID F12->手机模式-》application-》local storage-》填写过滤条件【token】 我开发的chatgpt网站: https://chat.xutongbao.top

25-k8s集群中-RBAC用户角色资源权限

一、RBAC概述 1,k8s集群的交互逻辑(简单了解) 我们通过k8s各组件架构,指导各个组件之间是使用https进行数据加密及交互的,那么同理,我们作为“使用”k8s的各种资源,也是通过https进行数据加密的…

爬虫基本库的使用(requests库的详细解析)

注:本文一共4万多字,希望读者能耐心读完!!! 前面,我们了解了urllib库的基本用法(爬虫基本库的使用(urllib库的详细解析)-CSDN博客)。其中,确实又不方便的地方。例如处理网页验证…

Android TV遥控器探索,Android 桌面应用程序

Android TV 的遥控功能是通过红外遥控器或蓝牙遥控器来实现的。下面分别介绍这两种遥控器的工作原理: 红外遥控器: 红外遥控器是最常见的 Android TV 遥控器类型之一。 红外遥控器通过发送红外信号来控制电视或机顶盒。每个按键都有一个特定的红外编码&…

【操作系统】12.文件系统是怎么管理的?

2.文件系统是怎么管理的? 2.1 文件系统的实现 2.1.1 文件系统层次结构 用户调用接口 文件目录系统 存取控制验证模块 逻辑文件系统与文件信息缓冲区 物理文件系统 辅助分配模块 设备管理程序模块 2.1.2 目录实现 线性列表 哈希表 2.1.3 文件实现 文件分配方式 连续…

基于ExtendSim的半导体制造工厂仿真

这是一个离散事件模型,使用ExtendeSim “高级资源管理(ARM)”功能来组织和分配资源。 此模型使用离散事件仿真和高级资源管理(ARM)功能。ARM是一个集成系统,用于组织资源、区分资源并在整个模型中分配资源。…

MySQL加锁策略详解

我们主要从三个方面来讨论这个问题: 啥时候加?如何加?什么时候该加什么时候不该加? 1、啥时候加 1.1 显式锁 MySQL 的加锁可以分为显式加锁和隐式加锁,显式加锁我们比较好识别的,因为他往往直接体现在 S…

“目标检测”任务基础认识

“目标检测”任务基础认识 1.目标检测初识 目标检测任务关注的是图片中特定目标物体的位置。 目标检测最终目的:检测在一个窗口中是否有物体。 eg:以猫脸检测举例,当给出一张图片时,我们需要框出猫脸的位置并给出猫脸的大小,如…

开源 - 一款可自定义的在线免杀平台|过x60、wd等

免责声明:本工具仅供安全研究和教学目的使用,用户须自行承担因使用该工具而引起的一切法律及相关责任。作者概不对任何法律责任承担责任,且保留随时中止、修改或终止本工具的权利。使用者应当遵循当地法律法规,并理解并同意本声明…

工作经验总结:Hex文件解析

一、Hex文件简介 由一行行符合Intel HEX文件格式的文本所构成的ASCII文本文件。一般用于MCU程序烧录,可以把hex文件理解为带有地址信息的bin数据的记录集合。(注:烧录时如果使用bin文件则需要指明对应首地址的位置,而使用hex文件…

最新2024FL Studio21.2.3中文免费版数字音乐工作站(DAW)

FL Studio 21作为一款功能强大的数字音乐工作站(DAW),被广泛应用于各种音乐制作场景中。以下是一些具体的案例: FL Studio 21 Win-安装包下载如下: https://wm.makeding.com/iclk/?zoneid55981 FL Studio 21 Mac-安装包下载如下…

<网络安全>《45 网络攻防专业课<第十一课 - NTFS/EFS/BitLocker数据加密与解密>》

1 NTFS文件系统 磁盘加密 1.1 NTFS安全简介 NTFS是Windows 2000及之后的操作系统的标准文件系统。NTFS不仅在性能上比起FAT 32强,NTFS支持的最大文件大小、最大分区大小也比FAT32大,NTFS还支持配额、安全功能。 在NTFS分区中,对于某个文件或…

一个C#开发的大小只有8KB的贪吃蛇开源游戏!

大家好,我是编程乐趣。 今天给大家推荐基于C#开发的、一个贪吃蛇开源项目,这个项目除了实现贪吃蛇的功能外,重点是讲解如何把编译后的程序,从65MB精简为8KB。 项目地址 https://github.com/MichalStrehovsky/SeeSharpSnake 编译…

Java SourceDataLine 播放音频 显示频谱

Java SourceDataLine 播放MP3音频 显示频谱 1 添加依赖2 快速傅里叶变换2.1 FFT.java2.2 Complex.java 3 音频播放3.1 Player.java3.1 XPlayer.java 4 显示频谱5 结果 项目Value音频格式 添加依赖*.wav(JDK 原生支持)*.pcm(JDK 原生支持)*.au(JDK 原生支持)*.aiff(JDK 原生支持…

vue3项目引入本地js文件,实现一个音频播放按钮

目前有一个需求就是在网页上放置一个音乐控制按钮,并且是在vue3项目里面。于是小白的我遇到了2个问题,第一个问题是如何实现没有进度条的播放按钮,这个网上有现成的代码,可以通过js代码切换不同的图片或者是别的样式,并…

SparkSQL学习02-编程入口

文章目录 1 DataFrame的构建方式方式一:JavaBean反射的方式1.1 创建Scala类1.2 创建Scala对象 方式二:动态编码的方式 2 DataSet的构建方式3 RDD和DataFrame以及DataSet之间的相互转换3.1【RDD-->DataFrame】和【RDD-->DataSet】3.2【DataFrame--&…

java基础之 SPI机制

SPI机制说明 什么是SPI Service Provider Interface 机制是Java提供的一套用来被第三方实现或扩展的API,他可以用来启用框架扩展和替换组件。通过“基于接口的编程 策略模式 配置文件”组合实现的动态加载机制。SPI机制为某个接口寻找服务实现的机制,…

二叉树基础知识总结

目录 二叉树基础知识 概念 : 根节点的五个形态 : 特殊的二叉树 满二叉树 : 完全二叉树 : 二叉搜索树 : 平衡二叉搜索树 : 二叉树的性质 : 二叉树的存储结构 二叉树的顺序存储结构 二叉树的链式存储结构 二叉树的遍历方式 : 基础概念 前中后遍历 层序遍历 :…

【Redis】理论进阶篇------浅谈Redis的缓存穿透和雪崩原理

一、缓存穿透 1、概念 缓存穿透(查不到数据),是指当用户想要查询数据的时候,会先去Redis中取命中,如果Redis中没有该数据,那么就会向数据库中去查找数据。如果数据库中也没有,则该次查询结果失…

Spring学习笔记(五)--Spring的AOP模块

一、AOP的底层原理 AOP的底层原理是动态代理,动态代理有两种方式:JDK动态代理和CGLib动态代理,在有接口的实现类时我们通常用JDK的动态代理方式(默认情况)为类创建代理对象,JDK的动态代理方式可以实现无入…