strace 哇,好多系统调用
我不敢在沒有認(rèn)真考慮后果的情況下在生產(chǎn)環(huán)境中運(yùn)行 strace(1),而是首先嘗試它的替代品。盡管廣為人知的是(并且不斷被重新發(fā)現(xiàn))strace 是一個(gè)神奇的工具,但少得多的人知道的是,它目前是 — 且一直以來都是 — 很危險(xiǎn)的。
strace 是 Linux 的系統(tǒng)調(diào)用追蹤器。它當(dāng)前使用了神秘的 ptrace() (進(jìn)程追蹤)調(diào)試接口,后者以一種很暴力的方式運(yùn)作:每次系統(tǒng)調(diào)用都暫停目標(biāo)進(jìn)程以使得調(diào)試器可以讀取狀態(tài)。且執(zhí)行兩次:系統(tǒng)調(diào)用開始時(shí)一次,結(jié)束時(shí)一次。
這意味著每次系統(tǒng)調(diào)用 strace 都暫停你的應(yīng)用程序兩次,且每次都在應(yīng)用程序和 strace 之間切換上下文。這就像在你的應(yīng)用程序中放了一個(gè)流量計(jì)量燈。
BUGS:被追蹤的進(jìn)程運(yùn)行緩慢
– strace(1) man page
在某些情況下這不是問題。你的應(yīng)用程序可能一開始就完全被破壞了,或者你可以在一個(gè)速度無所謂的測(cè)試環(huán)境中運(yùn)行它。在生產(chǎn)中,你將想要真正地小心,并考慮后果(比如,這可能會(huì)導(dǎo)致目標(biāo)因超出延時(shí)閾值而失敗么?)。最好是先找其它的替代品試試,比如 Linux 的 perf_events 。
開銷
strace 的性能開銷與它所檢測(cè)的系統(tǒng)調(diào)用的頻率相關(guān)。為了給它設(shè)定一個(gè)上界,這里是一個(gè)簡(jiǎn)單的最差情況的測(cè)試:
$ dd if=/dev/zero of=/dev/null bs=1 count=500k 記錄了512000+0 的讀入 記錄了512000+0 的寫出 512000 bytes (512 kB, 500 KiB) copied, 0.429502 s, 1.2 MB/s這在 0.429502 s 內(nèi)執(zhí)行完成。
現(xiàn)在通過 strace 來執(zhí)行(這里我追蹤一個(gè)從未被調(diào)用到的系統(tǒng)調(diào)用,accept(),但我們依然要為此付出開銷):
$ strace -eaccept dd if=/dev/zero of=/dev/null bs=1 count=500k 記錄了512000+0 的讀入 記錄了512000+0 的寫出 512000 bytes (512 kB, 500 KiB) copied, 82.7162 s, 6.2 kB/s +++ exited with 0 +++這次慢了 193 倍。這是最壞的情況,因?yàn)?dd(1) 正在盡可能快地執(zhí)行系統(tǒng)調(diào)用。
方便的 strace 一行短命令
# 降低目標(biāo)命令的速度并打印每個(gè)系統(tǒng)調(diào)用的詳細(xì)信息: strace command# 降低目標(biāo) PID 的速度并打印每個(gè)系統(tǒng)調(diào)用的詳細(xì)信息: strace -p PID# 降低目標(biāo) PID 及其任何新創(chuàng)建的子進(jìn)程的速度,打印系統(tǒng)調(diào)用的詳細(xì)信息: strace -fp PID# 降低目標(biāo) PID 的速度,記錄系統(tǒng)調(diào)用,并打印一個(gè)總結(jié): strace -cp PID# 降低目標(biāo) PID 的速度,并只打印 open() 系統(tǒng)調(diào)用: strace -eopen -p PID# 降低目標(biāo) PID 的速度,并只打印 open() 和 stat() 系統(tǒng)調(diào)用: strace -eopen,stat -p PID# 降低目標(biāo) PID 的速度,并只打印 connect() 和 accept() 系統(tǒng)調(diào)用: strace -econnect,accept -p PID# 降低目標(biāo)命令的速度并查看它啟動(dòng)了哪些其它程序(也降低它們的速度): strace -qfeexecve command# 降低目標(biāo) PID 的速度并以(扭曲的)微秒分辨率打印紀(jì)元時(shí)間: strace -ttt -p PID# 降低目標(biāo) PID 的速度并以(扭曲的)微秒分辨率打印系統(tǒng)調(diào)用的持續(xù)時(shí)間: strace -T -p PID如何學(xué)習(xí) strace
關(guān)于解釋 strace 的輸出,你有許多可以學(xué)的。下面兩個(gè)步驟應(yīng)該可以讓你開始關(guān)鍵的系統(tǒng)調(diào)用。
1. 學(xué)習(xí)關(guān)鍵的系統(tǒng)調(diào)用
你應(yīng)該了解如下 12 個(gè)關(guān)鍵的系統(tǒng)調(diào)用做了什么,你會(huì)經(jīng)常在 strace 的輸出中看到它們。測(cè)試你的知識(shí)!你知道這些系統(tǒng)調(diào)用中的多少呢?
| read | 從一個(gè)文件描述符(文件,socket)讀取字節(jié) |
| write | 向一個(gè)文件描述符(文件,socket)寫入字節(jié) |
| open | 打開一個(gè)文件(返回一個(gè)文件描述符) |
| close | 關(guān)閉一個(gè)文件描述符 |
| fork | 創(chuàng)建一個(gè)新進(jìn)程(當(dāng)前進(jìn)程被分叉) |
| exec | 執(zhí)行一個(gè)新程序 |
| connect | 連接到一個(gè)網(wǎng)絡(luò)主機(jī) |
| accept | 接受一個(gè)網(wǎng)絡(luò)連接 |
| stat | 讀取文件統(tǒng)計(jì)信息 |
| ioctl | 設(shè)置 I/O 屬性,或其它雜項(xiàng)函數(shù) |
| mmap | 將一個(gè)文件映射到進(jìn)程內(nèi)存地址空間 |
| brk | 擴(kuò)展堆指針 |
每個(gè)系統(tǒng)調(diào)用都有一個(gè)手冊(cè)頁,因此,如果你在命令行中,應(yīng)該只需要幾秒鐘就可以喚醒你的記憶。
這些系統(tǒng)調(diào)用有一些變體,因此你可能看到的 “exec” 的變體 “execve”,和 “read” 的變體 “pread”。這些應(yīng)該也有手冊(cè)頁。
2. 簡(jiǎn)單的 strace 例子
你可以通過一些基本的命令來練習(xí)和學(xué)習(xí)strace:ls(1),sleep(1),ssh(1),等等。
讓我們 strace 珍貴的 ls -l 作為我們的第一個(gè)例子。我們將列出 /etc 的內(nèi)容,以便于有大量的文件需要檢測(cè),輸出基本上是你所期望的(列出文件)而不是程序啟動(dòng)和初始化。
輸出從顯示程序初始化開始:
$ strace ls -l /etc/ execve("/bin/ls", ["ls", "/etc"], 0x7ffe22ee51b8 /* 59 vars */) = 0 brk(NULL) = 0x559965d4e000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "tls/haswell/x86_64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "tls/haswell/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "tls/x86_64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "tls/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "haswell/x86_64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "haswell/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "x86_64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "libselinux.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=110853, ...}) = 0 mmap(NULL, 110853, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdf9a94d000 close(3) = 0execve() 變體用于運(yùn)行 /bin/ls,然后是動(dòng)態(tài)鏈接庫(kù)被拉進(jìn)來。有多組以 openat() 一個(gè) /lib* 位置開始,以 close() 結(jié)束的輸出。
以讀取一行為例:看一眼 openat() 行,還有 openat(2) 的手冊(cè)頁。手冊(cè)頁總結(jié)了這個(gè)系統(tǒng)調(diào)用:
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);int creat(const char *pathname, mode_t mode);int openat(int dirfd, const char *pathname, int flags);int openat(int dirfd, const char *pathname, int flags, mode_t mode);它還詳細(xì)地解釋了這些參數(shù),和返回值。因此,我們的 “pathname” /etc/ld.so.cache,而 flags 是 O_RDONLY|O_CLOEXEC。它返回 3,一個(gè)文件描述符以備后面用于其它系統(tǒng)調(diào)用。
在稍后的輸出中,真正的操作開始了:
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000b\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=144976, ...}) = 0 mmap(NULL, 2221184, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f6de9063000 mprotect(0x7f6de907d000, 2093056, PROT_NONE) = 0 mmap(0x7f6de927c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19000) = 0x7f6de927c000 mmap(0x7f6de927e000, 13440, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f6de927e000 close(3) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6de9f18000 arch_prctl(ARCH_SET_FS, 0x7f6de9f19040) = 0 mprotect(0x7f6de9adf000, 16384, PROT_READ) = 0 mprotect(0x7f6de927c000, 4096, PROT_READ) = 0 mprotect(0x7f6de9484000, 4096, PROT_READ) = 0 mprotect(0x7f6de96f6000, 4096, PROT_READ) = 0 mprotect(0x7f6de9d0d000, 4096, PROT_READ) = 0 mprotect(0x561ff9332000, 8192, PROT_READ) = 0 mprotect(0x7f6de9f38000, 4096, PROT_READ) = 0 munmap(0x7f6de9f1c000, 110853) = 0 set_tid_address(0x7f6de9f19310) = 4539 set_robust_list(0x7f6de9f19320, 24) = 0 rt_sigaction(SIGRTMIN, {sa_handler=0x7f6de9068cb0, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f6de90758a0}, NULL, 8) = 0 rt_sigaction(SIGRT_1, {sa_handler=0x7f6de9068d50, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f6de90758a0}, NULL, 8) = 0 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0 prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 statfs("/sys/fs/selinux", 0x7ffcc72c6280) = -1 ENOENT (No such file or directory) statfs("/selinux", 0x7ffcc72c6280) = -1 ENOENT (No such file or directory) brk(NULL) = 0x561ffac34000 brk(0x561ffac55000) = 0x561ffac55000 openat(AT_FDCWD, "/proc/filesystems", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 read(3, "nodev\tsysfs\nnodev\ttmpfs\nnodev\tbd"..., 1024) = 395 read(3, "", 1024) = 0 close(3) = 0 access("/etc/selinux/config", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=4799968, ...}) = 0 mmap(NULL, 4799968, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f6de8bcf000 close(3) = 0 ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0 ioctl(1, TIOCGWINSZ, {ws_row=42, ws_col=161, ws_xpixel=0, ws_ypixel=0}) = 0 openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=2995, ...}) = 0 read(3, "# Locale name alias data base.\n#"..., 4096) = 2995 read(3, "", 4096) = 0 close(3) = 0 openat(AT_FDCWD, "/usr/share/locale/zh_CN/LC_TIME/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/share/locale/zh/LC_TIME/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=26376, ...}) = 0 mmap(NULL, 26376, PROT_READ, MAP_SHARED, 3, 0) = 0x7f6de9f31000 close(3) = 0 futex(0x7f6de9ae4a08, FUTEX_WAKE_PRIVATE, 2147483647) = 0 lstat("/etc/", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0 lgetxattr("/etc/", "security.selinux", 0x561ffac35ec0, 255) = -1 ENODATA (No data available) getxattr("/etc/", "system.posix_acl_access", NULL, 0) = -1 ENODATA (No data available) getxattr("/etc/", "system.posix_acl_default", NULL, 0) = -1 ENODATA (No data available) socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=556, ...}) = 0 read(3, "# /etc/nsswitch.conf\n#\n# Example"..., 4096) = 556 read(3, "", 4096) = 0 close(3) = 0 openat(AT_FDCWD, "tls/haswell/x86_64/libnss_compat.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) mprotect(0x7f57d149e000, 4096, PROT_READ) = 0 mprotect(0x7f57d16bc000, 4096, PROT_READ) = 0 mprotect(0x7f57d18ca000, 4096, PROT_READ) = 0 munmap(0x7f57d2e03000, 110853) = 0 openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3 lseek(3, 0, SEEK_CUR) = 0 fstat(3, {st_mode=S_IFREG|0644, st_size=2525, ...}) = 0 mmap(NULL, 2525, PROT_READ, MAP_SHARED, 3, 0) = 0x7f57d2e37000 lseek(3, 2525, SEEK_SET) = 2525 munmap(0x7f57d2e37000, 2525) = 0 close(3) = 0 socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3) = 0 openat(AT_FDCWD, "/etc/group", O_RDONLY|O_CLOEXEC) = 3 lseek(3, 0, SEEK_CUR) = 0 fstat(3, {st_mode=S_IFREG|0644, st_size=940, ...}) = 0 mmap(NULL, 940, PROT_READ, MAP_SHARED, 3, 0) = 0x7f57d2e37000 lseek(3, 940, SEEK_SET) = 940 munmap(0x7f57d2e37000, 940) = 0 close(3) = 0 openat(AT_FDCWD, "/etc/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3 fstat(3, {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0 getdents(3, /* 244 entries */, 32768) = 7904 [...]這里打開 /etc 并讀取目錄項(xiàng)(getdents()),然后針對(duì)每一個(gè)文件調(diào)用 lstat(),一個(gè) stat() 變體,即兩個(gè)擴(kuò)展屬性變體:lgetxattr() 和 getxattr()。getxattr() 被調(diào)了兩次,每一個(gè)命名空間一次:看它的第二個(gè)參數(shù)。
收集了所有這些文件信息之后,ls(1) 開始打印輸出,有序的:
openat(AT_FDCWD, "/etc/localtime", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=582, ...}) = 0 fstat(3, {st_mode=S_IFREG|0644, st_size=582, ...}) = 0 read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\3\0\0\0\0"..., 4096) = 582 lseek(3, -357, SEEK_CUR) = 225 read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\3\0\0\0\0"..., 4096) = 357 close(3) = 0 write(1, "\346\200\273\347\224\250\351\207\217 1432\ndrwxr-xr-x 3 roo"..., 4096總用量 1432 drwxr-xr-x 3 root root 4096 2月 4 2020 acpi -rw-r--r-- 1 root root 3028 2月 4 2020 adduser.conf drwxr-xr-x 2 root root 12288 11月 8 18:24 alternatives -rw-r--r-- 1 root root 401 5月 30 2017 anacrontab drwxr-xr-x 3 root root 4096 6月 28 2020 apache2 -rw-r--r-- 1 root root 433 10月 2 2017 apg.conf drwxr-xr-x 6 root root 4096 2月 4 2020 apm drwxr-xr-x 3 root root 4096 2月 4 2020 apparmor drwxr-xr-x 8 root root 4096 11月 5 06:38 apparmor.d drwxr-xr-x 4 root root 4096 10月 29 11:23 apport -rw-r--r-- 1 root root 769 4月 4 2018 appstream.conf [...]輸出有點(diǎn)混亂,這是由于 ls -l 和 strace 都寫入相同的終端。ls 通過幾次 write() 調(diào)用輸出所有的文件信息。
性能調(diào)優(yōu) ls(1)
線上的快速搜索(我接下來會(huì)去找 ls(1) 的根源)解釋了這里看到的問題:我的 TZ 環(huán)境變量沒有設(shè)置。設(shè)置它以消除那些針對(duì) /etc/localtime 的 stat() 調(diào)用。
通過這個(gè)簡(jiǎn)單的例子,我發(fā)現(xiàn)了一個(gè)性能上的優(yōu)勢(shì)。我當(dāng)然是這個(gè)意思了。
引導(dǎo) ls(1) 運(yùn)行足夠長(zhǎng)的時(shí)間以進(jìn)行性能測(cè)量:
$ time ls -l `perl -e 'print "/etc " x 1000'` >/dev/nullreal 0m3.562s user 0m1.004s sys 0m2.464s修復(fù)之后的結(jié)果:
$ time TZ=US/Pacific ls -l `perl -e 'print "/etc " x 1000'` >/dev/nullreal 0m2.753s user 0m0.804s sys 0m1.868s我只提升了性能 23% (2.753s 相比于 3.562s)。還不壞。(對(duì)于發(fā)現(xiàn)并消除 stat() 我沒感到很興奮,盡管,因?yàn)樗鼈兪强焖俚南到y(tǒng)調(diào)用,且獲得的回報(bào)通常不會(huì)這么高。)
這是一個(gè)很好的 strace 善于識(shí)別的性能問題的類型的例子:負(fù)載的問題。盡管我們是通過圍繞 strace 釣魚發(fā)現(xiàn)它的,這也能通過如下的性能方法論發(fā)現(xiàn):負(fù)載特征分析,針對(duì)系統(tǒng)調(diào)用的。
由于它會(huì)嚴(yán)重扭曲時(shí)間測(cè)量值(盡管它沒有 “-O overhead” 選項(xiàng),并啟發(fā)式地,去試圖補(bǔ)償),strace 不擅長(zhǎng)決定包括延遲在內(nèi)的性能問題。且在系統(tǒng)調(diào)用接口之外它也很少被用到,比如性能問題位于更深入的內(nèi)核或應(yīng)用程序中。
2018 年的 strace 版本中這個(gè)問題也早已經(jīng)被修復(fù)了。
與先進(jìn)跟蹤器對(duì)比
對(duì)于當(dāng)前版本的 strace(如,版本 4.8)和當(dāng)前版本的 perf_events/ktap/SystemTap/LTTng/dtrace4linux (根據(jù) 2014 年)相比。
優(yōu)勢(shì):
- strace 很簡(jiǎn)單。只針對(duì)系統(tǒng)調(diào)用。是 POSIX 命令行接口。
- 針對(duì)每個(gè)系統(tǒng)調(diào)用類型自動(dòng)的有意義的輸出。不需要自己編寫代碼。
- 廣泛可用且成熟。
缺點(diǎn):
- 警告:可能導(dǎo)致可見的且有時(shí)是 巨大的 性能開銷,在最壞的情況下,可能使目標(biāo)應(yīng)用程序慢超過 100 倍。這不僅使它不適合用于生產(chǎn)環(huán)境,而且任何時(shí)序信息可能也會(huì)被歪曲而使人誤解。
- 無法并發(fā)地追蹤多個(gè)進(jìn)程(除了跟隨的子進(jìn)程外)。
- 可見性僅限于系統(tǒng)調(diào)用接口。
有一個(gè)可能的缺點(diǎn):在過去,strace 有 bug,可能會(huì)留目標(biāo)進(jìn)程,或它跟隨的子進(jìn)程,處于 STOP 狀態(tài)(比如,這里,這里)。這可能會(huì)導(dǎo)致嚴(yán)重的生產(chǎn)故障,由于應(yīng)用程序現(xiàn)在在運(yùn)行中被凍結(jié)了。如果你立即意識(shí)到并能夠修復(fù)它(殺掉 strace,然后 kill -CONT 進(jìn)程),則你可以避免一次嚴(yán)重的故障。然而,你可能依然會(huì)導(dǎo)致一次突發(fā)的應(yīng)用程序請(qǐng)求有好幾秒(異常值)的延時(shí)的情況,這依賴于你鍵入 kill 命令的速度有多快。
如果你想寫一篇關(guān)于 strace 有多棒并展示它的用法的例子的博客,那很棒,但請(qǐng)復(fù)制上面警告的要點(diǎn)。
你知道么?
- strace 在法國(guó)是被禁止的,在那里它被歸類為破壞工具(它可以追蹤純文本 I/O)。
- strace 設(shè)計(jì)為運(yùn)行在系統(tǒng)調(diào)用層,也被稱為它的 “測(cè)試深度”。
- 在最新的開發(fā)版本(12.1)中,strace 有一個(gè)復(fù)活節(jié)彩色蛋:通過 “-VV” 它開始追蹤并打印 “DIVE! DIVE!”,然后在 /dev/audio 上播放一段 aah-WOO-gah 的警告。2018 年的版本中這個(gè)復(fù)活節(jié)彩蛋已經(jīng)不存在了,這個(gè)選項(xiàng)用于打印 strace 的版本號(hào)。
- 第一版的 strace 還展示在圣何塞的計(jì)算機(jī)歷史博物館。它是1961年制造的,使用的是線包電路和磁芯存儲(chǔ)器,每秒可以跟蹤多達(dá) 12 個(gè)系統(tǒng)調(diào)用。在它的金屬板外殼內(nèi)有四個(gè)座位:給外科醫(yī)生,副駕駛和他們的兩名秘書。它可以在系統(tǒng)調(diào)用深度停留長(zhǎng)達(dá)9小時(shí)。
- 1963 年,數(shù)字電子公司發(fā)布了一款更便宜的雙座版 strace,這款產(chǎn)品大受歡迎。
- 最大的 strace 是蘇聯(lián)的 “戰(zhàn)術(shù)呼叫” 級(jí),它可以在系統(tǒng)調(diào)用深度停留超過 120 天,最多有 160 名船員。是核動(dòng)力的,有 4 個(gè)前方和 2 個(gè)尾部信號(hào)管,每個(gè)典型地裝備 9 型信號(hào)管。
- 在舊金山灣的底部有幾千條未使用的 strace,這些 strace 是為了解決千年蟲問題而設(shè)計(jì)的,但從來沒有出現(xiàn)過,所以被廢棄了。
- 罕見的總統(tǒng) “strace one” 是一種高級(jí)模型,據(jù)傳在系統(tǒng)調(diào)用深度以下運(yùn)行;然而,它的規(guī)格是分類的。
- 在 2013 年最嚴(yán)重的一次 Twitter宕機(jī)事件中,工程師們被迫在崩潰深度(也被稱為“崩潰深度”)以下進(jìn)行測(cè)試。它們?cè)谶@種深度中幸存下來并找到了根本原因,但隨后卻被失敗的鯨魚吃掉了。
更多關(guān)于 strace
由于下面的文章似乎缺少對(duì)于開銷的警告,我在鏈接上添加了一個(gè)確認(rèn)框:
- 用 strace 調(diào)試晦澀的 Postgres 問題?(2013)
- 使用 strace 調(diào)試卡住的 Celery 任務(wù)?(2013)
- strace:為了運(yùn)行、利益,和調(diào)試?(2008)
- strace 簡(jiǎn)介 – 一個(gè)系統(tǒng)調(diào)用追蹤和信號(hào)報(bào)告工具?(2008)
- 使用 Strace 的5個(gè)簡(jiǎn)單的故障排除方法?(2008)
- Strace 的魔法?(2014)
- 用 strace 診斷 Magento 速度問題?(2012)
- Strace – 系統(tǒng)管理員的顯微鏡?(2010)
這些文章可能很有用,因?yàn)樗鼈冋故玖苏鎸?shí)問題的案例研究,并基于每個(gè)作者關(guān)心的領(lǐng)域和所擅長(zhǎng)的應(yīng)用 strace 的知識(shí)提供了不同的視角。
如果你想學(xué)習(xí)更多關(guān)于 strace 內(nèi)部和 ptrace() 的內(nèi)容,我建議:
- 70 行代碼編寫你自己的 Strace
這里列的一些文章,有許多鏈接已失效。
strace 之外
有許多關(guān)于 strace(1) 的文章和通訊,和 tcpdump(8) 及 top(1) 一樣,但關(guān)于更難的 Linux 觀測(cè)工具和框架的就少多了,比如 ltrace (庫(kù)追蹤),perf_events,tracepoints,kprobes,uprobes,等等。我在我的 SCaLE12x keynote 中調(diào)侃了這一點(diǎn),展示了一副 簡(jiǎn)化的 僅由 top,strace,和 tcpdump 組成的 Linux 內(nèi)部圖!
strace 能見度有限:(可測(cè)量版本的)系統(tǒng)調(diào)用接口。我右邊的 Linux 性能觀察工具圖顯示了 strace 與其他工具的范圍。現(xiàn)在,可以從系統(tǒng)調(diào)用請(qǐng)求推斷出內(nèi)核活動(dòng)的很多信息,但是有很多領(lǐng)域你無法直接看到。瀏覽該圖表,查看可以用于超越 strace 的其它工具。
我常常跳過 strace,而直接使用這些其它工具,比如 perf(perf_events)或者其它正在開發(fā)中的 Linux 動(dòng)態(tài)跟蹤器。這些給了我更深的能見度,更低的開銷,及可定制的輸出。盡管,有時(shí)候最快的方法只是啟動(dòng) strace,如果開銷是可以接受的,我也會(huì)這么做。
strace 的未來
strace(1) 是一個(gè)偉大的工具,但當(dāng)前的版本(使用 ptrace())可能嚴(yán)重地降低目標(biāo)的速度。注意 strace 引起的開銷,并考慮 Linux 的其它使用了開銷更低的緩沖追蹤機(jī)制的替代品。這些包括 perf_events,sysdig,ktap,和SystemTap。這些中的許多也允許定制內(nèi)核摘要,以進(jìn)一步減少開銷。
Linux 3.7 引入了 perf trace,一個(gè)緩沖的類 strace 的 perf_events 子命令,它作為一個(gè) “strace 替代品” 而被引入。還有 sysdig,它允許定制過濾器和輸出。未來很有可能 strace 將變成這些工具的包裝器,而我所有的關(guān)于開銷的警告也將過時(shí)(不過,這并不意味著可以忽略開銷!)。
【原文】strace Wow Much Syscall
總結(jié)
以上是生活随笔為你收集整理的strace 哇,好多系统调用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GStreamer 的调试工具
- 下一篇: 用 70 行代码给你自己写一个 stra