歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

Linux 技巧:重定向 stderr 和 stdout 輸出到 gdb 窗口

本文介紹了一個實用 gdb 調試技巧。 它結合實際例子,一步一步示意如何重定向

stderr 和 stdout 到 gdb窗口,使得查看應用程序的輸出信息更為方便,從而提高調試者的工作效率。

問題

為了調試基於 Eclipse 的 Java 和 C++ 混合的應用程序時,通常同時使用 Eclipse 和 gdb 來分別調試 Java 和 C++ 代碼。此時,被調試程序的標准輸出( stdout )和標准錯誤輸出( stderr )取決於這個該程序的啟動方式。如果程序是在 Eclipse 的 IDE 環境下啟動的,那默認情況下 stderr 和 stdout 都會輸出在 Eclipse 的 console 窗口下,如果這時又需要用 gdb 來調試 C++ 的代碼,那為了查看輸出的調試信息,還不得不切換到另外一個窗口(比如

Eclipse 的 console窗口)去查看,然後再切回來繼續調試,這是不是很不方便呢?

解決之道

下面本文將介紹個一個簡單的方法用以重定向 stderr 和 stdout 到指定的目的地,包括正在使用的 gdb 窗口。

由於這個方法是基於 gdb 提供的基本而又強大的兩個功能之上的,所以在介紹它之前,先簡單介紹一下 gdb 的這兩個功能。

使用 call 命令調用外部函數

GDB 提供的 call 命令允許調試者在當前函數調用棧的棧頂調用函數,猶如在被調試的程序中執行的一般。比如想關閉某個文件(文件描述符為 fd ),那只需要在 gdb 中輸入:

(gdb) call (int)close(fd)
有了它,gdb 就可以具有很強大的功能,因為只要把所需要的功能寫成一個函數編譯進應用程序,調試時候在 gdb 中 call 該函數便可。

利用 .gdbinit 來自定義 gdb 命令

GDB 在啟動的時候會按一定的路徑順序(通常是先當前目錄而後用戶目錄)尋找 .gdbinit 文件,一旦找到,就會自動執行裡面的命令。這個功能允許用戶把常用的一些命令放在這個文件裡,這樣就不用每次進入 gdb 後再去手動執行這些命令。事實上,.gdbinit 就是一個腳本,甚至可在裡面把常用的若干 gdb命令序列定義成一個新命令,這樣只要在 gdb 裡面輸入這個新命令就等於自動執行了被定義的那個命令序列。

另外,如果用戶已經在 gdb 裡後,再去修改 .gdbinit ,只要通過:

(gdb) source  ~/.gdbinit
便可以讓那些新增加的改動生效。

重定向 stdout和 stderr

首先,打開一個終端窗口,先用 ps 命令查到所需進程的 pid 。

$ ps ax | grep HelloWorld(要調試的應用程序名)
13522  ??  S    134:47.01 /Users/yyq/projects/1210/HelloWorld
24730  p5  S+     0:00.00 grep Notes
上面列出的第一行中的13522,就是 HelloWorld 的 pid 。

接著,我們使 gdb 連接上這個應用程序。如下:

$ gdb   –pid=13522
或者可以先運行 gdb ,然後用 attach 命令,如下:

$gdb
$atta 13522
不出意外的話,接下來就可以在 gdb 窗口調試了。

可以試一下看看是不是輸出信息不在 gdb 窗口中。

(gdb) call (void)printf(“\n Is this string printed on gdb window\n”)
這時在 gdb 窗口是看不見這個輸出的。 為了跟蹤查看某些重要的調試信息,得不停地切換到別的窗口去看,很不方便。

解決的方法如下:

先關閉 stdout ,和 stderr 對應的文件描述符。

(gdb) call (int)close(1)
(gdb) call (int)close(2)
然後使用以下命令查看一下當前 gdb 窗口所在的虛擬終端。

(gdb) shell tty
/dev/tty5
這時再重新打開 stdout 和 stderr , 把它們和 gdb 窗口所在的虛擬終端關聯起來。

(gdb) p (int)open("/dev/ttyp1", 2) 
$1 = 1 
(gdb) p (int)open("/dev/ttyp1", 2) 
$2 = 2
如果這兩個命令執行結果不是如上結果(1和2),意味著 open 執行失敗,需要重新進行 close 和 open.

另外,如果把這裡的 ”/dev/ttyp1” 替換成目標文件名,便可將 stderr 和 stdout 重定向到該文件。

接下來,重新執行如下命令:

(gdb) call (void)printf(“\n Is this string be printed on gdb window?\n”)
Is this string be printed on gdb window?
這次輸出到了 gdb 窗口,也證明成功重定向了被調試程序的 stdout和 stderr . 以後就可直接在 gdb 窗口中看到所有的輸出信息,勿需再切換窗口。

如果每次都要運行這麼多命令,還是較為繁瑣,此時可以利用 .gdbinit 來簡化用戶輸入:把這一系列命令定義一個新命令,放到 .gdbinit 文件裡,然後在 gdb 裡執行這個命令便可。

關於在 .gdbinit 中定義 gdb 新命令的語法可以參考 dW 上其他的文章。下面就針對重定向問題,看 .gdbinit 是如何通過引入新命令來簡化用戶輸入。其實, 只需在 .gdbinit 文件裡,增加如下腳本:

def  redirect 
call (void)close(1) 
call (void)close(2) 
call (int)open($arg0, 2) 
call (int)open($arg0, 2) 
end
上面這段腳本定義了一個新命令: redirect ,就是重定向的意思。

之後,當重啟 gdb (或者運行 source~/.gdbinit ),並且連接到要調試的應用程序後. 用以下簡單的兩步就可達到重定向的目的:

第一步, 仍是得到這個 gdb 窗口所在的虛擬終端:

(gdb)shell tty 
/dev/ttyp3
接著就可以調用 .gdbinit 中定義的命令了:

(gdb)redirect("/dev/ttyp3")
$1=1 
$2=2
為了易於理解記憶,甚至可以按如下方式為該命令增加幫助信息。

把下面這段腳本緊接添加到剛才 redirect 所對應的 end 後面

document redirect 
redirect("argument"), this is used to switch stderr and stdout to gdb window.
The argument is the name of gdb window.
end
這樣,在 gdb 窗口中,就可以使用 help 命令來查看這個命令的幫助信息了:

(gdb) help redirect
this is used to switch stderr and stdout to gdb window. 
The argument is the name of gdb window.

即時刷新 stdout 和 stderr

由於系統有時會對輸入輸出會進行緩存,因此有可能會碰到如下情況:執行了輸出語句,但還是不見輸出。尤其是在調試 Java/C++ 混合程序時容易發生這種情況,比如在 JNI 代碼中的printf 就很可能被緩存後再輸出。這種系統緩存機制很不利於調試程序,因為調試信息的及時輸出很重要,很多時候要利用這些輸出信息來判斷程序的行為是否正常。

為了解決這個問題,可以調用 fflush:

(gdb)call (int)fflush(0)

這樣所有的緩沖都會得到立刻刷新,包括 stdout 和 stderr . 調試者就能馬上看到前面執行的輸出的結果。

總結

gdb的命令很豐富,功能也很多,巧妙地利用它們可以大大地提高調試者的工作效率。本文通過利用 gdb 的 call 命令和 .gdbinit 文件的擴展功能,提出了一個簡單有效的方法, 用以重定向被調試程序的 stdout 和 stderr ,以及如何及時刷新顯示輸出結果。

Copyright © Linux教程網 All Rights Reserved