Linux:strace 简介

2023-12-01 20:30
文章标签 linux 简介 strace

本文主要是介绍Linux:strace 简介,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1. 前言
  • 2. 什么是 strace ?
  • 3. 使用 strace
    • 3.1 追踪指定进程
      • 3.1.1 通过程序名追踪进程
      • 3.1.2 通过 进程 ID (PID) 追踪程序
      • 3.1.3 追踪 子进程 或 线程
    • 3.2 系统调用情况统计
    • 3.3 追踪过滤
      • 3.3.1 追踪指定的系统调用集合
      • 3.3.2 追踪对指定文件句柄集合操作的系统调用
      • 3.3.3 追踪访问指定路径的系统调用
      • 3.3.4 追踪返回成功的系统调用
      • 3.3.5 追踪返回错误码的系统调用
      • 3.3.6 指定系统调用类别
    • 3.4 输出控制
      • 3.4.1 输出时间戳
      • 3.4.2 将追踪结果写入文件
    • 3.5 其它选项
  • 4. 交叉编译 strace
  • 5. 参考资料

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 什么是 strace ?

straceLinux 下一款诊断、调试、追踪工具,它可以用来监控和获取内核的系统调用、信号投递、进程状态变更等信息。其基本实现机制是通过 ptrace() 系统调用,来获取内核相关信息。对 strace 实现原理感兴趣的读者,可直接阅读 strace 源码,或参考博文 Linux:系统调用追踪原理简析 。

3. 使用 strace

3.1 追踪指定进程

3.1.1 通过程序名追踪进程

在程序启动时,可通过 strace 跟踪程序运行期间的所有系统调用。如:

# strace ls
execve("/bin/ls", ["ls"], 0xbeb73e50 /* 12 vars */) = 0
brk(NULL)                               = 0xc3000
uname({sysname="Linux", nodename="(none)", ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb6f0e000
...
stat64(".", {st_mode=S_IFDIR|0755, st_size=232, ...}) = 0
openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC|O_DIRECTORY) = 3
getdents64(3, 0xc30b0 /* 3 entries */, 32768) = 80
lstat64("./tcp_server", {st_mode=S_IFREG|0755, st_size=11192, ...}) = 0
getdents64(3, 0xc30b0 /* 0 entries */, 32768) = 0
close(3) 
...
exit_group(0)                           = ?
+++ exited with 0 +++

上面的输出,每一行显示一个系统调用,包含系统调用名、调用参数、以及返回值(= 后的内容),譬如输出:

openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC|O_DIRECTORY) = 3

表示调用了 open() ,打开的是当前目录 "." ,传递的 flags 参数为 O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC|O_DIRECTORY,返回值为 3

3.1.2 通过 进程 ID (PID) 追踪程序

首先找到目标进程的 PID:

# ps -ef | grep -v grep | grep InfiniteDsp160 root     ./InfiniteDsp

然后进行追踪:

# strace -p 160
strace: Process 160 attached
read(27, "\1\0\0\0", 4)                 = 4
[......]

3.1.3 追踪 子进程 或 线程

对于调用 fork()clone() 等系列接口的程序,在 3.1.1,3.1.2 基础上,需再加上 -f--follow-forks 命令行参数来进程追踪:

strace -f <program>
strace -fp <PID>

如有一个 PID 为 2622 的多线程程序,我们用 strace 来追踪它:

