上篇博客中给大家分享了使用Windbg进行Live Debugging:

Windbg程序调试系列4-Live Debugging

本篇中我们继续,跟大家分享常见的应用程序高CPU使用率问题分析。

先说Windows下CPU使用率这个概念:

CPU使用率:在任务管理器的刷新周期内CPU忙的时间与整个刷新周期的比值。默认的刷新周期是1s。

即1s内,反映出系统的CPU繁忙程度

我们打开Windows的任务管理器,可以看到CPU的使用率:

当然,这个CPU使用率是整个所有核心CPU的使用率。比如我本机是8核心的CPU。整体的CPU使用率 在某一瞬间是14%。

这个CPU使用率是如何计算出来的,有两个重要的时间sysTime和idleTime:

sysTime:表示该时间段内总的CPU时间=CPU处于用户态和内核态CPU时间的总和,即sysTime =kerneTimel + userTime

(注:这里并不包括idleTime,因为当CPU处于空闲状态时,是在内核模式下运行System Idle Process这个进程,所以kernelTime实际上已经包含了idleTime);

idleTime:表示在该时间段内CPU处于空闲状态的时间;

CPU% = 1 – idleTime / sysTime * 100

说到这里,我们分析一个应用的高cpu问题,更多的是:分析用户态的CPU耗时。即,我们应用程序本身运行时消耗的CPU时间片总和。

然后,进入今天的正题,使用Windbg分析高CPU问题:

一、首先我们用C#写一个Any CPU架构的Console模拟程序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace HighCpuDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var normalThread = new Thread(NormalMethod);
            normalThread.Start();             var longRunningThread = new Thread(LongRunningMethod);
            longRunningThread.Start();             Console.ReadKey();
        }         private static void NormalMethod()
        {
            int a = 0;
            int b = 100000;
            var list = new List<int>();
            for (int i = 0; i < b; i++)
            {
                a += i;
                list.Add(a);
                var max = list.Max();
                var min = list.Min();
                var avg = list.Average();
                Console.WriteLine(string.Format("Thread:{0}, writeline:{1}", Thread.CurrentThread.ManagedThreadId, a));                 //休息一下
                Thread.Sleep(100);
            }
        }         private static void LongRunningMethod()
        {
            for (int c = 0; c < 100000; c++)
            {
                int a = 0;
                int b = 100000;
                var list = new List<int>();
                for (int i = 0; i < b; i++)
                {
                    a += i;
                    list.Add(a);
                    var max = list.Max();
                    var min = list.Min();
                    var avg = list.Average();
                    Console.WriteLine(string.Format("Thread:{0}, writeline:{1}", Thread.CurrentThread.ManagedThreadId, a));
                }
            }
        }
    }
}

代码中有两个线程,一个是“正常”的计算输出线程NormalThread(每次输出,会休息一下 100s),一个是长时间运行的线程LongRunningThread,一直在计算,输出。

代码写好之后,设置为Any CPU架构,支持64位模式:

看程序输出:

很明显,3号线程是NormalThread, 4号线程是LongRunningThread。

二、 查看应用进程的CPU使用率

从任务管理器上,能够发现,HighCpuDemo这个进程的CPU使用率是12%

三、间隔30s,抓两个Dump

这里有个问题:为什么要间隔一段时间抓2个dump?我们先把问题放在这。

四、使用Windbg分析两个Dump文件,使用!runaway找到最消耗CPU时间片的线程,然后优化

Windbg打开这两个Dump后,分别执行以下命令:

:> .loadby sos clr
:> !runaway

对比看着两个Dump的输出:

第一个Dump:

Debug session time: Sun Nov  20:16:39.000 2018 (GMT+8)
System Uptime: days ::00.195
Process Uptime: days ::31.000
.............................
Loading unloaded module list
.
ntdll!ZwDeviceIoControlFile+0x14:
00007ffc`c01b03a4 c3 ret
:> .loadby sos clr
:> !runaway
User Mode Time
Thread Time
: 0 days 0:07:38.531
:325c days ::00.390
: days ::00.015
:4c3c days ::00.000
:17d0 days ::00.000
: days ::00.000
: days ::00.000

第二个Dump:

Debug session time: Sun Nov  20:17:06.000 2018 (GMT+8)
System Uptime: days ::27.136
Process Uptime: days ::58.000
.............................
Loading unloaded module list
.
ntdll!ZwDeviceIoControlFile+0x14:
00007ffc`c01b03a4 c3 ret
:> .loadby sos clr
:> !runaway
User Mode Time
Thread Time
: days 0:08:01.984
:325c days ::00.406
: days ::00.015
:4c3c days ::00.000
:17d0 days ::00.000
: days ::00.000
: days ::00.000

