标签:ESS 替换 detach 权限 格式化 设备信息 mina 宽度 百分号
https://man7.org/linux/man-pages/man1/strace.1.html
strace - trace system calls and signals[跟踪系统调用和信号]
strace [-ACdffhikqqrtttTvVwxxyyzZ] [-I n] [-b execve] [-e expr]...
[-O overhead] [-S sortby] [-U columns] [-a column] [-o file]
[-s strsize] [-X format] [-P path]... [-p pid]...
[--seccomp-bpf] { -p pid | [-DDD] [-E var[=val]]...
[-u username] command [args] }
strace -c [-dfwzZ] [-I n] [-b execve] [-e expr]... [-O overhead]
[-S sortby] [-U columns] [-P path]... [-p pid]...
[--seccomp-bpf] { -p pid | [-DDD] [-E var[=val]]...
[-u username] command [args] }
最简单的例子就是运行一个strace
的命令。它会拦截并记录提供进程的系统调用和信号。它会把每一个系统调用打印到标准错误输出或是通过-o
参数指定的一个文件中。
strace
是一个有用的诊断、研究和调试的工具。系统管理员,分析人员和解决问题人员解决程序bug的时候,如果源码没有了,不能重新编译跟踪问题,这时候strace
作用就非常重要了。学生,黑客和喜欢专研的人,就算用strace
跟踪普通的程序,也可以学到非常多的关于系统和系统调用的知识。程序员应该知道,系统调用和信号是在用户层和内核层接口触发的事件,仔细检查边界信息,对隔离bug、完整性检测和捕获竞争条件非常有用。
每一行跟踪输出的内容都包括系统调用的名字,后面跟着放在括号内的传入的参数和返回值。跟踪cat /dev/null
命令行的结果如下:
open("/dev/null", O_RDONLY) = 3
错误(一般是返回值是-1的时候)包含错误代码和错误解释
open("/foo/bar", O_RDONLY) = -1 ENOENT (No such file or directory)
如果是信号的话,就会输出信号的代码和对siginfo
结构体的解码内容。对sleep 666
命令跟踪和打断的摘要是:
sigsuspend([] <unfinished ...>
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=...} ---
+++ killed by SIGINT +++
如果一个系统调用已经被执行了,这时,另一个调用被一个其他的线程或是进程调用,strace
会尝试保护这些事件的顺序把正在跑的调用标为unfinished
。等调用返回时,标为resumed
。如下:
[pid 28772] select(4, [3], NULL, NULL, NULL <unfinished ...>
[pid 28779] clock_gettime(CLOCK_REALTIME, {1130322148, 939977000}) = 0
[pid 28772] <... select resumed> ) = 1 (in [3])
通过信号传递中断一个可重启的系统调用,这种处理起来不一样,因为内核会中止系统调用,当信号补货后立马重新执行,如下:
read(0, 0x7ffff72cf5cf, 1) = ? ERESTARTSYS (To be restarted)
--- SIGALRM ... ---
rt_sigreturn(0xe) = 0
read(0, "", 1) = 0
参数通过传递的符号打印出来。当执行>>xyzzy
的时候,输出示例如下:
open("xyzzy", O_WRONLY|O_APPEND|O_CREAT, 0666) = 3
在这里open
第二个和第三个参数通过解码,把标识参数分解成与位运算的样式,按照8进制的格式输出出来。传统的或是本地的用法与ANSI或是POSIX的区别就是,最新的表单优先级最高。在一些情况下,strace
的输出不源码可读性更好。
结构体指针会通过解析引用而展示出指向的成员内容。大多数情况下,使用类似于C语言的方式格式化参数。比如ls -l /dev/null
命令的本质被捕获到是:
lstat("/dev/null", {st_mode=S_IFCHR|0666, st_rdev=makedev(0x1, 0x3), ...}) = 0
注意‘struct stat‘参数是如何被解除引用的,还有就是每一个成员是符合抽象显示的。尤其是,观察st_mode
参数是如何被解析成位运算或的形式。同样主要在这个例子中第一个参数lstat
,这是一个系统调用的输入,第二个参数是输出。如果系统调用失败了,第二个参数不会被修改,不是所有的参数都会被解除引用。比如,尝试执行ls -l
,附带一个不存在的文件名,会显示如下信息:
lstat("/foo/bar", 0xb004) = -1 ENOENT (No such file or directory)
这种情况就是门廊的灯亮着,但是没有人在屋内。
如果使用strace
打印不知道的系统调用,就会展示最原始的信息,不知道的系统调用会以"syscall_"开头,然后附带16进制格式展示:
syscall_0xbad(0x1, 0x2, 0x3, 0x4, 0x5, 0x6) = -1 ENOSYS (Function not implemented)
character的指针会被解析成C格式的string。不能打印的字符将会通过C语言的转义字符展示。只有前几位(默认是32个)字符才会打印出来,更长的字符将以省略号的形式附加到后面。这里有一个ls -
的命令,通过getpwuid
读取密码文件中的内容:
read(3, "root::0:0:System Administrator:/"..., 1024) = 422
结构体通过花括号表示。基本的指针和数组通过方括号表示,每个元素用都和隔开。这里有一个调用id
的例子,显示附带群组的id:
getgroups(32, [100, 0]) = 2
通过位运算传入的参数也是用中括号表示,只不过每个元素用空格区分,这里有一个尝试执行外部命令的示例:
sigprocmask(SIG_BLOCK, [CHLD TTOU], []) = 0
第二个参数是一个位标记,包含两个信号:SIGCHLD和SIGTTOU。有时候这个参数中表示的信号位太多,以至于打印未设置的参数更有效果。这种情况下,已波浪线开头,类属于这样:
sigprocmask(SIG_UNBLOCK, ~[], NULL) = 0
这里第二个参数表示所有信号的全部集合。
一个语法关系的表达式,用来修饰如何跟踪他们,格式如下:
[qualifier=][!]value[,value]...
这个修饰语是trace(t) abbrev(a) verbose(v) raw(x) signal/signals(s) read/reads(r) write/writes(w) fault inject status quiet/silent/silence(q) decode-fds(decode-fd) kvm。value是描述符相关的标记或是数字。默认描述符是trace。使用感叹号表示不使用设置的值。比如,-e open从字面上就表示-e trace=open,跟踪仅仅打开系统的调用。除此之外,这些特殊的数值,都有着明确的意义。
注意,有些shell会把感叹号用作历史扩展,几十在引号中间,所以这个时候需要用反斜杠进行转意。
运行命令的时候会在环境变量中增加var=val参数
在运行命令时从环境变量中删除var
在跟踪是附加到啊进程pid。跟踪会在接收到键盘中断信号(CTRL-C)的时候中止。当从跟踪的进程分离的时候,对应的进程会继续执行,strace也会相应这种情况。多个-p参数也可以使用,用于跟踪多个进程(如果是一个选项,至少有一个-p参数是提供的)。-p "pidof PROG
"也是支持的。
使用用户id,群组id和支持的群组名称来运行命令。这个参数只有在root用户下并且启用了setuid或setgid可执行文件。除非使用了setuid或是setgid,不然这个参数没有额外的功能。
当一个特殊的系统消息syscall到达时,与跟踪的进程分离。当前只支持execve,主要用作多线程调试的时候,不跟踪子进程。
当跟踪进程是,使tracer作为子进程,而不是父进程。这样减少了跟踪时看到的信息,保证直接跟踪到对应的子进程。
已特殊的群组属性运行tracer为子进程。除了减少跟踪时的信息,还可以避免被群组kill而关闭掉跟踪进程。
除了避免额外的信息,还可以避免session中止来关闭跟踪进程。
跟踪通过fork vfork clone创建的子进程。注意-p PID -f会跟踪所有的线程,而不仅仅是一个。
如果--output=filename参数设置了,每个进程的跟踪数据都会被写入到filename。pid是每个进程的process id。
联合--follow-forks和--output-separately参数。与-c参数不兼容,因为没有保留每个进程的计数。
有人可能想使用strace-log-merge来获得联合的strace日志。
什么时候strace可以被信号(比如CTRL-C)中断。
没有信号可以中断
当解码系统调用(默认的)fatal信号可以中断
fatal永远都可以中断(-o FILE PROG参数下的默认形式)
fatal信号和SIGTSTP(CTRL-Z)永远可以中断。在strace使用-o FILE PROG而不会在CTRL-Z停止时,比较有效,默认是-D
仅仅跟踪设置的特殊的系统调用。syscall_set可以定义为[!]value[,value],value的值可以是下面的一个:
跟踪特殊的系统调用,有他的名字定义
在系统调用前询问,允许限制在没有系统调用的时候匹配到提供的错误。
仅仅跟踪匹配的正则表达式。可以使用POSIX嵌入的正则表达式规则。
仅仅跟踪64位系统的调用
仅仅跟踪32位系统的调用
仅仅跟踪在64位系统上跑的32为程序的调用
跟踪系统调用,附带一个文件名是file的参数。可以认为是-e trace=open,stat,chmod,unlink,...等等的缩写,在查看哪个文件被引用的时候比较好用。但是不要忘记,在使用上面的参数时,lstat需要调用进去。不使用前面带百分号的表达式(-e trace=file)是不建议的。
跟踪与进程生命周期相关的系统调用(creation, exec, termination)。不使用前面带百分号的表达式(-e trace=process)是不建议的。
跟踪与网络相关的系统调用。不使用前面带百分号的表达式(-e trace=network)是不建议的。
跟踪与信号相关的系统调用。不使用前面带百分号的表达式(-e trace=signal)是不建议的。
跟踪与IPC相关的系统调用。不使用前面带百分号的表达式(-e trace=ipc)是不建议的。
跟踪与文件描述符相关的系统调用。不使用前面带百分号的表达式(-e trace=desc)是不建议的。
跟踪与内存映射相关的系统调用。不使用前面带百分号的表达式(-e trace=memory)是不建议的。
跟踪修改用户和群组标志或权限的系统调用。
跟踪stat系统调用的变量。
跟踪lstat系统调用的变量。
跟踪fstat fstatat statx系统调用的变量。
跟踪查询文件状态的系统调用(stat lstat fstat fstatat statx variants)
跟踪statfs statfs64 statvfs osf_statfs osf_statfs64的系统调用。类似于-e trace=/^(.*_)?statv?fs正则表达式
跟踪fstatfs fstatfs64 fstatvfs osf_fstatfs osf_fstatfs64的系统调用。类似于-e trace=/fstatv?fs正则表达式。
跟踪与文件系统统计相关的系统调用(statfs-like, fstatfs-like, ustat)。类似于-e trace=/statv?fs|fsstat|ustat正则表达式
跟踪读或是修改系统时钟的系统调用。
跟踪系统调用,成功的时候,没有参数。目前包括arc_gettls(2), getdtablesize(2), getegid(2), getegid32(2), geteuid(2), geteuid32(2), getgid(2), getgid32(2), getpagesize(2), getpgrp(2), getpid(2), tppid(2), get_thread_area(2) (on architectures other than x86), gettid(2), get_tls(2), getuid(2), getuid32(2), getxgid(2), getxpid(2), getxuid(2), kern_features(2), and metag_get_tls(2) syscalls.
-c的选项在决定跟踪哪个系统调用的时候非常有用。比如trace=open,close,read,write表示只跟踪这四个系统调用。注意,在用户/系统边界使用接口的时候,系统的子集调用也会被监控,默认值是trace=all。
只跟踪信号的子集。默认是signal=all。比如,signal=!SIGIO (or signal=!io)会使signal无法跟踪。
仅仅在特定的状态返回时,才跟踪系统调用。默认是status=all。当使用状态修饰的时候,因为strace要等待系统回调返回,才能知道是否跟踪,所以原始的顺序是无法完全保证的。比如,两个系统调用通过当前的线程执行,strace将会打印第一个返回退出的的信息,而不管每个条目的时间。第二个退出的系统调用会稍后打印。这里有一个例子,当调用select(2)的时候,不通的线程调用clock_gettime(2)在select(2)结束之前:
[pid 28779] 1130322148.939977 clock_gettime(CLOCK_REALTIME, {1130322148, 939977000}) = 0
[pid 28772] 1130322148.438139 select(4, [3], NULL, NULL, NULL) = 1 (in [3])
set可以包含如下成员:
successful 跟踪没有错误返回的系统调用。-z选项和status=successful一样。
failed 跟踪返回错误代码的系统调用。-Z选项和status=failed一样。
unfinished 跟踪没有返回的系统调用。这个有可能发生,比如,在临近线程调用execve。
unavailable 跟踪系统调用返回了,但是strace获取错误状态失败。
detached 跟踪strace在返回之间离开的系统调用。
跟踪访问path目录的系统调用。多个-P可以同时使用。
打印返回没有出错的系统调用。
打印返回错误代码的系统调用。
使每行返回的数据在单独的一列。默认宽度是40
对于输出打印的大的结构体进行缩写。这个语法syscall_set和-e设置一样。默认是abbrev=all。-v的参数相当于abbrev=none。
解析系统调用的结构体。这个表达式(syscall_set)与-e参数一样。默认是verbose=all。
打印原来的,没有解码的系统调用。这个表达式与-e的设置一致。这个设置可以使所有的参数以十六进制的形式打印出来。如果你不相信解码或是你需要知道原始的参数值,可以使用这个设置。同样可以参考-X。
打印出列在set中的文件描述符读取的所有16进制和ASCII码。比如,想看所有输入文件描述符3和5,可以使用-e read=3,5。注意,这个与正常跟踪read系统调用的设置(-e trace=read)是独立的。
打印出列在set中的文件描述符写入的所有16进制和ASCII码。比如,想看所有输出文件描述符3和5,可以使用-e write=3,5。注意,这个与正常跟踪write系统调用的设置(-e trace=write)是独立的。
限制各种信息。默认是quiet=none。可以有如下参数:
限制输出关于attach和detach的信息,比如("[ Process NNNN attached ]", "[ Process NNNN detached ]")
限制输出进程推出的信息,比如("+++ exited with SSS +++")
限制输出通过-P提供的目录的信息,比如("Requested path "..." resolved into "..."")
限制输出进程自己更改的信息,比如("[ Process PID=NNNN runs in PPP mode. ]")
限制关于进程被废弃的信息,通过其他线程调用execve,比如("+++ superseded by execve in pid NNNN +++")
解码与文件描述符相关的各种信息。默认是decode-fds=none。可以包含以下参数:
path 打印路径
socket 打印socket端口定义的信息
dev 打印字符串/块设备号
pidfd 打印与pidfd相关的文件描述符
打印推出kvm vcpu的原因。
打印当前系统调用的指令结构体
在系统调用结束后打印当前进程的执行堆栈。
把跟踪的信息写入到filename而不是输出到stderr。如果-ff参数设置了,filename.pid就会使用。如果参数前面加了‘|‘或‘!‘,表示其他的变量作为一个命令,所有的输出都重定向到它。这样对于调试程序,而不修改程序的输出指向非常方便。最新的已经与-ff不兼容了。
通过-o设置打开一个文件,附加到程序上。
限制关于附加,断开和所属属性修改的信息。一般发生在重定向文件和不通过附加立马执行程序。
限制关于附加,分离,个性修改和线程退出状态的信息。
限制所有可以隐藏的信息(轻参考-e quiet设置的描述,关于所有列出来的隐藏消息)
打印进入每一个入口的时间戳。这个记录的时间与成功调用系统接口的时间并不是一个。precision可以是s表示秒,ms表示毫秒或是ns表示纳秒。默认是毫秒。注意-r参数使用单调的始终,用来确保时间不通,而不是挂钟,这样可以确保不通的时间回报从-t的设置。
设置最大的输出字符串的长度。默认是32。注意,filename不使用这个规则,一直是打印所有的内容。
在每一行前面增加挂钟的时间,用特定的格式和特定的精确度。可以是如下一种:
none 没有时间戳打印,可以用作重写原来的设置。
time 挂钟时间,strftime使用%T格式的时间
unix 从epoch开始的秒数,strftime使用%s格式的时间
精确度可以是s秒,ms毫秒,us微秒,ns纳秒。默认是format:time,precision:s
每一行前面增加挂钟时间
如果给定了两次,那么时间将会以毫秒打印。
如果给定了三次,那么时间将会以毫秒打印,并且附带从epoch开始的秒数
展示系统调用花费的时间。这个记录每个系统调用开始和结束的时间差。可以是s秒,ms毫秒,us微秒,ns纳秒,并且允许设置打印的精确度。默认是us微秒
打印不缩写的环境变量,状态,终端接口信息,默认行为打印结构成员的合理子集。
打印所有非ASCII的字符串为16进制形式
打印所有的字符串以16进制形式
设置打印内容的格式,有如下参数:
打印与文件描述符相关的路径
打印所有与文件描述符相关的信息:与socket相关的端口协议信息,块/字符设备信息,进程id
记录时间,调用和错误信息,在每次系统调用结束时报告。屏蔽常规输出。这个尝试展示系统时间(CPU时间花费在内核上的)而不是挂钟时间。如果-c用作-f一起,只有总的跟踪集合。
与-c一样,只是会在程序跑的时候输出常规信息。
设置跟踪系统调用时的消耗。对于重写默认的探索,用来猜测在测量上面的时间,当系统调用使用-c的参数时。关于猜测的精确度可以通过记录给定程序的时间,而不跟踪使用time和比较累计的系统时间调用通过-c。
通过overhead的定义已经在时间描述中介绍了。
通过-c的参数,排序输出的内容,通过直方图定义的规则。合法的数据是时间(或者时间百分比或者时间总数或者总数时间),最小时间(或者最短的时间或者时间最小),最大时间(或者最长时间或者时间最大),平均时间(或者时间平均),回调(或者统计),错误(或者错误),名称(或者系统调用或者系统调用名称)和什么都没有,默认是时间。
配置一个集合(和命令)根据展示的列,在调用总结中。这个列的参数是逗号分隔,可以包含如下参数:
time-percent (or time) 指定系统调用的累计时间的百分比
total-time (or time-total) 总的系统时间,被系统调用消耗的,如果是-w参数,就是挂钟时间。
min-time (or shortest or time-min) 调用持续的最小时间
max-time (or longest or time-max) 调用持续的最大时间
avg-time (or time-avg) 调用的平均时间
calls (or count) 调用总数
errors (or error) 错误总数
name (or syscall or syscall-name) 系统名称
默认参数是time-percent,total-time,avg-time,calls,errors,name。如果name不支持,会被添加到最后一列。
统计系统调用开始和结束的时间差,默认是系统时间。
对定义的系统调用合计进行干预。表达式syscall_set与-e的设置一样。
至少一个error, retval, signal, delay_enter, delay_exit参数需要设置。error和retval是互斥的。
如果:error=errno设置了,一个错误将会被注入到系统调用:系统调用号将会被-1替换如果是一个非法的调用,除非一个系统调用设定:syscall= option,并且错误代码是类似于ENOSYS的符号错误码或者1..4095范围内的数字。
如果:retval=value设置了,一个成功将会注入:系统符号替换为-1,但是一个虚假的成功将会返回给调用者。
如果:signal=sig被设置,参数是一个符号类似于SIGSEGV或者数字在1..SIGRTMAX范围内,这个信号将会对定义的集合中的每一个系统调用投递。
如果:delay_enter=delay或:delay_exit=delay被设置,延时将会被注入:跟踪将会在进入和退出系统调用的时候延时。延时的格式已经在时间定义中介绍了。
如果:signal=sig被设置,并且没有:error=errno,:retval=value或:delay_{enter,exit}=usecs,仅仅有一个信号sig会被投递,而没有错误和延时。相反的,:error=errno或者:retval=value设置,而没有:delay_enter=delay,:delay_exit=delay或者:signal=sig设置,会注入一个错误,而没有信号或者延时。
如果:error=errno或者:retval=value和:signal=sig被设置,错误和成功都会注入,并且信号会被分发。
如果:syscall=syscall被设置,相应的系统调用会被注入没有额外影响而不是-1。目前只有"pure"(-e trace=%pure description)可以定义。
除非:when=expr的子表达式被设置,子表达式会被设置,一个注入将会进入每一个调用对于这个子集。
子表达式的格式是:
first[..last][+[step]]
数字第一个表示第一个调用数字的范围,数字最后一个表示最后一个的调用范围,step表示两个连续的调用步骤,如下的组合是可用的:
first 对于集合中的每一个系统调用,都注入第一个数字。
first..last 对于集合中每一个系统调用,注入数字第一个,并且所有的调用知道数字last,包括。
first+ 对于所有集合中的系统调用,执行数字第一个的注入,所有子集都是。
first..last+ 对于所有集合中的系统调用,执行数字第一个的注入,所有子集也是,知道数字最后一个,包括。
first+step 对于任何集合中的系统调用,执行注入first,first+step,first+step+step等等。
first..last+step 与上一个一样,只不过数字会增加到last,包括。
比如,对于第三个和随后的chdir系统调用注入ENOENT,使用-e inject=chdir:error=ENOENT:when=3+
第一个合法的数字是1..65535,最后一个是1..65534
一个注入表达式只能包含一个error=或者retval=参数,只有一个signal=参数如果一个表达式包含了多个when=表达式,最后一个优先。
记录系统调用次数是注入在每个系统调用和跟踪时做的。
系统注入的参数可以绑定其他的过滤设置,比如-P /dev/urandom -e inject=file:error=ENOENT.
为指定的系统调用集合注入断点。
这个与-e inject= expression类似,默认错误是ENOSYS
在标准错误的时候显示调试的信息。
这个参数不建议使用了。保留是为了向后兼容。与-f的使用一样。
打印帮助信息
启用seccomp-bpf (see seccomp(2)) 包含ptrace(2)-stops,在跟踪进程时。除非使用了-f/--follow-forks,不然没有什么效果。--seccomp-bpf在使用-p/--attach的参数时也不合适。企图使用seccomp-bpf用来过滤,将会有各种各样的原因导致失败。比如没有这么多系统调用可以过滤,seccomp接口不可用,跟踪的自己也被跟踪。因此,当seccomp-bpf失败时,strace更有用,并且停驶跟踪进程的每一个系统调用。
打印版本信息
时间可以格式化为小数点的形式,通过strtod(3),附带一个后缀s (seconds), ms (milliseconds), us (microseconds), or ns (nanoseconds)。如果没有后缀,默认是毫秒。格式是-O, -e inject=delay_enter和-e inject=delay_exit options.
当命令退出时,strace也会以相同状态退出。如果命令被信号中断,strace也会以相同信号中断。所以strace可以用作调用父进程的中转。记住,父子之间的关系(信号体制通知,getppid)在跟踪进程之间,父进程不会保藏除非使用-D
当使用-p而不使用命令,退出的状态是0,知道没有进程被附加或者没有想要的错误在跟踪。
标签:ESS 替换 detach 权限 格式化 设备信息 mina 宽度 百分号
原文地址:https://www.cnblogs.com/studywithallofyou/p/13167222.html