systemtap 学习

SystemTap 提供了一个简单的命令行接口和强大的脚本语言,同时预定义了丰富的脚本库。基于内核中的 kprobes,SystemTap 允许用户自由地从运行中的内核收集调试信息和性能数据,来用于之后的分析和处理。用户可以随时开始或者停止这个收集过程,而无需漫长的修改代码、编译内核和重启系统这个循环
0x01 SystemTap Features

动态的性能分析能力,不打断分析目标的逻辑,不改写、侵入分析目标的代码。
多种多样的probe events
也可以调试分析功能性的问题
低开销,安全性高,适用于正式产品
持续长时间的性能分析
按需probe,可以用脚本编写,过滤信息的输出和采集
易用性较好

0x02 SystemTap 利用的技术Kprobes/Kretprobe/Uprobes/Relayfs
kernel层的一个接口,注册后可以非侵入的动态插入绝大多数(注1)函数并采集调试信息和性能数据。当插桩点被hit时,会指定一个自定义的处理函数(handler)来处理新的逻辑。
Kprobes 从 2.6.9 版本开始就添加到主流的 Linux 内核中,并且为探测内核提供一般性服务。它提供一些不同的服务,但最重要的两种服务是 Kprobe 和 Kretprobe。Kprobes插桩的技术很简单,同调试器和动态补丁技术的原理大致相同。首先会拷贝一份需要probe点的指令内容,并且把旧的代码段替换成一个断点指令(e.g., int3 on i386 and x86_64)。当断点指令被触发后,处理了新逻辑,然后会恢复原来的代码段内容,接着执行原始的指令(从断点开始)。
Kretprobes 有所不同,它操作调用函数的返回结果。注意,因为一个函数可能有多个返回点,所以听起来事情有些复杂。不过,它实际使用一种称为 trampoline 的简单技术。您将向函数条目添加一小段代码,而不是检查函数中的每个返回点。这段代码使用 trampoline 地址替换堆栈上的返回地址 —— Kretprobe 地址。当该函数存在时,它没有返回到调用方,而是调用 Kretprobe(执行它的功能),然后从 Kretprobe 返回到实际的调用方。
Relayfs 一种高速的数据转发文件系统。可以从kernel层高速,直接,可靠的把数据转发到用户空间读。
许多trace/debug工具都是利用了relayfs机制,eg. Systemtap/Lttng/Debugfs
0x03 SystemTap working
stap
语法分析阶段(parse)
主要是检查输入脚本是否存在语法错误,例如大括号是否匹配、变量定义是否规范等
细化(Elaborate)
细化是分析输入脚本并解析对内核的引用或用户符号与 tapsets 的处理阶段。Tapsets 是用来扩展脚本能力的脚本或 C 代码库。细化将脚本中的外部引用解析为符号信息并导入脚本子程序,为下一步转换为 C 程序做准备,这个过程就类似于将库文件链接到目标文件。对内核数据如函数参数、局部和全局变量、函数以及源地址都需要被解析为运行时实际的地址,这是通过对构建内核时编译器产生的 DWARF 调试信息进行处理来实现的。所有的调试数据都会在内核模块运行之前被处理。调试数据包含足够的信息来定位内联函数、局部变量、类型以及一些通常不被导出到内核模块的声明。
下面总结一下细化阶段所做的主要工作:

获取用户的探测脚本
预处理宏
包含所需要的脚本库
解析代码中对符号的引用
查找函数入口地址
查找行号信息
查找全局和局部变量的类型及地址

转换(Translate)
脚本被细化后它将会被转换为 C 语言代码,每个脚本子程序都被扩展成C语言代码块并进行安全检查,例如对循环结构进行递增检查了防止无限循环。由多个探测点共享的变量会被映射成适当的静态声明,并以块为单位进行访问保护。在内核中使用kprobes中的注册APIs对探测点处理器(probe handlers)进行注册。对于在内核中的探测点,会被插入到内核的内存空间中;对于用户级别探测点,它将被插入到可执行代码中,当处理器在内核中运行时将其载入到用户的内存空间。
构造(Build)
转换完成后,产生的 C 语言代码会被编译并与运行时进行链接成一个stand-alone 内核模块。该模块可被加密标记以用于安全归档或远程使用。
运行(Execution)
生成模块后 SystemTap 驱动程序会使用 insmod 将内核模块载入。该模块会进行初始化、插入探测点,然后等待探测点被触发。探测点被触发后会调用相关联的处理器程序并挂起执行线程,当该探测点所有的处理器都运行后,线程重新运行。当用户发送中断或脚本调用 exit 时,SystemTap 脚本将中止运行。然后卸载模块并移除探测点。
数据采集
SystemTap 需要将从内核中获取的数据传输到用户空间,并且需要很高的吞吐量、低延迟以及最小化对被监测系统性能的影响。缺省情况下,SystemTap 的输出会在脚本出口以批处理的方式写入 stdout,同时该输出会被自动保存到文件中。在用户空间,SystemTap 可以以简单的文本方式显示数据,或使用图形类的应用程序生成计算机可读的表格数据等。

