news 2026/1/1 7:45:05

strace命令详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
strace命令详解

strace 是一个用于诊断、调试和统计的工具,通过跟踪程序的系统调用和信号传递来分析程序行为。‌

基本用法:

最简单的用法是直接执行命令,strace 会记录进程的所有系统调用和信号,例如strace command [arguments]。输出默认到标准错误,可通过-o filename重定向到文件。‌

strace [OPTIONS] command [ARGS]

选项说明

-c 统计每个系统调用的时间、次数和错误,并在程序退出时报告摘要 -C 类似于 -c,但在进程运行时也打印常规输出 -D 将跟踪进程作为分离的孙进程运行,而不是作为跟踪对象的父进程运行。这通过保持跟踪对象是调用进程的直接子进程来减少 strace 的可见效果 -d 输出 strace 关于标准错误的调试信息 -f 跟踪由 fork(2), vfork(2) and clone(2) 调用所产生的子进程 -ff 如果提供 -o FILENAME,则所有进程的跟踪结果输出到相应的 FILENAME.pid 中,pid 是各进程的进程号 -F 该选项已废弃,作用等同于 -f -h 输出简要的帮助信息 -i 在系统调用时打印指令指针 -q 禁止附加、分离等信息。当输出被重定向到文件并直接运行命令而不是附加命令时,这将自动发生 -qq 如果给定两次,则禁止关于进程退出状态的消息 -r 在每次系统调用进入时打印相对时间戳。它记录连续系统调用开始之间的时间差 -t 在输出中的每一行前加上时间信息 -tt 如果给定两次,在输出中的每一行前加上微秒级的时间信息 -ttt 如果给定三次,则打印的时间将包括微秒,并且开始部分将打印自纪元以来的秒数 -T 显示每一系统调用所耗的时间 -v 输出所有的系统调用。一些调用关于环境变量,状态,输入输出等调用,由于使用频繁默认不输出 -V 输出 strace 的版本信息. -x 以十六进制形式输出非标准字符串 -xx 所有字符串以十六进制形式输出 -y 与文件描述符参数关联的打印路径 -a COLUMN 设置返回值的输出位置,默认为40 -b SYSCALL 如果达到指定的系统调用,与跟踪进程分离。目前,只支持 execve。如果希望跟踪多线程进程,因此需要 -f,但不希望跟踪其(可能非常复杂的)子进程,则此选项非常有用 -e EXPR 指定一个表达式,用来控制如何跟踪。格式如下: [qualifier=][!]value1[,value2]... qualifier 只能是 trace, abbrev, verbose, raw, signal, read, write 其中之一。value 是用来限定的符号或数字。默认的 qualifier 是 trace,感叹号是否定符号。例如:-e open 等价于 -e trace=open,表示只跟踪 open 调用。而 -etrace=!open 表示跟踪除了 ope 以外的所有其他调用。有两个特殊的符号 all 和 none,分别表示跟踪所有和不跟踪任何系统调用。注意有些 Shell 使用 ! 来执行历史记录里的命令,所以要使用反斜杠对 ! 进行转义 -e trace=SET 只跟踪指定的系统调用。例如: -e trace=open,close,rean,write 表示只跟踪这四个系统调用,默认的为 trace=all -e trace=file 只跟踪有关文件操作的系统调用 -e trace=process 只跟踪有关进程控制的系统调用 -e trace=network 跟踪与网络有关的所有系统调用 -e strace=signal 跟踪所有与系统信号有关的系统调用 -e trace=ipc 跟踪所有与进程通讯有关的系统调用 -e trace=desc 跟踪所有与文件描述符相关的系统调用 -e trace=memory 跟踪所有与内存映射相关的系统调用 -e abbrev=SET 缩写打印大型结构的每个成员的输出。默认值是 abbrev=all。-v 选项的效果是 abbrev=none -e verbose=SET 为指定的系统调用集取消引用结构。默认是 verbose=all -e raw=SET 将指定的系统调用的参数以十六进制显示 -e signal=SET 指定跟踪的系统信号,默认为 signal=all。如 signal=!SIGIO(或 signal=!io),表示不跟踪 SIGIO 信号 -e read=SET 输出从指定文件描述符中读出的数据。例如:-e read=3,5 -e write=SET 输出写入到指定文件中的数据 -o FILENAME 将 strace 的输出写入指定文件 -O OVERHEAD 将跟踪系统调用的开销设置为指定的微秒 -p PID 跟踪指定的进程 -P PATH 只跟踪系统调用的访问路径。多个 -P 选项可用于指定多个路径 -s STRSIZE 指定输出的字符串的最大长度,默认为 32。注意,文件名不被认为是字符串,总是全部打印 -S SORTBY 根据指定的条件对 -c 选项打印的直方图的输出进行排序。SORTBY 合法值是 time、calls、name 和 nothing,默认值是 time -u USERNAME 以指定用户的 UID、GID 和补充组执行被跟踪的命令 -E VAR=VAL 为命令设置环境变量 -E VAR 从继承的环境变量列表中删除变量 VAR,然后将其传递给命令

