使用 Process Explorer 和 Windbg 排查软件线程堵塞问题

目录

1、问题说明

2、线程堵塞的可能原因分析

3、使用Windbg和Process Explorer确定线程中发生了死循环

4、根据Windbg中显示的函数调用堆栈去查看源码,找到问题

4.1、在Windbg定位发生死循环的函数的方法

4.2、在Windbg中查看变量的值去辅助分析

4.3、是循环计数值没有累加导致的

5、可以从动态调试的Windbg中导出dump文件

6、最后


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       软件运行过程中有线程发生堵塞,是时常发生的事,最近就遇到一个典型的案例,虽然分析过程不是很复杂,但很有代表性,在这里给大家分享一下这个问题的详细排查过程,希望能给大家提供一个借鉴或参考。使用Process Explorer和Windbg排查软件线程堵塞问题

1、问题说明

       某天,客户在使用我们软件的过程中遇到了问题,在其Windows10系统中运行我们的客户端软件,加入了一个会议,一直在开会,没有做其他的操作,中间某个时间点用鼠标去点击软件窗口时发现点击没反应,好像是软件UI界面卡死了!但会议窗口中还能看到正在动的远端视频,能听到远端的声音,应该是UI线程卡死了,其他线程还在正常运行。这个问题比较严重,无法操作软件界面了,如果领导那边出现这样的问题,就比较麻烦了。于是联系到我们,希望我们尽快协调研发人员排查定位一下。

       我们研发这边接到任务后,和客户取得了联系,通过远程软件远程连到他的电脑上。我们初步怀疑是软件的UI界面所在的主线程卡死或堵塞了,于是将SPY++、Process Explorer和Windbg等工具拷贝到客户的电脑上准备详细分析一下。


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

专栏1:(该专栏订阅量接近350个,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到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、线程堵塞的可能原因分析

       软件为了并发处理事务一般都使用了多线程,如果代码处理不当,可能就会出现线程堵塞或卡死问题。一般是个别线程的堵塞,其他线程还是正常执行的,发生堵塞的线程中处理的业务出现异常。

       对于客户端软件,一旦有线程发生异常,我们可以通过软件界面的异常表现感知到,然后通过日志去大概地确定发生异常的业务线程。在本例中,UI界面不能操作了,判断应该是UI界面所在的主线程堵塞了。会议中音视频的解码播放是在底层模块的其他线程中进行的,这些线程是正常运行的,所以还能看到视频画面、听到会议中的声音。

       导致线程发生堵塞或卡死的原因主要有两种

1)死锁:软件中多线程发生了死锁,某个线程需要获取某个锁,但因为死锁导致该锁一直没释放,导致线程一直卡在WaitForSingleObject等等待函数接口上没返回,所以线程卡住了;
2)死循环:当前线程中某个函数中发生了死循环,接口调用一直没返回,导致线程卡住了。如果是死循环,还会导致一个现象,死循环会占用大量的CPU时间片,导致程序进程占用较高的CPU,用Process Explorer工具则可以看到某个线程的CPU占用的特别高,这个线程就是发生死循环的线程。

       在本案例中,出问题的是UI客户端软件,客户端界面没法操作了,可能是界面所在的UI线程(UI程序的主线程)发生了堵塞,可能是死锁或死循环导致的。UI界面没法操作,还有另外一个原因,可能是窗口被disable了,窗口之前在执行某个操作时被disable了(比如弹出了一个模态框),在执行完操作后出现异常,没有将窗口恢复到enable状态,这个问题场景我们以前在项目中遇到过几次。我们可以用SPY++工具查看一下当前不能操作的窗口属性,如果是当前的窗口被disable导致窗口不能操作的,那么使用SPY++查看的窗口属性中应该能看到 WS_DISABLED 窗口风格,如下所示:

本例中界面不能操作不是窗口被disable导致的。

       对于腾讯会议、企业微信、字节飞书、阿里钉钉等这类Windows桌面客户端软件,UI界面所在的线程称为UI线程,也是软件的主线程。如果UI界面或窗口不能操作了,可能就是UI线程出问题,发生堵塞了。

在Windbg中,被分析的软件有多个线程,线程除了有线程id,还有个线程编号,编号从0开始,UI主线程的编号就是0,Windbg切换线程的命令~ns中的n就是线程编号。

