使用 strace 了解程式讀取資料的來源
strace 會列出程式執行的 system call,效率很好且不需要 debug symbol。我比較常用它找出影響程式行為設定檔的位置。
基本概念是開檔、讀取、寫入最後都會用到 system call,system call 數量不多,知道要用什麼 system call 作什麼事,就可以用 strace 觀察特定的 system call 得知很多資訊。比方說開檔一定要用 open
,所以觀察 open
就能知道程式讀了那些檔案。
以下是用到參數的意思:
- -f: 一併列出 child process 執行的 system call,log 會帶有 pid。
- -e FUNCTION: 只列出 FUNCITON,可列多項。
- -s SIZE: 調整 log 的長度,需要看 read/write 參數內容時,需要加大。
- -o: 輸出到指定檔案。預設輸出到螢幕。
- -p PID: 追踪 PID。
例一: ps 從那裡取得 process 資訊?
$ strace -f -e open ps
...
open("/proc/31702/stat", O_RDONLY) = 6
open("/proc/31702/status", O_RDONLY) = 6
open("/proc/31703/stat", O_RDONLY) = 6
open("/proc/31703/status", O_RDONLY) = 6
+++ exited with 0 +++
得知 ps
從 /proc/ 讀取 process 的資訊。
例二: man page 原始檔的位置?
$ strace -f -e open man ps 2>&1 | grep /usr | grep ps
open("/usr/share/man/man1/ps.1.gz", O_RDONLY) = 4
open("/usr/share/man/man1/ps.1.gz", O_RDONLY) = 4
open("/usr/share/man/man1/ps.1.gz", O_RDONLY) = 4
[pid 23003] open("/usr/lib/groff/site-tmac/pspic.tmac", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 23003] open("/usr/share/groff/site-tmac/pspic.tmac", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 23003] open("/usr/share/groff/1.22.3/tmac/pspic.tmac", O_RDONLY) = 4
得知 man page 的檔案放在 /usr/share/man/ 下。
例三: bash script 是否會讀設定檔?
$ strace -f -e open bash -c "echo hello"
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libtinfo.so.5", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/dev/tty", O_RDWR|O_NONBLOCK) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
hello
+++ exited with 0 +++
確認執行 bash script 的時候,不會讀 ~/.bashrc 之類的 bash 設定檔。
例四: 查詢 domain name 的資料來源?
$ getent hosts ubuntu
127.0.1.1 ubuntu
$ host ubuntu
ubuntu has address 192.168.1.106
Host ubuntu not found: 3(NXDOMAIN)
Host ubuntu not found: 3(NXDOMAIN)
getent
和 host
回答的答案不同,這是為什麼呢?
$ strace -f -e open getent hosts ubuntu
...
open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 3
127.0.1.1 ubuntu
+++ exited with 0 +++$ grep ubuntu /etc/hosts
127.0.1.1 ubuntu
確認 getent hosts
從/etc/hosts 取得資料。再來看 host
:
$ strace -f -e open host ubuntu 2>&1 | grep "/etc"
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[pid 32168] open("/etc/resolv.conf", O_RDONLY) = 6$ cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 127.0.1.1
host
的執行結果較多,這裡用 grep
過濾輸出。可看出 host
沒有讀 /etc/hosts,而 /etc/resolv.conf 只有列出 nameserver 位置。
再來用 strace -f -s 1024 host ubuntu
列出全部輸出,觀察到以下有用的資訊:
在輸出 192.168.1.106 前,host 有用 UDP 送訊息到 127.0.1.1:53。
$ sudo netstat -lpn | grep "udp.*:53 "
udp 0 0 127.0.1.1:53 0.0.0.0:* 7627/dnsmasq
由 netstat
得知聽 port 53 的程式叫 dnsmasq,再來查查 dnsmasq 就知道這是安裝 ubuntu 後跑在本機的 dns server,可以用 dig 驗證 dnsmasq 運作是否正常:
$ dig ubuntu
...;; ADDITIONAL SECTION:
ubuntu. 1515259191 IN A 192.168.1.106...
;; SERVER: 127.0.1.1#53(127.0.1.1)
至此確認 host
從本機的 dnsmasq 取得 192.168.1.106。
結語
這些資訊大多能從文件或原始碼得知,不過用 strace 看又快又準。最後一個例子更加顯示 strace 強大便利之處。
延伸閱讀
《Strace — The Sysadmin’s Microscope》也有許多例子,有提到用 -t
、-tt
、-T
顯示時間,可以協助 profiling 效能瓶頸。