如何使用 Linux Strace 命令跟踪程序執行

strace 是一個強大的命令行工具,用於 Linux 中的進程監控、診斷和故障排除程序。 一般用於攔截和記錄進程接收到的系統調用和信號。 您可以使用 strace 分析程序如何與系統交互以調試任何程序。

如果程序不斷崩潰,或沒有按預期運行,此工具將非常有用。 它提供了對系統如何運行的深入洞察。 任何用戶都可以跟踪自己正在運行的進程。

在本教程中,我們將向您展示如何在 Linux 上使用 strace 命令行工具。

安裝 Strace

默認情況下,strace 在所有 Linux 操作系統的默認存儲庫中可用。

在 Debian 和 Ubuntu 操作系統上,使用以下命令安裝 strace:

apt-get install strace -y

在 RHEL 和 CentOS 操作系統上,使用以下命令安裝 strace:

dnf install strace -y

安裝 strace 後,您可以使用以下命令驗證 strace 版本:

strace -V

你應該得到以下輸出:

strace -- version 4.8

您可以使用以下命令打印 strace 命令可用的所有選項:

strace -help

Output
usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...
[-a column] [-o file] [-s strsize] [-P path]...
-p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]
or: strace -c[df] [-I n] [-e expr]... [-O overhead] [-S sortby]
-p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]
-c -- count time, calls, and errors for each syscall and report summary
-C -- like -c but also print regular output
-d -- enable debug output to stderr
-D -- run tracer process as a detached grandchild, not as parent
-f -- follow forks, -ff -- with output into separate files
-i -- print instruction pointer at time of syscall
-q -- suppress messages about attaching, detaching, etc.
-r -- print relative timestamp, -t -- absolute timestamp, -tt -- with usecs
-T -- print time spent in each syscall
-v -- verbose mode: print unabbreviated argv, stat, termios, etc. args
-x -- print non-ascii strings in hex, -xx -- print all strings in hex
-y -- print paths associated with file descriptor arguments
-h -- print help message, -V -- print version
-a column -- alignment COLUMN for printing syscall results (default 40)
-b execve -- detach on this syscall
-e expr -- a qualifying expression: option=[!]all or option=[!]val1[,val2]...
options: trace, abbrev, verbose, raw, signal, read, write
-I interruptible --
1: no signals are blocked
2: fatal signals are blocked while decoding syscall (default)
3: fatal signals are always blocked (default if '-o FILE PROG')
4: fatal signals and SIGTSTP (^Z) are always blocked
(useful to make 'strace -o FILE PROG' not stop on ^Z)
-o file -- send trace output to FILE instead of stderr
-O overhead -- set overhead for tracing syscalls to OVERHEAD usecs
-p pid -- trace process with process id PID, may be repeated
-s strsize -- limit length of print strings to STRSIZE chars (default 32)
-S sortby -- sort syscall counts by: time, calls, name, nothing (default time)
-u username -- run command as username handling setuid and/or setgid
-E var=val -- put var=val in the environment for command
-E var -- remove var from the environment for command
-P path -- trace accesses to path

1. 跟踪系統調用

如果要跟踪命令 ls 的系統調用,請運行以下命令:

strace ls file1.txt

輸出execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0 brk(0) = 0x1f2a000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (沒有那個文件或目錄) mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, - 1, 0) = 0x7f4dd0d30000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (沒有那個文件或目錄) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=114633, ...}) = 0 mmap(NULL, 114633, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4dd0d14000
close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (沒有那個文件或目錄) open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 read(3, "177ELF2113>1 ["..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=134296, ...}) = 0
mmap(NULL, 2238192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4dd08ed000
mprotect(0x7f4dd090d000, 2093056, PROT_NONE) = 0
mmap(0x7f4dd0b0c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0x7f4dd0b0c000
mmap(0x7f4dd0b0e000, 5872, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4dd0b0e000
NLY|O_CLOEXEC) = 3
MAP_DENYWRITE, 3, 0x2000) = 0x7f4dd00e0000
close(3) = 0
.......
.......
) = 10
close(1) = 0
munmap(0x7f4dd0d2f000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++

In the above output, you can see the system call and result of the call of ls command. You should also see that exit status is 0. That means there was no error.

One use of strace (Except debugging some problem) is that you can find out which configuration files are read by a program.

For example:

strace php 2>&1 | grep php.ini

2. Filter Specific System Calls

Be default, strace displays all system calls for the given executable. If you want to display only a specific system call, you can use strace -e option.

For example, to displays only the write system call of the ls command run the following command:

strace -e write ls file1.txt file2.txt

Output
write(1, "file1.txt file2.txtn", 21file1.txt file2.txt
) = 21
+++ exited with 0 +++

To displays only the open system call of the ls command run the following command:

strace -e open ls file1.txt file2.txt

Output
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libpcre.so.3", 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/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/proc/filesystems", O_RDONLY) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
file1.txt file2.txt
+++ exited with 0 +++