这里有个关键的Windbg指令  !runaway, 它有什么作用,输出的是什么:

This extension is a quick way to find out which threads are spinning out of control or consuming too much CPU time.

The display identifies each thread by the debugger's internal thread numbering and by the thread ID in hexadecimal. The debugger IDs are also shown.

Here is an example: 

:> !runaway 

 User Mode Time
Thread Time
:55c ::00.0093
:1a4 ::00.0000 Kernel Mode Time
Thread Time
:55c ::00.0140
:1a4 ::00.0000 Elapsed Time
Thread Time
:55c ::43.0533
:1a4 ::25.0876

使用这个指令,可以查看每个线程的"用户态"CPU使用时间:

从上面两个Dump中,我们能看出,4号线程 User Mode Time 一直在增加,我们看看4号线程的堆栈:

:> ~4s
*** WARNING: Unable to verify checksum for System.Core.ni.dll
System_Core_ni+0x588b44:
00007ffc`a4268b44 488b4de8 mov rcx,qword ptr [rbp-18h] ss:000000c1`df0ff2a8=000001d4633eb280
:> !clrstack
OS Thread Id: 0x3758 ()
Child SP IP Call Site
000000c1df0ff280 00007ffca4268b44 System.Linq.Enumerable.Min(System.Collections.Generic.IEnumerable`<Int32>)
000000c1df0ff2d0 00007ffc4b930660 *** WARNING: Unable to verify checksum for HighCpuDemo.exe
HighCpuDemo.Program.LongRunningMethod() [c:\users\zhougq\documents\visual studio \Projects\HighCpuDemo\Program.cs @ ]
000000c1df0ff3a0 00007ffca7e24770 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ ]
000000c1df0ff470 00007ffca7e24604 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ ]
000000c1df0ff4a0 00007ffca7e245d2 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ ]
000000c1df0ff4f0 00007ffca7eacff2 System.Threading.ThreadHelper.ThreadStart() [f:\dd\ndp\clr\src\BCL\system\threading\thread.cs @ ]
000000c1df0ff748 00007ffcaaf35863 [GCFrame: 000000c1df0ff748]
000000c1df0ffa98 00007ffcaaf35863 [DebuggerU2MCatchHandlerFrame: 000000c1df0ffa98]

正如我们代码中所写的,LongRunningMethod方法一直在执行、消耗CPU资源。

定位到代码问题,就可以进一步修改代码,解决问题了。

以上就是使用Windbg 调试高CPU问题的方法思路,总结一下:

1. 查看应用进程的CPU使用率
2. 间隔一段时间,抓两个Dump
3. 使用Windbg分析两个Dump文件,使用!runaway找到最消耗CPU时间片的线程,然后优化

分享给大家。

周国庆

2018/11/25

Windbg程序调试系列5-高CPU问题分析的更多相关文章

  1. Windbg程序调试系列3-线程阻塞问题

    上一篇博文给大家分享了使用Windbg分析内存泄露问题: Windbg程序调试系列2-内存泄露问题 本篇我们继续跟大家分享,如何分析解决线程阻塞问题. 从根本上讲,线程阻塞属于程序Hang的一种,其表 ...

  2. Windbg程序调试系列4-Live Debugging

    上篇博文中给大家分享了使用Windbg分析线程阻塞问题: Windbg程序调试系列3-线程阻塞问题 本篇中我们继续,跟大家分享附加进程实时调试-Live Debugging. 先说一下使用Windbg ...

  3. Windbg程序调试系列-索引篇

    最近整理了一下Windbg程序调试系列的文章,做个了索引贴,方便大家查询.搜索: Windbg程序调试系列1-常用命令说明&示例 Windbg程序调试系列1-Mex扩展使用总结 Windbg程 ...

  4. Windbg程序调试系列1-常用命令说明&示例

    Windbg程序调试是.Net高级开发需要掌握的必备技能,分析内存泄露.分析高CPU.分析线程阻塞.分析内存对象.分析线程堆栈.Live Dedugging.这个领域可以说一个技能+场景化应用的结合, ...

  5. Windbg程序调试系列2-内存泄露问题

    上篇文章给大家解释了Windbg的基本命令和说明,这一篇给大家介绍内存泄露场景的问题分析. 文章大纲: 描述问题背景和现象 确定问题是否是内存泄露 梳理问题分析思路 动手分析解决 总结 1. 先说问题 ...

  6. Windbg程序调试系列1-Mex扩展使用总结

    最近一直在频繁使用Windbg做线上Dump调试,与微软做Case交流的时候,发现微软CSS团队,用了一个非常效率的Windbg 插件,Mex: 使用介绍: https://blogs.msdn.mi ...

  7. Windows程序调试系列: 使用VC++生成调试信息 转

    Windows程序调试系列: 使用VC++生成调试信息 ZhangTao,zhangtao.it@gmail.com, 译自 “Generating debug information with Vi ...

  8. Windbg程序调试--转载

    WinDbg是微软发布的一款相当优秀的源码级(source-level)调试工具,可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件. WinDbg是微软很重要的诊断调试工具: 可以查 ...

  9. Expert 诊断优化系列------------------你的CPU高么?

    现在很多用户被数据库的慢的问题所困扰,又苦于花钱请一个专业的DBA成本太高.软件维护人员对数据库的了解又不是那么深入,所以导致问题迟迟不能解决,或只能暂时解决不能得到根治.开发人员解决数据问题基本又是 ...

随机推荐

  1. Python-Django学习

    1,安装Django与python版本的对应1.8 2.7,3.2--3.51.9,1.10 2.7,3.4,3.51.11 2.7,3.4,3.5,3.62.0 2.1 第一种安装:pip inst ...

  2. Hadoop日志文件

    初学者运行MapReduce作业时,经常会遇到各种错误,往往不知所云,一般直接将终端打印的错误贴到搜索引擎上查找,以借鉴前人的经验. 对于hadoop而言,当遇到错误时,第一时间应是查看日志,日志里通 ...

  3. Qt写websocketpp服务端

    1.下载websocketpp,地址为https://github.com/zaphoyd/websocketpp,版本为0.7. 2.下载boost,地址为https://www.boost.org ...

  4. 使用javascript和css模拟帧动画的几种方法浅析

    我们平时在开发前端页面的时候,经常会播放一段帧序列.这段帧序列就像gif图片那样,反复循环播放.那大家可能会说,直接用gif图片就好了,干嘛还去模拟呢?那是因为要做得更加灵活,我们要做到以下几点: 1 ...

  5. (92)Wangdao.com_第二十五天_线程机制_H5 Web Workers 分线程任务_事件 Event

    浏览器内核 支撑浏览器运行的最核心的程序 IE 浏览器内核            Trident内核,也是俗称的IE内核Chrome 浏览器内核            统称为 Chromium 内核或 ...

  6. 怎样将PDF文件转换成Excel表格

    PDF文件怎样转换成Excel表格呢?因为很多的数据信息现在都是通过PDF文件进行传输的,所以很多时候,信息的接受者都需要将这些PDF文件所传输的数据信息转换成Excel表格来进行整理,但是我们应该怎 ...

  7. 小甲鱼零基础python课后题 P24 023递归:这帮小兔崽子

    0.使用递归写一个十进制转换为二进制的函数(要求“取2取余”的方式,结果与调用bin()一样返回字符串式). 答: def Dec2Bin(dec): temp = [] result = '' wh ...

  8. Python 学习笔记9 循环语句 For in

    For in 循环主要适用于遍历一个对象中的所有元素.我们可以使用它遍历列表,元组和字典等等. 其主要的流程如下:(图片来源于: https://www.yiibai.com/python/pytho ...

  9. 关于HTTP协议学习(二)

    一,目录结构 HTTP Cookie & Session HTTP Cache (缓存) 二,HTTP Cookie & Session 1. 我们看到的 cookie 我们通过浏览器 ...

  10. Autofac之类型注册

    本次主要学习一下Autofac中实现类型注册的几种方式,这里并不打算一开始就从基于接口开发的服务关联切入,而是先从一个简单的类型注册来学起,虽然实际开发中可能不会这么做,但是个人感觉从这里学起理解能能 ...