当你的软件在某个时刻停止服务,CPU占用达到100%+,这种问题一个可能的原因是产生了死循环,假设程序某处存在潜在的死循环,并在某种条件下会引发,本文以一个示例来定位出现死循环的位置。
当程序某处存在死循环,通常定位问题及缩小范围的方法是,在可疑的代码处加log,或者注释掉可疑代码,这对于容易重现问题的程序来说还好,但对于“偶尔”才会产生问题程序却很难调试,因为我们很难重现程序故障。本文所述的调试过程正是在这种情况下,假设问题已经出现,我们要求环境保护现场,即出问题的程序还在运行中。
1.我们首先要知道是哪个线程出了问题:
首先查一下出问题进程的pid,例如
ovtsvn@ovtsvn:~/MASS4/src/icdn/src$
ps -ef
| grep icdn
ovtsvn
11065
1 50
11:57
? 00:00:07
./icdn
ovtsvn
11076
10971 0
11:57 pts/2
00:00:00 grep icdn
ovtsvn@ovtsvn:~/MASS4/src/icdn/src$
然后top命令查看线程信息:
top -H -p 11065

PID USER PR NI VIRT RES SHR S
%CPU
%MEM TIME+ COMMAND
11073 ovtsvn
25 0 325m
3980
2236 R 100
0.4
1:40.84 icdn
11065 ovtsvn
18 0 325m
3980
2236 S 0
0.4
0:00.01 icdn
11066 ovtsvn
18 0 325m
3980
2236 S 0
0.4
0:00.00 icdn
11067 ovtsvn
15 0 325m
3980
2236 S 0
0.4
0:00.00 icdn
11068 ovtsvn
15 0 325m
3980
2236 S 0
0.4
0:00.00 icdn
11069 ovtsvn
18 0 325m
3980
2236 S 0
0.4
0:00.00 icdn
11070 ovtsvn
18 0 325m
3980
2236 S 0
0.4
0:00.00 icdn
11071 ovtsvn
22 0 325m
3980
2236 S 0
0.4
0:00.00 icdn
11072 ovtsvn
15 0 325m
3980
2236 R 0
0.4
0:00.00 icdn
从上面可以看出,出问题线程PID为11073
2.接下来,我们用gdb来attach目标进程
执行: gdb icdn 11065
在gdb中,列出线程状态:
(gdb) info threads
9 Thread
47056948181264 (LWP
11066)
0x00002acc4a3dec91
in nanosleep () from
/lib/libc.so.6
8 Thread
47056956573968 (LWP
11067)
0x00002acc4a406fc2
in select () from
/lib/libc.so.6
7 Thread
47056964966672 (LWP
11068)
0x00002acc4a3dec91
in nanosleep () from
/lib/libc.so.6
6 Thread
47056973359376 (LWP
11069)
0x00002acc4a3dec91
in nanosleep () from
/lib/libc.so.6
5 Thread
47056981752080 (LWP
11070)
0x00002acc4a3dec91
in nanosleep () from
/lib/libc.so.6
4 Thread
47056990144784 (LWP
11071)
0x00002acc4a40e63c
in recvfrom () from
/lib/libc.so.6
3 Thread
47057194060048 (LWP
11072)
0x00002acc4a406fc2
in select () from
/lib/libc.so.6
2 Thread
47057226893584 (LWP
11073) CSendFile::SendFile (this=0x2acc5d4aff40,
pathname=@0x2acc5d4afee0)
at ../src/csendfile.cpp:101
1 Thread
47056939784832 (LWP
11065)
0x00002acc4a3dec91
in nanosleep () from
/lib/libc.so.6
(gdb)
gdb已经列出了各线程正在执行的函数,我们需要更多信息,记住11073对应的行首标号,这是gdb为线程分配的id,这里为2,然后执行切换:
(gdb) thread
2
[Switching to thread
2 (Thread
47057226893584 (LWP
11073))]#0 CSendFile::SendFile (this=0x2acc5d4aff40,
pathname=@0x2acc5d4afee0)
at ../src/csendfile.cpp:101
101
while(1)
(gdb)
bt一下:
(gdb) bt
#0 CSendFile::SendFile (this=0x2acc5d4aff40,
pathname=@0x2acc5d4afee0) at ../src/csendfile.cpp:101
#1
0x000000000040592e
in CIcdn::TaskThread (pParam=0x7fff617eafe0) at ../src/cicdn.cpp:128
#2
0x00002acc4a90b73a
in start_thread () from
/lib/libpthread.so.0
#3
0x00002acc4a40d6dd
in clone () from
/lib/libc.so.6
#4
0x0000000000000000
in ?? ()
来看一下101行的代码:
(gdb) l
96 }
97
98
int CSendFile::SendFile(const
string& pathname)

99
{
100
int n;
101
while(1)

102
{
103 n++;
104 }
105
//read file and send
现在我们定位到了出问题的代码位置,这里的循环只用来演示的。
最后别忘了detach
当程序某处存在死循环,通常定位问题及缩小范围的方法是,在可疑的代码处加log,或者注释掉可疑代码,这对于容易重现问题的程序来说还好,但对于“偶尔”才会产生问题程序却很难调试,因为我们很难重现程序故障。本文所述的调试过程正是在这种情况下,假设问题已经出现,我们要求环境保护现场,即出问题的程序还在运行中。
1.我们首先要知道是哪个线程出了问题:
首先查一下出问题进程的pid,例如




top -H -p 11065











2.接下来,我们用gdb来attach目标进程
执行: gdb icdn 11065
在gdb中,列出线程状态:












gdb已经列出了各线程正在执行的函数,我们需要更多信息,记住11073对应的行首标号,这是gdb为线程分配的id,这里为2,然后执行切换:





bt一下:






来看一下101行的代码:















现在我们定位到了出问题的代码位置,这里的循环只用来演示的。
最后别忘了detach