linux下的多線程調試(gdb)

來源:互聯網
上載者:User
 

GDB 是 linux 系統上常用的 c/c++ 調試工具,功能十分強大。對於較為複雜的系統,比如多進程系統,如何使用 GDB 調試呢?考慮下面這個三進程系統:

Proc2 是 Proc1 的子進程,Proc3 又是 Proc2 的子進程。如何使用 GDB 調試 proc2 或者 proc3 呢?

實際上,GDB 沒有對多進程程式調試提供直接支援。例如,使用GDB調試某個進程,如果該進程fork了子進程,GDB會繼續調試該進程,子進程會不受幹擾地運行下去。如果你事先在子進程代碼裡設定了斷點,子進程會收到SIGTRAP訊號並終止。那麼該如何調試子進程呢?其實我們可以利用GDB的特點或者其他一些輔助手段來達到目的。此外,GDB 也在較新核心上加入一些多進程調試支援。

接下來我們詳細介紹幾種方法,分別是 follow-fork-mode 方法,attach 子進程方法和 GDB wrapper 方法。

follow-fork-mode

在2.5.60版Linux核心及以後,GDB對使用fork/vfork建立子進程的程式提供了follow-fork-mode選項來支援多進程調試。

follow-fork-mode的用法為:

set follow-fork-mode [parent|child]

  • parent: fork之後繼續調試父進程,子進程不受影響。
  • child: fork之後調試子進程,父進程不受影響。

因此如果需要調試子進程,在啟動gdb後:

(gdb) set follow-fork-mode child

 

並在子進程代碼設定斷點。

此外還有detach-on-fork參數,指示GDB在fork之後是否斷開(detach)某個進程的調試,或者都交由GDB控制:

set detach-on-fork [on|off]

  • on: 斷開調試follow-fork-mode指定的進程。
  • off: gdb將控制父進程和子進程。follow-fork-mode指定的進程將被調試,另一個進程置於暫停(suspended)狀態。

注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。

follow-fork-mode/detach-on-fork的使用還是比較簡單的,但由於其系統核心/gdb版本限制,我們只能在符合要求的系統上才能使用。而且,由於follow-fork-mode的調試必然是從父進程開始的,對於fork多次,以至於出現孫進程或曾孫進程的系統,例如3進程系統,調試起來並不方便。

Attach子進程

眾所周知,GDB有附著(attach)到正在啟動並執行進程的功能,即attach <pid>命令。因此我們可以利用該命令attach到子進程然後進行調試。

例如我們要調試某個進程RIM_Oracle_Agent.9i,首先得到該進程的pid

[root@tivf09 tianq]# ps -ef|grep RIM_Oracle_Agent.9i

nobody    6722  6721  0 05:57 ?        00:00:00 RIM_Oracle_Agent.9i

root      7541 27816  0 06:10 pts/3    00:00:00 grep -i rim_oracle_agent.9i

 

通過pstree可以看到,這是一個三進程系統,oserv是RIM_Oracle_prog的父進程,RIM_Oracle_prog又是RIM_Oracle_Agent.9i的父進程。

[root@tivf09 root]# pstree -H 6722

通過 pstree 察看進程

啟動GDB,attach到該進程

用 GDB 串連進程

現在就可以調試了。一個新的問題是,子進程一直在運行,attach上去後都不知道運行到哪裡了。有沒有辦法解決呢?

一個辦法是,在要調試的子進程初始代碼中,比如main函數開始處,加入一段特殊代碼,使子進程在某個條件成立時便迴圈睡眠等待,attach到進程後在該程式碼片段後設上斷點,再把成立的條件取消,使代碼可以繼續執行下去。

至於這段代碼所採用的條件,看你的偏好了。比如我們可以檢查一個指定的環境變數的值,或者檢查一個特定的檔案存不存在。以檔案為例,其形式可以如下:

void debug_wait(char *tag_file)

{

    while(1)

    {

        if (tag_file存在)

            睡眠一段時間;

        else

            break;

    }

}

 

當attach到進程後,在該段代碼之後設上斷點,再把該檔案刪除就OK了。當然你也可以採用其他的條件或形式,只要這個條件可以設定/檢測即可。

Attach進程方法還是很方便的,它能夠應付各種各樣複雜的進程系統,比如孫子/曾孫進程,比如守護進程(daemon process),唯一需要的就是加入一小段代碼。

GDB wrapper

很多時候,父進程 fork 出子進程,子進程會緊接著調用 exec族函數來執行新的代碼。對於這種情況,我們也可以使用gdb wrapper 方法。它的優點是不用添加額外代碼。

其基本原理是以gdb調用待執行代碼作為一個新的整體來被exec函數執行,使得待執行代碼始終處於gdb的控制中,這樣我們自然能夠調試該子進程代碼。

還是上面那個例子,RIM_Oracle_prog fork出子進程後將緊接著執行RIM_Oracle_Agent.9i的二進位代碼檔案。我們將該檔案重新命名為RIM_Oracle_Agent.9i.binary,並建立一個名為RIM_Oracle_Agent.9i的shell指令檔,其內容如下:

