前言
最近工作上遇到別的團隊使用自己的 library,噴出了 Bad file descriptor 的錯誤 。不過對方一直推卸問題,莫名其妙自己 library 裡面 open 的 fd 被關掉,導致 Bad file descriptor,但光憑在程式上的 log 來說明不是自己 lirary 的問題,對方不買帳,只好更進一步的 debug 找關鍵點。
因此也是此次要介紹的 strace,根本就是 Bad file descriptor 神器。
這篇會先透過 Zam 的經驗讓沒用過的人了解 strace 用途,再介紹 strace 一些實用的功能。
案例分享: strace 可以做到什麼?
為了可以清楚知道是誰使用關閉了自己的 file ,透過 strace 把要搜尋的 system call 給 filter 出來,當然最簡單的就是什麼都不給,只不過會印出爆炸多的資訊而已。但如果你不確定自己要看的資訊是什麼,建議你直接從不 filter 開始。
印出完全的資訊:
strace -fp 29263 -tt -o /data/ccm_rac_trace_bfd.txt
印出指定的 system call:
strace -fp 29263 -e trace=open,close,read,socket,ioctl -tt -o /data/ccm_rac_trace_bfd_filter.txt
從下面加深的字體可以看出,當在 multi-thread 的情況下,fd 又是 global resource,這時候就發生了互搶同一個 fd 然後關掉對方的故事。
這個清楚的顯示時間與其執行先後順序,也可以看出罪魁禍首的那個 process 很明確的莫名多執行了一次 close,最終導致別人的 Bad file descriptor。
29263: 受害的 process
31255: 罪魁禍首 pid | time | info
31255 10:43:45.870226 socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 29
31255 10:43:45.870600 ioctl(29, SIOCGIFHWADDR, {ifr_name="br0", ifr_hwaddr=00:0c:29:07:c8:b2}) = 0
29263 10:43:45.870834 open("/data/ng/ccm_cache/RAC/RAC//knowledge", O_RDONLY <unfinished ...>
31255 10:43:45.870883 close(29) = 0
29263 10:43:45.870977 <... open resumed> ) = 29
31255 10:43:45.871026 close(29) = 0
31255 10:43:45.871198 socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 29
31255 10:43:45.871314 ioctl(29, SIOCGIFHWADDR, {ifr_name="br0", ifr_hwaddr=00:0c:29:07:c8:b2}) = 0
31255 10:43:45.871534 close(29) = 0
31255 10:43:45.871708 close(29) = -1 EBADF (Bad file descriptor)
29263 10:43:45.873415 close(29) = -1 EBADF (Bad file descriptor)
介紹 strace
trace system calls and signals
最好用的 options
- -p : 指定要 trace 哪一個 process (pid)
- -o : 指定要寫入的檔案位置
- -f : 一併列出此 process 底下的 child process
- -tt : 印出含有 microsecond 的時間
- -e : 定義要追蹤的 system call 種類
-e [qualifier=][!]value1[,value2]…
qualifier: trace, abbrev, verbose, raw, signal, read, or write
預設是使用 trace,因此也可以不給
-e trace=open 等同於 -e open
! 用法
-e trace=!open 等同於除了 open 其他都顯示
用法分類
- -e trace=file 等同於 -e trace=open,stat,chmod,unlink,…
其他可用的分類
- -e trace=process
- -e trace=network
- -e trace=signal
- -e trace=ipc
- -e trace=desc
一般來說,可以直接針對需要的系統指令就好
- -e trace=open,close,read,socket,ioctl
其他
- -ff : 跟 -f 類似,但是如果你有給 -o 指定檔案,他會在你的檔名加上 pid 區分各個 process log
- -i :印出 system call 當下的 instruction pointer
- -T : 印出 system call 執行時間