1. strace命令的基本使用场景
strace是非常常用的对用户态的进程运行状态进行跟踪的命令,默认启动一个命令并跟踪其执行过程中调用的所有系统调用和接收的信号。所有系统调用的名称,参数和返回值都会被输出到标准错误输出,或者通过 -o参数指定的文件。
strace有如下两种基本的使用方式,一种是直接启动一个命令,并跟踪到其运行周期结束。另一种是通过-p参数attach到一个进程上(底层的ptrace我们也在后续介绍下使用方法)。实时地得到其调用系统调用的输出。
strace cmd
strace -p $pid
如果是多线程或者多进程的程序,要进一步跟踪子进程,需要使用如下两个选项。
-f 跟踪由fork调用所产生的子进程.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
如下是一个strace 命令 attach到进程之后的基本的输出结果,可以观察到在未创建线程时,默认不显示pid,而在创建线程后,就会显示出具体的系统调用是在哪个pid上调用的。
$strace -f ./a.out
execve("./a.out", ["./a.out"], 0x7ef5b330 /* 42 vars */) = 0
brk(NULL) = 0x263000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f74000
...
[pid 17654] rt_sigprocmask(SIG_BLOCK, [CHLD], <unfinished ...>
[pid 17655] set_robust_list(0x76d454d0, 12 <unfinished ...>
[pid 17654] <... rt_sigprocmask resumed>[], 8) = 0
[pid 17654] rt_sigaction(SIGCHLD, NULL, <unfinished ...>
...
[pid 17655] exit(0) = ?
[pid 17655] +++ exited with 0 +++
<... nanosleep resumed>0x7e85801c) = 0
exit_group(0) = ?
+++ exited with 0 +++
a.out的具体代码示例如下,就是创建一个线程,然后在线程中创建一个文件进行编写的流程。
void * worker_thread1(void * pArg){
prctl(PR_SET_NAME, "thread1");
sleep(1);
FILE * fp = NULL;
fp = fopen("./test","w+");
fwrite("worker thread 1 end \n",20,1,fp);
fclose(fp);
return NULL;
}
int start_thread(){
pthread_t tid ;
pthread_attr_t threadattr;
pthread_attr_init(&threadattr);
pthread_create(&tid,&threadattr,worker_thread1,NULL);
}
int main(){
start_thread();
sleep(2);
return 0 ;
}
2. 优化显示内容
2.1 过滤特定系统调用
可以看到默认的strace的输出内容比较多,但可能包含了较多不关心的内容, strace提供了较多的过滤机制,其中最基本的就是以系统调用的集合来过滤,比如只关心特定的系统调用。
-e trace=syscall_set
e.g. -e trace=open,close,read,write //这样就是只跟踪上述系统调用。
-e expr 格式属于标准的格式,用于指定监控哪些事件,以及如何监控,表达式的格式如下
[qualifier=][!]value1[,value2]...
如下命令即显示了trace=access,只跟踪access系统调用的结果。
$ strace -f -e trace=access ./a.out
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
strace: Process 17487 attached
[pid 17487] +++ exited with 0 +++
+++ exited with 0 +++
2.2 过滤对特定路径的访问
如果想要监控访问了特定路径的系统调用,就需要使用-P命令。
-P path
Trace only system calls accessing path. Multiple -P options can be used to specify several paths.
示例如下,使用-P参数指定了test文件,注意这里指定的只能是绝对路径,而a.out中的代码如上的示例所示,就是打开并修改了这个test文件,如下中strace跟踪打印出了所有访问这个路径的系统调用,包括open,fstat64,write,close.
$ strace -f -P /path/to/test/strace/test ./a.out
strace: Process 17581 attached
[pid 17581] open("/path/to/test/strace/test", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
[pid 17581] fstat64(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
[pid 17581] write(3, "worker thread 1 end ", 20) = 20
[pid 17581] close(3) = 0
[pid 17581] +++ exited with 0 +++
+++ exited with 0 +++
2.3 strace输出中fd的显示优化
默认的strace 只显示了fd号,但是在实际应用中往往需要知道这个fd具体对应了什么内容,fd在实际运行中显然对于不同的模块来说会是一个随机的值。在比较新的strace版本中4.11以后,支持了decode-fds表达式,支持显示fd的具体信息。
decode-fds定义如下:
-e decode-fds=set
--decode-fds=set
Decode various information associated with file
descriptors. The default is decode-fds=none. set can
include the following elements:
path Print file paths. Also enables printing of
tracee's current working directory when AT_FDCWD
constant is used.
socket Print socket protocol-specific information,
dev Print character/block device numbers.
pidfd Print PIDs associated with pidfd file descriptors.
如上表述,默认的的decode-fds=none,因为解析肯定需要消耗性能。比如说path可以指定显示fd的路径,socket可以显示套接字对应的协议。以下是一个指定了decode-fds的应用例子。
如下是默认trace了close系统调用的例子,可以看到每个fd都是3,但根本不知道关闭了具体是啥
$ strace -f -e trace=close ./a.out
close(3) = 0
close(3) = 0
close(3) = 0
close(3) = 0
close(3) = 0
strace: Process 17649 attached
[pid 17649] close(3) = 0
[pid 17649] +++ exited with 0 +++
+++ exited with 0 +++
如下是显示了fd的式样,可以看到显示了出了每次close的fd对应的path路径。
$ strace -f -e trace=close -e decode-fds=path ./a.out
close(3</etc/ld.so.preload>) = 0
close(3</usr/lib/arm-linux-gnueabihf/libarmmem.so>) = 0
close(3</etc/ld.so.cache>) = 0
close(3</lib/arm-linux-gnueabihf/libpthread-2.19.so>) = 0
close(3</lib/arm-linux-gnueabihf/libc-2.19.so>) = 0
strace: Process 17700 attached
[pid 17700] close(3</path/to/test/strace/test>) = 0
[pid 17700] +++ exited with 0 +++
+++ exited with 0 +++
2.4 strace输出中pid的显示优化
默认的strace 即使携带了-f参数,也只显示了子进程的pid,但是在实际应用中往往需要知道这个pid具体是对应什么进程或者什么线程。
在比较新的strace版本中,笔者当前使用的是6.3.0的最新版本,支持了decode-pids表达式。支持显示pid的comm值。
decode-pids定义如下:
-e decode-pids=set
--decode-pids=set
Decode various information associated with process IDs
(and also thread IDs, process group IDs, and session IDs).
The default is decode-pids=none. set can include the
following elements:
comm Print command names associated with thread or
process IDs.
pidns Print thread, process, process group, and session
IDs in strace's PID namespace if the tracee is in
a different PID namespace.
如描述可以通过设置comm,来显示进程的名称。
如下是使用了decode-pids表达式,对nanosleep系统调用的跟踪结果,可以看到通过prctl命名的线程显示了其线程名称。
$ strace -f -e trace=nanosleep -e decode-pids=comm ./a.out
strace: Process 17809 attached
[pid 17808<a.out>] nanosleep({tv_sec=2, tv_nsec=0}, <unfinished ...>
[pid 17809<thread1>] nanosleep({tv_sec=1, tv_nsec=0}, 0x76d9ec8c) = 0
[pid 17809<thread1>] +++ exited with 0 +++
<... nanosleep resumed>0x7eb7001c) = 0
+++ exited with 0 +++
3.基于输出的显示内容进行过滤
如果说我们希望对strace的输出结果进行过滤要如何操作呢?。
比如说只关注对特定线程的系统调用,或者是只关注特定路径前缀的 fd的文件操作。
这样显然就需要对输出结果进行grep,不过strace默认是输出到标准错误的,所以我们在过滤之前,需要对其输出进行重定向。
如下给出了一个针对线程名称进行过滤的例子
$ strace -f -e trace=nanosleep -e decode-pids=comm ./a.out
strace: Process 18071 attached
[pid 18070<a.out>] nanosleep({tv_sec=2, tv_nsec=0}, <unfinished ...>
[pid 18071<thread1>] nanosleep({tv_sec=1, tv_nsec=0}, 0x76dd0c8c) = 0
[pid 18071<thread1>] +++ exited with 0 +++
<... nanosleep resumed>0x7eef801c) = 0
+++ exited with 0 +++
//如上是默认的显示结果,下面是在对输出进行重定向并用grep来过滤之后
$ strace -f -e trace=nanosleep -e decode-pids=comm ./a.out 2>&1 |grep thread1
[pid 18023<thread1>] nanosleep({tv_sec=1, tv_nsec=0}, 0x76de9c8c) = 0
[pid 18023<thread1>] +++ exited with 0 +++
4.监控特定文件描述符的读取输出内容
通过write和read表达式来实现。
-e write=set (监控文件描述符)
-e read=set
--read=set 从特定文件描述符中读取出来的内容,输出。
参考
https://man7.org/linux/man-pages/man1/strace.1.html