strace :监控程序的执行状况

在linux 空间下,运行一个程序时,操作系统会将应用程序封装一个进程的形式,参与操作系统的调度,可以使用strace 跟踪程序运行的情况。

基本功能

  • 监控用户进程与内核进程的交互
  • 追踪进程的系统调用、信号传递、状态变化

一、系统调用

  • 分类
  • 文件和设备访问:open、close、read、write、ioctl等
  • 进程管理:fork、clone、execve、exit 等
  • 信号:signal、kill等
  • 内存管理:brk、mmap、mlock等
  • 进程间通信:semget、信号量、消息队列
  • 网络通信:socket、connect等
  • 常用参数
-c   统计每一系统调用的所执行的时间,次数和出错的次数等
-d 输出strace关于标准错误的调试信息
-f 跟踪由目标进程及调用所产生的子进程
-F 尝试跟踪目标调用 在-f时,vfork不被跟踪
-a 设置返回值的输出位置.默认 为40
-r 打印出相对时间关于每一个系统调用
-t 在每行输出的后面,显示调用花费时间
-tt 在每行输出的前面,显示调用花费毫秒级别的时间
-T 每次系统调用所花费的时间
-v 对某些相关调用,把完整的环境变量、文件stat结构打印出来
-p pid 指定要跟踪的进程pid,同时跟踪的多个pid,重复多次-p选项即可
-o filename: 将跟踪输出写入文件名
-s 当系统调用的某个参数是字符串时,最多输出制定长度的内容,默认时32个字节

-e set: 仅跟踪某些系统调用
-e open,close: 仅跟踪打开/关闭系统调用
-e file: 仅跟踪文件系统调用/文件操作相关的
-e process: 跟踪所有涉及流程管理的系统调用
-e network: 跟踪所有与网络相关的系统调用
-e signal:跟踪所有与信号相关的系统调用
-e ipc: 跟踪所有与ipc相关的系统调用
-e desc: 跟踪所有与文件描述符相关的系统调用
-e memory: 跟踪所有与内存映射相关的系统调用
-e set: 仅跟踪指定的信号子集
示例《一》
  • 查看一个程序所有的open、close系统调用
  • 查看每个程序调用消耗的时间
  • 统计系统调用次数、错误次数统计
  • 打印系统调用的时间戳
  • 将追踪日志保存到log文件中
注释:

查看所有程序运行 strace python server.py
查看所有程序运行 strace ./aa.sh

查看打开系统调用情况 strace -e open ./aa.sh
查看打开关闭系统调用情况 strace -e open,close ./aa.sh
查看打开关闭系统调用时间 strace -T -e open,close python3 main.py (T 时间在后面显示)

查看打开关闭系统调用次数 strace -c -T -e open,close ./aa.sh (如程序一直在运行,停止后才会统计)
查看打开关闭系统调用时间 strace -t -T -e open,close ./aa.sh
查看打开关闭系统调用时间 strace -tt -T -e open,close ./aa.sh (tt
查看打开系统调用输入log strace -tt -T -e open -o trace.log ./aa.sh (-o 输入trace.log文件)

strace -tt -T -V -e trace=file -o /data/log/trace.log -s 1024 -p 23489

二、系统调用函数说明

  • 进程管理

函数名

说明

fork

创建一个新进程

clone

按制定条件创建子进程

execve

运行可执行文件

pause

进程将处于阻塞状态

wait

等待子进程终止

waitpid

等待制定子进程终止

  • 文件和设备访问

函数名

说明

open

打开文件

create

创建新文件

close

关闭文件描述符

read

读文件

write

写文件

pread

对文件随机读

pwrite

对文件随机写

poll

I/o多路转换

truncate

截断文件

  • 文件系统操作

函数名

说明

access

确定文件的可存取性

chmod

确定文件的可存取性

chown

改变文件的属主或用户组

chroot

改变文件状态信息

stat

获取文件的状态信息

lstat

获取文件的状态信息

getdents

读取目录项

mkdir

创建目录

link

建立链接

  • 内存管理

函数名

说明

mmap

映射虚拟内存页(分配内存)

sync

将内存缓冲区数据写回硬盘

  • socket 控制

函数名

说明

socketcall

socket系统调用

socket

建立socket

bind

绑定socket到端口

connect

链接远程主机

send

通过socket发送信息

sendto

发送UDP信息

sendmsg

参见send

listen

监听socket端口

select

对多路同步I/O进行轮询

示例《二》
  • ​ping baidu系统调用过程​
ping www.baidu.com
strace ping www.baidu.com
strace -e poll,select,connect,sendto ping www.baidu.com
  • ​cat /etc/passwd​​ 系统调用过程
strace cat /etc/passwd
[root@VM-0-14-centos ~]# strace cat /etc/passwd

运行命令
execve("/usr/bin/cat", ["cat", "/etc/passwd"], 0x7ffeee8bfe78 /* 28 vars */) = 0
brk(NULL) = 0x1d9f000

为运行命名分配内存空间
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6081756000

确定文件的可存取性 -1 表示文件不可读
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (没有那个文件或目录)

打开文件
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

文件状态:作用于已打开的文件的文件描述符而不是文件名
fstat(3, {st_mode=S_IFREG|0644, st_size=37645, ...}) = 0
mmap(NULL, 37645, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f608174c000
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\0`&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156352, ...}) = 0
mmap(NULL, 3985920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f6081168000

参见下文mprotect 命令
mprotect(0x7f608132c000, 2093056, PROT_NONE) = 0
mmap(0x7f608152b000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f608152b000
mmap(0x7f6081531000, 16896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f6081531000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f608174b000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6081749000
arch_prctl(ARCH_SET_FS, 0x7f6081749740) = 0
mprotect(0x7f608152b000, 16384, PROT_READ) = 0
mprotect(0x60b000, 4096, PROT_READ) = 0
mprotect(0x7f6081757000, 4096, PROT_READ) = 0
munmap(0x7f608174c000, 37645) = 0
brk(NULL) = 0x1d9f000
brk(0x1dc0000) = 0x1dc0000
brk(NULL) = 0x1dc0000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106176928, ...}) = 0
mmap(NULL, 106176928, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f607ac25000
close(3) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
open("/etc/passwd", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1419, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 65536) = 1419
write(1, "root:x:0:0:root:/root:/bin/bash\n"..., 1419root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
libstoragemgmt:x:998:997:daemon account for libstoragemgmt:/var/run/lsm:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:997:995::/var/lib/chrony:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
syslog:x:996:994::/home/syslog:/bin/false
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
nginx:x:995:991:nginx user:/var/cache/nginx:/sbin/nologin
redis:x:994:990:Redis Database Server:/var/lib/redis:/sbin/nologin
) = 1419
read(3, "", 65536) = 0
close(3) = 0
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++

三、其他

在Linux中,mprotect()函数可以用来修改一段指定内存区域的保护属性。
函数原型如下:

#include <unistd.h>
#include <sys/mmap.h>
int mprotect(const void *start, size_t len, int prot);
mprotect()
函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。

prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
1)PROT_READ:表示内存段内的内容可写;
2)PROT_WRITE:表示内存段内的内容可读;
3)PROT_EXEC:表示内存段中的内容可执行;
4)PROT_NONE:表示内存段中的内容根本没法访问。

需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。