3、使用Windbg和Process Explorer确定线程中发生了死循环

       当时软件的UI窗口不能操作了,使用SPY++查看窗口属性,窗口并没有被disable掉,所以基本可以断定,UI界面所在的UI线程发生堵塞了。

       那这个线程堵塞是死锁引起的,还是死循环引起的呢?其实要确定这个问题,很简单,当前程序进程还在的,只需要将Windbg附加到问题进程上,切换到0号UI主线程(上面已经讲了,UI程序的UI界面就在UI主线程中),然后查看该线程的函数调用堆栈,如果堆栈中调用了WaitForSingleObject或NtWaitForAlertByThreadId(进入临界区时会调用该函数)等等待函数时,就能确定是线程发生死锁了。

       这个地方需要提一下,线程堵塞和程序崩溃是两个完全不同的概念和场景,要注意区分一下

1)对于堵塞,堵塞只是发生在个别线程中,其他线程还是正常的,程序进程没有退出,进程还在的,此时可以将Windbg附加到进程上调试的。
2)对于程序崩溃,如果没弹出崩溃提示框,程序直接闪退,程序进程就不存在了,就没有机会将Windbg附加到进程上分析了。如果程序没有生成dump文件,只能在下次运行程序时将Windbg附加到进程上动态调试(Windbg和目标程序一起跑),然后去复现崩溃,一旦程序发生崩溃,Windbg就会感知到并中断下来,就可以分析了。此外,如果程序崩溃时弹出了系统报错提示框,只要不将该提示框点掉,则进程还在的,此时还有机会将Windbg附加到进程上进行分析的,我们在项目中遇到过这样的场景。

       将Windbg附加到当前出问题的程序进程上,使用~0s命令切换到UI线程中,然后输入kn命令查看UI线程此刻的函数调用堆栈,如下所示:

因为没有加载pdb符号文件,所以堆栈中看不到具体的函数名。于是使用lm命令查看堆栈中dll模块的时间戳,到我们的文件服务器上找到对应时间点的pdb文件(我们已经将各个版本的安装包、二进制文件和pdb文件保存到文件服务器上,维护起来了),然后将pdb文件路径设置到Windbg中,重新执行~0s和kn命令查看UI线程的详细函数调用堆栈信息,如下所示:

加载pdb文件后,调用堆栈中就可以看到具体的函数名了,但没有看到WaitForSingleObject或NtWaitForAlertByThreadId(进入临界区时会调用该函数)等等待函数的调用,所以基本可以断定UI线程的堵塞和死锁没有关系。

       基本只有一种可能,UI线程中的某个函数发生死循环了。死循环就是一直在执行代码,会占用大量的CPU时间片,死循环所在的线程会占用较高的CPU比例,直接导致程序进程占用较高的CPU。这个我们可以使用Process Explorer工具核验一下,打开Process Explorer,在进程列表中找到当前出问题的进程,然后双击该进程条目,查看进程的属性,在弹出的属性页面中点击Threads标签页,在该页面中可以查看到当前进程的各个线程信息,包括线程占用的CPU比例以及线程此刻的函数调用堆栈。

Process Explorer查看线程的函数调用堆栈可能不准确,可以使用另一个类似的工具Process Hacker,这个工具看线程的函数调用堆栈比较准确!

       查看到如下的线程列表:

果然看到了一个线程占用了20%的CPU,但只能看到线程号,我们如何确定这个线程就是当前出问题的UI主线程呢?

       其实很简单,我们可以回到Windbg中,使用~命令打印出当前问题进程的所有线程,如下所示:

我们上面讲了,UI应用程序的UI主线程在Windbg中的线程序号就是0,看着上述线程列表,第一个条目就是0号线程,就是UI主线程,找到其对应的线程id为0x2e20(16进制),转换成10进制数据为:11808,即UI线程的线程id为11808,然后跳回到Process Explorer的线程列表页面,占用CPU高的线程就是UI主线程。所以,可以确定UI线程中有函数发生死循环了。

4、根据Windbg中显示的函数调用堆栈去查看源码,找到问题

       确定UI线程发生了死循环,下面只要根据Windbg中显示的UI线程的函数调用堆栈,去查看C++源码去分析为什么出现死循环就可以了。

       回到Windbg中,使用~0s命令切换到UI线程中,然后输入kn命令查看UI线程的函数调用堆栈,如下所示:

