使用Process Explorer查看线程的函数调用堆栈去排查程序高CPU占用问题

目录

1、问题描述

2、使用Process Explorer排查软件高CPU占用的一般思路

3、使用Process Explorer工具进行分析

3.1、找到CPU占用高的线程

3.2、查看CPU占用高的线程的函数调用堆栈,找到出问题的代码

3.3、libwebsockets库导出接口lws_service的说明

3.4、解决办法

4、使用Process Explorer查看函数调用堆栈时可能需要pdb符号文件

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       前几天使用Process Explorer工具在同事的电脑上排查了一个软件高CPU占用问题,今天正好通过这个实例来完整地讲述如何使用Process Explorer去排查高CPU占用问题。

1、问题描述

       前几天同事在使用我们的软件时,系统出现明显的卡顿,查看Windows资源管理器得知,我们的软件进程占用了将近50%的CPU,所以导致系统出现了较明显的卡顿。查看软件的界面,显示正在重连服务器:

        同事使用的是笔记本电脑,据同事反馈,他之前连的是手机热点(通过手机的移动流量联网),后来他将手机上的热点关闭了,所以笔记本就连不上网络了。软件中连接了多个业务服务器,断网后,与服务器的连接就会陆续断开,软件中会自动发起对各服务器的重连,因为笔记本无法连接外网了,所以软件底层一直在不停地重连。这个高CPU占用可能是服务器自动重连触发的这个高CPU问题,在这个问题场景下是必现的,简单操作一下就能复现。

2、使用Process Explorer排查软件高CPU占用的一般思路

       我们一般在遇到软件高CPU占用时会优先使用Process Explorer工具来排查。使用该工具可以查看到进程中的所有线程,每个线程占用的CPU比例:

​然后双击CPU占用最高的那个线程,查看线程的函数调用堆栈,对照着源码,一般就能分析出高CPU占用问题了。

       导致高CPU占用一般是程序一直在不停歇的执行代码导致的,不停歇地执行代码的场景主要有以下几种:

1)程序中出现了死循环,程序一直在执行发生死循环的循环体中的代码。
2)线程函数中的循环体(通常是While循环)中没有添加Sleep,导致循环体代码一直在不停歇的执行。

所以排查程序高CPU占用问题时,主要考虑这两个排查方向。

3、使用Process Explorer工具进行分析

3.1、找到CPU占用高的线程

       在出问题的电脑上,打开Process Explorer,在进程列表中找到目标软件进程,双击该进程条目打开进程的属性页面,点击Threads标签页,就能看到线程列表页面。

       一般进程高CPU占用是由某个线程引发的,该线程会有明显的高CPU占用。可以在线程列表中点击表头中的CPU占用列,按照CPU占用比例排序,可以看到某个线程明显占用了较高的CPU,如下所示:

​双击高CPU占用的那个线程条目,查看该线程的函数调用堆栈,如下:

​       有时,我们需要多次点击左下角的refresh刷新按钮,查看多次函数调用堆栈,以查看到有效的函数调用堆栈。一般情况下,代码发生死循环或者线程函数中while循环出现不间断执行代码,使用Process Explorer多次查看到的函数调用堆栈都是类似的或者一样的。

3.2、查看CPU占用高的线程的函数调用堆栈,找到出问题的代码

       通过显示的函数调用堆栈,wsswrapperlib库在调用开源库libwebsockets中的接口。这个wsswrapperlib库是底层协议栈的模块,据了解当前的客户端软件与平台侧的某个服务器就是通过libwebsockets进行通信的,从当前堆栈上看,一直在connect远端的服务器。这个和界面层显示的正在重连服务器是一致的,从堆栈中看到wsswrapperlib模块中的WorkerThread字样,这说明当前的函数调用堆栈就和这个WorkerThread线程有关。

       之前我也遇到过调用libwebsockets的lws_service不当,导致程序高CPU占用的问题,当时问题也是出在自动重连服务器的场景下。对应的问题可以参见我之前写的文章:
使用Process Explorer和Clumsy工具定位软件高CPU占用问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/130038272        于是找到维护wsswrapperlib模块的协议栈同事,让他直接在代码中搜索lws_service接口,果然找到了一个线程对应的线程函数WorkerThread,在线程函数中的While循环中调用了lws_service接口:

