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:

 

在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必须是页大小的整数倍。