systemtap介绍
SystemTap是一个诊断Linux系统性能或功能问题的开源软件。它使得对运行时的Linux系统进行诊断调式变得更容易、更简单。有了它,开发者或调试人员不再需要重编译、安装新内核、重启动等烦人的步骤。网上找了一张图可以比较清楚的说明它的工作原理:
systemtap安装:
1.安装步骤:
1.1 yum install systemtap
1.2 yum install kernel-devel kernel-headers gcc elfutils
1.3 通过 http://debuginfo.centos.org/7/x86_64/
下载kernel-debuginfo以及kernel-debuginfo-common,要下载对应内核版本的(错误版本会提示semantic error: no match等报错);
例如我的环境安装的内核版本为3.10.0-693.el7.x86_64:
所以我们需要下载如下两个包:
kernel-common-x86_64-3.10.0-693.el7.x86_64.rpm
kernel-debuginfo-common-x86_64-3.10.0-693.el7.x86_64.rpm
执行rpm安装依赖包:
1.4 执行stap-prep验证环境是否已经正常安装
如上这是一个常见错误,自动安装时没有匹配对kernel和kernel-devel版本,我们可以 卸载kernel-devel-3.10.0-1062.4.3.el7.x86_64,去官方源下载正确的头文件包装上。
安装完后测试stap是否可以正常工作:(永恒的hello world)
stap -e 'probe kernel.function("sys_open") {log("hello world") exit()}'
典型案例
我们想知道文件是谁创建的或者谁在访问这个文件,其实如果是后台daemon程序访问,且获取句柄后并没有立即释放那查询很简单,可以举例说明一下:
如上图用tail打开文件/root/.ssh/known_hosts,我们通过扫描/proc文件系统可以很快定位是进程号为7424的进程占用,再通过进程号返找进程名就行了,但是试想一下,如果程序没有一直占用这个句柄呢,是每次使用完就立即释放了呢,就没这么容易找了。下面看下systemtap是如何解决这个问题的,如下例子是利用systemtap跟踪系统调用sys_open的方法:
!/usr/bin/env stap
function is_open_creating:long (flag:long)
{
CREAT_FLAG = 4
if (flag & CREAT_FLAG)
{
return 1
}
return 0
}
probe begin
{
printf("monitor file beginn")
}
probe kernel.function("sys_open")
{
creating = is_open_creating($mode);
filename = user_string_quoted(pointer_arg(1));
if(creating)
{
printf("pid %ld (%s) create the file %s\n",pid(),execname(),filename);
}
else
{
printf("pid %ld (%s) open the file %s \n",pid(),execname(),filename);
}
}
我们来测试一下效果:
如上是抓到的结果,可以看到PID,进程名以及对应的文件操作和文件名。
当我们发现系统硬盘I/O繁忙时,第一反应就是用pidstat和iotop看一下哪些进程使用I/O频繁,如下:(我们做dd写盘测试然后用两个工具分别统计一下)
如上分别是pidstat和iotop的结果,可以很清晰的看出高IO调用的进程。
这里还可以简单介绍下上述两个进程统计的I/O数据是从哪里来的,如下截图我们可以看到也是从/proc下面捞出来的:
rchar: //在read(),pread(),readv(),sendfile等系统调用中读取的字节数
wchar: //在write(),pwrite(),writev(),sendfile等系统调用中写入的字节数
syscr: //调用read(),pread(),readv(),sendfile等系统调用的次数
syscw: //调用write(),pwrite(),writev(),sendfile等系统调用的次数
read_bytes: //进程读取的物理I/O字节数,包括mmap pagein,在submit_bio()中统计的
write_bytes: //进程写出的物理I/O字节数,包括mmap pageout,在submit_bio()中统计
cancelled_write_bytes: //如果进程截短了cache中的文件,事实上就减少了原本要发生的写I/O
从图中看貌似也非常容易就发现了该IO占用的进程,但是试想一下另一种常见情况如果占用I/O的进程不止一个,同时有高I/O占用的硬盘又不止一块呢,而我们又希望找出是那几个进程占用了具体哪个硬盘的I/O呢,此时上述两个工具已经帮不上太多忙了,这时候又可以请systemtap出马了:
! /usr/bin/env stap
global device_name
probe begin {
device_name = $1 //设备名
printf ("device name: %x\n", device_name)
}
probe kernel.function("submit_bio")
{
dev = $bio->bi_bdev->bd_dev
if (dev == device_name)
printf ("[%s](%d) dev:0x%x rw:%d size:%d\n",
execname(), pid(), dev, bio->bi_size)
}
这里我们的设备名需要转换为十进制表述方式具体方法如下:
其中Device type: fd,0就是我们想要的,我们的主设备号fd(12-bit),次设备号0(20-bit),转换后fd00000=265289728,这里我们做下echo 111 >/root/test往test文件写几个字节数据试试:
如上图:bash dev:0xfd00000 rw:1 size:4096 这里指进程号6125 往块设备dm-0写了4096字节数据,为什么是4096?可以去了解下块设备的读写操作以及文件系统的block size对读写性能的影响。
参考文献:
https://sourceware.org/systemtap/man/stap.1.html