使用Windbg动态调试排查软件启动不了的问题

目录

1、问题说明

2、初步分析

3、使用Windbg启动程序进行动态调试

4、进一步分析

5、何时使用Windbg静态分析?何时使用Windbg进行动态调试?

6、最后


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       早上测试同事反映,安装新编译出来的版本后程序始终启动不起来,之前的版本都是正常的,就今天新出的版本有问题。于是用Windbg启动程序,快速分析了一下,很快定位了问题。下面来分享一下这个问题的完整排查过程。

1、问题说明

       测试同事安装完早上编译出来的软件版本,启动软件后一直没有反应,软件界面始终没有跳出来(应该显示软件的登录界面),到系统的任务管理器中查看进程一直在的,但就是没弹出软件界面。程序启动时没有报错,估计是软件底层模块出问题了。

目前软件的处理逻辑是,程序启动时会去初始化底层的模块,底层初始化完成后给上层回调一个初始化完成消息,上层在收到这个消息时会弹出软件登录界面。

2、初步分析

       以前我们遇到过这类问题,原因是底层在初始化时遇到问题导致长时间初始化没有完成,上层一直没收到初始化完成的通知消息,所以一直没显示软件界面。不知道这次是不是类似的问题?还有一种可能是,程序启动时调用的底层模块初始化接口一直没有返回,可能底层发生了死锁,导致接口卡住,一直没有返回,直接导致软件UI主线程卡死,也不会弹出软件主界面。

       这些都是猜测,需要详细分析后才能查出具体的原因。分析该问题的方式一般有两种,一种是查看运行日志,看看流程卡在那一块了;另一种是直接上Windbg调试器,用Windbg直接启动程序,分析启动时的运行轨迹。

3、使用Windbg启动程序进行动态调试

       启动Windbg,在工具栏中点击File -> Attach to a Process...,在弹出的窗口中找到目标exe程序的路径:

选中exe程序,然后点击确定,这样Windbg就将程序启动起来了。

      程序启动起来后,Windbg会附加上去,附加成功后Windbg会中断下来,如下所示:

输入g命令,将当前的中断给跳过去。但跳过去以后,显示几行信息后就不再跳动了:

似乎看不到啥有用的信息。

       软件界面线程是软件主线程,对应的线程号为0,于是使用~0s切换到UI主线程:

看到了ntdll!ZwWaitForSingleObject,这个是在等待某个内核对象,估计是UI线程一直在等待对象,估计是底层发生死锁了。

4、进一步分析

       于是输入kn命令查看UI主线程的函数调用堆栈,如下:

从调用堆栈中可以看出调用了WaitForSingleObject接口,因为没有加载pdb文件,所以调用堆栈中看不到有效的函数名。

函数调用堆栈中即使能看到函数名,一般都是导出接口的函数名,不是真实的函数名,相对于函数的偏移也比较大。比如getapistate+0x7b287这样的偏移,偏移值0x7b287很大,一般情况下函数不会太长,不会出现这么大的偏移值,所以一般出现较大的偏移值时显示的不是真实的函数名。

对于dll动态库,导出接口的符号对外才是可见的,很多时候是相对于导出函数的偏移,偏移可能会比较大。也有可能现对于模块名的偏移,比如libcurl++0x52396。如果要看具体的函数名,则需要拿到对应模块的pdb文件,Windbg加载pdb文件后就能显示具体的函数名了。

       从函数调用堆栈中可以看到,有哪些模块,然后使用lm命令查看二进制模块的时间戳,然后再到文件服务器上找对应时间点的pdb文件。拿到这些模块的pdb文件后,放到一个文件夹中,然后将该文件夹的路径设置到Windbg中,然后重新输入kn命令查看函数调用堆栈,就能看到具体的函数名了:

这样就能确定当前问题的具体原因了。从详细的函数调用堆栈信息可以看出,程序启动时调用底层初始化的接口,然后底层模块调用WaitForSingleObject接口去获取锁,一直拿不到锁,所以函数一直没返回,导致上层的UI线程卡住了,所以软件界面一直没显示出来。于是将问题反馈给底层模块的开发维护人员,让他们去排查发生死锁的原因。

维护底层模块同事最近对底层某个模块的代码进行了重构,在处理某个线程的代码时处理的有问题,所以导致了多线程死锁!

5、何时使用Windbg静态分析?何时使用Windbg进行动态调试?

       使用Windbg分析C++软件异常问题时,可以进行静态分析,也可以进行动态调试。 一般情况下,有dump文件生成时,则使用Windbg静态分析dump文件;没有dump文件时,则要尝试使用Windbg去动态调试目标进程。有时dump文件中的信息不足以分析出问题时,也可以尝试使用Windbg去动态调试。

       对于包含异常上下文信息的dump文件,一般是程序中安装的异常捕获模块感知到异常崩溃自动生成的,但并不是所有的异常异常捕获模块能感知到,感知不到也就无法生成dump文件了。程序发生异常崩溃时,如果程序中安装的异常捕获模块没有捕获到,则可以尝试到系统日志中去查看系统有没有生成dump文件。如果系统没有生成,可以尝试将Windbg挂到目标进程上进行动态调试,待复现异常崩溃时,Windbg就会中断下来,就可以进行分析了。

       对于系统生成dump文件的案例,可以参见我之前的案例文章:
使用Windbg分析从系统应用程序日志中找到的系统自动生成的dump文件去排查问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/132024253       关于使用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       有些软件运行异常并没有产生崩溃,比如死循环、死锁(本文中的问题就是死锁引发的)等,是没有dump文件的,需要使用Windbg进行动态调试分析。

       关于何时使用Windbg静态分析、何时使用Windbg进行动态调试,可以参见我之前写的文章:

何时使用Windbg静态分析?何时使用Windbg动态调试?icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131806819

6、最后

       本文通过Windbg启动目标程序进行动态调试,快速定位了问题,这也充分体现了在某些场景下使用Windbg动态调试的优势。希望本文分享的内容,能给大家提供一个借鉴或参考。

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

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

相关文章

14:00面试,14:08就出来了,问的问题有点变态

从小厂出来,没想到在另一家公司又寄了。 到这家公司开始上班,加班是每天必不可少的,看在钱给的比较多的份上,就不太计较了。没想到8月一纸通知,所有人不准加班,加班费不仅没有了,薪资还要降40%,…

Kubernetes可视化管理工具Kuboard部署使用及k8s常用命令梳理记录

温故知新 📚第一章 前言📗背景📗目的📗总体方向 📚第二章 安装 Kubernetes 多集群管理工具 - Kuboard v3📗部署方式📗通过Kuboard v3 - Kubernetes安装(在master节点执行)&#x1f4…

SpringBoot复习:(60)文件上传的自动配置类MultipartAutoConfiguration

可以看到,定义了一个类型为StandartServletMultipartResolver的bean 用来进行文件上传,定义了一个类型为MultipartConfigElement的bean用来进行上传相关的配置,其中使用了MultipartProperties中的属性,这个类的定义如下&#xff1…

vue+element-ui el-table组件二次封装实现虚拟滚动,解决数据量大渲染DOM过多而卡顿问题

一、此功能已集成到TTable组件中 二、最终效果 三、需求 某些页面不做分页时,当数据过多,会导致页面卡顿,甚至卡死 四、虚拟滚动 一、固定一个可视区域的大小并且其大小是不变的,那么要做到性能最大化就需要尽量少地渲染 DOM 元素…

梯度下降算法入门

提到梯度下降我们知道梯度下降算法是很多机器学习算法、深度学习算法的基础。 首先我们需要明确一些概念什么是梯度: 梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处…

软件测试/测试开发丨Python 内置库 正则表达式

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接:https://ceshiren.com/t/topic/27058 python 内置库 正则表达式 目录 正则表达式使用re模块实现正则表达式操作 正则表达式 正则表达式就是记录文本规则的代码可以查找操作符合某些复…

HBuilderX修改manifest.json设置,解决跨域问题(CORS、Cross-Origin)

搭建一个前台uniapp,后台springboot的开发环境时,遇到了跨域问题。 console提示错误信息: Access to XMLHttpRequest at http://10.0.180.203/api/cms/getAdList?apId1 from origin http://localhost:8080 has been blocked by CORS policy…

ROS-5.自定义topic消息格式

自定义topic消息格式 1. 定义消息1.1. 定义msg文件1.2. 在package.xml中添加功能包依赖1.3. 在CMakeList.txt添加编译选项1.4. 编译 2.定义发布者和订阅者2.1 定义发布者2.2. 定义订阅者2.3. 修改CMakeList.txt2.4 编译 3. 使用消息3.1 启动ros主程序3.2. 启动发布者3.3 启动订…

如何制作并运行 jar 程序

以下是用 Intellij 制作 jar 程序,并运行的方法。 【1】新建工程,保持默认选项,Next 【2】保持默认选项,Next 【3】给工程命名,设置保存位置,Finish 【4】新建工程结束,进入开发界面 【5】展开…

ArcGIS将两个相同范围但不同比例或位置的矢量数据移动到相同位置

有两个市图层,一个是正确经纬度的市行政范围图层,另一个是其他软件导出获取的不正确经纬度信息或缺失信息。 如果单纯的依靠移动图层,使不正确的移动到正确位置需要很久。尝试定义投影等也不能解决。 使用ArcMap 的空间校正工具条&#xff…

nnUNet v2数据准备及格式转换 (二)

如果你曾经使用过nnUNet V1,那你一定明白数据集的命名是有严格要求的,必须按照特定的格式来进行命名才能正常使用。 这一节的学习需要有数据,如果你有自己的数据,可以拿自己的数据来实验,如果没有,可以用十…

【负载均衡】常见的负载均衡策略有哪些?

文章目录 前言负载均衡分类常见负载均衡策略小结 前言 负载均衡策略是实现负载均衡器的关键,而负载均衡器又是分布式系统中不可或缺的重要组件。使用它有助于提高系统的整体性能、可用性、可靠性和安全性,同时支持系统的扩展和故障容忍性。对于处理大量…