怎么做网站导航栏,个性化定制产品,图书销售网站开发与实现,传统行业网站建设文章目录 1. 前言2. 什么是 strace ?3. 使用 strace3.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 … 文章目录 1. 前言2. 什么是 strace ?3. 使用 strace3.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. 交叉编译 strace5. 参考资料 1. 前言
限于作者能力水平本文可能存在谬误因此而给读者带来的损失作者不做任何承诺。
2. 什么是 strace ?
strace 是 Linux 下一款诊断、调试、追踪工具它可以用来监控和获取内核的系统调用、信号投递、进程状态变更等信息。其基本实现机制是通过 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({sysnameLinux, nodename(none), ...}) 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) 0xb6f0e000
...
stat64(., {st_modeS_IFDIR|0755, st_size232, ...}) 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_modeS_IFREG|0755, st_size11192, ...}) 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.13.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 detacheddetached ...
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 tracesyscall_set
-e tsyscall_set
--tracesyscall_set
指定想追踪或不想追踪的系统调用集合。如只想追踪程序的 open() 调用
# strace -e topen /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 topen,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-fdset
-e trace-fdsset
-e fdset
-e fdsset
追踪对指定文件句柄集合操作的系统调用。如追踪所有对文件句柄 3 的操作
# strace -e fd3 /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_modeS_IFREG|0777, st_size898752, ...}) 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-pathpath
追踪指定访问目录的系统调用。
# strace --trace-path/var /bin/ls /var
stat64(/var, {st_modeS_IFDIR|0777, st_size672, ...}) 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 tracesyscall_set
-e tsyscall_set
--tracesyscall_set
可以指定要追踪的系统调用集合。前面已经示范了通过具体的名字来指定集合这里介绍通过类别指定系统调用集合的方式即 syscall_set 还可以通过如下方式指定
/regex : 正则表达式
%file, file: 追踪文件操作相关的系统调用(open,close,access,...)
%process, process: 追踪进程操作相关的系统调用(exec,...)
%net, %network, network: 追踪网络操作相关的系统调用(exec,...)
......
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_modeS_IFREG|0644, st_size100481, ...}) 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
--outputfilename
将追踪结果写入文件。
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
CCarm-linux-gnueabihf-gcc LDarm-linux-gnueabihf-ld RANLIBarm-linux-gnueabihf-ranlib \
./configure --prefix$(pwd)/out --hostarm-linux-gnueabihf --targetarm-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