strace 命令详解
一、strace 是什么?
按照 strace 官網(wǎng)的描述,strace 是一個可用于診斷、調(diào)試和教學(xué)的 Linux 用戶空間跟蹤器。我們用它來監(jiān)控用戶空間進(jìn)程和內(nèi)核的交互,比如系統(tǒng)調(diào)用、信號傳遞、進(jìn)程狀態(tài)變更等。
strace 底層使用內(nèi)核的 ptrace 特性來實現(xiàn)其功能。
在運維的日常工作中,故障處理和問題診斷是個主要的內(nèi)容,也是必備的技能。strace 作為一種動態(tài)跟蹤工具,能夠幫助運維高效地定位進(jìn)程和服務(wù)故障。它像是一個偵探,通過系統(tǒng)調(diào)用的蛛絲馬跡,告訴你異常的真相。
二、strace 能做什么?
運維工程師都是實踐派的人,我們還是先來個例子吧。
我們從別的機器 copy 了個叫做 some_server 的軟件包過來,開發(fā)說直接啟動就行,啥都不用改。可是嘗試啟動時卻報錯,根本起不來!
啟動命令:
./some_server ../conf/some_server.conf輸出:?
FATAL: InitLogFile failed iRet: -1! Init error: -1655為什么起不來呢?從日志看,似乎是初始化日志文件失敗,真相到底怎樣呢?我們用 strace 來看看。?
strace -tt -f ./some_server ../conf/some_server.conf我們注意到,在輸出 InitLogFile failed 錯誤的前一行,有個 open 系統(tǒng)調(diào)用:?
23:14:24.448034?open("/usr/local/apps/some_server/log//server_agent.log",?O_RDWR|O_CREAT|O_APPEND|O_LARGEFILE,?0666)?=?-1?ENOENT?(No?such?file?or?directory)它嘗試打開文件 /usr/local/apps/some_server/log//server_agent.log 來寫(不存在則創(chuàng)建),可是卻出錯了,返回碼是 -1 , 系統(tǒng)錯誤號 errorno 為 ENOENT。 查下 open 系統(tǒng)調(diào)用的手冊頁:
man 2 open
搜索 ENOENT 這個錯誤號 errno 的解釋
ENOENT O_CREAT ?is not set and the named file does not exist. ?Or, a directory component in pathname does not exist or is a dangling symbolic link.
這里說得比較清楚,因為我們例子中的 open 選項指定了 O_CREAT 選項,這里 errno 為 ENOENT 的原因是日志路徑中某個部分不存在或者是一個失效的符號鏈接。我們來一級一級看下路徑中的哪部分不存在:
?
ls -l /usr/local/apps/some_server/log ls: cannot access /usr/local/apps/some_server/log: No such file or directory ls -l /usr/local/apps/some_server total 8 drwxr-xr-x 2 root users 4096 May 14 23:13 bin drwxr-xr-x 2 root users 4096 May 14 22:48 conf原來是 log 子目錄不存在!上層目錄都是存在的。手工創(chuàng)建 log 子目錄后,服務(wù)就能正常啟動了。
回過頭來, strace 究竟能做什么呢?
它能夠打開應(yīng)用進(jìn)程的這個黑盒,通過系統(tǒng)調(diào)用的線索,告訴你進(jìn)程大概在干嘛。
三、strace怎么用?
strace 有兩種運行模式。
一種是通過它啟動要跟蹤的進(jìn)程。用法很簡單,在原本的命令前加上 strace 即可。比如我們要跟蹤 "ls -lh /var/log/messages"?這個命令的執(zhí)行,可以這樣:
strace ls -lh /var/log/messages另外一種運行模式,是跟蹤已經(jīng)在運行的進(jìn)程,在不中斷進(jìn)程執(zhí)行的情況下,理解它在干嘛。 這種情況,給 strace 傳遞個 -p pid 選項即可。
比如,有個在運行的 some_server 服務(wù),第一步,查看 pid:
pidof some_server 17553得到其 pid 17553 然后就可以用 strace 跟蹤其執(zhí)行:?
strace?-p?17553完成跟蹤時,按 Ctrl + C 結(jié)束 strace 即可。
strace 有一些選項可以調(diào)整其行為,我們這里介紹下其中幾個比較常用的,然后通過示例講解其實際應(yīng)用效果。
strace 常用選項:
從一個示例命令來看:
strace -tt -T -v -f -e trace=file -o /data/log/strace.log -s 1024 -p 23489- -tt:在每行輸出的前面,顯示毫秒級別的時間
- -T:顯示每次系統(tǒng)調(diào)用所花費的時間
- -v:對于某些相關(guān)調(diào)用,把完整的環(huán)境變量,文件 stat 結(jié)構(gòu)等打出來。
- -f:跟蹤目標(biāo)進(jìn)程,以及目標(biāo)進(jìn)程創(chuàng)建的所有子進(jìn)程
- -e:控制要跟蹤的事件和跟蹤行為,比如指定要跟蹤的系統(tǒng)調(diào)用名稱
- -o:把 strace 的輸出單獨寫到指定的文件
- -s:當(dāng)系統(tǒng)調(diào)用的某個參數(shù)是字符串時,最多輸出指定長度的內(nèi)容,默認(rèn)是 32 個字節(jié)
- -p:指定要跟蹤的進(jìn)程 pid,要同時跟蹤多個 pid,重復(fù)多次 -p 選項即可。
四、strace問題定位案例
1、定位進(jìn)程異常退出
問題:機器上有個叫做run.sh的常駐腳本,運行一分鐘后會死掉。需要查出死因。
定位:進(jìn)程還在運行時,通過ps命令獲取其pid, 假設(shè)我們得到的pid是24298
strace -o strace.log -tt -p 24298?查看 strace.log,我們在最后 2 行看到如下內(nèi)容:
22:47:42.803937?wait4(-1,? <unfinished?...> 22:47:43.228422?+++?killed?by?SIGKILL?+++這里可以看出,進(jìn)程是被其他進(jìn)程用 KILL 信號殺死的。
實際上,通過分析,我們發(fā)現(xiàn)機器上別的服務(wù)有個監(jiān)控腳本,它監(jiān)控一個也叫做 run.sh 的進(jìn)程,當(dāng)發(fā)現(xiàn) run.sh 進(jìn)程數(shù)大于 2 時,就會把它殺死重啟。結(jié)果導(dǎo)致我們這個 run.sh 腳本被誤殺。
進(jìn)程被殺退出時,strace 會輸出 killed by SIGX(SIGX 代表發(fā)送給進(jìn)程的信號)等,那么,進(jìn)程自己退出時會輸出什么呢?
這里有個叫做 test_exit 的程序,其代碼如下:
#include <stdio.h> #include <stdlib.h>int main(int argc, char **argv) {exit(1); }我們 strace 看下它退出時 strace 上能看到什么痕跡。?
strace -tt -e trace=process -f ./test_exit說明: -e trace=process 表示只跟蹤和進(jìn)程管理相關(guān)的系統(tǒng)調(diào)用。
輸出:
23:07:24.672849?execve("./test_exit",?["./test_exit"],?[/*?35?vars?*/])?=?0 23:07:24.674665?arch_prctl(ARCH_SET_FS,?0x7f1c0eca7740)?=?0 23:07:24.675108?exit_group(1)? ? ? ? ? ?=?? 23:07:24.675259?+++?exited?with?1?+++可以看出,進(jìn)程自己退出時(調(diào)用 exit 函數(shù),或者從 main 函數(shù)返回), 最終調(diào)用的是 exit_group 系統(tǒng)調(diào)用, 并且 strace 會輸出 exited with X(X為退出碼)。
可能有人會疑惑,代碼里面明明調(diào)用的是 exit,怎么顯示為 exit_group?
這是因為這里的 exit 函數(shù)不是系統(tǒng)調(diào)用,而是 glibc 庫提供的一個函數(shù),exit 函數(shù)的調(diào)用最終會轉(zhuǎn)化為 exit_group 系統(tǒng)調(diào)用,它會退出當(dāng)前進(jìn)程的所有線程。實際上,有一個叫做 _exit()的系統(tǒng)調(diào)用(注意 exit 前面的下劃線),線程退出時最終會調(diào)用它。
2、定位共享內(nèi)存異常
有個服務(wù)啟動時報錯:
shmget 267264 30097568: Invalid argument Can not get shm...exit!錯誤日志大概告訴我們是獲取共享內(nèi)存出錯,通過 strace 看下:?
strace -tt -f -e trace=ipc ./a_mon_svr ../conf/a_mon_svr.conf輸出:?
22:46:36.351798 shmget(0x5feb, 12000, 0666) = 0 22:46:36.351939 shmat(0, 0, 0) = ? Process 21406 attached 22:46:36.355439 shmget(0x41400, 30097568, 0666) = -1 EINVAL (Invalid argument) shmget 267264 30097568: Invalid argument Can not get shm...exit!這里,我們通過 -e trace=ipc 選項,讓 strace 只跟蹤和進(jìn)程通信相關(guān)的系統(tǒng)調(diào)用。
從 strace 輸出,我們知道是 shmget 系統(tǒng)調(diào)用出錯了,errno 是 EINVAL。同樣, 查詢下 shmget 手冊頁,搜索 EINVAL 的錯誤碼的說明:
?EINVAL A new segment was to be created and size < SHMMIN or size > SHMMAX, or no new segment was to be created, a segment with given key existed, but size is greater than the size of that segment
翻譯下,shmget 設(shè)置 EINVAL 錯誤碼的原因為下列之一:
-
要創(chuàng)建的共享內(nèi)存段比 SHMMIN 小 (一般是1個字節(jié))
-
要創(chuàng)建的共享內(nèi)存段比 SHMMAX 大 (內(nèi)核參數(shù) kernel.shmmax 配置)
-
指定 key 的共享內(nèi)存段已存在,其大小和調(diào)用 shmget 時傳遞的值不同。
從 strace 輸出看,我們要連的共享內(nèi)存 key 0x41400,指定的大小是 30097568 字節(jié),明顯與第1、2 種情況不匹配。那只剩下第三種情況。使用 ipcs 看下是否真的是大小不匹配:
?
ipcs? -m?|?grep?41400 key? ? ? ? shmid? ? ? owner? ? ? perms? ? ? bytes? ? ? nattch? ? ?status? ? 0x00041400?1015822? ? root? ? ? ?666? ? ? ? 30095516? ?1可以看到,已經(jīng) 0x41400 這個 key 已經(jīng)存在,并且其大小為 30095516 字節(jié),和我們調(diào)用參數(shù)中的 30097568 不匹配,于是產(chǎn)生了這個錯誤。
在我們這個案例里面,導(dǎo)致共享內(nèi)存大小不一致的原因,是一組程序中,其中一個編譯為32位,另外一個編譯為64位,代碼里面使用了long這個變長int數(shù)據(jù)類型。
把兩個程序都編譯為64解決了這個問題。
這里特別說下 strace 的 -e trace 選項。
要跟蹤某個具體的系統(tǒng)調(diào)用,-e trace=xxx 即可。但有時候我們要跟蹤一類系統(tǒng)調(diào)用,比如所有和文件名有關(guān)的調(diào)用、所有和內(nèi)存分配有關(guān)的調(diào)用。
如果人工輸入每一個具體的系統(tǒng)調(diào)用名稱,可能容易遺漏。于是strace提供了幾類常用的系統(tǒng)調(diào)用組合名字。
?
-e trace=file ? ? 跟蹤和文件訪問相關(guān)的調(diào)用(參數(shù)中有文件名)
-e trace=process ?和進(jìn)程管理相關(guān)的調(diào)用,比如fork/exec/exit_group
-e trace=network ?和網(wǎng)絡(luò)通信相關(guān)的調(diào)用,比如socket/sendto/connect
-e trace=signal ? ?信號發(fā)送和處理相關(guān),比如kill/sigaction
-e trace=desc ?和文件描述符相關(guān),比如write/read/select/epoll等
-e trace=ipc 進(jìn)程見同學(xué)相關(guān),比如shmget等
絕大多數(shù)情況,我們使用上面的組合名字就夠了。實在需要跟蹤具體的系統(tǒng)調(diào)用時,可能需要注意C 庫實現(xiàn)的差異。
比如我們知道創(chuàng)建進(jìn)程使用的是 fork 系統(tǒng)調(diào)用,但在 glibc 里面,fork 的調(diào)用實際上映射到了更底層的 clone 系統(tǒng)調(diào)用。使用 strace 時,得指定 -e trace=clone,指定 -e trace=fork 什么也匹配不上。
3、 性能分析
假如有個需求,統(tǒng)計 Linux 4.5.4 版本內(nèi)核中的代碼行數(shù)(包含匯編和 C 代碼)。這里提供兩個Shell 腳本實現(xiàn):
poor_script.sh:
!/bin/bash total_line=0 while read filename; doline=$(wc -l $filename | awk ‘{print $1}’)(( total_line += line )) done < <( find linux-4.5.4 -type f ( -iname ‘.c’ -o -iname ‘.h’ -o -iname ‘*.S’ ) ) echo “total line: $total_line”good_script.sh:?
!/bin/bash find linux-4.5.4 -type f ( -iname ‘.c’ -o -iname ‘.h’ -o -iname ‘*.S’ ) -print0 \ | wc -l —files0-from - | tail -n 1兩段代碼實現(xiàn)的目的是一樣的。 我們通過 strace 的 -c 選項來分別統(tǒng)計兩種版本的系統(tǒng)調(diào)用情況和其所花的時間(使用 -f 同時統(tǒng)計子進(jìn)程的情況)
從兩個輸出可以看出,good_script.sh 只需要 2 秒就可以得到結(jié)果:19613114 行。它大部分的調(diào)用(calls)開銷是文件操作(read/open/write/close)等,統(tǒng)計代碼行數(shù)本來就是干這些事情。
而 poor_script.sh 完成同樣的任務(wù)則花了 539 秒。它大部分的調(diào)用開銷都在進(jìn)程和內(nèi)存管理上(wait4/mmap/getpid…)。
實際上,從兩個圖中 clone 系統(tǒng)調(diào)用的次數(shù),我們可以看出 good_script.sh 只需要啟動 3 個進(jìn)程,而 poor_script.sh 完成整個任務(wù)居然啟動了 126335 個進(jìn)程!
而進(jìn)程創(chuàng)建和銷毀的代價是相當(dāng)高的,性能不差才怪。
五、總結(jié)
當(dāng)發(fā)現(xiàn)進(jìn)程或服務(wù)異常時,我們可以通過 strace 來跟蹤其系統(tǒng)調(diào)用,“看看它在干啥”,進(jìn)而找到異常的原因。熟悉常用系統(tǒng)調(diào)用,能夠更好地理解和使用strace。
當(dāng)然,萬能的 strace 也不是真正的萬能。當(dāng)目標(biāo)進(jìn)程卡死在用戶態(tài)時,strace 就沒有輸出了。
這個時候我們需要其他的跟蹤手段,比如 gdb / perf?/ SystemTap 等。
備注:
1、perf 原因 kernel 支持
2、ftrace ?kernel 支持可編程
3、systemtap 功能強大,RedHat?系統(tǒng)支持,對用戶態(tài),內(nèi)核態(tài)邏輯都能探查,使用范圍更廣。
轉(zhuǎn)載:strace命令詳解 - 馬昌偉 - 博客園
(SAW:Game Over!)
總結(jié)
以上是生活随笔為你收集整理的strace 命令详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux桌面环境介绍以及优缺点分析
- 下一篇: 数据脱敏的 6 种方案