Systemtap简介
来源:互联网 发布:网络监控手机 编辑:程序博客网 时间:2024/05/29 18:50
一、 Systemtap简介
systemtap最早用于kernel分析,扩展了utrace/uprobe模块后可以用于监视用户程序。Systemtap允许使用者向内核代码或者用户空间的程序设置一个观测点,当内核代码或者用户程序运行到这个观测点时,使用者有机会执行一个自己编写的内核函数,读取该观测点上下文,进行分析与统计。常见的用法有函数调用的Callgraph生成,程序性能分析时用到的FlameGraph,内存瓶颈分析,网络流量统计等。例如下面的python脚本性能分析时画出的FlameGraph:
二、 systemtap安装
在debian8上的编译安装,首先下载systemtap-3.1,
确认gcc、make等基础编译环境已经安装好,然后:
$sudo apt-get install gettext$sudo apt-get install elfutils$sudo apt-get install libdw-dev$cd systemtap-3.1$./configure$make & sudo make install
编译完成后,在systemtap-3.1目录下会生成stap-prep,执行./stap-prep按照提示安装缺少的linux headers和debug包
三、 systemtap基本使用
Systemtap与ptrace相比,在易用性上有明显优势,ptrace要求写一段复杂的C代码进调用ptrace API完成追踪的功能,而systemtap只需要提供一个高级的awk-like脚本,格式为:
probe event {statements}
其中probe是脚本关键字,表示定义一个探测点,event是需要探测的事件名字,例如syscall.open, process(‘procName’).function(‘funcName’),一个简单的探测脚本只需要几行:
probe system.open{ printf("%s open %s\n", pid(), user_string($filename))}
具体用法可以参见:https://sourceware.org/systemtap/SystemTap_Beginners_Guide.pdf
四、 systemtap原理
为了实现探测,systemtap主要对脚本进行以下几步处理:
- stap命令解析systemtap脚本,在/usr/share/systemtap/tapset/找到输入脚本依赖的所有库脚本
- 把systemtap脚本翻译成C代码,然后调用GCC把C代码编译成一个内核模块,翻译的过程中需要用到被探测的程序的符号信息,所以目标程序是需要包含debug信息的或者是通过-d参数向systemtap提供符号表信息
- Staprun命令通过调用modprobe命令加载编译后的模块到内核,同时打开probes开关
- 事件触发时就执行我们写的handler,所有事件完成最后卸载内核模块
内核模块
内核模块是为了精简linux kernel而设计的,对于一些不一定需要的模块,启动时并不需要加载,等真正需要用的时候再通过内核提供的接口动态加载,可以保证内核比较精简的情况下,又不失可扩展的灵活性。内核模块可以被单独编译,但不能单独运行,必须通过内核加载后才能发挥作用,内核模块并没有main函数,主要有两种写法:
#include <linux/module.h> /* Needed by all modules */#include <linux/kernel.h> /* Needed for KERN_INFO */int init_module(void){ printk(KERN_INFO "Hello world 1.\n"); /* * A non 0 return means init_module failed; module can't be loaded. */ return 0;}void cleanup_module(void){ printk(KERN_INFO "Goodbye world 1.\n");}
上面的写法需要实现两个接口,init_module和cleanup_module,名字不能修改,但在linux2.4以后,支持自己定义内核模块入口等名字,只需要用module_init和module_exit宏来指定:
#include <linux/module.h> /* Needed by all modules */#include <linux/kernel.h> /* Needed for KERN_INFO */#include <linux/init.h> /* Needed for the macros */static int __init hello_2_init(void){ printk(KERN_INFO "Hello, world 2\n"); return 0;}static void __exit hello_2_exit(void){ printk(KERN_INFO "Goodbye, world 2\n");}module_init(hello_2_init);module_exit(hello_2_exit);
systemtap使用的是第一种写法,可以指定参数查看systemtap生成的C代码来确定: stap -v test.stp -p 3
可以看到生成的C代码中有#include “runtime.h”,其中又包含了#include
int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)@inode: the file in which the probe has to be placed.@offset: offset from the start of the file.@uc: information on howto handle the probe..
注册一个探测点,其中inode是目标程序的文件inode,offset是探测点距离elf文件加载开始位置的偏移量,uc是自定义的事件处理器,可以发现uprobe是基于文件和符号地址工作的,所以所有从这个elf文件启动的进程都将受uprobe模块监测,并且如果目标进程是多线程的,每个线程都会触发探测点事件。
不难看出,上面这个接口的关键在于找到这个偏移量,那么怎么找这个便宜量呢,举个例子假设我们有一个已经在运行的进程a.out,我们想监测a.out中名字为reg_server的函数,首先我们查看a.out进程的内存空间分布:
zt@debian:~$cat /proc/`pgrep a.out`/maps|grep a.out08048000-080ac000 r-xp 00000000 08:01 791802 /home/zt/server/bin/a.out080ac000-080ae000 rw-p 00063000 08:01 791802 /home/zt/server/bin/a.out
第二列是段的权限,可以看出上面的段是只读的,下面的是读写的,所以上面的是.text,下面的是.data,那现在要找的reg_server函数是在.text中,加载的开始地址是0x08048000我们再去看看a.out中这个函数的地址:
zt@debian:~/server$ readelf -Ws ./bin/a.out | c++filt | grep 'reg_server' 153: 08090183 13 OBJECT LOCAL DEFAULT 15 unreg_server 155: 08090d14 11 OBJECT LOCAL DEFAULT 15 reg_server 848: 08060e80 980 FUNC GLOBAL DEFAULT 13 unreg_server 1581: 08061260 1503 FUNC GLOBAL DEFAULT 13 reg_server
可以看到函数reg_server的地址为0x08061260,最终计算偏移量为0x08061260-0x08048000=0x19260,找到这个地址,uprobe模块会把该地址的指令替换为0xcc(即int 3指令),当程序执行到这里的时候就会产生一个sigtrap信号,用户程序就停到这里,然后执行uprobe_consumer 参数指定的自定义的函数,完成一次事件触发及事件处理
- Systemtap简介
- 内核探测工具systemtap简介
- 内核探测工具systemtap简介
- 内核探测工具systemtap简介
- 内核探测工具systemtap简介
- 内核探测工具systemtap简介
- systemtap
- SystemTap
- SystemTap
- systemtap
- 【简介】一个捕捉Linux系统事件工具:systemtap
- 内核调试神器SystemTap — 简介与使用(一)
- 内核调试神器SystemTap — 简介与使用
- 内核调试神器SystemTap — 简介与使用(一)
- 内核调试神器SystemTap — 简介与使用(一)
- 【SystemTap】SystemTap的疑问
- linux :内核调试神器SystemTap — 简介与使用(一)
- SystemTap Beginner
- eclipse导入项目后打开html文件乱码问题
- Activity生命周期
- qemu-nbd 挂载
- CodeForces
- Java集合框架
- Systemtap简介
- Kotlin 学习之数据类型
- CDH5.X完全卸载步骤
- 众多中缀运算符的调用机制
- Redis 常用命令汇总
- 嵌入式使用Symmetricds进行数据库同步
- js中,if条件为 null/undefined/0/NaN/"",结果均被解释为false
- 网易2018校招内推编程题--彩色砖块
- 单向数据流动的函数式 View Controller