static void* WorkerThread(void* lpParam)
{int n = 0:CWSSContext* context = (CWSSContext*)1pParam;while (n >=0
#ifdef __ANDROID__&& context->flag()
#endif){n = lws_service( context->getContext,50 )context->po11();}return NULL;
}

3.3、libwebsockets库导出接口lws_service的说明

libwebsockets开源库中关于lws_service接口的说明如下:

/*** lws_service() - Service any pending websocket activity* @context:    Websocket context* @timeout_ms:    Timeout for poll; 0 means return immediately if nothing needed*        service otherwise block and service immediately, returning*        after the timeout if nothing needed service.**    This function deals with any pending websocket traffic, for three*    kinds of event.  It handles these events on both server and client*    types of connection the same.**    1) Accept new connections to our context's server**    2) Call the receive callback for incoming frame data received by*        server or client connections.**    You need to call this service function periodically to all the above*    functions to happen; if your application is single-threaded you can*    just call it in your main event loop.**    Alternatively you can fork a new process that asynchronously handles*    calling this service in a loop.  In that case you are happy if this*    call blocks your thread until it needs to take care of something and*    would call it with a large nonzero timeout.  Your loop then takes no*    CPU while there is nothing happening.**    If you are calling it in a single-threaded app, you don't want it to*    wait around blocking other things in your loop from happening, so you*    would call it with a timeout_ms of 0, so it returns immediately if*    nothing is pending, or as soon as it services whatever was pending.*/LWS_VISIBLE int
lws_service(struct lws_context *context, int timeout_ms)
{return lws_plat_service(context, timeout_ms);
}

上述注释的翻译如下:

This function deals with any pending websocket traffic, for three kinds of event.  It handles these events on both server and client types of connection the same.
1) Accept new connections to our context's server
2) Call the receive callback for incoming frame data received by server or client connections.
此函数处理任何需要处理的(悬而未决的)websocket 流量,适用于三种事件。它以相同的方式处理服务器和客户端连接类型上的这些事件。
1) 接受到我们上下文服务器的新连接
2) 调用回调函数,将服务器或客户端连接接收到的数据,回调出去。

You need to call this service function periodically to all the above functions to happen; if your application is single-threaded you can just call it in your main event loop.
你需要周期性地调用这个服务函数来使上述所有函数发生; 如果您的应用程序是单线程的,您可以在主事件循环中调用它。

从上面的注释可以看出,要保证libwebsockets库中能正常的收发数据,必须要调用lws_service接口。

3.4、解决办法

       在服务器连不上时,调用lws_service函数传递的第二个timeout参数起不到Sleep的作用,我们不管lws_service函数内部会做什么,但lws_service函数会快速的返回,会导致While一直在持续的不停歇地执行,会占用大量的CPU时间片,导致线程占用大量的CPU比例。为了防止出现线程代码不停歇的运行,要在此处人为地添加一个Sleep,代码如下:

static void* WorkerThread(void* lpParam)
{int n = 0:CWSSContext* context = (CWSSContext*)1pParam;while (n >=0
#ifdef __ANDROID__&& context->flag()
#endif){n = lws_service( context->getContext,50 )context->po11();Sleep(50);  // 人为地去Sleep一下}return NULL;
}

一般在业务线程中都要人为地添加Sleep的调用,以防止线程不停歇运行导致高CPU占用问题

4、使用Process Explorer查看函数调用堆栈时可能需要pdb符号文件

       本例中涉及到的接口lws_service,是开源库libwebsockets.dll中的导出接口,导出接口对外部是公开可见的,所以函数调用堆栈中可以直接看到lws_service接口的调用。如果涉及到的接口是库内部的非公开的接口,要在函数调用堆栈中看到详细的函数名,则需要对应模块的pdb符号文件。可以将pdb符号文件事先放到二进制文件的同级目录(一般是程序的安装目录)中。Process Explorer在需要加载pdb文件时,会到exe主程序所在的目录中去搜索pdb文件,搜索到后去自动加载。

       当然,也可以将pdb符号文件统一放在某个目录中,然后将目录设置到Process Explorer中。具体操作方法是,点击Process Explorer菜单栏中的Options -> Configure Symbols...,打开如下的配置窗口:

​配置pdb文件的路径即可。

       还有其他工具可能会用到pdb符号文件,可以参见我之前写的文章:

哪些软件分析工具需要使用到pdb符号文件?icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131574418

5、最后

       本文通过一个具体的实例讲述了如何使用Process Explorer排查程序高CPU占用问题,有一定的参考或借鉴价值。本例中使用Process Explorer查看进程中某线程的函数调用堆栈,Process Explorer工具还有其他的用处,Process Explorer的用途可以总结为:

1)Process Explorer可以查看程序整个进程占用的总的虚拟内存,而Windows任务管理器中看不到,在排查内存泄漏时可以用上。
2)Process Explorer可以看目标程序加载了哪些库以及这些库的路径,还可以看动态加载的dll库有没有加载起来。
3)Process Explorer可以看启动程序时给程序进程传递了哪些命令行参数,比如chrome浏览器运行时会启动多个进程,每个进程负责处理不同的事务,我们可以通过给进程传递的命令行参数得知这个进程是做什么任务的,比如render渲染进程和GPU加速线程。
4)Process Explorer可以看启动程序各个线程的函数调用堆栈,排查死循环、高CPU占用和多线程死锁问题。

