文章目录
Linux下,进程不能直接访问硬件设备。当进程需要访问硬件设备时(读取磁盘文件、接收网络数据等),则必须由用户态切换为内核态,然后通过系统调用来访问硬件设备。
strace是跟踪进程执行时的系统调用和所接收的信号(即它跟踪到一个进程产生的系统调用,包括参数、返回值、执行消耗的时间)。
strace最简单的用法是执行一个指定的命令(过程中,starce会记录和解析命令进程的所有系统调用及这个进程的所有的信号值),在指定命令结束后立即退出。
1、strace的基本介绍
参数:
-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column 设置返回值的输出位置.默认 为40.
-e expr 指定一个表达式,用来控制如何跟踪.格式:[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open 表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none. 注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
-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 abbrev=set 设定strace输出的系统调用的结果集.-v 等于abbrev=none.默认为abbrev=all.
-e raw=set 将指定的系统调用的参数以十六进制显示.
-e signal=set 指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set 输出从指定文件中读出 的数据.例如: -e read=3,5 -e write=set 输出写入到指定文件中的数据.
-o filename 将strace的输出写入文件filename
-p pid 跟踪指定的进程pid.
-s strsize 指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username 以username的UID和GID执行被跟踪的命令
2、strace的使用实例
对于如下test.cpp程序
#include<iostream>
using namespace std;
int main(){
int a;
cin>>a;
cout<<a<<endl;
return 0;
}
使用g++ -o test test.cpp
进行编译。
2.1、直接运行结果
2.2、strace追踪系统调用(strace ./test)
[root@init1 strace]# strace ./test
execve("./test", ["./test"], [/* 25 vars */]) = 0
brk(NULL) = 0x1ec7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd40f43f000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=29479, ...}) = 0
mmap(NULL, 29479, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd40f437000
close(3) = 0
open("/lib64/libstdc++.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 \262\5\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=991616, ...}) = 0
mmap(NULL, 3171168, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd40ef18000
mprotect(0x7fd40f001000, 2093056, PROT_NONE) = 0
mmap(0x7fd40f200000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xe8000) = 0x7fd40f200000
mmap(0x7fd40f20a000, 82784, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fd40f20a000
close(3) = 0
open("/lib64/libm.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\0pS\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1139680, ...}) = 0
mmap(NULL, 3150136, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd40ec16000
mprotect(0x7fd40ed17000, 2093056, PROT_NONE) = 0
mmap(0x7fd40ef16000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x100000) = 0x7fd40ef16000
close(3) = 0
open("/lib64/libgcc_s.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\220*\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=88776, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd40f436000
mmap(NULL, 2184192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd40ea00000
mprotect(0x7fd40ea15000, 2093056, PROT_NONE) = 0
mmap(0x7fd40ec14000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x7fd40ec14000
close(3) = 0
open("/lib64/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\0P%\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2173512, ...}) = 0
mmap(NULL, 3981792, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd40e633000
mprotect(0x7fd40e7f6000, 2093056, PROT_NONE) = 0
mmap(0x7fd40e9f5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c2000) = 0x7fd40e9f5000
mmap(0x7fd40e9fb000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fd40e9fb000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd40f435000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd40f433000
arch_prctl(ARCH_SET_FS, 0x7fd40f433740) = 0
mprotect(0x7fd40e9f5000, 16384, PROT_READ) = 0
mprotect(0x7fd40ec14000, 4096, PROT_READ) = 0
mprotect(0x7fd40ef16000, 4096, PROT_READ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd40f432000
mprotect(0x7fd40f200000, 32768, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7fd40f440000, 4096, PROT_READ) = 0
munmap(0x7fd40f437000, 29479) = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd40f43e000
read(0, 123456
"123456\n", 1024) = 7
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd40f43d000
write(1, "123456\n", 7123456
) = 7
exit_group(0) = ?
+++ exited with 0 +++
注:以上每一行都是一条系统调用,等号左边是系统调用的函数名和参数,右边是该调用的返回值
strace显示这些调用的参数并返回符号形式的值。
strace从内核接收信息,而且不需要以任何特殊的方式来构建内核
2.3、strace跟踪信号传递
首先,strace ./test
,等到等待输入的那一步时,不输入任何东西,然后打开另外一个窗口,输入命令:killall test
我们观察第一次打开的strace窗口中,此时,我们看见程序退出了,结果如下(仅截取末尾几行):
2.4、系统调用统计
使用-c
参数,它会将进程的所有系统调用做一个统计分析展示出来
这里清楚的告诉我们,在这一过程中调用了哪些系统函数,调用了多少次,消耗了多少时间等信息,这对我们分析一个程序是很有帮助的。实例如下:
-o
选项重定向输出
-o 将strace 的结果输出到文件中若不指定 -o 参数的话,默认的输出设备是STDERR,也就是说,使用 -o filename
和 2>filename
的结果是一样的。执行strace -c -o file ./test
结果如下:
-T
选项对系统调用进行计时
-T 将每个系统调用所花费的时间打印出来。每个调用的花销体现在调用行最右边的尖括号里边(下面只是调用的一部分)
strace -T ./tes
t执行后结果如下(截取前几行):
系统调用的时间
-t/-tt/-ttt
-t 精确到秒
-tt 精确到微秒
-ttt 精确到微秒,而且时间表示为unix时间戳。
调用strace -t ./test
结果如下(截取前几行):
截断输出
-s
指定trace结果的每一行输出的字符串长度
现指定-s 5
,而在read时,我们输入一个超过5个字符的字符串
追踪现有的进程
strace -p pid
2.5、用strace调试程序
对于如下程序:
#include<iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
int main() {
char buffer[256];
ifstream in("input.txt");
if(!in.is_open()) {
cout<<"error open file"<<endl;
exit(1);
}
while(!in.eof()) {
in.getline(buffer,100);
cout<<buffer<<endl;
}
return 0;
}
如果不创建input.txt文件夹的话直接执行肯定会报错,如下:
但是仅仅看这个提示我并不知道究竟是哪个系统调用出错,或者说我想更精确的定位出错的位置要怎么办?
执行strace ./stracetest
后就能够直接定位具体的错误位置了: