天天看点

systemtap系列之系统诊断

systemtap系列之系统诊断

SystemTap 是监控和跟踪运行中的 Linux 内核的操作的动态方法。SystemTap 没有使用工具构建一个特殊的内核,而是允许您在运行时动态地安装该工具。它通过一个名为Kprobes 的应用编程接口(API)来实现该目的

SystemTap 与一种名为 DTrace 的老技术相似,该技术源于 Sun Solaris 操作系统。

测试内核看它是否支持 SystemTap:

stap -ve 'probe begin { log("hello world") exit() }'

如果不支持就安装一下,yum install systemtap就好了,缺什么就安装什么,光盘自带了。如果出现如下错误:semantic error: missing x86_64 kernel/module debuginfo [man warning::debuginfo]:答:yum install kernel-debuginfo 安装内核debug信息。

SystemTap 用于检查运行内核的两种方法是 Kprobes 和 返回探针。但是理解任何内核的最关键要素是内核的映射,它提供符号信息(比如函数、变量以及它们的地址)。有了内核映射之后,就可以解决任何符号的地址,以及更改探针的行为。

  1. SystemTap 的基本流程

    SystemTap 的基本流程,涉及到 3 个交互实用程序和 5 个阶段。流程首先从 SystemTap 脚本开始。您使用 stap 实用程序将 stap 脚本转换成提供探针行为的内核模块。stap 流程从将脚本转换成解析树开始 (pass 1)。然后使用细化(elaboration)步骤 (pass 2) 中关于当前运行的内核的符号信息解析符号。接下来,转换流程将解析树转换成 C 源代码 (pass 3) 并使用解析后的信息和 tapset 脚本(SystemTap 定义的库,包含有用的功能)。stap 的最后步骤是构造使用本地内核模块构建进程的内核模块 (pass 4)。

    有了可用的内核模块之后,stap 完成了自己的任务,并将控制权交给其他两个实用程序 SystemTap:staprun 和 stapio。这两个实用程序协调工作,负责将模块安装到内核中并将输出发送到 stdout (pass 5)。如果在 shell 中按组合键 Ctrl-C 或脚本退出,将执行清除进程,这将导致卸载模块并退出所有相关的实用程序。

    从内核态/用户态来了解如下:

  2. systemtap工作原理
  3. SystemTap 脚本编写

    SystemTap 脚本由探针和在触发探针时需要执行的代码块组成。

    使用-L可以测试某条语句是否是正确的:

    stap -L 'process("/usr/local/mysql/libexec/mysqld").function("apply_event")'

  4. 关于探针

    基本参考如下:

    探针类型 说明

    begin 在脚本开始时触发

    end 在脚本结束时触发

    kernel.function("sys_sync") 调用 sys_sync 时触发

    kernel.function("sys_sync").call 同上

    kernel.function("sys_sync").return 返回 sys_sync 时触发

    kernel.syscall.* 进行任何系统调用时触发

    kernel.function("*@kernel/fork.c:934") 到达 fork.c 的第 934 行时触发

    module("ext3").function("ext3_file_write") 调用 ext3 write 函数时触发

    timer.jiffies(1000) 每隔 1000 个内核 jiffy 触发一次

    timer.ms(200).randomize(50) 每隔 200 毫秒触发一次,带有线性分布的随机附加时间(-50 到 +50)

  5. 变量和类型

    SystemTap 允许定义多种类型的变量,类型可以从上下文推断得出的,因此不需要使用类型声明。在 SystemTap 中,您可以找到数字(64 位签名的整数)、整数(64 位)、字符串和字面量(字符串或整数)。还可以使用关联数组和统计数据。

    表达式

    SystemTap 提供 C 语言中常用的所有必要操作符,并且用法也是一样的。还可以找到算术操作符、二进制操作符、赋值操作符和指针废弃。还看到从 C 语言带来的简化,其中包括字符串连接、关联数组元素和合并操作符。

    语言元素

    在探针内部,SystemTap 提供一组类似于 C 一样易于使用的语句。需要注意的是,尽管该语言允许您开发复杂的脚本,但每个探针只能执行 1000 条语句(这个数量是可配置的)。许多元素和 C 中的一样,尽管有一些附加的东西是特定于 SystemTap 的。

    语句 说明

    if (exp) {} else {} 标准的 if-then-else 语句

    for (exp1 ; exp2 ; exp3 ) {} 一个 for 循环

    while (exp) {} 标准的 while 循环

    do {} while (exp) 一个 do-while 循环

    break 退出迭代

    continue 继续迭代

    next 从探针返回

    return 从函数返回一个表达式

    foreach (VAR in ARRAY) {} 迭代一个数组,将当前的键分配给 VAR

    SystemTap 提供许多内部函数,这些函数提供关于当前上下文的额外信息。可以使用 caller() 识别当前的调用函数,使用 cpu() 识别当前的处理器号码,以及使用 pid() 返回 PID。

  6. 统计系统调用 sys_sync

    调用内核系统调用 sys_sync 时触发。当该探针触发时,计算调用的次数,并发送这个计数以及表示调用进程 ID(PID)的信息。首先,声明一个任何探针都可以使用的全局值(全局名称空间对所有探针都是通用的),然后将它初始化为 0。其次,定义探针,它是一个探测内核函数 sys_sync 的条目。与探针相关联的脚本将递增 count 变量,然后发出一条消息,该消息定义调用的次数和当前调用的 PID。