我博客中有多个使用Process Explorer排查问题的案例,可以参见专栏《C++软件分析工具案例集锦》中的相关文章:

C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795

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

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

相关文章

无涯教程-JavaScript - LOOKUP函数

描述 需要查看单个行或一列并从第二行或第二列的同一位置查找值时,请使用LOOKUP函数。使用"查找"功能搜索一行或一列。 使用VLOOKUP函数可搜索一行或一列,或搜索多行和多列(如表)。它是LOOKUP的改进版本。 有两种使用LOOKUP的方法- 矢量形式 − Use this form of…

ArrayList

目录 一、ArrayList是什么 二、ArrayList的使用 (1)导包 (2)ArrayList的构造方法 三、ArrayList的常用方法 (1)添加元素 (2)删除元素 (3)获取元素 &a…

一场深刻的开源聚会:KCC@北京 9.2 活动回顾

开源为我们带来了什么?这是这场聚会的宣传文的标题:https://mp.weixin.qq.com/s/5sR6TPEpQmYNBnCtVilkzg 同样这个问题也可以是极具个体化的:开源为我带来了什么?秋天的周末,预报有雨,北京的开源人还是相聚…

《golang设计模式》第二部分·结构型模式-05-门面模式Facade)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.2 类图 1. 概述 门面(Facade)向客户端提供使用子系统的统一接口,用于简化客户端使用子系统的操作。 1.1 角色 门面角色(Facade) 客户端可以调用的接…

【Spring面试】三、Bean的配置、线程安全、自动装配

文章目录 Q1、什么是Spring Bean?和对象有什么区别Q2、配置Bean有哪几种方式?Q3、Spring支持的Bean有哪几种作用域?Q4、单例Bean的优势是什么?Q5、Spring的Bean是线程安全的吗?Q6、Spring如何处理线程并发问题&#xf…

C语言——指针进阶(三)

目录 一.前言摘要 二.排序函数qsort的模拟实现 三.指针和数组笔试题解析 一.前言摘要 讲述关于strlen和sizeof对于各种数组与指针的计算规则与用法。另外还有qsort函数的模拟实现(可以排序任意类型变量) 二.排序函数qsort的模拟实现 目标:…

uniapp 模糊搜索(小白必看)

实现模糊搜索很简单,按照下面的步骤: 1. 搜索栏 <view class"search-box"><uni-search-bar class"uni-mt-10" radius"100" placeholder"请输入移交信息" clearButton"auto" bgColor"#F8F8F8"cancelBut…

开源库源码分析:Okhttp源码分析(一)

开源库源码分析&#xff1a;OkHttp源码分析 导言 接下来就要开始分析一些常用开源库的源码了&#xff0c;作为最常用的网络请求库&#xff0c;OkHttp以其强大的功能深受Android开发者的喜爱&#xff08;比如说我&#xff09;&#xff0c;还有对该库进行二次封装而成的热门库&a…

怎样下载和安装HBuilder软件?【附HBuilder快捷键】

HBuilder是一款深度集成Eelipse的IDE编辑器&#xff0c;但其主要集中在Web前端的开发&#xff0c;不能进行Java等后台开发。HBuilder提供了对JavaScrijpt、jQuery、HTML5、MUI等语法的提示功能&#xff0c;同时包含很多快捷键&#xff0c;让前端开发更加便捷。 访问HBuilder官…