之前已经设置pdb到windbg中了,所以堆栈中显示了详细的函数名和代码行号,看到最后调用的一个函数是xxxlib!LoginManager::OnSrvAddrsChangedNtf。

4.1、在Windbg定位发生死循环的函数的方法

       这个地方说几个关于使用Windbg排查死循环的技巧。

我们当前的问题相对较简单,没用到此处提的方法。大家后面可能会用到,所以在此说明一下。

       如果当前要确定当前线程是否发生死循环,可以尝试多次输入g命令,让程序继续跑,然后再break中断下来,如果每次堆栈都是一样的话,可能发生死循环了。但这个需要我们根据调用堆栈辨别一下,不是堆栈一样就一定是死循环,比如我们的某个业务线程就在循环执行业务,每次查看的堆栈肯定是一样的,这不是程序中发生了死循环。业务线程中虽然是个循环,但中间会人为sleep一下,不让线程跑满。

       此外,我们在Windbg中可以使用bp命令设置断点,比如可以在函数调用堆栈中的多个函数中设置断点,然后输入g命令让程序继续跑,看都命中了哪些函数中的断点,这样就能确定死循环发生在哪个函数中了。

Windbg中支持设置断点,相关的命令如下:

1)bp:添加断点;

2)bc:清除断点;

3)bl:列出断点;

4)ba:设置数据断点。

这几个命令的详细说明及使用方法,可以查看Windbg帮助文档,文档中有相关的示例可以参考。

4.2、在Windbg中查看变量的值去辅助分析

       以前我们多次讲过,可以尝试在Windbg中查看函数中局部变量或者函数所在类的成员变量的值,某些变量的值可能是分析问题的关键线索。在Windbg中查看变量的值有几个场景,此处说明一下:

1)当前用Windbg分析的是小dump文件(程序中安装的异常捕获模块捕获到的,文件比较小,可能就几MB大小),dump文件中只保存了少部分变量的值,能不能看到自己想看的变量的值,要看运气的。
2)当前用Windbg分析的是全dump文件(从Windows任务管理器中导出的或者从正在动态调试的Windbg中使用.dump命令导出的),全dump保存了所有内存信息,其大小接近当前进程占用的用户态虚拟内存的大小,可以看到所有变量的值。

如果要查看程序中全局变量的值,可以使用x命令去搜索,前提是要有pdb符号文件,因为pdb文件中有变量的符号信息。

       当前函数调用堆栈显示的最后一个函数是LoginManager::OnSrvAddrsChangedNtf,代码如下:

估计是函数中的for循环发生了死循环,for循环的条件中访问了TServerAddrs_Api结构体变量的dwCount成员的值,是不是这个dwCount值有问题?是一个异常大的值,导致循环一直跳不出来?于是想查看该指针变量的值,于是点击函数调用堆栈前面的序号:

将所在函数的栈展开:

只能看到函数所在类对象指针this值,但我们想看TServerAddrs_Api* ptServerAddrs指针变量展开的结构体对象中的值。并没有显示,但该结构体指针对象的值就是wParam值,wParam值是能看到的,所以我们是有办法看到TServerAddrs_Api* ptServerAddrs指针变量展开的结构体对象中的值的。

       我们把wParam局部变量中的值0x336200a0当成指针变量TServerAddrs_Api* ptServerAddrs的值去解析,需要使用Windbg的一个较复杂的命令,但我们并没有记这个命令,该怎么办呢?我们点击this指针的超链接:

这个超链接自动生成查看这个指针中变量的值的Windbg命令并执行该命令,我们直接借鉴这个自动生成的命令:

dx -r1 ((xxxlib!LoginManager *)0x55a8cc0)

将之改成:

dx -r1 ((xxxlib!TServerAddrs_Api *)0x336200a0)

然后执行该命令:

可以看到dwCount值为1,所以底层传上来的数据不是异常值,不是上面假定的问题。

4.3、是循环计数值没有累加导致的

       我们继续查看函数pcmt_mtclib!LoginManager::OnSrvAddrsChangedNtf:

这个接口是处理底层模块投递上来的消息的(消息的处理函数),乍一看函数没有明显问题,难道是底层一直在抛这个消息,导致我们的代码一直在执行,导致CPU占用高?

       我甚至找来了维护底层模块的同事,让他们看日志,看看是不是底层一直在持续抛同一个消息。同事并没有找到问题,后来我无意中瞄了一下代码,居然是for循环中索引值没有累加导致的,如下所示:

