SystemTap介绍

简介
SystemTap是一个诊断Linux系统性能或功能问题的开源软件。它使得对运行时的Linux系统进行诊断调式变得更容易、更简单。有了它,开发者或调试人员不再需要重编译、安装新内核、重启动等烦人的步骤。

为了诊断系统问题或性能,开发者或调试人员只需要写一些脚本,而且SystemTap本身也提供了很多脚本,称为”tapset”方便开发,然后通过SystemTap提供的命令行接口就可以对正在运行的内核进行诊断调试,以前需要的修改或插入调试代码、重新编译内核、安装内核和重启动等这些琐碎的工作完全消除。目前该工具并不支持对用户态应用的诊断调试,但是它们在以后会被添加进去。当前该项目的主要开发人员为来自Red Hat, IBM, Intel和Hitachi的工程师。其中Redhat主要负责脚本转换/翻译器和运行时库,IBM负责kprobe和relayfs,Intel负责转换器安全检查以及performance monitor tapset。

安装
ubuntu快速安装
apt-get install systemtap
下载debug-info 中找到对应内核版本的包,安装:
dpkg -i linux-image-3.0.0-15-generic-dbgsym_3.0.0-15.24_i386.ddeb
具体包的名称与内核版本相关。

其它系统安装
使用yum安装下列rpm包即可。
1. systemtap SystemTap包
yum install systemtap systemtap-runtime
2. gcc:C语言编译器
3. elfutils:提供库函数来分析调试信息
4. 手动安装内核信息包

kernel-debuginfo
kernel-debuginfo-common
kernel-devel
比如在2.6.18-53.el5内核i686机器上使用,下载下面的RPM包:

kernel-debuginfo-2.6.18-53.1.13.el5.i686.rpm
kernel-debuginfo-common-2.6.18-53.1.13.el5.i686.rpm
kernel-devel-2.6.18-53.1.13.el5.i686.rpm
测试
执行下面的脚本(需要特殊权限)
stap -v -e 'probe vfs.read {printf("read performed\n"); exit()}'

可能会输出下面的结果:

Pass 1: parsed user script and 45 library script(s) in 340usr/0sys/358real ms.
Pass 2: analyzed script: 1 probe(s), 1 function(s), 0 embed(s), 0 global(s) in
290usr/260sys/568real ms.
Pass 3: translated to C into “/tmp/stapiArgLX/stap_e5886fa50499994e6a87aacdc43cd392_399.c” in
490usr/430sys/938real ms.
Pass 4: compiled C into “stap_e5886fa50499994e6a87aacdc43cd392_399.ko” in
3310usr/430sys/3714real ms.
Pass 5: starting run.
read performed
Pass 5: run completed in 10usr/40sys/73real ms.

使用方法
SystemTap支持很多参数,可以直接将字符串当做STAP脚本执行,也可以执行STAP文件。

直接执行
与上一节的测试用例相同,可以直接把一个字符串当做stap脚本来执行。
stap -v -e 'probe vfs.read {printf("read performed\n"); exit()}'

使用-e执行的字符串,并用单引号括起来。

执行脚本文件
执行脚本文件的方法更简单。

