strace命令是一个集诊断、调试、统计与一体的工具,可以用来监控用户进程和内核的交互,比如对应用程序的系统调用、信号传递与进程状态变更等进行跟踪与分析。
命令解析
常见命令
-
[-o file]:将strace的输出写入指定的文件。
-
[-s strsize]:将打印字符串的长度限制为strsize个字符(默认值为32)。
-
-d:输出strace关于标准错误的调试信息。
-
-r:打印出相对时间关于每一个系统调用。
-
-f:跟踪由fork()调用所产生的子进程。
-
-t:在输出中的每一行前加上时间信息。
-
-tt:在输出中的每一行前加上时间信息(微秒级)。
-
-T:打印在每个系统调用中花费的时间。
-
-r:打印相对时间戳。
-
-x:以十六进制打印非ascii字符串。
-
-xx:以十六进制打印所有字符串。
-
-y:打印与文件描述符参数关联的路径。
-
-yy:打印与套接字文件描述符关联的协议特定信息。
命令统计
-
-c:统计每一系统调用的所执行的时间,次数和出错的次数等。
-
-C:像-c一样,也可以打印常规输出。
-
-w:汇总系统调用延迟(默认为系统时间)
-
[-O overhead]:设置将系统调用跟踪到OVERUSE的开销
-
[-S sortby]:按以下顺序对系统调用计数:时间,调用,名称等
命令过滤
- [-e expr]…:指定一个表达式,用来控制如何跟踪。格式如下:
[qualifier=][!]value1[,value2]...。
描述说明:
qualifier只能是trace、abbrev、verbose、raw、signal、read、write其中之一,value是用来限定的符号或数字,默认的 qualifier是trace。感叹号是否定符号,例如:-e open等价于-e trace=open,表示只跟踪open调用;而-etrace!=open表示跟踪除了open以外的其他调用,还有两个特殊的符号all和none,它们分别代表所有选项与没有选项。
-
-e trace=set只跟踪指定的系统调用,例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用,默认的为set=all。 -
-e trace=file只跟踪有关文件操作的系统调用。 -
-e trace=process只跟踪有关进程控制的系统调用。 -
-e trace=network跟踪与网络有关的所有系统调用。 -
-e strace=signal跟踪所有与信号有关的系统调用。 -
-e trace=ipc跟踪所有与进程通讯有关的系统调用。 -
-e raw=set将指定的系统调用的参数以十六进制显示。 -
-e signal=set 指定跟踪的系统信号。默认为all(所有信号)。如signal=!SIGIO或者signal=!io,表示不跟踪SIGIO信号。 -
-e read=set输出从指定文件中读出的数据。 -
-e write=set输出写入到指定文件中的数据。
eg
打印Hello World
#include <stdio.h>
int main() {
printf(".");
return 0;
}strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffc0b5355e0 /* 72 vars */) = 0
brk(NULL) = 0x5865db828000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=199691, ...}) = 0
mmap(NULL, 199691, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7da562d32000
close(3) = 0
openat(AT_FDCWD, "/usr/lib/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`^\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
fstat(3, {st_mode=S_IFREG|0755, st_size=1989944, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7da562d30000
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
mmap(NULL, 2014064, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7da562b44000
mmap(0x7da562b68000, 1490944, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x24000) = 0x7da562b68000
mmap(0x7da562cd4000, 319488, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x190000) = 0x7da562cd4000
mmap(0x7da562d22000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1dd000) = 0x7da562d22000
mmap(0x7da562d28000, 31600, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7da562d28000
close(3) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7da562b41000
arch_prctl(ARCH_SET_FS, 0x7da562b41740) = 0
set_tid_address(0x7da562b41a10) = 60198
set_robust_list(0x7da562b41a20, 24) = 0
rseq(0x7da562b42060, 0x20, 0, 0x53053053) = 0
mprotect(0x7da562d22000, 16384, PROT_READ) = 0
mprotect(0x5865da155000, 4096, PROT_READ) = 0
mprotect(0x7da562d95000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7da562d32000, 199691) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}) = 0
getrandom("\x1a\x1f\x54\xd6\x62\x46\xb1\x4c", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x5865db828000
brk(0x5865db849000) = 0x5865db849000
write(1, ".", 1.) = 1
exit_group(0) = ?
+++ exited with 0 +++可见,一个简单的打印程序就需要调用大量的系统调用,编写高效的系统内核显得尤其重要。