SystemTap(stap)脚本举例 包括系统调用监控,函数执行时间 函数出参信息和信号捕捉脚本等

SystemTap 脚本举例

运行环境检查

要想使用SystemTap,需要依赖环境支持。可以执行如下命令查看stap是否已经安装。

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

对内核函数增加探针,需要debuginfo信息,可以通过如下步检查环境是否安装了内核相应debuginfo。
例如,我想对nfs_file_read增加探针。

  1. grep nfs_file_read /proc/kallsyms 检查内核是否已经加载相应ko
    在这里插入图片描述

  2. stap -L ‘module(“nfs”).function(“nfs_file_read”)’ 检查是否有debuginfo信息,能列出参数,可以确认已经安装了debuginfo,否则需要安装debuginfo包。
    在这里插入图片描述

  3. parastor相关ko的debuginfo安装方法,是将/home/parastor/tools/client下面相关的*.unstripped.ko变为*.ko.debug,就可以了,不再需要重新卸载和安装有debuginfo的ko了。
    注意由于parastor相关ko没有在lib目录下,在加载模块的时候,需要填入全路径名,包括后缀.ko。例如/home/parastor/tools/client/parastor.ko。

系统调用监控

根据传入的进程名,监控其系统调用,并每10秒打印一次统计信息

global syscalllist

probe begin {
  printf("Monitoring %s Started (10 seconds)...\n", @1)
}

probe syscall.*
{
  if (execname() == @1) {
    syscalllist[name]++
  }
}
 
probe timer.ms(10000) {
  printf("====%s====\n", tz_ctime(gettimeofday_s()))
  foreach (name in syscalllist) {
    printf("%s = %d\n", name, syscalllist[name])
  }
}

运行结果如下:
在这里插入图片描述

聚合数据捕捉

聚合实例时捕捉数字值的统计数据的出色方法。当您捕捉大量数据时,这个方法非常高效有用。下面这个例子可以用来收集网络包接收和发送的数据。其中<<<表示将每一次的长度添加到聚合数组对应index成员中,不是替换或累加,即每个成员可能会记录多个length,便于后面进行统计。

//按照网络接收或发送次数降序排列,每5秒打印一次
global ifxmit,ifrecv
global ifmerged

probe netdev.transmit {
  ifxmit[pid(),dev_name,execname(),uid()] <<< length //将发送的包长度存放到ifxmit
}

probe netdev.receive {
  ifrecv[pid(),dev_name,execname(),uid()] <<< length //将接受的包长度存放到ifrecv
}

function print_activity() {
  printf("%5s %5s %-7s %7s %7s %7s %7s %-15s\n",
          "PID", "UID", "DEV", "XMIT_PK", "RECV_PK",
          "XMIT_KB", "RECV_KB", "COMMAND")
  foreach ([pid,dev,exec,uid] in ifxmit) {//遍历ifxmit,计算相同属性进程发送和接受包的总次数
    ifmerged[pid,dev,exec,uid] += @count(ifxmit[pid,dev,exec,uid]); 
  }
  foreach ([pid,dev,exec,uid] in ifrecv) {
    ifmerged[pid,dev,exec,uid] += @count(ifrecv[pid,dev,exec,uid]);
  }
  foreach ([pid,dev,exec,uid] in ifmerged-) {//按发送+接收次数降序排列
    n_xmit = @count(ifxmit[pid,dev,exec,uid]);//发送次数
    n_recv = @count(ifrecv[pid,dev,exec,uid]);//接收次数
    if (n_xmit > 100 || n_recv > 100) {
      printf("%5d %5d %-7s %7d %7d %7d %7d %-15s\n",
        pid, uid, dev, n_xmit, n_recv,
        n_xmit ? @sum(ifxmit[pid, dev, exec, uid])/1024 : 0,
        n_recv ? @sum(ifrecv[pid, dev, exec, uid])/1024 : 0,
        exec)
    }
  }
  delete ifmerged
  delete ifxmit
  delete ifrecv
}

probe timer.ms(5000), end, error {
  print_activity()
}

运行结果如下
在这里插入图片描述

4.4 函数执行时间
下面这个例子计算nfs的读写运行时间,探针类型为return,获取进入函数的时间方法为@entry(gettimeofday_us()),在用返回时获取的时间减去其值就可以了。

probe begin {
  printf("%5s      %-10s %8s %-15s\n",
         "PID", "FUNC", "TIME(us)", "COMMAND")
}

probe module("nfs").function("nfs_file_read").return,
      module("nfs").function("nfs_file_write").return {
  now = gettimeofday_us()
  delta_time = now - @entry(gettimeofday_us())#计算nfs读写运行的时间
  printf("%5d %-15s %8ld %-15s\n", pid(), ppfunc(), delta_time, execname())
}

nfs客户段进行读写操作后,会有信息打印,运行的结果如下:
在这里插入图片描述

4.5 函数出参信息
这次的例子捕捉nfs文件系统的总空间、剩余空间和可以用空间。对下面的函数增加返回探针。函数和对应的结构体如下:
在这里插入图片描述
在这里插入图片描述

脚本如下:

probe begin {
  printf("%12s %12s %12s\n", "total", "free", "avail")
}

probe module("nfsv3").function("nfs3_proc_statfs").return {
  statfs = &@cast(@entry($stat), "struct nfs_fsstat")
  printf("%12ldKB %12ldKB %12ldKB\n",
          statfs->tbytes / 1024, statfs->fbytes / 1024, statfs->abytes / 1024)
}

df执行结果如下:
在这里插入图片描述

脚本运行结果如下:
在这里插入图片描述

捕捉信号信息

下面的脚本可以捕捉指定的信号发送者和接受者的信息

probe signal.send{
  if (sig_name == @1) {
    printf("%s was send to %s(pid:%d) by %s(pid:%d) uid:%d\n",
           sig_name, pid_name, sig_pid, execname(), pid(), uid())
  }
}

运行结果如下,可以通过kill命令触发

在这里插入图片描述

  • 25
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值