CPU占用率100%的问题追踪

在网页上增加了一个banding选择的选项,同时也包括了restore的功能,结果在测试时发现网页访问(尤其是resotre后)时不时会出现CPU 100%被占用,这下郁闷了,赶紧想折子确认问题,憋了半天总算想到一些方法,本文记录了整个找问题的过程,也可谓一波三折。
先总结一下用过的法子:
1. printf打印
2. oprofile
3. gdb
4. proc文件系统

1. printf打印
直接在源码中加入打印语句是最直接了当的做法。但是在本次调试中却不可行,因为1该程序的代码不是本人所写,2代码量不小,将近2W行的代码要想完全看明白再去定位问题,估计每个十天半个月很难搞定。3这个一个多线程的程序,而且涉及了大量与其他进程通信的语句,所以并不存在所谓的过程,也无法使用所谓二分法来缩小问题范围

2.oprofile
oprofile本身是非常好的性能测试工具,用它可以获取一段时间以内所有调用的程序所占用的系统tick数,因为很容易找出到底是哪个函数大量占用CPU,也就抓到了耗CPU大户。这个方法本身十分完美,但是当用这个工具生成的report出来后,我还是傻眼了。oprofile统计的最底层的函数的CPU占用数,因此从上面看不到更上层的函数名字。举个例子,存在A调用B,B调用C的程序,那么A,B都不会出现在oprofile的统计中,只有C会出现,但是调用C的程序可能远不止B,也许还有EFGHO都是调用了该函数,同时悲吹的还有,仅有A才是真正出现在我们程序中的函数,B,C都是Lib库函数,所以要想由此回溯到我们程序中的函数,可谓难上加难,光这个推理过程就有得好搞了。

3.gdb
gdb一度是我理想中最好的解决此问题的工具了,想象一下,只要在gdb中运行此函数,然后停下来,看一下目前正执行到的函数,最多再back trace一下,就能彻底搞清到底是哪个函数经过什么过程从而占用CPU的。根据此思路,于是编译了gdb并导入到我们的文件系统中,本想:
1)关闭我们要定位的进程, 如果此进程正在跑的话
2) 打开gdb,并file我们的进程
3)执行run
4) 用Ctrl+C停止进程
5)查看当前所在代码行,并运行back trace

然而,我在PC机上写了个小的程序并按此顺序做了个演习,一切OK。然而,又是然而,当我真正用到实际系统上时各种问题都涌现出来:
1) 我们的这个程序有大量需与外部进程通信的子程序,一旦强制关闭此进程,很多其他与本程序通信的进程就开始猛烈刷屏(很多错误提示的log),搞得我根本无法再往串口上写东西。最后,想到一个办法,用ssh登录,这样即便串口主控制台那边刷屏不断,至少我可以在ssh终端安心输入我所希望的命令
2) 当我run这个程序时,此程序并不能终止串口控制台那边的无尽抱怨,似乎重新打开的这个程序进程并不能保持与其他进程的通信。猜测是当前进程与其他进程所建立的通信机制与原有的那些通信子没啥关系,例如也许是用了新的套接字,而对原进程的套接字不闻不问,从而导致还是不断刷屏
3) Ctrl+C停不了当前正在gdb中运行的程序,究其原因,是因为该程序是一个多进程的程序,所以多进程,是指该程序在执行中动态创建了其他进程,例如fork函数,当其他的这些进程没有被干掉时,这个主程序无法被Ctrl+C干掉。(干掉了,不就成孤儿进程了吧)
综上,失败。

4.proc
proc文件系统向我们展现了内核丰富多彩的一面,里面的很多信息能帮助我们确认或者诊断问题。例如在proc中会列出一个进程包含了几个线程,而每个线程又有占用了什么资源等等。本次调试,我是抱着问题去的,因此也没有全面的欣赏整个proc的内容,而只关心对我们最重要的CPU占用信息。
在proc目录下,每个进程号都是一个文件夹,在该文件夹中有一个stat文件,其中包含了此进程分别在用户态和核心态所运行的tick数。而同样在此文件夹下,还有task文件夹,里面放置了每一个线程的信息,包括线程的stat信息,利用这些信息,我写了个脚本,抓出此进程中所有线程各自占用的CPU时间,就可以很容易的对比出来到底谁是罪魁祸首。

#!/bin/bash

Name=psTree
PID=$1

if [ -z $PID ]; then
    echo "You must input the PID"
    exit
fi

echo "Running " $Name

if [ ! -x /proc/$PID ]; then
    echo "PID:" $PID " Not existed "
    exit
fi

echo "Thread_Name     usr  kern"
for n in /proc/$PID/task/* ;
do
    cat $n/stat | awk '{print  $1, $2, $14, $15}'
done

通过此程序,最后能很清晰的看到例如A进程里面的c线程大量占用CPU,此时,范围已经被大大减小了,然后可以阅读源码,并放置printk语句进行调试。由于单个线程代码不多,而且程序流清晰,所以还是能够比较快的定位到出错信息。最后的出错信息竟然是在system()函数里面,CPU都是耗在此函数中了。

至此,情况已经大致明了了,进一步的挖掘,这下就可以用oprofile了,通过它,我们又定位到了system函数下的folk函数中有一个内存分配的函数出错了,这个错误好像还是glibc出错。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值