# strace -fp 2622
strace: Process 2622 attached with 4 threads
[pid  2627] restart_syscall(<... resuming interrupted poll ...> <unfinished ...>
[pid  2626] restart_syscall(<... resuming interrupted poll ...> <unfinished ...>
[pid  2625] restart_syscall(<... resuming interrupted restart_syscall ...> <unfinished ...>
[pid  2622] restart_syscall(<... resuming interrupted poll ...> <unfinished ...>
[pid  2626] <... restart_syscall resumed> ) = 0

从输出看到,PID 为 2622 的进程有 4 个线程,同时输出每行开头都带上了线程的 PID ,这样就能知道每个系统调用是由哪个线程发起的。

3.2 系统调用情况统计

通过 -c-C 命令行参数,统计系统调用消耗的总时间、调用总次数、每次消耗的平均时间、出错总次数等信息,并在结束追踪时输出这些信息。-c-C 的差别在于:-c 不会在追踪期间输出系统调用情况,而 -C 则相反。看一个简单的例子:

# strace -C -fp 2622
strace: Process 2622 attached with 4 threads
[pid  2626] restart_syscall(<... resuming interrupted poll ...> <unfinished ...>
[pid  2627] restart_syscall(<... resuming interrupted restart_syscall ...> <unfinished ...>
[pid  2625] restart_syscall(<... resuming interrupted restart_syscall ...> <unfinished ...>
[pid  2622] restart_syscall(<... resuming interrupted poll ...> <unfinished ...>
[pid  2626] <... restart_syscall resumed> ) = 0
[......]
^Cstrace: Process 2622 detached<detached ...>
strace: Process 2625 detached
strace: Process 2626 detached
strace: Process 2627 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------84.95    0.000948         474         2           restart_syscall8.42    0.000094          47         2         2 inotify_add_watch6.63    0.000074          25         3         2 recvmsg
------ ----------- ----------- --------- --------- ----------------
100.00    0.001116                     7         4 total

3.3 追踪过滤

3.3.1 追踪指定的系统调用集合

有时候并不想追踪程序所有的系统调用,通过下列选项

-e trace=syscall_set
-e t=syscall_set
--trace=syscall_set

指定想追踪不想追踪的系统调用集合。如只想追踪程序的 open() 调用:

# strace -e t=open /bin/ls
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/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/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/proc/filesystems", O_RDONLY)     = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open(".", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
[......]
+++ exited with 0 +++

strace 只输出程序运行过程中所有 open() 调用。如果想追踪多个系统调用,可以用 , 分隔,如:

# strace -e t=open,read /bin/ls
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/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\260Z\0\0\0\0\0\0"..., 832) = 832
open("/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\0`\t\2\0\0\0\0\0"..., 832) = 832
open("/lib/x86_64-linux-gnu/libpcre.so.3", 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\0000\25\0\0\0\0\0\0"..., 832) = 832
open("/lib/x86_64-linux-gnu/libdl.so.2", 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\240\r\0\0\0\0\0\0"..., 832) = 832
open("/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\0\260`\0\0\0\0\0\0"..., 832) = 832
open("/proc/filesystems", O_RDONLY)     = 3
read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tr"..., 1024) = 429
read(3, "", 1024)                       = 0
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open(".", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
[......]
+++ exited with 0 +++

3.3.2 追踪对指定文件句柄集合操作的系统调用

通过下列选项:

-e trace-fd=set
-e trace-fds=set
-e fd=set
-e fds=set

追踪对指定文件句柄集合操作的系统调用。如追踪所有对文件句柄 3 的操作:

# strace -e fd=3 /bin/ls
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\235h\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0777, st_size=898752, ...}) = 0
mmap2(NULL, 968040, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb6e01000
mmap2(0xb6ee8000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xd7000) = 0xb6ee8000
close(3)                                = 0
getdents64(3, 0xc30b0 /* 14 entries */, 32768) = 456
getdents64(3, 0xc30b0 /* 0 entries */, 32768) = 0
close(3)                                = 0
[......]
+++ exited with 0 +++

3.3.3 追踪访问指定路径的系统调用

通过下列选项:

-P path
--trace-path=path

追踪指定访问目录的系统调用。

# strace --trace-path=/var /bin/ls /var
stat64("/var", {st_mode=S_IFDIR|0777, st_size=672, ...}) = 0
openat(AT_FDCWD, "/var", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC|O_DIRECTORY) = 3
getdents64(3, 0xc30b0 /* 10 entries */, 32768) = 256
getdents64(3, 0xc30b0 /* 0 entries */, 32768) = 0
close(3)                                = 0
[......]
+++ exited with 0 +++

3.3.4 追踪返回成功的系统调用

通过下列选项:

-z
--successful-only

只追踪返回成功的系统调用。

3.3.5 追踪返回错误码的系统调用

通过下列选项:

-Z
--failed-only

只追踪返回错误码的系统调用。

3.3.6 指定系统调用类别

从前面知道,下列选项

-e trace=syscall_set
-e t=syscall_set
--trace=syscall_set

可以指定要追踪的系统调用集合。前面已经示范了通过具体的名字来指定集合,这里介绍通过类别指定系统调用集合的方式,即 syscall_set 还可以通过如下方式指定:

/regex : 正则表达式
%file, file: 追踪文件操作相关的系统调用(open,close,access,...)
%process, process: 追踪进程操作相关的系统调用(exec,...)
%net, %network, network: 追踪网络操作相关的系统调用(recv,...)
......

3.4 输出控制

3.4.1 输出时间戳

选项

-r 
--relative-timestamps[=precision]

输出相对时间(相对于程序启动时间):

# strace -r /bin/ls0.000000 execve("/bin/ls", ["/bin/ls"], [/* 26 vars */]) = 00.000589 brk(NULL)                 = 0x188c0000.000323 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)0.000285 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)0.000209 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 30.000335 fstat(3, {st_mode=S_IFREG|0644, st_size=100481, ...}) = 00.000566 mmap(NULL, 100481, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f9a94f300000.000304 close(3)                  = 0[......]0.000096 exit_group(0)             = ?0.000101 +++ exited with 0 +++

选项

-t
--absolute-timestamps

输出绝对时间:

# strace -t /bin/ls
11:18:35 execve("/bin/ls", ["/bin/ls"], [/* 26 vars */]) = 0
11:18:35 brk(NULL)                      = 0x14f0000
11:18:35 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
11:18:35 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
[......]

选项

-T
--syscall-times[=precision]

输出每个系统调用消耗的时间:

# strace -T /bin/ls
execve("/bin/ls", ["/bin/ls"], [/* 26 vars */]) = 0 <0.000381>
brk(NULL)                               = 0x1701000 <0.000126>
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory) <0.000135>
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory) <0.000094>
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000084>
[......]

3.4.2 将追踪结果写入文件

选项

-o filename
--output=filename

将追踪结果写入文件。

3.5 其它选项

strace 的更多使用方法可参考其手册 https://www.man7.org/linux/man-pages/man1/strace.1.html 。

4. 交叉编译 strace

先获取 strace 源码(当前版本为 6.6):

git clone https://gitlab.com/strace/strace.git

交叉编译 strace 源码(假定目标平台为 ARM32,交叉编译器为 arm-linux-gnueabihf-gcc):

./bootstrap
CC=arm-linux-gnueabihf-gcc LD=arm-linux-gnueabihf-ld RANLIB=arm-linux-gnueabihf-ranlib \
./configure --prefix=$(pwd)/out --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf
make && make install

将在 out 目录下生成 strace 程序:

$ tree out
out
├── bin
│   ├── strace
│   └── strace-log-merge
└── share└── man└── man1├── strace.1└── strace-log-merge.1

5. 参考资料

[1] https://strace.io/
[2] https://www.man7.org/linux/man-pages/man1/strace.1.html

这篇关于Linux:strace 简介的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/442591

相关文章

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

业务协同平台--简介

一、使用场景         1.多个系统统一在业务协同平台定义协同策略,由业务协同平台代替人工完成一系列的单据录入         2.同时业务协同平台将执行任务推送给pda、pad等执行终端,通知各人员、设备进行作业执行         3.作业过程中,可设置完成时间预警、作业节点通知,时刻了解作业进程         4.做完再给你做过程分析,给出优化建议         就问你这一套下