使用 strace 了解程式讀取資料的來源

fcamel
fcamel的程式開發心得
6 min readJan 6, 2018

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)

getenthost 回答的答案不同,這是為什麼呢?

$ 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 效能瓶頸。

--

--