[root@tivf09 bin]# mv RIM_Oracle_Agent.9i RIM_Oracle_Agent.9i.binary

[root@tivf09 bin]# cat RIM_Oracle_Agent.9i

#!/bin/sh

gdb RIM_Oracle_Agent.binary

 

當fork的子進程執行名為RIM_Oracle_Agent.9i的檔案時,gdb會被首先啟動,使得要調試的代碼處於gdb控制之下。

新的問題來了。子進程是在gdb的控制下了,但還是不能調試:如何與gdb互動呢?我們必須以某種方式啟動gdb,以便能在某個視窗/終端與gdb互動。具體來說,可以使用xterm產生這個視窗。

xterm是X window系統下的類比終端程式。比如我們在Linux案頭環境GNOME中敲入xterm命令:

xterm

就會跳出一個終端視窗:

 

終端

如果你是在一台遠程linux伺服器上調試,那麼可以使用VNC(Virtual Network Computing) viewer從本地機器串連到伺服器上使用xterm。在此之前,需要在你的本地機器上安裝VNC viewer,在伺服器上安裝並啟動VNC server。大多數linux發行版都預裝了vnc-server軟體包,所以我們可以直接運行vncserver命令。注意,第一次運行vncserver時會提示輸入密碼,用作VNC viewer從用戶端串連時的密碼。可以在VNC server機器上使用vncpasswd命令修改密碼。

[root@tivf09 root]# vncserver

 

New 'tivf09:1 (root)' desktop is tivf09:1

 

Starting applications specified in /root/.vnc/xstartup

Log file is /root/.vnc/tivf09:1.log

 

[root@tivf09 root]#

[root@tivf09 root]# ps -ef|grep -i vnc

root     19609     1  0 Jun05 ?        00:08:46 Xvnc :1 -desktop tivf09:1 (root)

  -httpd /usr/share/vnc/classes -auth /root/.Xauthority -geometry 1024x768

  -depth 16 -rfbwait 30000 -rfbauth /root/.vnc/passwd -rfbport 5901 -pn

root     19627     1  0 Jun05 ?        00:00:00 vncconfig -iconic

root     12714 10599  0 01:23 pts/0    00:00:00 grep -i vnc

[root@tivf09 root]#

 

Vncserver是一個Perl指令碼,用來啟動Xvnc(X VNC server)。X client應用,比如xterm,VNC viewer都是和它通訊的。如上所示,我們可以使用的DISPLAY值為tivf09:1。現在就可以從本地機器使用VNC viewer串連過去:


VNC viewer

下面我們來修改RIM_Oracle_Agent.9i指令碼,使它看起來像下面這樣:

#!/bin/sh

export DISPLAY=tivf09:1.0; xterm -e gdb RIM_Oracle_Agent.binary

 

如果你的程式在exec的時候還傳入了參數,可以改成:

#!/bin/sh

export DISPLAY=tivf09:1.0; xterm -e gdb --args RIM_Oracle_Agent.binary $@

 

最後加上執行許可權

[root@tivf09 bin]# chmod 755 RIM_Oracle_Agent.9i

 

現在就可以調試了。運行啟動子進程的程式:

[root@tivf09 root]# wrimtest -l 9i_linux

Resource Type  : RIM

Resource Label : 9i_linux

Host Name      : tivf09

User Name      : mdstatus

Vendor         : Oracle

Database       : rim

Database Home  : /data/oracle9i/920

Server ID      : rim

Instance Home  :

Instance Name  :

Opening Regular Session...

 

程式停住了。從VNC viewer中可以看到,一個新的gdb xterm視窗在伺服器端開啟了

gdb xterm 視窗

[root@tivf09 root]# ps -ef|grep gdb

nobody   24312 24311  0 04:30 ?        00:00:00 xterm -e gdb RIM_Oracle_Agent.binary

nobody   24314 24312  0 04:30 pts/2    00:00:00 gdb RIM_Oracle_Agent.binary

root     24326 10599  0 04:30 pts/0    00:00:00 grep gdb

 

啟動並執行正是要調試的程式。設定好斷點,開始調試吧!

注意,下面的錯誤一般是許可權的問題,使用 xhost 命令來修改許可權:

xterm 錯誤

[root@tivf09 bin]# export DISPLAY=tivf09:1.0

[root@tivf09 bin]# xhost +

access control disabled, clients can connect from any host

 

xhost + 禁止了存取控制,從任何機器都可以串連過來。考慮到安全問題,你也可以使用xhost + <你的機器名>。

小結

上述三種方法各有特點和優劣,因此適應於不同的場合和環境:

  • follow-fork-mode方法:方便易用,對系統核心和GDB版本有限制,適合於較為簡單的多進程系統
  • attach子進程方法:靈活強大,但需要添加額外代碼,適合於各種複雜情況,特別是守護進程
  • GDB wrapper方法:專用於fork+exec模式,不用添加額外代碼,但需要X環境支援(xterm/VNC)。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.