使用 GDB 除錯多程式程式
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:輸入伺服器
輸入密碼:
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)。
參考資料
- GDB 官方參考資料:http://sourceware.org/gdb/documentation/
- 更多 VNC 資訊:http://www.realvnc.com/
相關文章
- gdb除錯多程式2016-11-19除錯
- GDB多程式除錯2015-04-15除錯
- GDB程式碼除錯與使用2016-12-04除錯
- GDB 除錯程式碼2018-11-22除錯
- 用GDB除錯程式2013-08-16除錯
- Linux中使用GDB除錯程式2020-10-30Linux除錯
- 使用GDB命令列偵錯程式除錯C/C++程式2014-11-25命令列除錯C++
- 用GDB除錯程式(六)2020-11-15除錯
- gdb除錯多程序2024-03-14除錯
- 在MacOS上使用gdb(cgdb)除錯Golang程式2018-12-10Mac除錯Golang
- Linux GDB 程式除錯工具使用詳解2016-04-01Linux除錯
- 用GDB除錯程式(二) (轉)2008-01-21除錯
- 用GDB除錯程式(四) (轉)2007-08-15除錯
- 用GDB除錯程式(三) (轉)2007-11-13除錯
- 使用GDB除錯Android Native 層程式碼2021-09-09除錯Android
- gdb除錯正在執行的程式2024-03-15除錯
- 使用gdb和gdbserver除錯Android C/C++程式2013-08-26Server除錯AndroidC++
- linux下用gdb除錯c程式2013-03-21Linux除錯C程式
- GDB除錯使用記錄2020-11-02除錯
- 使用 gdb 工具除錯 Go2015-08-14除錯Go
- GDB多執行緒除錯分析2017-03-19執行緒除錯
- gdb多執行緒多程序除錯命令2024-10-11執行緒除錯
- gdb除錯2020-12-27除錯
- gdb除錯命令小結_與多檔案除錯_遠端除錯2013-10-11除錯
- C編譯: 使用gdb除錯2019-11-07編譯除錯
- Linux核心使用gdb除錯2018-11-15Linux除錯
- C 編譯: 使用 gdb 除錯2016-11-21編譯除錯
- 使用gdb編譯除錯mysql2015-08-21編譯除錯MySql
- c/c++ gdb 除錯帶引數的程式2018-10-11C++除錯
- Linux環境組合語言程式設計初步——使用gdb除錯程式(轉)2007-08-15Linux組合語言程式設計除錯
- GDB除錯MySQL2017-08-25除錯MySql
- gdb除錯命令2017-02-08除錯
- GDB除錯指令2024-03-31除錯
- GDB除錯基礎使用方法2020-11-05除錯
- OpenHarmony系統使用gdb除錯init2023-01-10除錯
- 如何在Docker內部使用gdb偵錯程式2021-09-09Docker
- GDB除錯彙總2014-06-08除錯
- gdb除錯快速上手2024-06-16除錯