If you want to display files opened by a specific process like SSH, run the following command:

strace -f -e open /usr/sbin/sshd 2>&1 | grep ssh

Output
open("/etc/ssh/sshd_config", O_RDONLY) = 3
open("/etc/ssh/ssh_host_rsa_key", O_RDONLY) = -1 EACCES (Permission denied)
open("/etc/ssh/ssh_host_rsa_key", O_RDONLY) = -1 EACCES (Permission denied)
open("/etc/ssh/ssh_host_ecdsa_key.pub", O_RDONLY) = 3
Could not load host key: /etc/ssh/ssh_host_ecdsa_key
open("/etc/ssh/ssh_host_ed25519_key.pub", O_RDONLY) = 3
Could not load host key: /etc/ssh/ssh_host_ed25519_key

To trace network-related system calls, run the following command:

strace -e network nc -v -n 127.0.0.1 80

Output
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 連接到 127.0.0.1 80 端口 [tcp/*] 成功了!

3.附加到已經運行的進程

如果進程已經在運行,您可以使用其 PID 跟踪它,如下所示:

strace -p 5315

Output
Process 5315 attached
restart_syscall(<... resuming interrupted call ...>) = -1 ETIMEDOUT (Connection timed out)
futex(0x7ffdc25fd048, FUTEX_WAKE_PRIVATE, 1) = 0
lseek(31, 0, SEEK_SET) = 0
read(31, "1185755 22902 18214 39954 0 1079"..., 4095) = 38
lseek(32, 0, SEEK_SET) = 0
read(32, "Name:tchromenState:tR (running)n"..., 4095) = 1020
futex(0x7ffdc25fd074, FUTEX_WAIT_BITSET_PRIVATE, 1, {3799, 9175}, ffffffff) = -1 ETIMEDOUT (Connection timed out)
futex(0x7ffdc25fd048, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x7ffdc25fd074, FUTEX_WAIT_BITSET_PRIVATE, 1, {3802, 10202}, ffffffff^CProcess 5315 detached

此命令將持續顯示進程進行的系統調用。 您可以按 CTRL+C 停止它。

在哪裡:

5315是正在運行的進程的進程ID。

4. 將跟踪輸出重定向到文件

您可以在 strace 命令中使用 -o 標誌將 strace 輸出保存到指定文件。

strace -o file_out.txt ls file1.txt

您現在可以使用以下命令顯示文件 file_out.txt 的內容:

cat file_out.txt

5. 打印花費在系統調用上的時間

要打印每個調用的相對時間戳,請使用 -r 標誌,如下所示。

strace -r ls file1.txt

輸出
0.000000 execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0
0.000947 brk(0) = 0xaf3000
0.000450 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
0.000706 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f01b1ccb000
0.000319 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000093 close(1) = 0
0.000069 munmap(0x7f01b1cca000, 4096) = 0
0.000078 close(2) = 0
0.000104 exit_group(0) = ?
0.000184 +++ exited with 0 +++

要顯示 ls 命令進行的每個系統調用的開始和結束之間的時間差,請使用 -T 選項。

strace -T ls file1.txt

Output
execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0 <0.000908>
brk(0) = 0x1d72000 <0.000050>
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000066>
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc895610000 <0.000059>
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) <0.000060>
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000072>
fstat(3, {st_mode=S_IFREG|0644, st_size=114633, ...}) = 0 <0.000052>
mmap(NULL, 114633, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc8955f4000 <0.000062>
close(3) = 0 <0.000050>
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000059>
open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 <0.000068>
read(3, "177ELF2113>1["..., 832) = 832 <0.000057>
fstat(3, {st_mode=S_IFREG|0644, st_size=134296, ...}) = 0 <0.000052>
mmap(NULL, 2238192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc8951cd000 <0.000064>
mprotect(0x7fc8951ed000, 2093056, PROT_NONE) = 0 <0.000085>
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc89560f000 <0.000026>
write(1, "file1.txtn", 10file1.txt
) = 10 <0.000029>
close(1) = 0 <0.000021>
munmap(0x7fc89560f000, 4096) = 0 <0.000029>
close(2) = 0 <0.000022>
exit_group(0) = ?
+++ exited with 0 +++

要打印每個系統調用的掛鐘時間,請運行以下命令:

strace -t ls file1.txt

Output
10:58:25 execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0
10:58:25 brk(0) = 0xc30000
10:58:25 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
10:58:25 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd4db396000
10:58:25 write(1, "file1.txtn", 10file1.txt
) = 10
10:58:25 close(1) = 0
10:58:25 exit_group(0) = ?
10:58:25 +++ exited with 0 +++

-tt 選項顯示時間戳後跟微秒。

strace -tt ls file1.txt

6. 顯示系統調用指令指針

您可以將 -i 標誌與 strace 命令一起使用,以在該命令進行的每個系統調用時打印指令指針:

strace -i ls file1.txt

Output
[00007efd8cfeb337] execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0
[00007f5ab611e18c] brk(0) = 0x239e000
[00007f5ab611f537] access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
[00007f5ab611f65a] mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5ab6326000
[00007f5ab5a01870] write(1, "file1.txtn", 10file1.txt
) = 10
[00007f5ab5a01f60] close(1) = 0
[00007f5ab5a0a9f7] munmap(0x7f5ab6325000, 4096) = 0
[00007f5ab5a01f60] close(2) = 0
[00007f5ab59d7309] exit_group(0) = ?
[????????????????] +++ exited with 0 +++

7. 生成系統調用報告

您可以使用 -c 標誌來獲取有用的執行跟踪統計報告。

strace -c ls /var/www/html

Output
index.html
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 8 read
0.00 0.000000 0 1 write
0.00 0.000000 0 9 open
0.00 0.000000 0 12 close
0.00 0.000000 0 1 stat
0.00 0.000000 0 10 fstat
0.00 0.000000 0 23 mmap
0.00 0.000000 0 14 mprotect
0.00 0.000000 0 3 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 2 ioctl
0.00 0.000000 0 8 8 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 2 getdents
0.00 0.000000 0 2 2 statfs
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 openat
------ ----------- ----------- --------- --------- ----------------
100.00 0.000000 101 10 total

在上面的輸出中,“調用”列指示該特定係統調用被執行了多少次。

8.打印strace的調試輸出

要打印 strace 命令的調試信息,請使用 -d 標誌,如下所示:

strace -d ls file1.txt

Output
ptrace_setoptions = 0x11
new tcb for pid 6453, active tcbs:1
[wait(0x80137f) = 6453] ?? (128),PTRACE_EVENT_?? (128)
pid 6453 has TCB_STARTUP, initializing it
setting opts 11 on pid 6453
[wait(0x80057f) = 6453] ?? (128),PTRACE_EVENT_?? (128)
[wait(0x127f) = 6453] WIFSTOPPED,sig=SIGCONT
[wait(0x857f) = 6453] WIFSTOPPED,sig=133
execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */] [wait(0x4057f) = 6453] WIFSTOPPED,sig=SIGTRAP,PTRACE_EVENT_EXEC
[wait(0x857f) = 6453] WIFSTOPPED,sig=133
) = 0
[wait(0x857f) = 6453] WIFSTOPPED,sig=133
brk(0 [wait(0x857f) = 6453] WIFSTOPPED,sig=133
) = 0x25bc000
[wait(0x857f) = 6453] WIFSTOPPED,sig=133
exit_group(0) = ?
[wait(0x0000) = 6453] WIFEXITED,exitcode=0
+++ exited with 0 +++
dropped tcb for pid 6453, 0 remain