stap `stap.stp'

也可以直接在脚本的第一行加入下面一句话,就可以直接执行:

#!/usr/bin/env stap

这句话告诉系统使用stap执行下面的内容。

脚本示例

#!/usr/bin/env stap
#
# This script continuously lists the top 20 systemcalls on the system
#

global syscalls

function print_top () {
        cnt=0
        log ("SYSCALL\t\t\t\tCOUNT")
        foreach ([name] in syscalls-) {
                printf("%-20s\t\t%5d\n",name, syscalls[name])
                if (cnt++ == 20)
                        break
        }
        printf("--------------------------------------\n")
        delete syscalls
}

probe kernel.function("sys_*") {
        syscalls[probefunc()]++
}

# print top syscalls every 5 seconds
probe timer.ms(5000) {
        print_top ()
}

这个脚本的功能是每隔5秒钟输出系统调用最多的20个系统调用。

stap选项
-v

让SystemTap输出更详细的信息。可以多次重复这个选项来输出更详细的信息,比如:    

stap -vvv script.stp

-o file_name

结果输出到文件

-S size,count

限制输出文件的大小(单位MB)和最大的文件数。这个选项会实现一个回滚机制的日志输出,日志文件名会加一个序列号的后缀。

-x process_id

设置SystemTap处理函数```target()```指向特定进程号。

-c command

设置SystemTap处理函数```target()```为指定的命令。

-e script

执行脚本而不是文件。

-F

使用SystemTap的长期记录模式,脚本后台执行。

原理
Systemtap使用了类似于awk和C语言的脚本语言(类似于Dtrace的D语言),它只使用了三种数据类型,整数(integers)、字符串(strings)以及关联数组(associative Arrays),它有完整的控制结构,包括块(blocks)、条件(conditionals)、循环(loops)和函数(functions)。语句分割符;是可选的,变量不需要声明类型,它们是根据上下文自动推测和检查的,它使用了kprobe提供的接口来实现探测,对于每一个探测,需要定义探测点以及相应的处理函数,探测点就是指kprobe中被探测的函数或指令地址(也被称为内核事件)的),但在Systemtap中,用户可以指定原文件,原代码的某一行,或者一个异步事件,如周期性的定时器,探测点使用了层次化的命名方式,探测点处理函数能够立刻输出数据,与printk很类似,它也能查看内核数据。脚本然后被一个翻译器转换成C代码并编译成一个内核模块。探测点根据内核的DWARF调试信息映射到内核的虚地址(因此Systemtap要求用户必须准备好可用的内核调试信息),所有的脚本内容在转换时进行严格的检查,并且在运行时也要检查(如无限循环、内存使用、递归和无效指针等),因此有好的安全性,不会影响正在运行的系统(这对生产系统是非常重要的)。 Systemtap包含了一个黑名单,其中列出的函数不能被Systemtap探测,因为它们会导致无限探测循环、锁重入等问题。
下图直观地给出了Systemtap的工作原理:

Systemtap脚本文件是.stp后缀的文件,使用的脚本语言是前面讲到的Systemtap自己定义的脚本语言,一个Systemtap脚本描述了将要探测的探测点以及定义了相关联的处理函数,每一个探测点对应于一个内核函数或事件或函数内部的某一位置。被关联的处理函数将在内核执行到对应的探测点时被执行。

tapsets是一个脚本库,包含了许多tapset,每一个tapset一般为某一内核子系统或特定的功能块预定义了一套探测点、辅助函数或全局变量供用户脚本或其它的tapset引用,它定义的一些数据能够被每一个探测点处理函数或脚本使用,这些数据通常通过使用处理函数语句块(HSB Handler Statement Block)来出口,HSB语句块中的变量就是被出口的数据。tapset一般由该内核子系统的开发者或对子系统非常了解的开发者编写,既使用了脚本语言,也使用了C语言,并且它已经被测试和验证,可以安全使用。tapsets属于Systemtap发行包的一部分。

Systemtap实现了一个脚本转换器/翻译器,当用户执行一个Systemtap脚本时,Systemtap将首先对它进行分析和一些安全检查,如果它引用了Systemtap预定义的脚本库提供的函数,Systemtap也将读取脚本库得到相应的代码,对于一些内核变量或符号的引用,它必须根据内核调试信息来解析到相应的地址。然后,它被转换成C代码,在这个转换中,Systemtap将根据需要增加必要的锁和安全检查代码。探测点之间共享的变量将被转换成恰当的静态声明并有锁保护,每组本地变量被转换到一个合成的调用帧结构中以避免消耗内核的栈空间。关联到探测点的处理函数被封装成一个接口函数,那调用恰当的kprobe接口函数来注册该探测点。

产生的C代码包含了一些对运行时tapset的引用,运行时tapset库提供了许多Systemtap接口函数,如通用的查询表、受限内存管理、启动、关闭、I/O操作以及其它一些函数。生成的C代码编译链接之后生成一个可加载的内核模块。为了快速得到运行结果,Systemtap使用了relayfs,当加载生成的内核模块后,该模块的初始化函数初始化自身,然后调用kprobe接口函数注册脚本中定义的探测点。当内核运行到注册的探测点时,相应的处理函数被调用,用户在处理函数中的输出语句将调用relayfs接口函数输出结果数据,用户在处理函数也可以调用一些内核的性能测量函数。当用户主动停止或脚本设定的条件满足时,模块将调用退出函数卸载已经注册的探测点并做一些清理处理就卸载模块自身。

Systemtap在运行时启动了一个进程,它专门负责通过relayfs读去模块的输出数据并即时地输出给用户。

深入理解SystemTap
SystemTap中有两个重要的概念:event和handler。在SystemTap执行一个脚本时,它会监控事件(event)。当事件发生时,Linux 内核就会执行handler。
事件的类型有开始/结束、定时器超时、会话终止等。handler就是在制定事件发生时需要做的一些脚本语句。

SystemTap 会话
当执行一个SystemTap脚本时,会话就开始了。
1. SystemTap检查已存的tapset库(一般放在/usr/share/systemtap/tapset/目录下);
2. SystemTap把脚本转换成C代码,用C编译器把C代码编译成一个内核模块;
3. SystemTap加载这个模块,然后开启脚本中的探测点。这个功能由systemtap-runtime包中的staprun提供;
4. 当某个事件发生时,对应的处理器(handler)就会执行;
5. 当SystemTap会话终止时,探测就会停止,对应的内核模块也会卸载。

SystemTap脚本
SystemTap脚本包含两个重要元素:event和handler。
SystemTap脚本使用扩展名.stp。
1. 探测点
探测点(probe)的语法格式如下:

probe event {statements}

一个探测点(probe)可以有多个事件,多个事件之间用逗号(,)隔开。
每个探测点都有一个对应的语句块(statement block)。语句块使用{}括起来。

NOTE: 脚本中的语句块语法和语义与C一样。一个语句块中可以嵌套其它的语句块。

2. 函数
函数定义语法如下:

function function_name(arguments) {statements}
probe event {function_name(arguments)}


3. 事件
事件可以分为两类:同步事件和异步事件。
同步事件
同步事件指执行到特定内核代码中时发生的事件。
同步事件包含下列几种:

syscall.system_call

监控系统调用,system_call可以改为要监控的系统调用,比如syscall.close。后面加一个.return用来监控系统调用返回,比如syscall.close.return。

vfs.file_operation

监控VFS(Virtual File System)的操作。也可以加.return后缀。

kernel.function(“function”)

监控内核函数。比如:kernel.function("sys_open")。同样,加.return就是监控函数返回。
还可以使用通配符(*)来监控某个文件上的多个函数,比如:

probe kernel.function("*@net/socket.c") {}
probe kernel.function("*@net/socket.c").return { }


kernel.trace(“tracepoint”)

tracepoint的静态探测。2.6.30版本之后的内核支持。需要在内核中静态的标记为tracepoint。例如kernel.trace("kfree_skb")指示每次释放一个网络缓冲区。

module(“module”).function(“function”)

模块中的函数:

probe module("ext3").function("*") { }
probe module("ext3").function("*").return { }


异步事件
异步事件包括:

begin

SystemTap会话开始,即脚本开始执行的时候触发的事件。

end

会话结束的事件。

timer events

周期性触发的事件。例如:

probe timer.s(4)
{
    printf("hello world\n");
}

例子中每4秒钟打一次hello world。SystemTap支持下面几种定时器:

timer.ms(milliseconds)
timer.us(microseconds)
timer.ns(nanoseconds)
timer.hz(hertz)
timer.jiffies(jiffies)
SystemTap脚本语法
快速浏览
stap的语法与C语法很像,不过是事件触发语言。
示例:

probe begin
{
    printf("hello world\n")
    exit()
}

这个脚本仅仅在SystemTap会话开始时(probe begin)打印了一个"hello world"就退出了(exit())。
如果没有exit(),可以在脚本执行过程中按Ctrl+C,或者直接kill掉进程。

printf使用

SystemTap中的printf与C中的printf用法一样:

printf("format string\n", arguments)

这里的format string支持%d, %s等格式,arguments是不定参数,参数之间用逗号(,)隔开。
示例:

probe syscall.open
{
    printf("%s(%d) open\n", execname(), pid())
}

这个脚本监控系统调用open。printf格式化打印一个字符串%s,即运行程序的名字(execname()),和一个整数,即进程的进程号(pid())。

输出:

vmware-guestd(2206) open
hald(2360) open
hald(2360) open
hald(2360) open
df(3433) open
df(3433) open
df(3433) open
hald(2360) open

变量

普通变量

SystemTap中的变量主要有三种类型:整数、字符串和数组。没有浮点数,整数当做int64处理,没有无符号整数。
变量类型不用明确指出,SystemTap会自动推导。
数组与C语言不同,具体可以参考SystemTap官方手册langref.pdf。
SystemTap中的变量也有全局变量和局部变量的区别。定义在{}中的变量都是局部变量,除非使用global说明。全局变量还可以在所有的probe和function外定义。
示例:

global count_jiffies, count_ms
probe timer.jiffies(100) { count_jiffies ++ }
probe timer.ms(100) { count_ms ++ }
probe timer.ms(12345)
{
    hz=(1000*count_jiffies) / count_ms
    printf ("jiffies:ms ratio %d:%d => CONFIG_HZ=%d\n",
    count_jiffies, count_ms, hz)
    exit ()
}

目标变量

目标变量就是在探测点上可以见到的代码中的一些变量。可以使用-L选项列出探测点上可用的目标变量。例如:
stap -L 'kernel.function("vfs_read")'
执行结果:

kernel.function(“vfs_read@fs/read_write.c:277”) $file:struct file* $buf:char* $count:size_t
$pos:loff_t*

目标变量都用$做前缀,:后跟上类型。
如果目标变量不是局部变量,比如全局变量或者其它文件中的静态变量,可以使用这种方式访问:

@var("varname@src/file.c")

函数
还是看例子:

function isprime(x) {
    if (x < 2) return 0
    for (i = 2; i < x; i++) {
        if (x % i == 0) return 0
        if (i * i > x) break
    }
    return 1
}

probe begin {
    for (i = 0; i < 50; i++)
        if (isprime(i)) printf("%d\n", i)
    exit()
}


这个例子的功能是打印0到49之间的素数。SystemTap的函数有几个特性:
- 只有一个返回值;
- 可以有多个参数;
- 函数的返回值,参数类型和变量类型都是可以自动推导的(也可以指定)。

SystemTap还列出了一些常用的函数:
tid()

线程ID

pid()

进程ID

uid()

用户ID

cpu()

CPU number

gettimeofday_s()

1970年1月1号开始的秒数

ctime()

秒数转换成日期

pp()

当前处理的探测点,字符串

execname()

执行程序的名字

name()

系统调用的名称。只能用在syscall.system_call的探测(probe)中。

target()

如果执行脚本时使用-x参数指定了进程ID,或者用-c参数指定了执行命令,就可以使用target()来判断是否是要处理的目标,比如:

probe syscall.* {
    if (pid() == target())
        printf("%s\n", name)
}
 

参考链接:

Systemtap原理简介_四不舍五不入的博客-CSDN博客_systemtap一、 Systemtap简介systemtap最早用于kernel分析,扩展了utrace/uprobe模块后可以用于监视用户程序。Systemtap允许使用者向内核代码或者用户空间的程序设置一个观测点,当内核代码或者用户程序运行到这个观测点时,使用者有机会执行一个自己编写的内核函数,读取该观测点上下文,进行分析与统计。常见的用法有函数调用的Callgraph生成,程序性能分析时用到的Flamehttps://blog.csdn.net/benjamin606/article/details/77854376SystemTap使用技巧【一】_金九 的技术博客-CSDN博客_docker systemtapSystemTap是一个强大的调试工具,确切的说应该是一门调试语言,因为它有自己的语法,也有解析、编译、运行等过程(准确的说有五个阶段),但它主要解决的问题是收集Linux内核或者用户进程的信息,主要目的是调试。我一直以为gdb、kgdb是Linux最强大的调试器,曾经爱不释手,自从发现了SystemTap之后,又有了当初喜欢gdb的那种感觉了,真的是相见恨晚啊。gdb和SystemTap不是竞争https://blog.csdn.net/wangzuxi/article/details/42849053转载链接:

 SystemTap介绍_羽飞的专栏-CSDN博客_systemtapSystemTap是一个诊断Linux系统性能或功能问题的开源软件。它使得对运行时的Linux系统进行诊断调式变得更容易、更简单。有了它,开发者或调试人员不再需要重编译、安装新内核、重启动等烦人的步骤。https://blog.csdn.net/hnwyllmm/article/details/50912362

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值