没有对索引值进行自加操作,导致了死循环。这么简单的问题,居然如此动干戈!属实不应该啊!

5、可以从动态调试的Windbg中导出dump文件

       当前我们到客户的电脑上将Windbg附加到目标进程上调试时,如果一时半会没查出问题,我们不能一直占用别人的电脑,他们还有自己的事情,我们可以使用.dump命令将进程的上下文信息导出到dump文件中:

.dump /ma D:\1214.dmp

然后将dump文件发回去,我们事后再去详细分析。

6、最后

       这个案例给我们展示了如何使用工具高效地排查软件运行过程中遇到的问题,虽然不复杂,但讲到了使用Windbg等工具的多个细节,有很大的参考价值!我们在研学技能时,要多关注细节,多关注分析问题的思路和办法。

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

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

相关文章

基于net6的asp.net core webapi项目打包为docker镜像,并推送至私有镜像仓库harbor中

基于net6的asp.net core webapi项目打包为docker镜像,并推送至私有镜像仓库harbor中 0、环境说明1、打包步骤1.1 创建Asp.net core WebApi项目1.2 在Asp.net core WebApi项目根目录下创建Dockerfile文件1.3 在子系统Ubuntu20.04.4中通过docker build生成docker镜像1…

【angular教程240105】02绑定属性 绑定数据、条件判断、加载图片、【ngClass】 【ngStyle】、Angular管道

【angular】02绑定属性 绑定数据、条件判断、加载图片、【ngClass】 【ngStyle】、Angular管道 0 一些基础的概念 标记为可注入的服务 在Angular中,一个服务是一个通常提供特定功能的类,比如获取数据、日志记录或者业务逻辑等。标记为可注入的服务意味着…

PCL 计算异面直线的距离

目录 一、算法原理二、代码实现三、结果展示四、相关链接本文由CSDN点云侠原创,PCL 计算异面直线的距离,爬虫自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 设置直线 A B AB A

【JVM 基础】 Java 类加载机制

JVM 基础 - Java 类加载机制 类的生命周期类的加载: 查找并加载类的二进制数据连接验证: 确保被加载的类的正确性准备: 为类的静态变量分配内存,并将其初始化为默认值解析: 把类中的符号引用转换为直接引用 初始化使用卸载 类加载器, JVM类加载机制类加载…

nuxt 不解析HTML结构bug

记录一个本人Vue3迁移Nuxt3的报错 报错信息 [Vue warn]: Failed to resolve directive: top [nitro] [unhandledRejection] TypeError: Cannot read properties of undefined (reading ‘getSSRProps’) 原因是Vue3在迁移到nuxt3的时候有一个自定义指令没有搬过来&#xff0…

flutter 打包安卓apk 常用配置

打包之前需要先不配置不然会报错 Execution failed for task ‘:app:mergeReleaseResources’. APP目录下的build.gradleaaptOptions.cruncherEnabled falseaaptOptions.useNewCruncher false如图 配置targetSdkVersion 、minSdkVersion 在android/app/src目录下的build.…

