你的軟件在某個時刻停止服務,CPU占用達到100%+,這種問題一個可能的原因是產生了死循環,假設程序某處存在潛在的死循環,並在某種條件下會引發,本文以一個示例來定位出現死循環的位置。
當程序某處存在死循環,通常定位問題及縮小范圍的方法是,在可疑的代碼處加log,或者注釋掉可疑代碼,這對於容易重現問題的程序來說還好,但對於“偶爾”才會產生問題程序卻很難調試,因為我們很難重現程序故障。本文所述的調試過程正是在這種情況下,假設問題已經出現,我們要求環境保護現場,即出問題的程序還在運行中。
1.我們首先要知道是哪個線程出了問題:
首先查一下出問題進程的pid,例如
ovtsvn
11065
1
50
11
:
57
?
00
:
00
:
07
.
/
icdn

ovtsvn
11076
10971
0
11
:
57
pts
/
2
00
:
00
:
00
grep
ovtsvn@ovtsvn:
~/
MASS4
/
src
/
icdn
/
src$
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中,列出線程狀態:


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一下:


#
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行的代碼:


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()
調試完指定進程后,可以運行detach命令來讓GDB釋放該進程,該進程得以繼續運行。當回車時,detach不會重復。當執行完detach后,進程和GDB不再相關,GDB可以attach其他進程。