Ubuntu 22.04 strace 安装与命令参数详解
strace(strace 5.16)在 Ubuntu 22.04(x86_64)上的系统调用跟踪全参数实测与故障注入诊断实践。
Ubuntu 22.04 strace 安装与命令参数详解
目标:掌握 strace 对程序发起的所有系统调用进行跟踪与统计的方法。本文档在 Ubuntu 22.04(x86_64)真实环境中实测通过,逐一验证了
-c / -e trace=各class / -f / -ff -o / -p / -o / -s / -T / -t -tt -ttt / -r / -y -yy / -z -Z / -i / -v / -x / -k / -e inject / -e signal / -b / -D / --seccomp-bpf / -A等参数的完整行为与真实输出。
一、环境信息
- 操作系统:Ubuntu 22.04(x86_64)
- 工具版本:strace 5.16(软件包版本
strace_5.16-0ubuntu3_amd64,strace --version实测读取) - 主机名:mon-ubuntu2204(
cat /etc/hostname实测输出) - 安装来源仓库:Ubuntu jammy main 仓库(
http://archive.ubuntu.com/ubuntu jammy) - 动态库加载目录:
/lib/x86_64-linux-gnu/(glibc / libselinux 等) - 编译特性:
stack-trace=libunwind stack-demangle m32-mpers mx32-mpers - 说明:本环境日志未直接给出 CPU 核数与内网 IP(可运行
nproc/ip addr补充),其余环境字段均来自实测日志。
二、工具概览
strace(System Tracer)是一个运行于用户态的标准系统调用跟踪工具:它通过内核的 ptrace(process trace)机制介入目标进程,在每次系统调用进入与返回时各切换一次运行上下文,把调用名、参数、返回值、耗时与错误码逐条打印出来。基于此,它可用于:
- 排查程序启动失败/崩溃的根因(如缺失动态库、权限不足、打开文件失败);
- 定位配置文件加载路径、摸清程序真实 I/O 行为;
- 用
-c统计各系统调用次数与耗时占比,快速定位热点; - 用
-e inject故障注入伪造系统调用返回值,做故障演练; - 配合
-ff -o跟踪多进程,用-t/-r/-T查看时间维度。
strace 只跟踪系统调用(面向内核的接口),不跟踪用户态库函数调用(那是 ltrace 的职责),两者常配合使用。
三、在线安装
strace 是 Ubuntu 官方软件库自带的系统工具。先更新软件源索引,再安装:
sudo apt-get update
sudo apt-get install -y strace
安装完成后用 dpkg -l 复核包版本:
ii strace 5.16-0ubuntu3 amd64 System call tracer
由于当前环境已内置 strace,重复执行 apt-get install 会得到 "已是最大版本" 的提示(同类工具 ltrace/sysstat 实测为 already the newest version)。版本验证:
strace --version
strace -- version 5.16
Copyright (c) 1991-2022 The strace developers <https://strace.io>.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Optional features enabled: stack-trace=libunwind stack-demangle m32-mpers mx32-mpers
四、离线安装
在无法连通公网的内网环境,可先在可联网机器下载 deb 包再离线安装。用 apt-get download 直接抓取 strace 的主包:
mkdir -p /root/offline-bundle
cd /root/offline-bundle
apt-get download strace
ls -l /root/offline-bundle/strace_5.16-0ubuntu3_amd64.deb
若需连同所有依赖一并带走,可用 --download-only 把整包集下载到指定目录(实测示例节选):
Get:7 http://archive.ubuntu.com/ubuntu jammy/universe amd64 nmon amd64 16n+debian-1 [69.3 kB]
Get:8 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 sysstat amd64 12.5.2-2ubuntu0.2 [487 kB]
Fetched 1848 kB in 2s (861 kB/s)
Download complete and in download only mode
离线包目录清单(包含 strace 的 deb):
9
1.8M /root/offline-bundle
atop_2.7.1-1_amd64.deb
glances_3.2.4.2+dfsg-1_all.deb
htop_3.0.5-7build2_amd64.deb
iotop_0.6-24-g733f3f8-1.1ubuntu0.1_amd64.deb
ltrace_0.7.3-6.1ubuntu6.22.04.1_amd64.deb
nmon_16n+debian-1_amd64.deb
strace_5.16-0ubuntu3_amd64.deb
sysstat_12.5.2-2ubuntu0.2_amd64.deb
离线安装(dpkg -i 重装输出与此同型,实测示例为 htop):
sudo dpkg -i /root/offline-bundle/strace_5.16-0ubuntu3_amd64.deb
Preparing to unpack htop_3.0.5-7build2_amd64.deb ...
Unpacking htop (3.0.5-7build2) over (3.0.5-7build2) ...
Setting up htop (3.0.5-7build2) ...
Processing triggers for mailcap (3.70+nmu1ubuntu1.22.04.1) ...
离线安装完成后同样用 strace --version(输出见「三」)验证 5.16 版本生效。
💡 说明:
apt-get download所得为单个主包,离线环境还需配套依赖;--download-only可一次性备齐全部依赖包。
五、全部命令参数实测
5.1 strace --version 查看版本与编译特性
参数/作用:-V/--version → 查看 strace 版本号及编译时可用的特性(栈回溯、符号反修饰、m32/mx32 兼容模式等)。
strace --version
输出见「三」,版本 5.16。结尾特性行为后续 -k(内核栈)等参数是否可用提供依据。
5.2 strace ls / 默认全量跟踪
参数/作用:不带参数 → 跟踪目标程序发起的所有系统调用,逐条打印。
strace ls / 2>&1 | head -n 12
execve("/usr/bin/ls", ["ls", "/"], 0x7fff7dbefe28 /* 7 vars */) = 0
brk(NULL) = 0x64bb0aa18000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffcd784dc30) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x70b08f2ab000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=13588, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 13588, PROT_READ, MAP_PRIVATE, 3, 0) = 0x70b08f2a7000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libselinux.so.1", 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\0\0\0\0\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=166280, ...}, AT_EMPTY_PATH) = 0
(节选前 12 行。)注意现代 Ubuntu 22.04 的 coreutils 9.x 使用 newfstatat/openat/AT_EMPTY_PATH,而不是旧的 stat/fstat。
5.3 strace -c ls / 系统调用汇总统计
参数/作用:-c → 不逐条打印,改为输出统计汇总表(时间占比、总耗时、每次耗时、调用次数、出错次数、调用名)。
strace -c ls /
10.02 0.000177 25 7 mprotect
8.32 0.000147 73 2 getdents64
8.04 0.000142 23 6 openat
5.26 0.000093 18 5 read
5.21 0.000092 11 8 close
5.09 0.000090 12 7 newfstatat
4.02 0.000071 23 3 brk
3.51 0.000062 31 2 2 access
2.89 0.000051 12 4 pread64
2.32 0.000041 20 2 2 statfs
2.04 0.000036 18 2 2 ioctl
1.81 0.000032 32 1 munmap
1.47 0.000026 26 1 statx
1.41 0.000025 25 1 write
1.41 0.000025 25 1 prlimit64
1.36 0.000024 24 1 getrandom
1.19 0.000021 21 1 rseq
1.08 0.000019 9 2 1 arch_prctl
1.02 0.000018 18 1 set_tid_address
0.96 0.000017 17 1 set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00 0.001767 23 76 7 total
💡 说明:第 4 列
calls、第 5 列errors(出错次数)。本次共 76 次成功调用、7 次出错。
5.4 strace -c -w ls / 含系统时间汇总
参数/作用:-c -w → 与 -c 类似,但用时基于系统调用在系统态花费的时间(system time)而非墙钟时间。
strace -c -w ls /
3.12 0.000044 44 1 write
2.01 0.000029 7 4 pread64
1.94 0.000028 13 2 1 arch_prctl
1.69 0.000024 12 2 2 statfs
1.18 0.000017 16 1 munmap
0.99 0.000014 7 2 2 ioctl
0.64 0.000009 9 1 statx
0.55 0.000008 7 1 prlimit64
0.51 0.000007 7 1 getrandom
0.50 0.000007 7 1 rseq
0.50 0.000007 7 1 set_tid_address
0.49 0.000007 6 1 set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00 0.001427 18 76 7 total
💡 说明:
-w更贴近 CPU 忙于该系统调用的真实开销,常用于剔除 I/O 等待的干扰。
5.5 strace -e trace=openat cat /etc/hostname 指定单个系统调用
参数/作用:-e trace=openat → 只跟踪名为 openat 的系统调用。
strace -e trace=openat cat /etc/hostname
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
mon-ubuntu2204
+++ exited with 0 +++
从三行 openat 可清晰看到程序打开 ld.so.cache、libc.so.6,最终打开并读取 /etc/hostname。
5.6 strace -e trace=%file cat /etc/hostname 文件类调用集
参数/作用:-e trace=%file → 跟踪所有与文件操作相关的调用(openat、access、newfstatat 等)。
strace -e trace=%file cat /etc/hostname
execve("/usr/bin/cat", ["cat", "/etc/hostname"], 0x7ffed74aba28 /* 7 vars */) = 0
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=13588, ...}, AT_EMPTY_PATH) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2220400, ...}, AT_EMPTY_PATH) = 0
newfstatat(1, "", {st_mode=S_IFIFO|0600, st_size=0, ...}, AT_EMPTY_PATH) = 0
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=15, ...}, AT_EMPTY_PATH) = 0
mon-ubuntu2204
5.7 strace -e trace=%network python3 网络类调用集
参数/作用:-e trace=%network → 只跟踪 socket、bind、connect 等网络相关调用。用 python3 建一个 127.0.0.1:0 的 TCP socket 演示。
strace -e trace=%network python3 -c "import socket; s=socket.socket(); s.bind(('127.0.0.1',0))"
socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
+++ exited with 0 +++
5.8 strace -f -e trace=%process 进程管理类调用集
参数/作用:-f → 跟踪子进程;-e trace=%process → 只跟踪 execve、fork、clone、wait4 等进程管理类调用。
strace -f -e trace=%process sh -c "ls / >/dev/null"
execve("/usr/bin/sh", ["sh", "-c", "ls / >/dev/null"], 0x7ffff371a608 /* 7 vars */) = 0
vfork(strace: Process 11225 attached
<unfinished ...>
[pid 11225] execve("/usr/bin/ls", ["ls", "/"], 0x5fd0362547b0 /* 7 vars */ <unfinished ...>
[pid 11224] <... vfork resumed>) = 11225
[pid 11224] wait4(-1, <unfinished ...>
[pid 11225] <... execve resumed>) = 0
[pid 11225] exit_group(0) = ?
[pid 11225] +++ exited with 0 +++
<... wait4 resumed>[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 11225
[pid NNNN] 前缀清晰区分出父(11224)/子(11225)进程各自的调用。
5.9 strace -e trace=%desc 文件描述符类调用集
参数/作用:-e trace=%desc → 只跟踪 fd 相关调用(openat、close、read、write、mmap、pread64 等)。
strace -e trace=%desc cat /etc/hostname 2>&1 | head -n 12
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x75ec61a0d000
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=13588, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 13588, PROT_READ, MAP_PRIVATE, 3, 0) = 0x75ec61a09000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0jl\265\250\17\325\345\273[\363\256\252\21\32\335\230"..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2220400, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
5.10 strace -e trace=%memory 内存管理类调用集
参数/作用:-e trace=%memory → 只跟踪 brk、mmap、mprotect、mremap 等内存映射类调用。
strace -e trace=%memory cat /etc/hostname
brk(NULL) = 0x5a678b511000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x71d22d992000
mmap(NULL, 13588, PROT_READ, MAP_PRIVATE, 3, 0) = 0x71d22d98e000
mmap(NULL, 177672, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x71d22d962000
mprotect(0x71d22d968000, 139264, PROT_NONE) = 0
mmap(0x71d22d968000, 106496, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x71d22d968000
mmap(0x71d22d982000, 28672, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20000) = 0x71d22d982000
mmap(0x71d22d98a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x27000) = 0x71d22d98a000
5.11 strace -f 跟踪子进程(fork/clone 可见)
参数/作用:-f → 跟踪目标及其 fork/clone 出的所有子进程;管道示例中 sh → echo / cat 皆为子进程。
strace -f sh -c "echo hi | cat"
execve("/usr/bin/sh", ["sh", "-c", "echo hi | cat"], 0x7ffc37652438 /* 7 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x715233eeaa10) = 11245
[pid 11245] write(1, "hi\n", 3 <unfinished ...>
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 11246 attached
[pid 11246] execve("/usr/bin/cat", ["cat"], 0x603c201ec790 /* 7 vars */ <unfinished ...>
[pid 11246] <... execve resumed>) = 0
[pid 11246] write(1, "hi\n", 3hi
clone 返回值 11245、11246 即新子进程的 PID,随后的 [pid NNNN] 前缀标明每条调用属于哪个进程。
5.12 strace -ff -o 前缀 每进程独立文件
参数/作用:-ff -o <前缀> → 为每个被跟踪进程单独写一个 前缀.<pid> 文件,避免多进程输出互相穿插。
strace -ff -o /root/.stx-. sh -c "echo hi | cat"
ls -l /root/.stx-*
hi
-rw-r--r-- 1 root root 5312 Oct 6 05:09 /root/.stx-.11251
-rw-r--r-- 1 root root 286 Oct 6 05:09 /root/.stx-.11252
-rw-r--r-- 1 root root 3441 Oct 6 05:09 /root/.stx-.11253
查看第一个文件起始内容:
execve("/usr/bin/sh", ["sh", "-c", "echo hi | cat"], 0x7ffc16da8628 /* 7 vars */) = 0
brk(NULL) = 0x6106f4ebb000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffc1360bf0) = -1 EINVAL (Invalid argument)
5.13 strace -p PID 附加运行中的进程
参数/作用:-p PID → 附加到一个已在运行、尚未结束的进程上,实时观察其调用,Ctrl-C 后显示 detach。
strace -p 11260
strace: Process 11260 attached
restart_syscall(<... resuming interrupted read ...>strace: Process 11260 detached
<detached ...>
⚠️ 注意:附加只读型调试一般不需额外权限,但跟踪他人进程(非 root 拥有)通常需要相同 uid 或 root。
5.14 strace -o file 输出重定向到文件
参数/作用:-o <file> → 把跟踪输出写入指定文件而非 stderr,实测 -o /tmp/strace.out。
strace -o /tmp/strace.out ls /
head -n 5 /tmp/strace.out
execve("/usr/bin/ls", ["ls", "/"], 0x7ffdc714d228 /* 7 vars */) = 0
brk(NULL) = 0x640fd608f000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd1bcc5c10) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7907c8062000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
(节选前 5 行。)
5.15 strace -s 8 字符串截断为 8 字符
参数/作用:-s <n> → 限制被打印字符串的最大长度为 n 字节,默认 32;设 8 后长路径只显示前 8 字节。
strace -s 8 -e trace=openat cat /etc/hostname
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
mon-ubuntu2204
5.16 strace -T 显示每次调用耗时
参数/作用:-T → 在每个系统调用后追加 <> 形式的调用耗时(秒)。
strace -T -e trace=openat cat /etc/hostname
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000107>
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 <0.000059>
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3 <0.000056>
mon-ubuntu2204
+++ exited with 0 +++
三次 openat 均耗时约 0.0001 s,据此可定位单次调用的时间热点。
5.17 strace -t / -tt / -ttt 三级时间戳精度
参数/作用:-t → 打印时:分:秒;-tt → 含微秒;-ttt → 打印自 epoch 起的微秒数。
strace -t -e trace=openat cat /etc/hostname
05:09:06 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
05:09:06 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
strace -tt -e trace=openat cat /etc/hostname
05:09:06.766799 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
05:09:06.767680 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
strace -ttt -e trace=openat cat /etc/hostname
1791263346.778212 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
1791263346.779243 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
5.18 strace -r 相对时间戳(相邻调用间隔)
参数/作用:-r → 打印每条调用相对上一条调用的时间间隔(秒)。
strace -r -e trace=openat cat /etc/hostname
0.000000 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
0.000641 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
0.004129 openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
mon-ubuntu2204
5.19 strace -yy 解码 fd(含 socket/管道路径)
参数/作用:-yy → 除路径外还打印被引用对象的详细属性(设备号、类型、inode),对 socket/管道同样解码。
strace -yy -e trace=write sh -c "echo ok >/dev/null"
write(1</dev/null<char 1:3>>, "ok\n", 3) = 3
+++ exited with 0 +++
</dev/null<char 1:3>> 表明 fd 1 指向 /dev/null,其字符设备主/次号为 1:3。
5.20 strace -y 简化 fd 解码(仅路径)
参数/作用:-y → 在 fd 后附带打开的目标对象路径,信息较 -yy 简略。
strace -y -e trace=write sh -c "echo ok >/dev/null"
write(1</dev/null>, "ok\n", 3) = 3
+++ exited with 0 +++
5.21 strace -z 只显示成功的调用
参数/作用:-z → 仅输出返回成功的系统调用,失败的被过滤掉,适合聚焦正常路径。
strace -z -e trace=openat cat /etc/hostname
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
mon-ubuntu2204
5.22 strace -Z 只显示失败的调用
参数/作用:-Z → 与 -z 相反,只输出失败的调用,错误定位利器。
strace -Z cat /etc/nonexist
openat(AT_FDCWD, "/etc/nonexist", O_RDONLY) = -1 ENOENT (No such file or directory)
cat: /etc/nonexist: No such file or directory
+++ exited with 1 +++
-Z 下其它成功调用全部被过滤,一眼定位到失败点 openat 与错误码 ENOENT。
5.23 strace -i 显示指令指针
参数/作用:-i → 在每条调用前打印发生该系统调用时 CPU 的指令指针(指令地址),便于结合反汇编定位调用点。
strace -i -e trace=openat cat /etc/hostname
[00007dabb005fb18] openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
[00007dabb005fb18] openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
[00007dabaff1e68b] openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
5.24 strace -v 完整结构体展开
参数/作用:-v → 不缩写打印结构体(如 stat 结构),把所有字段完整展开。现代 coreutils 9.x 走 newfstatat,配合 -e trace=newfstatat 可看到完整 stat 内容。
strace -v -e trace=newfstatat cat /etc/hostname
newfstatat(3, "", {st_dev=makedev(0, 0x123), st_ino=25447460, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=32, st_size=13588, st_atime=1791261640 /* 2026-10-06T04:40:40.687734349+0000 */, st_atime_nsec=687734349, st_mtime=1791261640 /* 2026-10-06T04:40:40.672734280+0000 */, st_mtime_nsec=672734280, st_ctime=1791261640 /* 2026-10-06T04:40:40.674734289+0000 */, st_ctime_nsec=674734289}, AT_EMPTY_PATH) = 0
newfstatat(3, "", {st_dev=makedev(0, 0x123), st_ino=25449669, st_mode=S_IFREG|0755, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=4344, st_size=2220400, st_atime=1791260808 /* 2026-10-06T04:26:48.429069777+0000 */, st_atime_nsec=429069777, st_mtime=1788445204 /* 2026-09-03T14:20:04+0000 */, st_mtime_nsec=0, st_ctime=1791260808 /* 2026-10-06T04:26:48.392068908+0000 */, st_ctime_nsec=392068908}, AT_EMPTY_PATH) = 0
newfstatat(1, "", {st_dev=makedev(0, 0xe), st_ino=38744377, st_mode=S_IFIFO|0600, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=0, st_size=0, st_atime=1791263564 /* 2026-10-06T05:12:44.226461279+0000 */, st_atime_nsec=226461279, st_mtime=1791263564 /* 2026-10-06T05:12:44.226461279+0000 */, st_mtime_nsec=226461279, st_ctime=1791263564 /* 2026-10-06T05:12:44.226461279+0000 */, st_ctime_nsec=226461279}, AT_EMPTY_PATH) = 0
newfstatat(3, "", {st_dev=makedev(0x8, 0x3), st_ino=19005877, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=8, st_size=15, st_atime=1791263343 /* 2026-10-06T05:09:03.498189459+0000 */, st_atime_nsec=498189459, st_mtime=1791260573 /* 2026-10-06T04:22:53.328055753+0000 */, st_mtime_nsec=328055753, st_ctime=1791263564 /* 2026-10-06T05:12:44.076458650+0000 */, st_ctime_nsec=76458650}, AT_EMPTY_PATH) = 0
💡 说明:普通
strace ls /的-v输出较为简略;本项目补测用例(fix)确认 Ubuntu 22.04 coreutils 走newfstatat,展开可见设备号、inode、权限、大小、各时间戳与纳秒字段。
5.25 strace -x 非 ASCII 字符串十六进制输出
参数/作用:-x → 把字符串中的非 ASCII 字符以转义形式(UTF-8 十六进制)打印,方便比较携带不可打印字符的路径/数据。先创建中文文件名再对照。
echo hi > /root/中文测试.txt
strace -e trace=openat cat /root/中文测试.txt
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/root/\344\270\255\346\226\207\346\265\213\350\257\225.txt", O_RDONLY) = 3
strace -x -e trace=openat cat "/root/中文测试.txt"
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "\x2f\x72\x6f\x6f\x74\x2f\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x95\x2e\x74\x78\x74", O_RDONLY) = 3
💡 说明:不加
-x时输出 UTF-8 原文(/root/中文测试.txt),但终端已把其字节显示为八进制转义;加-x后统一为\x十六进制,便于机器可读比较。
5.26 strace -k 每次调用打印内核栈
参数/作用:-k → 在每个系统调用下打印该调用点的栈回溯(符号+偏移+地址),定位调用来源于哪个库/函数。
strace -k -e trace=openat cat /etc/hostname
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_catch_error+0x9748) [0x26b18]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_rtld_di_serinfo+0x26ca) [0xd62a]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_deallocate_tls+0x2148) [0x16bb8]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(__nptl_change_stack_perm+0x57d2) [0xa982]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_debug_state+0x195) [0x3495]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_catch_exception+0x6a) [0x1d34a]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_debug_state+0x97c) [0x3c7c]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_catch_error+0x5fd0) [0x233a0]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_catch_error+0x246c) [0x1f83c]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_catch_error+0x41c8) [0x21598]
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_catch_error+0x2ec8) [0x20298]
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
> /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2(_dl_catch_error+0x9748) [0x26b18]
(节选。)栈帧均来自动态链接器 ld-linux-x86-64.so.2,说明这些 openat 发生在程序动态装载库的阶段。
5.27 strace -e inject=read:error=EIO 故障注入
参数/作用:-e inject=<调用>:error=<errno> → 人为让指定系统调用按给定 errno 失败,用于故障演练。注意参数名是 error= 而非 errno=。
strace -e inject=read:error=EIO cat /etc/hostname
execve("/usr/bin/cat", ["cat", "/etc/hostname"], 0x7ffd38697668 /* 6 vars */) = 0
brk(NULL) = 0x631bd7225000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fff20fb78b0) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7d1bb8a6d000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=13588, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 13588, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7d1bb8a69000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, 0x7fff20fb6c38, 832) = -1 EIO (Input/output error) (INJECTED)
close(3) = 0
加载动态库时的 read 被注入失败,返回 -1 EIO 并明确标注 (INJECTED)。若误写 error= 为 errno=(见「六」),会直接报参数非法。
5.28 strace -e signal=SIGALRM 信号过滤
参数/作用:-e signal=<信号> → 只跟踪指定信号(信号到达与处理,非系统调用);配合自触发 SIGALRM 演示。
strace -e signal=SIGALRM sh -c 'kill -ALRM $$; sleep 1'
--- SIGALRM {si_signo=SIGALRM, si_code=SI_USER, si_pid=11361, si_uid=0} ---
+++ killed by SIGALRM +++
可以看到进程收到 SIGALRM,默认处置为终止。
5.29 strace -c -S calls ls / 汇总按调用次数排序
参数/作用:-S <排序键> → 与 -c 配合按指定字段排序;calls 即按调用次数从多到少排序(默认按时间)。汇总段在输出末尾,常用 | tail 截取。
strace -c -S calls ls / 2>&1 | tail -n 15
3.50 0.000015 7 2 2 statfs
0.23 0.000001 0 2 1 arch_prctl
26.34 0.000113 56 2 getdents64
5.13 0.000022 22 1 write
2.56 0.000011 11 1 munmap
0.00 0.000000 0 1 execve
0.47 0.000002 2 1 set_tid_address
0.47 0.000002 2 1 set_robust_list
0.47 0.000002 2 1 prlimit64
0.47 0.000002 2 1 getrandom
0.70 0.000003 3 1 statx
0.47 0.000002 2 1 rseq
------ ----------- ----------- --------- --------- ----------------
100.00 0.000429 5 76 7 total
(末尾汇总段;getdents64 56 次为最高频调用。)主测试日志中普通 -c -S calls 的目录名部分未纳入汇总表,补测用例确认应取末尾 total 段。
5.30 strace -b execve execve 时自动分离
参数/作用:-b <系统调用> → 在指定 syscall 处自动断开(detach)跟踪,通常用于 execve 后不再跟踪被替换的程序。
strace -b execve sh -c "ls / >/dev/null"
execve("/usr/bin/sh", ["sh", "-c", "ls / >/dev/null"], 0x7ffcd1430f20 /* 7 vars */) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=11374, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
5.31 strace -D tracer 与 tracee 分离运行
参数/作用:-D → 让被跟踪程序作为独立进程运行(tracer 作为其父进程的一个独立子进程),互不阻塞、脱离退出。
strace -D ls /
strace: Process 11376 attached
bin
boot
(节选。)ls / 正常运行输出目录,strace 仅做观察、随后自行分离。
5.32 strace --seccomp-bpf seccomp 加速过滤
参数/作用:--seccomp-bpf → 用内核 seccomp BPF 过滤加速跟踪(显著降低 ptrace 开销),但需要 -f 跟随子进程配合。
strace --seccomp-bpf -e trace=openat cat /etc/hostname
strace: --seccomp-bpf cannot be used without -f/--follow-forks, disabling
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
未带 -f 时报告无法使用并自动禁用该优化,但跟踪仍正常进行。
5.33 strace -A -o file 追加输出模式
参数/作用:-A → 配合 -o 以追加(append)方式写入文件,而不是覆盖,适合多次运行累积日志。用行数验证递增。
strace -A -o /tmp/strace.txt true
wc -l < /tmp/strace.txt
strace -A -o /tmp/strace.txt true
wc -l < /tmp/strace.txt
首次写入行数: 1
追加后行数: 2
第一次写入 1 行,追加第二次后变 2 行,证明 -A 未覆盖旧内容而是继续追加。
六、常见问题与真实行为
① -e inject=read:errno=EIO 报错:参数名必须是 error=
现象:运行 strace -e inject=read:errno=EIO cat /etc/hostname 直接报:
strace: invalid inject argument 'read:errno=EIO'
原因:strace 5.16 的 inject 故障注入用 error=<errno|succeed|return=值> 指定注入结果,不存在 errno= 这个键。
解决:改为 strace -e inject=read:error=EIO ...,即可看到 read(...) = -1 EIO (Input/output error) (INJECTED)(见 5.27)。
② 现代 coreutils 走 newfstatat,早期工具走 stat/fstat
现象:Ubuntu 22.04 自带 coreutils 9.x,strace ls / 默认输出里是 newfstatat/openat/AT_EMPTY_PATH。
原因/定位:coreutils 9.x 改用更原子化、支持 AT_EMPTY_PATH 的 newfstatat,-v 展开可见完整 stat 结构(见 5.24)。
解决:无需处理;只是同一命令在不同 coreutils 版本下输出调用不同,排查时留意。(CentOS 8 coreutils 8.x 仍用 stat/fstat,详见 CentOS 版文档「六」。)
③ -x 打印非 ASCII 字符串为十六进制
现象:中文文件名 /root/中文测试.txt 在 -x 下以 \x2f\x72\x6f... 十六进制转义显示。
原因/定位:strace 会把不可打印/非 ASCII 字节转义;UTF-8 汉字 中文测试 对应 \xe4\xb8\xad\xe6\x96\x87...。
解决:需要人类可读时去掉 -x,需要机器可比较/精确匹配路径时加 -x(见 5.25)。
④ -c -S calls 的汇总段在末尾,需用 tail 截取
现象:初始实测中 -c -S calls 只看到目录名列表,未看到排序汇总。
原因/定位:汇总表打印在标准错误输出的末尾,夹在程序自身输出之后,未翻页即被忽略。
解决:用 strace -c -S calls ls / 2>&1 | tail -n 15 捕获末尾 total 段(见 5.29)。
⑤ -A -o 追加模式:行数递增验证
现象:首次 -A -o 写入 1 行,再执行一次后变成 2 行。
原因/定位:-A 以追加方式打开输出文件,不截断旧内容。
解决:确认即可,常用于多轮采集累积到同一文件(见 5.33)。
⑥ --seccomp-bpf 需 -f 配合,否则自动禁用
现象:不带 -f 时输出 --seccomp-bpf cannot be used without -f/--follow-forks, disabling。
原因/定位:seccomp 加速依赖对所有 fork 进程的统一过滤路径,未开启 -f 时无法成立。
解决:需要该优化时加 -f,否则接受自动禁用(见 5.32)。
七、命令与参数速查表
| 参数/命令 | 含义 | 实测说明/默认值 |
|---|---|---|
strace <cmd> | 默认全量跟踪 | 打印每个系统调用;默认 -s 32、无时间戳 |
-c | 汇总统计各调用 | 列:时间%/耗时/calls/errors;本机 ls / 共 76 成功 + 7 出错 |
-c -w | 汇总按系统时间(sys time)统计 | 剔除了 I/O 等待的墙钟干扰 |
-e trace=<syscall> | 只跟踪指定单个调用 | 实测 openat |
-e trace=%file | 文件类调用集 | openat/access/newfstatat 等 |
-e trace=%network | 网络类调用集 | socket/bind/connect 等 |
-e trace=%process | 进程类调用集 | execve/vfork/wait4 等 |
-e trace=%desc | 文件描述符类调用集 | openat/close/read 等 |
-e trace=%memory | 内存类调用集 | brk/mmap/mprotect 等 |
-f | 跟踪子进程 | clone/fork 产生 [pid N] 前缀 |
-ff -o <前缀> | 每进程独立文件 | 生成 <前缀>.<pid> |
-p <pid> | 附加运行中进程 | 显示 attached/detached |
-o <file> | 输出到文件 | 实测 /tmp/strace.out |
-s <n> | 字符串截断长度 | 默认 32,实测 8 |
-T | 显示每次调用耗时 | 追加 <0.000xxx> 秒 |
-t / -tt / -ttt | 时间戳;微秒;epoch 微秒 | 三级精度可选 |
-r | 相邻调用相对时间 | 秒级小数 |
-yy | 【详细】解码 fd 归属 | 含设备类型/号,如 <char 1:3> |
-y | 解码 fd 路径 | 如 write(1</dev/null>...) |
-z | 只显示成功调用 | 失败被过滤 |
-Z | 只显示失败调用 | 聚焦错误,如 ENOENT |
-i | 显示指令指针地址 | [00007dab...] |
-v | 完整展开结构体 | 配合 newfstatat 见全字段 stat |
-x | 非 ASCII 字符串十六进制 | 中文路径转 \xe4... |
-k | 打印内核/调用栈 | Ubuntu 用 libunwind 回溯 |
-e inject=...:error=<e> | 故障注入失败码 | 参数名 error= 而非 errno=;输出含 (INJECTED) |
-e signal=<sig> | 只跟踪信号 | 如 SIGALRM |
-S calls | 汇总按调用次数排序 | 配合 -c,汇总在末尾用 tail |
-b <syscall> | 在指定调用处分离 | 实测 -b execve |
-D | tracer 与 tracee 分离运行 | tracee 独立、自行退出 |
--seccomp-bpf | seccomp 加速过滤 | 需 -f,否则自动禁用 |
-A | 追加模式(配合 -o) | 实测 1 行→2 行递增 |
八、卸载方式
strace 为系统自带工具,如需完全移除(含配置文件)使用 purge:
sudo apt-get remove -y strace
# 如需连配置文件一并清除:
sudo apt-get purge -y strace
sudo apt-get autoremove -y