引言
“Oops,系統掛死了..."
“Oops,程序崩潰了..."
“Oops,命令執行報錯..."
對於維護人員來說,這樣的悲劇每天都在上演。理想情況下,系統或應用程序的錯誤日誌提供了足夠全面的信息,通過查看相關日誌,維護人員就能很快地定位出問題發生的原因。但現實情況,許多錯誤日誌打印模淩兩可,更多地描述了出錯時的現象(比如"could not open file","connect to XXX time out"),而非出錯的原因。
錯誤日誌不能滿足定位問題的需求,我們能從更“深層”的方面著手分析嗎?程序或命令的執行,需要通過系統調用(system call)與操作系統產生交互,其實我們可以通過觀察這些系統調用及其參數、返回值,界定出錯的範圍,甚至找出問題出現的根因。
在Linux中,strace就是這樣一款工具。通過它,我們可以跟蹤程序執行過程中產生的系統調用及接收到的信號,幫助我們分析程序或命令執行中遇到的異常情況。
一個簡單的例子
如何使用strace對程序進行跟蹤,如何查看相應的輸出?下面我們通過一個例子來說明。
1.被跟蹤程序示例
//main.c
#include
#include
#include int main( )
{ int fd ; int i = 0 ;
fd = open( “/tmp/foo”, O_RDONLY ) ; if ( fd < 0 )
i=5; else i=2; return i;
}
以上程序嘗試以只讀的方式打開/tmp/foo文件,然後退出,其中只使用了open這一個系統調用函數。之後我們對該程序進行編譯,生成可執行文件:
lx@LX:~$ gcc main.c -o main
2.strace跟蹤輸出
使用以下命令,我們將使用strace對以上程序進行跟蹤,並將結果重定向至main.strace文件:
lx@LX:~$ strace -o main.strace ./main
接下來我們來看main.strace文件的內容:
lx@LX:~$ cat main.strace
1 execve("./main", ["./main"], [/* 43 vars */]) = 0
2 brk(0) = 0x9ac4000
3 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
4 mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7739000
5 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
6 open("/etc/ld.so.cache", O_RDONLY) = 3
7 fstat64(3, {st_mode=S_IFREG|0644, st_size=80682, ...}) = 0
8 mmap2(NULL, 80682, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7725000
9 close(3) = 0
10 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
11 open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
12 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220o\1\0004\0\0\0"..., 512) = 512
13 fstat64(3, {st_mode=S_IFREG|0755, st_size=1434180, ...}) = 0
14 mmap2(NULL, 1444360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x56d000
15 mprotect(0x6c7000, 4096, PROT_NONE) = 0
16 mmap2(0x6c8000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15a) = 0x6c8000
17 mmap2(0x6cb000, 10760, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x6cb000
18 close(3) = 0
19 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7724000
20 set_thread_area({entry_number:-1 -> 6, base_addr:0xb77248d0, limit:1048575, seg_32bit:1, contents:0, read_exec_ only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
21 mprotect(0x6c8000, 8192, PROT_READ) = 0
22 mprotect(0x8049000, 4096, PROT_READ) = 0
23 mprotect(0x4b0000, 4096, PROT_READ) = 0
24 munmap(0xb7725000, 80682) = 0
25 open("/tmp/foo", O_RDONLY) = -1 ENOENT (No such file or directory)
26 exit_group(5) = ?
//標紅的行號為方便說明而添加,非strace執行輸出
看到這一堆輸出,是否心生畏難情緒?不用擔心,下面我們對輸出逐條進行分析。
strace跟蹤程序與系統交互時產生的系統調用,以上每一行就對應一個系統調用,格式為:
系統調用的名稱( 參數... ) = 返回值 錯誤標誌和描述
Line 1: 對於命令行下執行的程序,execve(或exec系列調用中的某一個)均為strace輸出系統調用中的第一個。strace首先調用fork或clone函數新建一個子進程,然後在子進程中調用exec載入需要執行的程序(這裏為./main)
Line 2: 以0作為參數調用brk,返回值為內存管理的起始地址(若在子進程中調用malloc,則從0x9ac4000地址開始分配空間)
Line 3: 調用access函數檢驗/etc/ld.so.nohwcap是否存在
Line 4: 使用mmap2函數進行匿名內存映射,以此來獲取8192bytes內存空間,該空間起始地址為0xb7739000,關於匿名內存映射,可以看這裏
Line 6: 調用open函數嘗試打開/etc/ld.so.cache文件,返回文件描述符為3
Line 7: fstat64函數獲取/etc/ld.so.cache文件信息
Line 8: 調用mmap2函數將/etc/ld.so.cache文件映射至內存,關於使用mmap映射文件至內存,可以看這裏
Line 9: close關閉文件描述符為3指向的/etc/ld.so.cache文件
Line12: 調用read,從/lib/i386-linux-gnu/libc.so.6該libc庫文件中讀取512bytes,即讀取ELF頭信息
Line15: 使用mprotect函數對0x6c7000起始的4096bytes空間進行保護(PROT_NONE表示不能訪問,PROT_READ表示可以讀取)
Line24: 調用munmap函數,將/etc/ld.so.cache文件從內存中去映射,與Line 8的mmap2對應
Line25: 對應源碼中使用到的唯一的系統調用——open函數,使用其打開/tmp/foo文件
Line26: 子進程結束,退出碼為5(為什麽退出值為5?返回前面程序示例部分看看源碼吧:)
3.輸出分析
呼呼!看完這麽多系統調用函數,是不是有點摸不著北?讓我們從整體入手,回到主題strace上來。
從上面輸出可以發現,真正能與源碼對應上的只有open這一個系統調用(Line25),其他系統調用幾乎都用於進行進程初始化工作:裝載被執行程序、載入libc函數庫、設置內存映射等。
源碼中的if語句或其他代碼在相應strace輸出中並沒有體現,因為它們並沒有喚起系統調用。strace只關心程序與系統之間產生的交互,因而strace不適用於程序邏輯代碼的排錯和分析。
對於Linux中幾百個系統調用,上面strace輸出的幾個只是冰山一角,想要更深入地了解Linux系統調用,那就man一下吧!
man 2 系統調用名稱
man ld.so //Linux動態鏈接的manpage
strace常用選項
該節介紹經常用到的幾個strace命令選項,以及在何時使用這些選項合適。
1.跟蹤子進程
默認情況下,strace只跟蹤指定的進程,而不對指定進程中新建的子進程進行跟蹤。使用-f選項,可對進程中新建的子進程進行跟蹤,並在輸出結果中打印相應進程PID:
mprotect(0x5b1000, 4096, PROT_READ) = 0 munmap(0xb77fc000, 80682) = 0 clone(Process 13600 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb77fb938) = 13600 [pid 13599] fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 [pid 13600] fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 [pid 13599] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0
[pid 13600] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb780f000
……
對多進程程序、命令和腳本使用strace進行跟蹤的時,一般打開-f選項。
2.記錄系統調用時間
strace還可以記錄程序與系統交互時,各個系統調用發生時的時間信息,有r、t、tt、ttt、T等幾個選項,它們記錄時間的方式為:
-T: 記錄各個系統調用花費的時間,精確到微秒
-r: 以第一個系統調用(通常為execve)計時,精確到微秒
-t: 時:分:秒
-tt: 時:分:秒 . 微秒
-ttt: 計算機紀元以來的秒數 . 微秒
比較常用的為T選項,因為其提供了每個系統調用花費時間。而其他選項的時間記錄既包含系統調用時間,又算上用戶級代碼執行用時,參考意義就小一些。對部分時間選項我們可以組合起來使用,例如:
strace -Tr ./main 0.000000 execve(“./main”, [“main”], [/* 64 vars */]) = 0 0.000931 fcntl64(0, F_GETFD)= 0 <0.000012> 0.000090 fcntl64(1, F_GETFD)= 0 <0.000022> 0.000060 fcntl64(2, F_GETFD)= 0 <0.000012> 0.000054 uname({sys=”Linux”, node=”ion”, ...}) = 0 <0.000014> 0.000307 geteuid32()= 7903 <0.000011> 0.000040 getuid32()= 7903 <0.000012> 0.000039 getegid32()= 200 <0.000011> 0.000039 getgid32()= 200 <0.000011>
……
最左邊一列為-r選項對應的時間輸出,最右邊一列為-T選項對應的輸出。
3.跟蹤正在運行的進程
使用strace對運行中的程序進行跟蹤,使用命令“strace -p PID”即可,命令執行之後,被跟蹤的進程照常執行,strace的其他選項也適用於運行中的進程跟蹤。
使用strace處理程序掛死
最後我們通過一個程序示例,學習使用strace分析程序掛死的方法。
1.掛死程序源碼
//hang.c
#include
#include
#include
#include <string.h> int main(int argc, char** argv)
{
getpid(); //該系統調用起到標識作用 if(argc < 2)
{
printf("hang (user|system)\n"); return 1;
} if(!strcmp(argv[1], "user")) while(1); else if(!strcmp(argv[1], "system"))
sleep(500); return 0;
}
可向該程序傳送user和system參數,以上代碼使用死循環模擬用戶態掛死,調用sleep模擬內核態程序掛死。
2.strace跟蹤輸出
用戶態掛死跟蹤輸出:
lx@LX:~$ gcc hang.c -o hang
lx@LX:~$ strace ./hang user
……
mprotect(0x8049000, 4096, PROT_READ) = 0 mprotect(0xb59000, 4096, PROT_READ) = 0 munmap(0xb77bf000, 80682) = 0 getpid() = 14539
內核態掛死跟蹤輸出:
lx@LX:~$ strace ./hang system
……
mprotect(0x8049000, 4096, PROT_READ) = 0 mprotect(0xddf000, 4096, PROT_READ) = 0 munmap(0xb7855000, 80682) = 0 getpid() = 14543 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 nanosleep({500, 0},
3.輸出分析
用戶態掛死情況下,strace在getpid()一行輸出之後沒有其他系統調用輸出;進程在內核態掛死,最後一行的系統調用nanosleep不能完整顯示,這裏nanosleep沒有返回值表示該調用尚未完成。
因而我們可以得出以下結論:使用strace跟蹤掛死程序,如果最後一行系統調用顯示完整,程序在邏輯代碼處掛死;如果最後一行系統調用顯示不完整,程序在該系統調用處掛死。
當程序掛死在系統調用處,我們可以查看相應系統調用的man手冊,了解在什麽情況下該系統調用會出現掛死情況。另外,系統調用的參數也為我們提供了一些信息,例如掛死在如下系統調用:
read(16,
那我們可以知道read函數正在對文件描述符為16的文件或socket進行讀取,進一步地,我們可以使用lsof工具,獲取對應於文件描述符為16的文件名、該文件被哪些進程占用等信息。
小結
本文對Linux中常用的問題診斷工具strace進行了介紹,通過程序示例,介紹了strace的使用方法、輸出格式以及使用strace分析程序掛死問題的方法,另外對strace工具的幾個常用選項進行了說明,描述了這幾個選項適用的場景。
下次再遇到程序掛死、命令執行報錯的問題,如果從程序日誌和系統日誌中看不出問題出現的原因,先別急著google或找高手幫忙,別忘了一個強大的工具它就在那裏,不離不棄,strace一下吧!
功能:
跟蹤程式執行時的系統調用和所接收的信號.通常的用 法是strace執行一直到commande結束.
並且將所調用的系統調用的名稱、參數和返回值輸出到標準輸出或者輸出到-o指定的文件.
strace 是一個功能強大的調試,分析診斷工具.你將發現他是一個極好的幫手在你要調試一個無法看到源碼或者源碼無法在編譯的程序.
你將輕松的學習到一個 軟件是如何通過系統調用來實現他的功能的.而且作為一個程序設計師,你可以了解到在用戶態和內核態是如何通過系統調用和信號來實現程序的功能的.
strace 的每一行輸出包括系統調用名稱,然後是參數和返回值.這個例子:
strace cat /dev/null
他的輸出會有:
open("/dev/null",O_RDONLY) = 3
有錯誤產生時,一般會返回-1.所以會有錯誤標誌和描述:
open("/foor/bar",)_RDONLY) = -1 ENOENT (no such file or directory)
信號將輸出餵信號標誌和信號的描述.跟蹤並中斷這個命 令"sleep 600":
sigsuspend({}
--- SIGINT (Interrupt) ---
+++ killed by SIGINT +++
參數的輸出有些不一致.如shell命令中的 ">>tmp",將輸出:
open("tmp",O_WRONLY|O_APPEND|A_CREAT,0666) = 3
對於結構指針,將進行適當的顯示.如:"ls -l /dev/null":
lstat("/dev/null",{st_mode=S_IFCHR|0666},st_rdev=makdev[1,3],...}) = 0
請註意"struct stat" 的聲明和這裏的輸出.lstat的第一個參數是輸入參數,而第二個參數是向外傳值.
當 你嘗試"ls -l" 一個不存在的文件時,會有:
lstat(/foot/ball",0xb004) = -1 ENOENT (no such file or directory)
char*將作為C的字符串類型輸出.沒有字符串輸出時一般是char* 是一個轉義字符,只輸出字符串的長度.
當字符串過長是會使用"..."省略.如在"ls -l"會有一個gepwuid調用讀取password文件:
read(3,"root::0:0:System Administrator:/"...,1024) = 422
當參數是結構數組時,將按照簡單的指針和數組輸出如:
getgroups(4,[0,2,4,5]) = 4
關於bit作為參數的情形,也是使用方括號,並且用空格將每一項參數隔開.如:
sigprocmask(SIG_BLOCK,[CHLD TTOU],[]) = 0
這裏第二個參數代表兩個信號SIGCHLD 和 SIGTTOU.如果bit型參數全部置位,則有如下的輸出:
sigprocmask(SIG_UNBLOCK,~[],NULL) = 0
這裏第二個參數全部置位.
參數說明:
-c 統計每一系統調用的所執行的時間,次數和出錯的次數等.
-d 輸出strace關於標準錯誤的調試信息.
-f 跟蹤由fork調用所產生的子進程.
-ff 如果提供-o filename,則所有進程的跟蹤結果輸出到相應的filename.pid中,pid是各進程的進程號.
-F 嘗試跟蹤vfork調用.在-f時,vfork不被跟蹤.
-h 輸出簡要的幫助信息.
-i 輸出系統調用的入口指針.
-q 禁止輸出關於脫離的消息.
-r 打印出相對時間關於,,每一個系統調用.
-t 在輸出中的每一行前加上時間信息.
-tt 在輸出中的每一行前加上時間信息,微秒級.
-ttt 微秒級輸出,以秒了表示時間.
-T 顯示每一調用所耗的時間.
-v 輸出所有的系統調用.一些調用關於環境變量,狀態,輸入輸出等調用由於使用頻繁,默認不輸出.
-V 輸出strace的版本信息.
-x 以十六進制形式輸出非標準字符串
-xx 所有字符串以十六進制形式輸出.
-a column
設置返回值的輸出位置.默認 為40.
-e expr
指定一個表達式,用來控制如何跟蹤.格式如下:
[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用來限定的符號或數字.默認的 qualifier是 trace.感嘆號是否定符號.例如:
-eopen等價於 -e trace=open,表示只跟蹤open調用.而-etrace!=open表示跟蹤除了open以外的其他調用.有兩個特殊的符號 all 和 none.
註意有些shell使用!來執行歷史記錄裏的命令,所以要使用\\.
-e trace=set
只跟蹤指定的系統 調用.例如:-e trace=open,close,rean,write表示只跟蹤這四個系統調用.默認的為set=all.
-e trace=file
只跟蹤有關文件操作的系統調用.
-e trace=process
只跟蹤有關進程控制的系統調用.
-e trace=network
跟蹤與網絡有關的所有系統調用.
-e strace=signal
跟蹤所有與系統信號有關的 系統調用
-e trace=ipc
跟蹤所有與進程通訊有關的系統調用
-e abbrev=set
設定 strace輸出的系統調用的結果集.-v 等與 abbrev=none.默認為abbrev=all.
-e raw=set
將指 定的系統調用的參數以十六進制顯示.
-e signal=set
指定跟蹤的系統信號.默認為all.如 signal=!SIGIO(或者signal=!io),表示不跟蹤SIGIO信號.
-e read=set
輸出從指定文件中讀出 的數據.例如:
-e read=3,5
-e write=set
輸出寫入到指定文件中的數據.
-o filename
將strace的輸出寫入文件filename
-p pid
跟蹤指定的進程pid.
-s strsize
指定輸出的字符串的最大長度.默認為32.文件名一直全部輸出.
-u username
以username 的UID和GID執行被跟蹤的命令.
用strace調試程序
在理想世界裏,每當一個程序不能正常執行一個功能時,它就會給出一個有用的錯誤提示,告訴你在足夠的改正錯誤的線索。但遺憾的是,我們不是生活在理想世界 裏,起碼不總是生活在理想世界裏。有時候一個程序出現了問題,你無法找到原因。
這就是調試程序出現的原因。strace是一個必不可少的 調試工具,strace用來監視系統調用。你不僅可以調試一個新開始的程序,也可以調試一個已經在運行的程序(把strace綁定到一個已有的PID上 面)。
首先讓我們看一個真實的例子:
[BOLD]啟動KDE時出現問題[/BOLD]
前一段時間,我在 啟動KDE的時候出了問題,KDE的錯誤信息無法給我任何有幫助的線索。
_KDE_IceTransSocketCreateListener: failed to bind listener
_KDE_IceTransSocketUNIXCreateListener: ...SocketCreateListener() failed
_KDE_IceTransMakeAllCOTSServerListeners: failed to create listener for local
Cannot establish any listening sockets DCOPServer self-test failed.
對 我來說這個錯誤信息沒有太多意義,只是一個對KDE來說至關重要的負責進程間通信的程序無法啟動。我還可以知道這個錯誤和ICE協議(Inter Client Exchange)有關,除此之外,我不知道什麽是KDE啟動出錯的原因。
我決定采用strace看一下在啟動 dcopserver時到底程序做了什麽:
strace -f -F -o ~/dcop-strace.txt dcopserver
這 裏 -f -F選項告訴strace同時跟蹤fork和vfork出來的進程,-o選項把所有strace輸出寫到~/dcop-strace.txt裏 面,dcopserver是要啟動和調試的程序。
再次出現錯誤之後,我檢查了錯誤輸出文件dcop-strace.txt,文件裏有很多 系統調用的記錄。在程序運行出錯前的有關記錄如下:
27207 mkdir("/tmp/.ICE-unix", 0777) = -1 EEXIST (File exists)
27207 lstat64("/tmp/.ICE-unix", {st_mode=S_IFDIR|S_ISVTX|0755, st_size=4096, ...}) = 0
27207 unlink("/tmp/.ICE-unix/dcop27207-1066844596") = -1 ENOENT (No such file or directory)
27207 bind(3, {sin_family=AF_UNIX, path="/tmp/.ICE-unix/dcop27207-1066844596"}, 38) = -1 EACCES (Permission denied)
27207 write(2, "_KDE_IceTrans", 13) = 13
27207 write(2, "SocketCreateListener: failed to "..., 46) = 46
27207 close(3) = 0 27207 write(2, "_KDE_IceTrans", 13) = 13
27207 write(2, "SocketUNIXCreateListener: ...Soc"..., 59) = 59
27207 umask(0) = 0 27207 write(2, "_KDE_IceTrans", 13) = 13
27207 write(2, "MakeAllCOTSServerListeners: fail"..., 64) = 64
27207 write(2, "Cannot establish any listening s"..., 39) = 39
其 中第一行顯示程序試圖創建/tmp/.ICE-unix目錄,權限為0777,這個操作因為目錄已經存在而失敗了。第二個系統調用(lstat64)檢查 了目錄狀態,並顯示這個目錄的權限是0755,這裏出現了第一個程序運行錯誤的線索:程序試圖創建屬性為0777的目錄,但是已經存在了一個屬性為 0755的目錄。第三個系統調用(unlink)試圖刪除一個文件,但是這個文件並不存在。這並不奇怪,因為這個操作只是試圖刪掉可能存在的老文件。
但 是,第四行確認了錯誤所在。他試圖綁定到/tmp/.ICE-unix/dcop27207-1066844596,但是出現了拒絕訪問錯誤。. ICE_unix目錄的用戶和組都是root,並且只有所有者具有寫權限。一個非root用戶無法在這個目錄下面建立文件,如果把目錄屬性改成0777, 則前面的操作有可能可以執行,而這正是第一步錯誤出現時進行過的操作。
所以我運行了chmod 0777 /tmp/.ICE-unix之後KDE就可以正常啟動了,問題解決了,用strace進行跟蹤調試只需要花很短的幾分鐘時間跟蹤程序運行,然後檢查並分 析輸出文件。
說明:運行chmod 0777只是一個測試,一般不要把一個目錄設置成所有用戶可讀寫,同時不設置粘滯位(sticky bit)。給目錄設置粘滯位可以阻止一個用戶隨意刪除可寫目錄下面其他人的文件。一般你會發現/tmp目錄因為這個原因設置了粘滯位。KDE可以正常啟動 之後,運行chmod +t /tmp/.ICE-unix給.ICE_unix設置粘滯位。
[BOLD]解決庫依賴問題[/BOLD]
starce 的另一個用處是解決和動態庫相關的問題。當對一個可執行文件運行ldd時,它會告訴你程序使用的動態庫和找到動態庫的位置。但是如果你正在使用一個比較老 的glibc版本(2.2或更早),你可能會有一個有bug的ldd程序,它可能會報告在一個目錄下發現一個動態庫,但是真正運行程序時動態連接程序 (/lib/ld-linux.so.2)卻可能到另外一個目錄去找動態連接庫。這通常因為/etc/ld.so.conf和 /etc/ld.so.cache文件不一致,或者/etc/ld.so.cache被破壞。在glibc 2.3.2版本上這個錯誤不會出現,可能ld-linux的這個bug已經被解決了。
盡管這樣,ldd並不能把所有程序依賴的動態庫列出 來,系統調用dlopen可以在需要的時候自動調入需要的動態庫,而這些庫可能不會被ldd列出來。作為glibc的一部分的NSS(Name Server Switch)庫就是一個典型的例子,NSS的一個作用就是告訴應用程序到哪裏去尋找系統帳號數據庫。應用程序不會直接連接到NSS庫,glibc則會通 過dlopen自動調入NSS庫。如果這樣的庫偶然丟失,你不會被告知存在庫依賴問題,但這樣的程序就無法通過用戶名解析得到用戶ID了。讓我們看一個例 子:
whoami程序會給出你自己的用戶名,這個程序在一些需要知道運行程序的真正用戶的腳本程序裏面非常有用,whoami的一個示例 輸出如下:
# whoami
root
假設因為某種原因在升 級glibc的過程中負責用戶名和用戶ID轉換的庫NSS丟失,我們可以通過把nss庫改名來模擬這個環境:
# mv /lib/libnss_files.so.2 /lib/libnss_files.so.2.backup
# whoami
whoami: cannot find username for UID 0
這 裏你可以看到,運行whoami時出現了錯誤,ldd程序的輸出不會提供有用的幫助:
# ldd /usr/bin/whoami
libc.so.6 => /lib/libc.so.6 (0x4001f000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
你 只會看到whoami依賴Libc.so.6和ld-linux.so.2,它沒有給出運行whoami所必須的其他庫。這裏時用strace跟蹤 whoami時的輸出:
strace -o whoami-strace.txt whoami
open("/lib/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib/i686/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/lib/i686/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
open("/lib/i686/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/lib/i686", 0xbffff190) = -1 ENOENT (No such file or directory)
open("/lib/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/lib/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
open("/lib/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/lib", {st_mode=S_IFDIR|0755, st_size=2352, ...}) = 0
open("/usr/lib/i686/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/usr/lib/i686/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
open("/usr/lib/i686/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
你 可以發現在不同目錄下面查找libnss.so.2的嘗試,但是都失敗了。如果沒有strace這樣的工具,很難發現這個錯誤是由於缺少動態庫造成的。現 在只需要找到libnss.so.2並把它放回到正確的位置就可以了。
[BOLD]限制strace只跟蹤特定的系統調用[/BOLD]
如 果你已經知道你要找什麽,你可以讓strace只跟蹤一些類型的系統調用。例如,你需要看看在configure腳本裏面執行的程序,你需要監視的系統調 用就是execve。讓strace只記錄execve的調用用這個命令:
strace -f -o configure-strace.txt -e execve ./configure
部 分輸出結果為:
2720 execve("/usr/bin/expr", ["expr", "a", ":", "(a)"], []) = 0
2725 execve("/bin/basename", ["basename", "./configure"], []) = 0
2726 execve("/bin/chmod", ["chmod", "+x", "conftest.sh"], []) = 0
2729 execve("/bin/rm", ["rm", "-f", "conftest.sh"], []) = 0
2731 execve("/usr/bin/expr", ["expr", "99", "+", "1"], []) = 0
2736 execve("/bin/ln", ["ln", "-s", "conf2693.file", "conf2693"], []) = 0
你已經看到了,strace不僅可以被程序員 使用,普通系統管理員和用戶也可以使用strace來調試系統錯誤。必須承認,strace的輸出不總是容易理解,但是很多輸出對大多數人來說是不重要 的。你會慢慢學會從大量輸出中找到你可能需要的信息,像權限錯誤,文件未找到之類的,那時strace就會成為一個有力的工具了
- Jul 22 Wed 2015 17:31
strace工具診斷Linux問題
close
全站熱搜
留言列表