使用示例

strace有两种运行模式。

一种是通过它启动要跟踪的进程。用法很简单,在原本的命令前加上strace即可。比如我们要跟踪 "ls -lh /var/log/messages" 这个命令的执行,可以这样:

strace ls -lh /var/log/messages

另外一种运行模式,是跟踪已经在运行的进程,在不中断进程执行的情况下,理解它在干嘛。 这种情况,给strace传递个-p pid 选项即可。

比如,有个在运行的some_server服务,第一步,查看pid:

pidof some_server 17553

得到其pid 17553然后就可以用strace跟踪其执行:

strace -p 17553

完成跟踪时,按ctrl + C 结束strace即可。

strace常用选项:

从一个示例命令来看:

strace -tt -T -v -f -e trace=file -o /data/log/strace.log -s 1024 -p 23489

-tt 在每行输出的前面,显示毫秒级别的时间
-T 显示每次系统调用所花费的时间
-v 对于某些相关调用,把完整的环境变量,文件stat结构等打出来。
-f 跟踪目标进程,以及目标进程创建的所有子进程
-e 控制要跟踪的事件和跟踪行为,比如指定要跟踪的系统调用名称
-o 把strace的输出单独写到指定的文件
-s 当系统调用的某个参数是字符串时,最多输出指定长度的内容,默认是32个字节
-p 指定要跟踪的进程pid, 要同时跟踪多个pid, 重复多次-p选项即可。

跟踪进程的系统调用

strace -p 1234 # 跟踪 PID=1234 的进程 strace -f -p 1234 # 跟踪 PID=1234 及其子进程 strace -e trace=open,read ls # 仅跟踪 `ls` 命令的 open 和 read 调用
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\177\0\0\0\0\0\0"..., 832) = 832 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000#\0\0\0\0\0\0"..., 832) = 832 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\\\2\0\0\0\0\0"..., 832) = 832 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\"\0\0\0\0\0\0"..., 832) = 832 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\21\0\0\0\0\0\0"..., 832) = 832 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@|\0\0\0\0\0\0"..., 832) = 832 read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tr"..., 1024) = 332 read(3, "", 1024) = 0 +++ exited with 0 +++

生成耗时统计

strace -c -p 1234 # 统计 PID=1234 的系统调用耗时 strace -c ls # 统计 `ls` 命令的系统调用摘要
% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 33.13 0.001648 51 32 mmap 11.16 0.000555 46 12 close 10.64 0.000529 52 10 openat 8.64 0.000430 39 11 mprotect 8.50 0.000423 42 10 fstat 6.76 0.000336 42 8 read 4.22 0.000210 35 6 3 prctl 3.00 0.000149 149 1 execve 2.11 0.000105 35 3 brk 1.55 0.000077 38 2 getdents64 1.51 0.000075 37 2 1 access 1.45 0.000072 36 2 ioctl 1.35 0.000067 67 1 arch_prctl 1.17 0.000058 29 2 2 statfs 0.94 0.000047 23 2 rt_sigaction 0.92 0.000046 46 1 munmap 0.84 0.000042 42 1 set_robust_list 0.72 0.000036 36 1 prlimit64 0.70 0.000035 35 1 rt_sigprocmask 0.68 0.000034 34 1 set_tid_address ------ ----------- ----------- --------- --------- ---------------- 100.00 0.004974 45 109 6 total
% time 该系统调用占总时间的百分比(越高表示对性能影响越大)。 seconds 该系统调用的总耗时(秒)。 usecs/call 每次调用的平均耗时(微秒)。 calls 该系统调用的总次数。 errors 该系统调用返回错误码的次数(需重点关注)。 syscall 系统调用名称。

调试文件访问

strace -e trace=file -y ls # 跟踪文件相关调用并显示路径 strace -P /etc/passwd cat /etc/passwd # 跟踪对 /etc/passwd 的访问
openat(AT_FDCWD, "/etc/passwd", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=1364, ...}) = 0 fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0 read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 131072) = 1364 root:x:0:0:root:/root:/bin/bash .... wgs:x:1000:1000::/home/wgs:/bin/bash read(3, "", 131072) = 0 close(3) = 0 +++ exited with 0 +++

分析网络连接