首家!亚信科技AntDB数据库完成中国信通院数据库迁移工具专项测试

近日&#xff0c;在中国信通院“可信数据库”数据库迁移工具专项测试中&#xff0c;湖南亚信安慧科技有限公司&#xff08;简称&#xff1a;亚信安慧科技&#xff09;数据库数据同步平台V2.1产品依据《数据库迁移工具能力要求》、结合亚信科技AntDB分布式关系型数据库产品&…

vue 使用canvas 详细教程

Vue.js 中使用 Canvas Vue.js 是一个流行的 JavaScript 框架&#xff0c;用于构建用户界面。它提供了一种简洁的方式来管理和渲染数据&#xff0c;同时也支持与其他库和工具的集成。要在 Vue.js 中使用 Canvas&#xff0c;您可以按照以下步骤进行操作&#xff1a; 在 Vue.js …

vue3+scss开启写轮眼

vue3scss开启写轮眼 一、相关技术二、使用步骤1.安装依赖2.眼球3 勾玉4 旋转动画5 综合 一、相关技术 采用vue3vitescss的技术内容进行开发 二、使用步骤 1.安装依赖 代码如下&#xff1a; npm install sass2.眼球 首先我们根据需要 将眼睛的基础形状描绘出来&#xff0c…

SkyWalking入门之Agent原理初步分析

一、简介 当前稍微上点体量的互联网公司已经逐渐采用微服务的开发模式&#xff0c;将之前早期的单体架构系统拆分为很多的子系统&#xff0c;子系统封装为微服务&#xff0c;彼此间通过HTTP协议RESET API的方式进行相互调用或者gRPC协议进行数据协作。 早期微服务只有几个的情况…

Springboot 实践(15)spring config 配置与运用—自动刷新

目前&#xff0c;网络上讲解spring config的自动刷新&#xff0c;都是通过git服务站的webhook功能执行“actuator/bus-refresh”服务实现的自动刷新。我们的前文讲解的配置中心&#xff0c;配置中心仓库使用的时本地地址&#xff0c;如下图所示&#xff1a; 那么&#xff0c;配…

vim的使用介绍以及命令大全

懒羊羊感谢大家的关注和三连支持~ 目录 前言 一、vim的使用介绍 二、命令大全 1.命令模式 &#xff08;1&#xff09;复制&#xff08;配合粘贴命令p使用&#xff09; &#xff08;2&#xff09;剪切 &#xff08;3&#xff09;粘贴 &#xff08;4&#xff09;删除 …

GIS前端编程-Leaflet插件扩展

GIS前端编程-Leaflet插件扩展 Leaflet插件扩展基本原理Leaflet插件扩展开发方法1. L.Handler扩展2. L.Control扩展为了高效率地进行软件开发 Leaflet插件扩展基本原理 Leaflet是面向移动设备和Web的开源JavaScript库&#xff0c;具有设计简单、性能良好和可用性强的特点&#…

c#.NET技术做到ChatGPT流式响应并实现打字机效果 实现ChatGPT的Stream传输

.NET技术做到ChatGPT流式响应并实现打字机效果 ChatGPT是当前备受瞩目的人工智能产品之一&#xff0c;它具备与人类进行智能对话的能力&#xff0c;同时能够理解人类的想法和需求。在内容创作、营销、智能客服、教育、投资等领域和场景中&#xff0c;ChatGPT都展现出了巨大的…

【openKylin】OpenKylin1.0 x86_64 VMWare安装手册

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的帮助&#x1f338;文…

选择器进阶与表单表格

华子目录 选择器并集选择器后代选择器子代选择器伪类选择器伪元素选择器结构选择器属性选择器相邻选择器 表单&#xff08;form&#xff09;label标签 表格&#xff08;table标签&#xff09;合并单元格 选择器 下面是我们之前学习过的选择器 *{}&#xff1a;通配符选择器&am…

分销小程序商城功能_小程序商城适合谁_OctShop

微信推出小程序后&#xff0c;分销小程序商城就受到了非常多企业和商家的关注&#xff0c;通过分销商城小程序企业或商家就可以获得庞大的用户裂变过来的用户&#xff0c;组成一个不断裂变拉新用户的网络&#xff0c;可以大大提高企业或品牌的曝光度&#xff0c;从而提高企业或…