strace 哇,好多系统调用
我不敢在沒有認真考慮后果的情況下在生產環境中運行 strace(1),而是首先嘗試它的替代品。盡管廣為人知的是(并且不斷被重新發現)strace 是一個神奇的工具,但少得多的人知道的是,它目前是 — 且一直以來都是 — 很危險的。
strace 是 Linux 的系統調用追蹤器。它當前使用了神秘的 ptrace() (進程追蹤)調試接口,后者以一種很暴力的方式運作:每次系統調用都暫停目標進程以使得調試器可以讀取狀態。且執行兩次:系統調用開始時一次,結束時一次。
這意味著每次系統調用 strace 都暫停你的應用程序兩次,且每次都在應用程序和 strace 之間切換上下文。這就像在你的應用程序中放了一個流量計量燈。
BUGS:被追蹤的進程運行緩慢
– strace(1) man page
在某些情況下這不是問題。你的應用程序可能一開始就完全被破壞了,或者你可以在一個速度無所謂的測試環境中運行它。在生產中,你將想要真正地小心,并考慮后果(比如,這可能會導致目標因超出延時閾值而失敗么?)。最好是先找其它的替代品試試,比如 Linux 的 perf_events 。
開銷
strace 的性能開銷與它所檢測的系統調用的頻率相關。為了給它設定一個上界,這里是一個簡單的最差情況的測試:
$ 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 內執行完成。
現在通過 strace 來執行(這里我追蹤一個從未被調用到的系統調用,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 倍。這是最壞的情況,因為 dd(1) 正在盡可能快地執行系統調用。
方便的 strace 一行短命令
# 降低目標命令的速度并打印每個系統調用的詳細信息: strace command# 降低目標 PID 的速度并打印每個系統調用的詳細信息: strace -p PID# 降低目標 PID 及其任何新創建的子進程的速度,打印系統調用的詳細信息: strace -fp PID# 降低目標 PID 的速度,記錄系統調用,并打印一個總結: strace -cp PID# 降低目標 PID 的速度,并只打印 open() 系統調用: strace -eopen -p PID# 降低目標 PID 的速度,并只打印 open() 和 stat() 系統調用: strace -eopen,stat -p PID# 降低目標 PID 的速度,并只打印 connect() 和 accept() 系統調用: strace -econnect,accept -p PID# 降低目標命令的速度并查看它啟動了哪些其它程序(也降低它們的速度): strace -qfeexecve command# 降低目標 PID 的速度并以(扭曲的)微秒分辨率打印紀元時間: strace -ttt -p PID# 降低目標 PID 的速度并以(扭曲的)微秒分辨率打印系統調用的持續時間: strace -T -p PID如何學習 strace
關于解釋 strace 的輸出,你有許多可以學的。下面兩個步驟應該可以讓你開始關鍵的系統調用。
1. 學習關鍵的系統調用
你應該了解如下 12 個關鍵的系統調用做了什么,你會經常在 strace 的輸出中看到它們。測試你的知識!你知道這些系統調用中的多少呢?
| read | 從一個文件描述符(文件,socket)讀取字節 |
| write | 向一個文件描述符(文件,socket)寫入字節 |
| open | 打開一個文件(返回一個文件描述符) |
| close | 關閉一個文件描述符 |
| fork | 創建一個新進程(當前進程被分叉) |
| exec | 執行一個新程序 |
| connect | 連接到一個網絡主機 |
| accept | 接受一個網絡連接 |
| stat | 讀取文件統計信息 |
| ioctl | 設置 I/O 屬性,或其它雜項函數 |
| mmap | 將一個文件映射到進程內存地址空間 |
| brk | 擴展堆指針 |
每個系統調用都有一個手冊頁,因此,如果你在命令行中,應該只需要幾秒鐘就可以喚醒你的記憶。
這些系統調用有一些變體,因此你可能看到的 “exec” 的變體 “execve”,和 “read” 的變體 “pread”。這些應該也有手冊頁。
2. 簡單的 strace 例子
你可以通過一些基本的命令來練習和學習strace:ls(1),sleep(1),ssh(1),等等。
讓我們 strace 珍貴的 ls -l 作為我們的第一個例子。我們將列出 /etc 的內容,以便于有大量的文件需要檢測,輸出基本上是你所期望的(列出文件)而不是程序啟動和初始化。
輸出從顯示程序初始化開始:
$ 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() 變體用于運行 /bin/ls,然后是動態鏈接庫被拉進來。有多組以 openat() 一個 /lib* 位置開始,以 close() 結束的輸出。
以讀取一行為例:看一眼 openat() 行,還有 openat(2) 的手冊頁。手冊頁總結了這個系統調用:
#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);它還詳細地解釋了這些參數,和返回值。因此,我們的 “pathname” /etc/ld.so.cache,而 flags 是 O_RDONLY|O_CLOEXEC。它返回 3,一個文件描述符以備后面用于其它系統調用。
在稍后的輸出中,真正的操作開始了:
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 并讀取目錄項(getdents()),然后針對每一個文件調用 lstat(),一個 stat() 變體,即兩個擴展屬性變體:lgetxattr() 和 getxattr()。getxattr() 被調了兩次,每一個命名空間一次:看它的第二個參數。
收集了所有這些文件信息之后,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 [...]輸出有點混亂,這是由于 ls -l 和 strace 都寫入相同的終端。ls 通過幾次 write() 調用輸出所有的文件信息。
性能調優 ls(1)
線上的快速搜索(我接下來會去找 ls(1) 的根源)解釋了這里看到的問題:我的 TZ 環境變量沒有設置。設置它以消除那些針對 /etc/localtime 的 stat() 調用。
通過這個簡單的例子,我發現了一個性能上的優勢。我當然是這個意思了。
引導 ls(1) 運行足夠長的時間以進行性能測量:
$ time ls -l `perl -e 'print "/etc " x 1000'` >/dev/nullreal 0m3.562s user 0m1.004s sys 0m2.464s修復之后的結果:
$ 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)。還不壞。(對于發現并消除 stat() 我沒感到很興奮,盡管,因為它們是快速的系統調用,且獲得的回報通常不會這么高。)
這是一個很好的 strace 善于識別的性能問題的類型的例子:負載的問題。盡管我們是通過圍繞 strace 釣魚發現它的,這也能通過如下的性能方法論發現:負載特征分析,針對系統調用的。
由于它會嚴重扭曲時間測量值(盡管它沒有 “-O overhead” 選項,并啟發式地,去試圖補償),strace 不擅長決定包括延遲在內的性能問題。且在系統調用接口之外它也很少被用到,比如性能問題位于更深入的內核或應用程序中。
2018 年的 strace 版本中這個問題也早已經被修復了。
與先進跟蹤器對比
對于當前版本的 strace(如,版本 4.8)和當前版本的 perf_events/ktap/SystemTap/LTTng/dtrace4linux (根據 2014 年)相比。
優勢:
- strace 很簡單。只針對系統調用。是 POSIX 命令行接口。
- 針對每個系統調用類型自動的有意義的輸出。不需要自己編寫代碼。
- 廣泛可用且成熟。
缺點:
- 警告:可能導致可見的且有時是 巨大的 性能開銷,在最壞的情況下,可能使目標應用程序慢超過 100 倍。這不僅使它不適合用于生產環境,而且任何時序信息可能也會被歪曲而使人誤解。
- 無法并發地追蹤多個進程(除了跟隨的子進程外)。
- 可見性僅限于系統調用接口。
有一個可能的缺點:在過去,strace 有 bug,可能會留目標進程,或它跟隨的子進程,處于 STOP 狀態(比如,這里,這里)。這可能會導致嚴重的生產故障,由于應用程序現在在運行中被凍結了。如果你立即意識到并能夠修復它(殺掉 strace,然后 kill -CONT 進程),則你可以避免一次嚴重的故障。然而,你可能依然會導致一次突發的應用程序請求有好幾秒(異常值)的延時的情況,這依賴于你鍵入 kill 命令的速度有多快。
如果你想寫一篇關于 strace 有多棒并展示它的用法的例子的博客,那很棒,但請復制上面警告的要點。
你知道么?
- strace 在法國是被禁止的,在那里它被歸類為破壞工具(它可以追蹤純文本 I/O)。
- strace 設計為運行在系統調用層,也被稱為它的 “測試深度”。
- 在最新的開發版本(12.1)中,strace 有一個復活節彩色蛋:通過 “-VV” 它開始追蹤并打印 “DIVE! DIVE!”,然后在 /dev/audio 上播放一段 aah-WOO-gah 的警告。2018 年的版本中這個復活節彩蛋已經不存在了,這個選項用于打印 strace 的版本號。
- 第一版的 strace 還展示在圣何塞的計算機歷史博物館。它是1961年制造的,使用的是線包電路和磁芯存儲器,每秒可以跟蹤多達 12 個系統調用。在它的金屬板外殼內有四個座位:給外科醫生,副駕駛和他們的兩名秘書。它可以在系統調用深度停留長達9小時。
- 1963 年,數字電子公司發布了一款更便宜的雙座版 strace,這款產品大受歡迎。
- 最大的 strace 是蘇聯的 “戰術呼叫” 級,它可以在系統調用深度停留超過 120 天,最多有 160 名船員。是核動力的,有 4 個前方和 2 個尾部信號管,每個典型地裝備 9 型信號管。
- 在舊金山灣的底部有幾千條未使用的 strace,這些 strace 是為了解決千年蟲問題而設計的,但從來沒有出現過,所以被廢棄了。
- 罕見的總統 “strace one” 是一種高級模型,據傳在系統調用深度以下運行;然而,它的規格是分類的。
- 在 2013 年最嚴重的一次 Twitter宕機事件中,工程師們被迫在崩潰深度(也被稱為“崩潰深度”)以下進行測試。它們在這種深度中幸存下來并找到了根本原因,但隨后卻被失敗的鯨魚吃掉了。
更多關于 strace
由于下面的文章似乎缺少對于開銷的警告,我在鏈接上添加了一個確認框:
- 用 strace 調試晦澀的 Postgres 問題?(2013)
- 使用 strace 調試卡住的 Celery 任務?(2013)
- strace:為了運行、利益,和調試?(2008)
- strace 簡介 – 一個系統調用追蹤和信號報告工具?(2008)
- 使用 Strace 的5個簡單的故障排除方法?(2008)
- Strace 的魔法?(2014)
- 用 strace 診斷 Magento 速度問題?(2012)
- Strace – 系統管理員的顯微鏡?(2010)
這些文章可能很有用,因為它們展示了真實問題的案例研究,并基于每個作者關心的領域和所擅長的應用 strace 的知識提供了不同的視角。
如果你想學習更多關于 strace 內部和 ptrace() 的內容,我建議:
- 70 行代碼編寫你自己的 Strace
這里列的一些文章,有許多鏈接已失效。
strace 之外
有許多關于 strace(1) 的文章和通訊,和 tcpdump(8) 及 top(1) 一樣,但關于更難的 Linux 觀測工具和框架的就少多了,比如 ltrace (庫追蹤),perf_events,tracepoints,kprobes,uprobes,等等。我在我的 SCaLE12x keynote 中調侃了這一點,展示了一副 簡化的 僅由 top,strace,和 tcpdump 組成的 Linux 內部圖!
strace 能見度有限:(可測量版本的)系統調用接口。我右邊的 Linux 性能觀察工具圖顯示了 strace 與其他工具的范圍。現在,可以從系統調用請求推斷出內核活動的很多信息,但是有很多領域你無法直接看到。瀏覽該圖表,查看可以用于超越 strace 的其它工具。
我常常跳過 strace,而直接使用這些其它工具,比如 perf(perf_events)或者其它正在開發中的 Linux 動態跟蹤器。這些給了我更深的能見度,更低的開銷,及可定制的輸出。盡管,有時候最快的方法只是啟動 strace,如果開銷是可以接受的,我也會這么做。
strace 的未來
strace(1) 是一個偉大的工具,但當前的版本(使用 ptrace())可能嚴重地降低目標的速度。注意 strace 引起的開銷,并考慮 Linux 的其它使用了開銷更低的緩沖追蹤機制的替代品。這些包括 perf_events,sysdig,ktap,和SystemTap。這些中的許多也允許定制內核摘要,以進一步減少開銷。
Linux 3.7 引入了 perf trace,一個緩沖的類 strace 的 perf_events 子命令,它作為一個 “strace 替代品” 而被引入。還有 sysdig,它允許定制過濾器和輸出。未來很有可能 strace 將變成這些工具的包裝器,而我所有的關于開銷的警告也將過時(不過,這并不意味著可以忽略開銷!)。
【原文】strace Wow Much Syscall
總結
以上是生活随笔為你收集整理的strace 哇,好多系统调用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GStreamer 的调试工具
- 下一篇: Windows 平台编译 WebRTC