0x04 语言基础
编写函数体和探测处理器的语法大部分借鉴于 awk 和 C 语言。SystemTap 还允许 C、C++和 awk 风格的注释。除了$也是合法的字符外,SystemTap 定义标识符的语法与 C 语言完全一样。标识符用来命名变量和函数,以$开头的标识符被认为是对目标程序(即被监测程序)中变量的引用而不是 SystemTap 脚本中的变量。SystemTap 的语言中包括少量的数据类型,但是没有类型声明:变量的类型是从它的使用中推断出来的。类似的,字符串和数字之间也没有隐式的类型转换。
语言中包含传统的if-then-else 语句以及 C语言和 awk 中的表达式,同时还支持结构化控制语句如 for和 while 循环,但不支持非结构化操作如标号和 goto 语句。为支持关联数组,SystemTap 语言包含了迭代器和 delete 语句。迭代器语句允许程序员指定执行数组中所有成员的操作,delete 操作可以移除数组中一个或全部成员。C 语言中典型的算术、位、赋值及一元操作在 SystemTap中都可以使用,但都是操作在 64 位数字上的,赋值和比较运算符可以重载为字符串使用。
Events
探测点指定了在内核的什么位置进行探测,探测点还可以定义在与目标程序上某个点没有联系的抽象事件上,但这时转换器就不能获得太多关于探测点的符号信息。

begin The startup of the systemtap session.
end The end of the systemtap session.
kernel.function("sys_open") The entry to the function named sys_open in the kernel.
syscall.close.return The return from the close system call.
module("ext3").statement(0xdeadbeef) The addressed instruction in the ext3 filesystem driver.
timer.ms(200) A timer that fires every 200 milliseconds.
timer.profile A timer that fires periodically on every CPU.
perf.hw.cache_misses A particular number of CPU cache misses have occurred.
procfs("status").read A process trying to read a synthetic 

Handlers
探测点名称后紧跟的一组大括号内定义了每次内核运行到该探测点时需要进行的操作,比如可以指定输出数据过滤的条件,或者更为复杂的控制逻辑。这些操作完成后再返回探测点,继续下面的指令。
Helper functions

tid() The id of the current thread.
pid() The process (task group) id of the current thread.
uid() The id of the current user.
execname() The name of the current process.
cpu() The current cpu number.
gettimeofday_s() Number of seconds since epoch.
get_cycles() Snapshot of hardware cycle counter.
pp() A string describing the probe point being currently handled.
ppfunc() If known, the the function name in which this probe was placed.
$$vars If available, a pretty-printed listing of all local variables in scope.
print_backtrace() If possible, print a kernel backtrace.
print_ubacktrace() If possible, print a user-space backtrace

Tapset
当我们诊断系统级的问题时,通常需要跟踪操作系统和应用程序的不同子系统。为使我们更方便的进行诊断,SystemTap 包含了一个为多个子系统编写好的监测模块库,即 tapsets,用户可以在脚本中使用已经发布的tapsets。Tapset 的主要思想是:一个给定子系统的专家知道哪些方面对于深入理解该子系统是重要的;该专家会将重要的数据以 tapset 的形式,通过在探测点中调用一个或多个函数进行输出。这样,我们不需要成为内核所有领域中的专家,但仍然可以从系统中获得重要的数据。Tapsets已经覆盖包含的子系统: systemcalls, process, scheduler, filesystem, networking etc.

0x05 Systemtap安全特性
使用的toolchain经过了很好的测试,没有用新的compiler or interpreter
使用了经过完善测试的内核特性
另外systemtap语言本身的安全特性有:

No dynamic memory allocation
Types and type conversions limited
Limited pointer operations

内嵌的安全检查:

Infinite loops and recursion
Invalid variable access
Division by zero
Restricted access to kernel memory
Array bound checks
Version compatibility checks

Limitations

Off kernel. Generating kernel modules
Running kernel modules. Requires privilege to load, mistakes are fatal, mitigated by protected stap language
No early boot tracing
Review kernel codes detailedly, or have to understand tapsets clearly
Single step break

0x06 备注
1.实际上,systemtap也不是所有的kernel函数都可以插桩,有个blacklist屏蔽了systemstap插桩可能会引起问题的函数列表
2.尽管该语言允许开发复杂的脚本,但每个探针只能执行 1000 条语句(这个数量是可配置的)
3 systemtap tutorial
4 l-systemtap
5 systemtap-intro

发表评论

您的电子邮箱地址不会被公开。