自承载 Self-Host ASP.NET Web API 1 (C#)

本教程介绍如何在控制台应用程序中托管 Web API。 ASP.NET Web API不需要 IIS。 可以在自己的主机进程中自托管 Web API。 创建控制台应用程序项目 启动 Visual Studio,然后从“开始”页中选择“新建项目”。 或者,从“ 文件 ”菜单中选择“ 新建 ”&a…

【小工具】pixi-live2d-display,直接可用的live2d的交互网页/桌面应用

效果&#xff1a; <script src"https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js"></script> <script src"https://cdn.jsdelivr.net/gh/dylanNew/live2d/webgl/Live2D/lib/live2d.min.js"></script> <…

CCSC,一种CPU架构

core-circuit-separate-computer 核与执行电路的分离&#xff0c;最初是为了省电。 用寄存器实现这种分离。 V寄存器控制着执行电路的供电&#xff0c;V0则不供电&#xff0c;进入省电模式&#xff1b;V1则供电&#xff0c;进入工作模式。 P寄存器是parameter-register&#xf…

qt打包完整详细过程 包你成功

找问题找了一个多小时&#xff0c;不停调试&#xff0c;还修改文件路径&#xff0c;配置路径&#xff0c;开机关机&#xff0c;最后终于做出来了&#xff0c;得出来了一个结论 我绝对是天才 首先 看这个视频 k14 打包发布_哔哩哔哩_bilibili 不出意外&#xff0c;你绝对会在…

2024.01.09.Apple_UI_BUG

我是软件行业的&#xff0c;虽然不是手机设计的&#xff0c;但是这个设计真的导致经常看信息不完整&#xff0c;要下拉的。 特别读取文本或者其他文件的时候&#xff0c;上面有个抬头就是看不到&#xff0c;烦&#xff0c;体验感很差

一氧化碳中毒悲剧频发:探究道合顺电化学传感器促进家庭取暖安全

1月6日&#xff0c;陕西省榆林市发生了一起疑似因使用煤炭炉取暖中毒事件。通报称&#xff0c;经公安部门现场调查&#xff0c;并结合医院救治情况&#xff0c;初步判断5人属一氧化碳中毒&#xff0c;其中4人抢救无效死亡&#xff0c;令人痛心。 一般来说&#xff0c;这种在日…

74应急响应-winlinux分析后门勒索病毒攻击

#操作系统(windows,linux)应急响应&#xff1a; 1.常见危害&#xff1a;暴力破解&#xff0c;漏洞利用&#xff0c;流量攻击&#xff0c;木马控制(Webshell,PC 木马等)&#xff0c;病毒感染(挖矿&#xff0c;蠕虫&#xff0c;勒索等)。 2.常见分析&#xff1a;计算机账户&…

ELK生命周期

ELKkafka <es生命周期可视化配置界面> 一、创建索引模式 根据logstash中的日志规则 匹配对应系统日志 二、创建索引生命周期策略&#xff1a;可以控制生成索引的生命周期 共4个阶段&#xff1a;热阶段——温阶段——冷阶段——删除阶段 阶段1. hot: 索引被频繁写入和查…

PostgreSQL ZIP版安装完全手册

前言 ZIP免安装版下载地址&#xff1a;社区 DL 页面 (enterprisedb.com) 选择所需的版本下载即可。 安装 将下载的zip安装包&#xff08;我这里下载的是postgresql-15.5-1-windows-x64-binaries.zip&#xff09;解压至D盘根路径下&#xff0c;解压后路径&#xff1a;D:/pgsq…

MongoDB 设置账号密码_mongodb设置用户名和密码

MongoDB 设置账号密码_mongodb设置用户名和密码 1、安装 安装可以看我这篇文章:https://blog.csdn.net/u014641168/article/details/123937775 2、说明 由于默认安装的MongoDB是没有设置用户密码的,极其危险,所以需要设置一下用户密码 3、创建用户 用Navicat15连接Mon…

三、nginx代理功能

目录 SQUID代理服务器配置安装squid编辑squid配置文件 定义拒绝访问启动squid服务 linux客户端配置linux客户端配置正向代理测试http代理服务器上查看日志/var/log/squid/access.log windows 也可以配置网页代理 SQUID代理服务器配置 安装squid yum install squid -y 编辑squ…

c#自动更新升级工具

c#更新工具,wpf开发,所有windows桌面程序均可使用,基于.net 4.0,最低支持windos xp系统 更新工具优点 使用简单批量更新跨版本更新数据备份手动还原数据体积小 程序更新使用效果 使用简单 只需添加两个类,以及三个路径的指定,就可以从任何地方下载更新包,并解压到主程序目录…

AI人工智能虚拟现实行业发展分析

AI人工智能和虚拟现实是当今科技领域最受关注和研究的两个领域。这两项技术的迅速发展给各行各业带来了巨大的变革和机遇。在过去的几年里&#xff0c;AI和虚拟现实已经取得了显著的进展&#xff0c;并且有着广阔的发展前景。 AI人工智能作为一种模拟人类智能的技术&#xff0…

openEuler22.0.3安装oracle11.2.0.4报错总结

openEuler是CentOS8系列魔改来的 1.xstart无法打开报错x11拒绝转义 yum install *x11* vi /etc/ssh/sshd_config X11Forwarding yes systemctl restart sshd 2.执行runinstaller报错,无论是直接无法打开界面报错: when installed in the jdk 1.2 Linux 还是打开界面报错: no o…