9. 基於特定條件跟踪系統調用

您還可以根據特定條件跟踪系統調用。 為了 example,跟踪所有與內存管理相關的系統調用,運行以下命令:

strace -q -e memory ls file1.txt

Output
brk(0) = 0x248d000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f68e9ac4000
mmap(NULL, 114633, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f68e9aa8000
mmap(NULL, 2238192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f68e9681000
mprotect(0x7f68e96a1000, 2093056, PROT_NONE) = 0
mmap(0x7f68e98a0000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0x7f68e98a0000
mmap(0x7f68e98a2000, 5872, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f68e98a2000
mmap(NULL, 2126336, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f68e9479000
mmap(0x7f68e8e74000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f68e8e74000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f68e9aa6000
mmap(NULL, 2113760, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f68e8a6d000
mprotect(0x7f68e8a71000, 2093056, PROT_NONE) = 0
mmap(0x7f68e8c70000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f68e8c70000
brk(0) = 0x248d000
brk(0x24ae000) = 0x24ae000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f68e9ac3000
munmap(0x7f68e9ac3000, 4096) = 0
mmap(NULL, 7216688, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f68e838b000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f68e9ac3000
file1.txt
munmap(0x7f68e9ac3000, 4096) = 0
+++ exited with 0 +++

要跟踪與信號相關的系統調用,請運行以下命令:

strace -e signal ls file1.txt

Output
file1.txt
+++ exited with 0 +++

要跟踪進程相關的系統調用,請運行以下命令:

strace -e process ls file1.txt

Output
execve("/bin/ls", ["ls", "file1.txt"], [/* 61 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7fb8196a8840) = 0
file1.txt
exit_group(0) = ?
+++ exited with 0 +++

結論

在上面的指南中,您通過幾個示例學習瞭如何使用 strace 命令。 該工具對於系統管理員和程序員調試和排除任何程序的故障非常有用。