1. 程式人生 > >多執行緒環境死迴圈定位

多執行緒環境死迴圈定位

你的軟體在某個時刻停止服務,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 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 180 325m 39802236 S 00.40:00.00 icdn 11070 ovtsvn 180 325m 39802236 S 00.40:00.00 icdn 11071 ovtsvn 220 325m 39802236 S 00.40:00.00 icdn 11072 ovtsvn 150 325m 39802236 R 00.40: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, [email protected]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, [email protected])     at ../src/csendfile.cpp:101 101             while(1) 
(gdb) 

bt一下:

(gdb) bt 
#0  CSendFile::SendFile (this=0x2acc5d4aff40, [email protected]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()

除錯完指定程序後,可以執行detach命令來讓GDB釋放該程序,該程序得以繼續執行。當回車時,detach不會重複。當執行完detach後,程序和GDB不再相關,GDB可以attach其他程序。