global count=0

probe kernel.function("sys_sync") {

count++

printf( "sys_sync called %d times, currently by pid %d\n", count, pid() );

}

执行stap syscall.stp

  1. stap命令与staprun命令

    命令的区别在于:

    stap命令的操作对象是stp文件或script命令等,而staprun命令的操作对象是编译生成的内核模块。

  2. 监控所有系统调用

    脚本如下:

    global syscalllist

    probe begin {

    printf("System Call Monitoring Started (10 seconds)...\n")

probe syscall.* {

syscalllist[pid(), execname()]++

probe timer.ms(10000) {

foreach ( [pid, procname] in syscalllist ) {

printf("%s[%d] = %d\n", procname, pid, syscalllist[pid, procname] )

exit()

  1. 统计10秒内系统调用

    脚本如,代码中定义了一个10秒的定时器:

    printf("System Call Monitoring Started (10 seconds).../n")

    probe syscall.*

    {

    printf("%s[%d] = %d/n", procname, pid, syscalllist[pid, procname] )

  2. 收集网络包长度
参考如下脚本:
           

global recv, xmit

printf("Starting network capture (Ctl-C to end)\n")

probe netdev.receive {

recv[dev_name, pid(), execname()] <<< length

probe netdev.transmit {

xmit[dev_name, pid(), execname()] <<< length

probe end {

printf("\nEnd Capture\n\n")

printf("Iface Process........ PID.. RcvPktCnt XmtPktCnt\n")

foreach ([dev, pid, name] in recv) {

recvcount = @count(recv[dev, pid, name])

xmitcount = @count(xmit[dev, pid, name])

printf( "%5s %-15s %-5d %9d %9d\n", dev, name, pid, recvcount, xmitcount )

delete recv

delete xmit

按 Ctrl-C 时退出脚本,然后发送捕获的数据。

  1. 柱状显示数据
以柱状图的形式显示数据,将数据捕获到一个名为 histogram 的聚合中。然后,使用 netdev 接收和发送探针以捕捉包长度数据。当探针结束时,使用 @hist_log 提取器以柱状图的形式呈现数据。@hist_log 提取器是一个以 2 为底数的对数柱状图.
           

代码如下:

global histogram

printf("Capturing...\n")

histogram <<< length

printf( "\n" )

print( @hist_log(histogram) )

  1. systemtap其他作用
定位函数位置
           

stap -l 'process("/lib/x86_64-redhat-linux6E/lib64/libc.so").function("printf")'

  1. 查找内核代码中的函数
如果内核函数sys_open被新定义,可以通过如下命令进行查找。
           

#stap -L 'kernel.function("sys_open")'

kernel.function("SyS_open@fs/open.c:1036") $filename:long int $flags:long int $mode:long int

  1. 参考

linuxperformance-tools

http://www.brendangregg.com/linuxperf.html

Linux 自检和 SystemTap

https://www.ibm.com/developerworks/cn/linux/l-systemtap/

Linux 下的一个全新的性能测量和调式诊断工具 Systemtap, 第 3 部分: Systemtap

https://www.ibm.com/developerworks/cn/linux/l-cn-systemtap3/

systamtap编程参考

https://sourceware.org/systemtap/langref/

systemtap有用的IO监控脚本

https://segmentfault.com/a/1190000000680628

SystemTap使用技巧【一】

http://blog.csdn.net/wangzuxi/article/details/42849053

调试内核模块

http://blog.chinaunix.net/uid-14528823-id-4726046.html

下一篇: Iptables配置