strace -e trace=network -yy curl www.baidu.com # 跟踪网络相关调用并显示 socket 详情
# 创建套接字 socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3<UDPv6:[44848]> socketpair(AF_UNIX, SOCK_STREAM, 0, [3<UNIX:[42876->42877]>, 4<UNIX:[42877->42876]>]) = 0 socketpair(AF_UNIX, SOCK_STREAM, 0, [5<UNIX:[44851->44852]>, 6<UNIX:[44852->44851]>]) = 0 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 5<TCP:[45543]> # 设置 TCP 选项 setsockopt(5<TCP:[45543]>, SOL_TCP, TCP_NODELAY, [1], 4) = 0 setsockopt(5<TCP:[45543]>, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0 setsockopt(5<TCP:[45543]>, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0 setsockopt(5<TCP:[45543]>, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0 # 非阻塞连接与状态确认 connect(5<TCP:[45543]>, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("110.242.69.21")}, 16) = -1 EINPROGRESS (Operation now in progress) getsockopt(5<TCP:[192.168.174.144:52818->110.242.69.21:80]>, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 # 获取连接信息 getpeername(5<TCP:[192.168.174.144:52818->110.242.69.21:80]>, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("110.242.69.21")}, [128->16]) = 0 getsockname(5<TCP:[192.168.174.144:52818->110.242.69.21:80]>, {sa_family=AF_INET, sin_port=htons(52818), sin_addr=inet_addr("192.168.174.144")}, [128->16]) = 0 # 发送 HTTP 请求 sendto(5<TCP:[192.168.174.144:52818->110.242.69.21:80]>, "GET / HTTP/1.1\r\nHost: www.baidu."..., 77, MSG_NOSIGNAL, NULL, 0) = 77 # 接收 HTTP 响应 recvfrom(5<TCP:[192.168.174.144:52818->110.242.69.21:80]>, "HTTP/1.1 200 OK\r\nAccept-Ranges: "..., 102400, 0, NULL, NULL) = 2781 <!DOCTYPE html> <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html> +++ exited with 0 +++

模拟故障注入

strace -e inject=write:error=EPIPE ./my_program # 使所有 write 调用返回管道错误

strace问题定位案例

1、定位进程异常退出

问题:机器上有个叫做run.sh的常驻脚本,运行一分钟后会死掉。需要查出死因。

定位:进程还在运行时,通过ps命令获取其pid, 假设我们得到的pid是24298

strace -o strace.log -tt -p 24298

查看strace.log, 我们在最后2行看到如下内容:

22:47:42.803937 wait4(-1, <unfinished ...> 22:47:43.228422 +++ killed by SIGKILL +++

这里可以看出,进程是被其他进程用KILL信号杀死的。

实际上,通过分析,我们发现机器上别的服务有个监控脚本,它监控一个也叫做run.sh的进程,当发现run.sh进程数大于2时,就会把它杀死重启。结果导致我们这个run.sh脚本被误杀。

进程被杀退出时,strace会输出killed by SIGX(SIGX代表发送给进程的信号)等,那么,进程自己退出时会输出什么呢?

这里有个叫做test_exit的程序,其代码如下:

#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { exit(1); }

我们strace看下它退出时strace上能看到什么痕迹。

strace -tt -e trace=process -f ./test_exit

说明: -e trace=process 表示只跟踪和进程管理相关的系统调用。

输出:

23:07:24.672849 execve("./test_exit", ["./test_exit"], [/* 35 vars */]) = 0 23:07:24.674665 arch_prctl(ARCH_SET_FS, 0x7f1c0eca7740) = 0 23:07:24.675108 exit_group(1) = ? 23:07:24.675259 +++ exited with 1 +++

可以看出,进程自己退出时(调用exit函数,或者从main函数返回), 最终调用的是exit_group系统调用, 并且strace会输出exited with X(X为退出码)。

可能有人会疑惑,代码里面明明调用的是exit, 怎么显示为exit_group?

这是因为这里的exit函数不是系统调用,而是glibc库提供的一个函数,exit函数的调用最终会转化为exit_group系统调用,它会退出当前进程的所有线程。实际上,有一个叫做_exit()的系统调用(注意exit前面的下划线), 线程退出时最终会调用它。

2、定位共享内存异常

有个服务启动时报错:

shmget 267264 30097568: Invalid argument Can not get shm...exit!

错误日志大概告诉我们是获取共享内存出错,通过strace看下:

strace -tt -f -e trace=ipc ./a_mon_svr ../conf/a_mon_svr.conf

输出:

22:46:36.351798 shmget(0x5feb, 12000, 0666) = 0 22:46:36.351939 shmat(0, 0, 0) = ? Process 21406 attached 22:46:36.355439 shmget(0x41400, 30097568, 0666) = -1 EINVAL (Invalid argument) shmget 267264 30097568: Invalid argument Can not get shm...exit!

这里,我们通过-e trace=ipc 选项,让strace只跟踪和进程通信相关的系统调用。

从strace输出,我们知道是shmget系统调用出错了,errno是EINVAL。同样, 查询下shmget手册页,搜索EINVAL的错误码的说明:

EINVAL A new segment was to be created and size < SHMMIN or size > SHMMAX, or no new segment was to be created, a segment with given key existed, but size is greater than the size of that segment

翻译下,shmget设置EINVAL错误码的原因为下列之一:

  • 要创建的共享内存段比 SHMMIN小 (一般是1个字节)

  • 要创建的共享内存段比 SHMMAX 大 (内核参数kernel.shmmax配置)

  • 指定key的共享内存段已存在,其大小和调用shmget时传递的值不同。

从strace输出看,我们要连的共享内存key 0x41400, 指定的大小是30097568字节,明显与第1、2种情况不匹配。那只剩下第三种情况。使用ipcs看下是否真的是大小不匹配:

ipcs -m | grep 41400 key shmid owner perms bytes nattch status 0x00041400 1015822 root 666 30095516 1

可以看到,已经0x41400这个key已经存在,并且其大小为30095516字节,和我们调用参数中的30097568不匹配,于是产生了这个错误。

在我们这个案例里面,导致共享内存大小不一致的原因,是一组程序中,其中一个编译为32位,另外一个编译为64位,代码里面使用了long这个变长int数据类型。

把两个程序都编译为64解决了这个问题。

这里特别说下strace的-e trace选项。

要跟踪某个具体的系统调用,-e trace=xxx即可。但有时候我们要跟踪一类系统调用,比如所有和文件名有关的调用、所有和内存分配有关的调用。

如果人工输入每一个具体的系统调用名称,可能容易遗漏。于是strace提供了几类常用的系统调用组合名字。

-e trace=file 跟踪和文件访问相关的调用(参数中有文件名)
-e trace=process 和进程管理相关的调用,比如fork/exec/exit_group
-e trace=network 和网络通信相关的调用,比如socket/sendto/connect
-e trace=signal 信号发送和处理相关,比如kill/sigaction
-e trace=desc 和文件描述符相关,比如write/read/select/epoll等
-e trace=ipc 进程见同学相关,比如shmget等

绝大多数情况,我们使用上面的组合名字就够了。实在需要跟踪具体的系统调用时,可能需要注意C库实现的差异。

比如我们知道创建进程使用的是fork系统调用,但在glibc里面,fork的调用实际上映射到了更底层的clone系统调用。使用strace时,得指定-e trace=clone, 指定-e trace=fork什么也匹配不上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/31 15:22:32

osThreadId 获取当前线程的身份证号

osThreadId 中文释义&#xff1a;线程标识符 / 任务标识符详细说明&#xff1a;指在操作系统中创建线程&#xff08;或称任务&#xff09;时&#xff0c;系统为该线程分配的唯一身份标识。例如&#xff0c;调用 osThreadCreate 函数创建线程后&#xff0c;其返回值就是一个 o…

作者头像 李华
网站建设 2025/12/31 8:10:22

人工智能之数学基础 离散数学:第一章 集合论与逻辑推理

人工智能之数据基础 离散数学 第一章 集合论与逻辑推理—公式关注公众号 文章目录人工智能之数据基础 离散数学前言一、集合论&#xff08;Set Theory&#xff09;1. 基本概念2. 集合运算3. 集合恒等式&#xff08;定律&#xff09;✅ Python 实现&#xff1a;集合运算与幂集二…

作者头像 李华
网站建设 2025/12/29 20:02:08

Monodepth2:自监督单目深度估计的改进-k学长深度学习专栏

本文来源&#xff1a;k学长的深度学习宝库&#xff0c;点击查看源码&详细教程。深度学习&#xff0c;从入门到进阶&#xff0c;你想要的&#xff0c;都在这里。包含学习专栏、视频课程、论文源码、实战项目、云盘资源等。 1、Monodepth v2 的背景和动机 在 Monodepth&#…

作者头像 李华
网站建设 2025/12/31 21:27:31

缓存测试:Redis/Memcached数据一致性与击穿、雪崩问题模拟

测试视角下的缓存风险‌ 在高并发、高性能的现代应用架构中&#xff0c;缓存&#xff08;以Redis和Memcached为代表&#xff09;已成为标准配置。然而&#xff0c;它并非“银弹”&#xff0c;其引入在解决读写性能瓶颈的同时&#xff0c;也带来了新的复杂性和故障模式。对于测…

作者头像 李华
网站建设 2025/12/30 17:36:57

软件测试工程师的35岁危机:是确有其事,还是伪命题?

一个行业热议的十字路口‌ 在技术快速迭代的软件行业&#xff0c;“35岁危机”已成为一个高频词&#xff0c;常与开发、运维等岗位紧密相连。然而&#xff0c;当这一话题延伸至软件测试领域时&#xff0c;却引发了两极分化的讨论&#xff1a;一方认为测试工程师同样面临年龄增…

作者头像 李华