[转帖]s-systemtap工具使用图谱(持续更新)

systemtap,工具,使用,图谱,持续,更新 · 浏览次数 : 0

小编点评

**获取传入的count参数** 当通过函数调用时,传入的count参数通常被保存到**STAP_RETVALUE**中。如果使用snprintf、sprintf或其他格式化函数格式化**STAP_RETVALUE**,会显示出传入的count参数值。 **跟踪特定进程的执行过程** 1. 使用pid获取目标进程的ID。 2. 使用“probe module”功能在特定内核文件中查找指定模块的函数。 3. 在函数中使用“target() == pid”筛选出目标进程。 4. 使用“probe module”功能在目标进程中查找“ceph_statfs”函数。 5. 使用“probe module”功能在目标进程中查找“ceph_statfs”函数的执行行号。 6. 使用“stap -x pid trace_mdsclient.stp”命令抓取进程的执行流程。 **调试指定模块** 1. 在目标模块的目录下创建子目录“/usr/lib/modules/”. 2. 将模块复制到该目录下。 3. 使用“module(\"ceph\").function(\"\")"等格式化语句调用模块函数。 4. 使用“probe”模块监控模块的执行过程。

正文


整体的学习思维导图如下,后续持续更新完善

s-systemtap工具使用图谱(持续更新)_函数返回

文章目录

  • ​​安装​​
  • ​​简介​​
  • ​​执行流程​​
  • ​​执行方式​​
  • ​​stap脚本语法​​
  • ​​探针语法​​
  • ​​API函数​​
  • ​​探针举例​​
  • ​​变量使用​​
  • ​​基本应用​​
  • ​​1. 定位函数位置​​
  • ​​2. 查看文件能够添加探针的位置​​
  • ​​3. 打印函数参数(结构体)​​
  • ​​4. 打印函数局部变量​​
  • ​​5. 修改函数局部变量(慎重)​​
  • ​​6. 打印函数返回时的变量​​
  • ​​7. 打印函数调用栈​​
  • ​​8. 嵌入C代码​​
  • ​​9. 追踪函数流程​​
  • ​​10. 跟踪特定进程​​
  • ​​11. 查看代码执行路径​​
  • ​​12. 查看内核文件函数的执行流程​​
  • ​​13. 调试指定模块​​
  • ​​14. 抓取`kill -l`相关的信号​​


环境:

​3.10.0-957.5.1.el7.x86_64​

安装

centos下安装如下rpm包, 安装前​​uname -r​​核对自己的内核版本,下载自己对应的内核版kernel包进行安装即可

rpm -ivh kernel-debuginfo-common-x86_64-3.10.0-123.el7.x86_64.rpm
rpm -ivh kernel-debuginfo-3.10.0-123.el7.x86_64.rpm
rpm -ivh kernel-debug-devel-3.10.0-123.el7.x86_64.rpm
  • 1.
  • 2.
  • 3.

安装systemtap

yum install systemtap-devel-2.4-14.el7.x86_64
yum install systemtap-client-2.4-14.el7.x86_64
  • 1.
  • 2.

安装成功后测试如下:

[root@node1 kernel_compile]# stap -L 'kernel.statement("sys_open")'
kernel.statement("SyS_open@fs/open.c:1063") $filename:long int $flags:long int $mode:long int
  • 1.
  • 2.

简介

systemtap 是一个非常强大的性能诊断以及内核调试工具,相比于传统的内核调试方法(开debug级别,printk,加打印。。):修改代码,编译模块,安装模块,运行模块 的调试过程,systemtap提供友好的内核调试方法,极大得节省了调试损耗的精力。
官网地址:​​ ​systemtap​

执行流程

s-systemtap工具使用图谱(持续更新)_调用栈_02


主要分为5步,从开始到结果最终是将具有探针行为的Stap脚本转换为内核模块并加载

  • 将stap 脚本转换为解析树
  • 解析stap脚本中的符号
  • 将解析后的结果转为c代码
  • 将c代码编译成对应的ko驱动文件
  • 加载驱动,开始运行(staprun加载生成的模块,stapio将结果输出到终端)

可以使用命令​​stap -v xxx.stp​​将解析以及加载过程打印出来

[root@node1 ~]# stap -v -g vfs.stp
Pass 1: parsed user script and 116 library script(s) using 230588virt/40808res/3156shr/38524data kb, in 190usr/10sys/195real ms.
Pass 2: analyzed script: 43 probe(s), 28 function(s), 1 embed(s), 0 global(s) using 709984virt/91896res/8712shr/81664data kb, in 880usr/190sys/1072real ms.
Pass 3: using cached /root/.systemtap/cache/b0/stap_b066af4ce5ad5d99a19d4ad9ec41fc8e_23076.c
Pass 4: using cached /root/.systemtap/cache/b0/stap_b066af4ce5ad5d99a19d4ad9ec41fc8e_23076.ko
Pass 5: starting run

    执行方式

    1. 终端执行stap 命令
      ​​​stap -L 'kernel.statement("sys_read")'​​ 查看sys_read系统调用的函数位置
    2. 执行stp脚本
      ​​​cat test.stp​​​ 该脚本为打印调用​​sys_read​​系统调用的进程名称和进程号
    3. #!/usr/bin/stap
      probe begin {
      printf("begin to probe");
      }

      probe kernel.function("vfs_read") {
      printf("%s %d\n", execname(),pid());
      }

      probe end {
      printf("end to probe");
      }
      • 执行方式 ​​stap test.stp​​ 或者​​./test.stp​

      脚本方式执行更加规范,且更容易编写,所以这里建议使用脚本方式进行执行

      stap脚本语法

      探针语法

      kernel.function(pattern)

      在内核函数的入口处放置探测点,可以获取参数$parm

      kernel.function(pattern).return

      在内核函数返回时的出口处放置探测点,可以获取返回时的参数$parm

      kernel.function(pattern).call

      内核函数的调用入口处放置探测点,获取对应函数信息

      kernel.fuction(pattern).inline

      获取符合条件的内联函数

      kernel.function(pattern).exported

      只选择导出的函数

      module(moduname).fuction(pattern)

      在模块modulename中调用的函数入口处放置探测点

      module(moduname).fuction(pattern).return

      在模块module中调用的函数返回时放置探测点

      module(moduname).fuction(pattern).call

      在模块modulename中调用的函数入口处放置探测点

      module(moduname).fuction(pattern).inline

      在模块modulename中调用的内联函数处放置探测点

      kernel.statement(pattern)

      在内核中的某个地址处增加探针(函数、文件行号)

      kernel.statement(pattern).absolute

      在内核中的某个地址处增加探针(函数、文件行号),精确匹配地址

      module(modulename).statement(pattern)

      在内核模块中的某个地址处增加探针(函数、文件行号)

      API函数

      函数

      说明

      execname()

      获取当前进程名称

      pid()

      当前进程的ID

      tid()

      当前线程ID

      cpu()

      当前cpu号

      gettimeofday_s()

      获取当前系统时间,秒

      gettimeofday_usec()

      获取当前系统时间,微秒

      ppfunc()

      获取当前probe的函数名称,可以知道当前probe位于哪个函数

      print_backtrace()

      打印内核函数调用栈

      print_ubacktrace()

      打印用户态函数调用栈

      探针举例

      探针名称

      探针含义

      begin

      脚本开始时触发

      end

      脚本结束时触发

      kernel.function(“sys_read”)

      调用sys_read时触发

      kernel.function(“sys_read”).call

      同上

      kernel.function(“sys_read”).return

      sys_read执行完,返回时触发

      kernel.syscall.*

      调用任何系统调用时触发

      kernel.function("*@kernel/fork.c:934")

      执行到fork.c的934行时触发

      module(“ext3”).function(“ext3_file_write”)

      调用ext3模块中的ext3_file_write时触发

      timer.jiffies(1000)

      每隔1000个内核jiffy时触发一次

      timer.ms(200).randomize(50)

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

      变量使用

      变量格式

      使用

      $varname

      引用变量varname

      $var->field

      引用结构的成员变量

      $var[N]

      引用数组的成员变量

      &$var

      变量的地址

      @var(“varname”)

      引用变量varname

      @var(“var@src/file.c”)

      引用src中file.c编译时的全局变量

      @var(“var@src/file.c”)->field

      src/file.c 中全局结构的成员变量

      @var(“var@src/file.c”)[N]

      src/file.c中全局数据变量

      &@var(“var@src/file.c”)

      引用变量的地址

      $var$

      将变量转为字符串类型

      $$vars

      包含函数所有参数,局部变量,需以字符串类型输出

      $$locals

      包含函数所有局部变量,需以字符串类型输出

      $$params

      包含所有函数参数的变量,需以字符串类型输出

      基本应用

      1. 定位函数位置

      ​stap -L 'kernel.function("vfs_statfs")'​

      kernel.function("vfs_statfs@fs/statfs.c:68") $path:struct path* $buf:struct kstatfs* $error:int
      • 1.

      打印出函数的所处文件位置及行号,同时​​-L​​ 参数支持打印函数的参数及类型

      s-systemtap工具使用图谱(持续更新)_函数返回_03


      ​stap -l 'kernel.function("vfs_statfs")'​​​ ​​-l​​参数打印的信息就稍微简略一点

      kernel.function("vfs_statfs@fs/statfs.c:68")
      • 1.
      2. 查看文件能够添加探针的位置

      ​stap -L 'kernel.statement("*@fs/statfs.c")'​​ 查看statfs.c 文件中能够添加探针的位置

      kernel.statement("SYSC_fstatfs64@fs/statfs.c:204") $fd:unsigned int $sz:size_t $buf:struct statfs64* $st:struct kstatfs
      kernel.statement("SYSC_fstatfs@fs/statfs.c:195") $fd:unsigned int $buf:struct statfs* $st:struct kstatfs
      kernel.statement("SYSC_statfs64@fs/statfs.c:183") $pathname:char const* $sz:size_t $buf:struct statfs64* $st:struct kstatfs
      kernel.statement("SYSC_statfs@fs/statfs.c:174") $pathname:char const* $buf:struct statfs* $st:struct kstatfs
      kernel.statement("SYSC_ustat@fs/statfs.c:230") $dev:unsigned int $ubuf:struct ustat* $tmp:struct ustat $sbuf:struct kstatfs
      kernel.statement("SyS_fstatfs64@fs/statfs.c:204") $fd:long int $sz:long int $buf:long int $ret:long int
      kernel.statement("SyS_fstatfs@fs/statfs.c:195") $fd:long int $buf:long int $ret:long int
      kernel.statement("SyS_statfs64@fs/statfs.c:183") $pathname:long int $sz:long int $buf:long int $ret:long int
      kernel.statement("SyS_statfs@fs/statfs.c:174") $pathname:long int $buf:long int $ret:long int
      kernel.statement("SyS_ustat@fs/statfs.c:230") $dev:long int $ubuf:long int $ret:long int
      kernel.statement("calculate_f_flags@fs/statfs.c:45")
      kernel.statement("do_statfs64@fs/statfs.c:150") $st:struct kstatfs* $p:struct statfs64* $buf:struct statfs64
      kernel.statement("do_statfs_native@fs/statfs.c:108") $st:struct kstatfs* $p:struct statfs* $buf:struct statfs
      kernel.statement("fd_statfs@fs/statfs.c:97") $fd:int $st:struct kstatfs*
      kernel.statement("flags_by_mnt@fs/statfs.c:12") $mnt_flags:int
      kernel.statement("flags_by_sb@fs/statfs.c:33") $s_flags:int
      kernel.statement("statfs_by_dentry@fs/statfs.c:51") $dentry:struct dentry* $buf:struct kstatfs*
      kernel.statement("user_statfs@fs/statfs.c:79") $pathname:char const* $st:struct kstatfs* $path:struct path
      kernel.statement("vfs_statfs@fs/statfs.c:68") $path:struct path* $buf:struct kstatfs* $error:int
      kernel.statement("vfs_ustat@fs/statfs.c:218") $dev:dev_t $sbuf:struct kstatfs*
        3. 打印函数参数(结构体)

        vfs_statfs函数如下:

        int vfs_statfs(struct path *path, struct kstatfs *buf)
        {
        int error;

        error = statfs_by_dentry(path->dentry, buf);
        if (!error)
        buf->f_flags = calculate_f_flags(path->mnt);
        return error;
        }

          如下脚本​​test.stp​​:

          #!/usr/bin/stap

          probe begin {
          printf("begin to probe");
          }

          probe kernel.function("vfs_statfs") { //在调用vfs_statfs处添加探针
          printf("%s %d\n", execname(),pid());//打印调用vfs_statfs函数的进程名称以及进程号
          printf("path : %s buf : %s\n",$path->mnt->mnt_root->d_iname$,$buf$); //打印path参数的结构体成员,以及整个buf结构体

          probe end {
          printf("end to probe");
          }

            运行​​stap test.stp​​ 输出如下:

            [root@node1 ~]# stap test.stp
            begin to probesafe_timer 145286
            df 2041244
            path : "/" buf : {.f_type=393, .f_bsize=140207161574688, .f_blocks=94629230035116, .f_bfree=29879, .f_bavail=18446620715516395336, .f_files=18446744071970029248, .f_ffree=4294967295, .f_fsid={...}, .f_namelen=4294967295, .f_frsize=94629230035016, .f_flags=1574682515518227985, .f_spare=[...]}
            safe_timer 145286
              4. 打印函数局部变量

              vfs_statfs函数如下:

              int vfs_statfs(struct path *path, struct kstatfs *buf)
              {
              int error;

              error = statfs_by_dentry(path->dentry, buf);
              if (!error)
              buf->f_flags = calculate_f_flags(path->mnt);
              return error;
              }

                我想要查看​​error​​​在函数​​statfs_by_dentry​​​执行完成之后的结果,那么就在下一行处添加探针
                查看如下脚本​​​test.stp​​:

                #!/usr/bin/stap

                probe begin {
                printf("begin to probe");
                }

                probe kernel.statement("vfs_statfs@fs/statfs.c:73") {//探测statfs.c中的第73行
                printf("%s %d\n", execname(),pid());
                printf("error number is %d\n",$error);
                }

                probe end {
                printf("end to probe");
                }

                  这里需要注意脚本中在statfs.c的第73行增加探测点,必须填写行号正确,如果73行处没有接下来想要探测的​​error​​变量,执行报错

                  执行​​stap test.stp​​输出如下

                  begin to probe
                  safe_timer 145286
                  error number is 0
                  ...
                    5. 修改函数局部变量(慎重)

                    我们想要将上一个打印的变量的值从0 更改为其他的数值
                    查看如下脚本​​​test.stp​

                    #!/usr/bin/stap

                    probe begin {
                    printf("begin to probe");
                    }

                    probe kernel.statement("vfs_statfs@fs/statfs.c:73") {
                    printf("%s %d\n", execname(),pid());
                    printf("error number before modify is %d\n",$error);
                    $error=$1; //传入的第一个参数
                    printf("error number after modify is %d\n",$error);
                    }

                    probe end {
                    printf("end to probe");
                    }

                      执行​​stap -g test.stp 2​​​ 将2传入,但是运行的时候需要增加​​-g​​​参数
                      输出如下:

                      begin to probe
                      df 3173946
                      error number before modify is 0
                      error number after modify is 2
                      ...
                        6. 打印函数返回时的变量

                        还是举例我们的​​vfs_statfs​​​,这个函数主要是在ls,df,stat…类似获取文件或者文件夹属性时由系统调用​​SyS_statfs​​​调用的
                        函数实现如下

                        int vfs_statfs(struct path *path, struct kstatfs *buf)
                        {
                        int error;

                        error = statfs_by_dentry(path->dentry, buf);
                        if (!error)
                        buf->f_flags = calculate_f_flags(path->mnt);
                        return error;
                        }

                          这里我们想要查看一下函数返回时error变量的值,查看如下​​test1.stp​

                          #!/usr/bin/stap

                          probe begin {
                          printf("begin to probe\n");
                          }

                          probe kernel.function("vfs_statfs").return { //在调用vfs_statfs处添加探针
                          printf("%s %d\n", execname(),pid());//打印调用vfs_statfs函数的进程名称以及进程号
                          printf("error's return value is %d\n", @entry($error));//打印局部变量需使用@entry($varname)
                          }

                          probe end {
                          printf("end to probe");
                          }

                            输出如下:

                            begin to probe
                            safe_timer 752879
                            error's return value is 0
                            safe_timer 752879
                            error's return value is 0
                              7. 打印函数调用栈

                              我们想要打印某一个系统调用的调用栈,可以执行如下脚本

                              #!/usr/bin/stap

                              probe kernel.function("vfs_statfs") {
                              printf("%s %d\n", execname(),pid());
                              printf("----------------------------------\n");
                              print_backtrace();//打印vfs_statfs在内核态的调用栈
                              printf("----------------------------------\n");
                              }

                                如果过程中发现调用栈打印不全,则尝试如下两种办法解决

                                1. 执行时增加参数​​--all-modules​​​,类似如:​​stap --all-modules test.stp​​,探测所有的系统模块
                                2. 检查​​stap​​​版本,像我的环境版本为​​version 2.4/0.158, rpm 2.4-14.el7​​​导致调用栈没有任何打印;需要升级stap的库才行,执行​​yum install systemtap -y​​​即可,升级之后我的版本​​version 4.0/0.176, rpm 4.0-10.el7_7​​,这个时候成功打印内核调用栈

                                输出如下:

                                safe_timer 752879
                                ----------------------------------
                                0xffffffff98677430 : vfs_statfs+0x0/0xc0 [kernel]
                                0xffffffff98677725 : user_statfs+0x55/0xa0 [kernel]
                                0xffffffff98677797 : SYSC_statfs+0x27/0x60 [kernel]
                                0xffffffff9867799e : SyS_statfs+0xe/0x10 [kernel]
                                0xffffffff98b5fddb : system_call_fastpath+0x22/0x27 [kernel]
                                0x7fcb8bcfe787
                                ----------------------------------

                                  如果想要通过​​print_ubacktrace()​​​函数来打印用户态的系统调用,则需要安装glibc的符号调试包​​glibc-debuginfo​​​从而能够对​​libc​​库进行调试

                                  8. 嵌入C代码

                                  如下脚本​​test.stp​​​,想要获取系统调用了​​vfs_statfs​​函数的次数

                                  #!/usr/bin/stap
                                  global count = 0;//全局变量
                                  function getcount:long(task:long) //C函数计算次数
                                  %{
                                  int count = (int) STAP_ARG_task;
                                  count ++;
                                  STAP_RETURN(count);
                                  %}

                                  probe begin {
                                  printf("begin to probe\n");
                                  }

                                  probe kernel.function("vfs_statfs") {
                                  printf("%s %d\n", execname(),pid());
                                  printf("----------------------------------\n");
                                  print_ubacktrace();
                                  printf("----------------------------------\n");

                                  count = getcount(count);
                                  printf("c code caculate the count is %d\n", count);
                                  }

                                  probe end {
                                  printf("end to probe\n");
                                  }

                                    最终输出如下,可以看到count一直在增加:

                                    [root@node1 ~]# stap -g test.stp
                                    WARNING: Missing unwind data for a module, rerun with 'stap -d /usr/lib64/libc-2.17.so'
                                    begin to probe
                                    safe_timer 752879
                                    ----------------------------------
                                    0x7fcb8bcfe787 [/usr/lib64/libc-2.17.so+0xef787/0x3c8000]
                                    ----------------------------------
                                    c code caculate the count is 1
                                    safe_timer 752879
                                    ----------------------------------
                                    0x7fcb8bcfe787 [/usr/lib64/libc-2.17.so+0xef787/0x3c8000]
                                    ----------------------------------
                                    c code caculate the count is 2
                                    systemd-journal 564
                                    ----------------------------------
                                    0x7f6176e2f7b7 [/usr/lib64/libc-2.17.so+0xef7b7/0x3c8000]
                                    ----------------------------------
                                    c code caculate the count is 3
                                    safe_timer 752879

                                      以上出现的warning是因为系统并未安装glibc的调试库导致的
                                      关于Stap嵌入C代码需要注意以下几点

                                      1. 格式上:C语言代码要在每个大括号前加%前缀,是%{…… %} 而不是%{ …… }%;
                                      2. 获取脚本函数参数要用STAP_ARG_前缀,即​​getcount​​​函数使用的是​​STAP_ARG_task​​来获取传入的count参数
                                      3. 一般long等返回值用​​STAP_RETURN​​​,一般字符串则使用snprintf、strncat等方式把字符串复制到​​STAP_RETVALUE​​里面
                                      9. 追踪函数流程

                                      我们想要知道当前函数被哪个进程调用,且在该函数处执行了多长时间,可以使用如下脚本进程探测
                                      ​​​trace_sys_read.stp​

                                      #!/usr/bin/stap

                                      probe begin {
                                      printf("begin to probe");
                                      }

                                      probe kernel.function("sys_read").call {
                                      printf("%s -> %s\n",thread_indent(4),ppfunc());
                                      }
                                      probe kernel.function("sys_read").return {
                                      printf("%s <- %s\n",thread_indent(-4),ppfunc());
                                      }

                                        其中​​thread_indent()​​​函数为​​/usr/share/systemtap/tapset/indent.stp​​​中实现的一个stap脚本,该函数的功能是增加函数执行时间(微妙),进程名称(pid)打印出来,传入的参数是打印空格的个数
                                        输出如下:

                                        0 msgr-worker-1(2445831): -> SyS_read
                                        31 ps(2445722): <- SyS_read
                                        0 ps(2445722): -> SyS_read
                                        1 ps(2445722): <- SyS_read
                                        0 sed(2445856): -> SyS_read
                                        2 sed(2445856): <- SyS_read
                                        24 msgr-worker-1(2445831): <- SyS_read

                                          发现​​Sys_read​​系统调用被某个进程调用时的开始到返回的执行时间,这个信息对内核代码的流程分析非常有利

                                          10. 跟踪特定进程

                                          除了跟踪具体的某个函数被哪个进程调用之外我们还能够跟踪一个进程所调用过的函数
                                          如下脚本,我们追踪​​​sshd​​进程调用的系统调用

                                          #!/usr/bin/stap
                                          probe begin {
                                          printf("begin to probe\n");
                                          }

                                          probe syscall.* //探测所有的系统调用
                                          {
                                          procname = execname();
                                          if (procname =~ "sshd.*"){ //使用stp脚本中的通配符匹配所有的sshd服务的子进程
                                          printf("%s[%d]: %s -> %s\n", procname,pid(),name,ppfunc()); //name为sshd内部函数,ppfunc为该函数调用的系统调用
                                          }
                                          }

                                            输出如下,非常直观

                                            sshd[2087388]: write -> SyS_write
                                            sshd[2087388]: clock_gettime -> SyS_clock_gettime
                                            sshd[2087388]: select -> SyS_select
                                            sshd[2087388]: rt_sigprocmask -> SyS_rt_sigprocmask
                                            sshd[2087388]: rt_sigprocmask -> SyS_rt_sigprocmask
                                            sshd[2087388]: clock_gettime -> SyS_clock_gettime
                                            sshd[2087388]: write -> SyS_write
                                            sshd[2087388]: clock_gettime -> SyS_clock_gettime
                                            sshd[2087388]: select -> SyS_select
                                              11. 查看代码执行路径

                                              我们来看一个有意思且非常便捷准确的阅码方式,如下代码为内核处理文件属性的逻辑

                                              s-systemtap工具使用图谱(持续更新)_调用栈_04


                                              分支相对来说较多,我们想要知道当前系统针对该函数的处理过程,走到哪个分支,则执行如下探测脚本

                                              #!/usr/bin/stap

                                              probe begin
                                              {
                                              printf("begin to probe\n");
                                              }

                                              probe kernel.statement("do_statfs_native@fs/statfs.c:*")
                                              {
                                              printf("%s\n",pp());
                                              }

                                                输出结果如下:

                                                [root@node1 stap]# stap trace_code.stp
                                                begin to probe
                                                kernel.statement("do_statfs_native@fs/statfs.c:109")
                                                kernel.statement("do_statfs_native@fs/statfs.c:113")
                                                kernel.statement("do_statfs_native@fs/statfs.c:146")
                                                kernel.statement("do_statfs_native@fs/statfs.c:148")

                                                  执行过程的具体行号已经打印出来了,此时对照代码即可知道内核处理该函数时如何进行分支处理

                                                  12. 查看内核文件函数的执行流程

                                                  我们想要查看一个源码文件中函数的执行流程时怎么查看呢?因为上文已经描述了如何跟踪特定进程的执行过程,对代码稍作修改如下

                                                  #!/usr/bin/stap

                                                  probe begin
                                                  {
                                                  printf("begin to probe\n");
                                                  }

                                                  probe module("ceph").function("*@mds_client.c").call{ //监控mds_client.c文件中所有的函数,调用时打印
                                                  printf("%s -> %s \n", thread_indent(4), ppfunc());
                                                  }

                                                  probe module("ceph").function("*@mds_client.c").return{//监控mds_client.c文件中所有的函数,返回时打印
                                                  printf("%s <- %s \n", thread_indent(-4), ppfunc());
                                                  }
                                                    >

                                                  输出如下:

                                                  [root@node3 stap]# stap trace_mdsclient.stp
                                                  begin to probe
                                                  0 kworker/3:2(582080): -> delayed_work
                                                  6 kworker/3:2(582080): -> __ceph_lookup_mds_session
                                                  8 kworker/3:2(582080): -> get_session
                                                  10 kworker/3:2(582080): <- get_session
                                                  12 kworker/3:2(582080): <- __ceph_lookup_mds_session
                                                  14 kworker/3:2(582080): -> con_get
                                                  16 kworker/3:2(582080): -> get_session
                                                  16 kworker/3:2(582080): <- get_session
                                                  18 kworker/3:2(582080): <- con_get

                                                    如果我们想要监控指定的进程在指定的内核文件中的执行过程,可以使用如下代码进行监控

                                                    #!/usr/bin/stap

                                                    probe begin
                                                    {
                                                    printf("begin to probe\n");
                                                    }

                                                    probe module("ceph").function("*@mds_client.c").call{
                                                    if(target() == pid()) {//使用target过滤我们输入的进程ip
                                                    printf("%s -> %s \n", thread_indent(4), ppfunc());
                                                    }
                                                    }

                                                    probe module("ceph").function("*@mds_client.c").return{
                                                    if(target() == pid()) {
                                                    printf("%s <- %s \n", thread_indent(-4), ppfunc());
                                                    }
                                                    }

                                                      执行如下​​stap -x pid trace_mdsclient.stp​​​ 即可对指定的进程id​​pid​​的过滤,输出其在mds_client.c代码中的执行流程

                                                      13. 调试指定模块

                                                      如我想要调试ceph模块,基本的脚本编写语法我们已经在前文脚本语法中提到过,类似如下​​module("ceph").function("")​​​,整体的调试方式和我们前面描述的内核调试方式类似
                                                      需要注意调试模块之前需要将模块拷贝到目录/usr/lib/modules/`uname -r`/extra/ 之下才能够正常调试
                                                      如何检测能够正常调试一个自己的模块呢,使用如下命令
                                                      这里使用ceph模块中的ceph_statfs来做测试
                                                      ​​​stap -l 'module("ceph").function("ceph_statfs")'​​,显示如下输出即可

                                                      module("ceph").function("ceph_statfs@fs/ceph/super.c:55")
                                                      • 1.
                                                      14. 抓取​​kill -l​​相关的信号

                                                      想要抓取系统中哪个进程发送的kill信号

                                                      global target
                                                      global signal
                                                      probe nd_syscall.kill
                                                      {
                                                      target[tid()] = uint_arg(1);
                                                      signal[tid()] = uint_arg(2);
                                                      }

                                                      probe nd_syscall.kill.return
                                                      {
                                                      if (target[tid()] != 0) {
                                                      printf("%-6d %-12s %-5d %-6d %6d\n", pid(), execname(),
                                                      signal[tid()], target[tid()], int_arg(1));
                                                      delete target[tid()];
                                                      delete signal[tid()];
                                                      }
                                                      }

                                                        ​stap test.stp​​会抓取发送kill信号的进程


                                                        与[转帖]s-systemtap工具使用图谱(持续更新)相似的内容:

                                                        [转帖]s-systemtap工具使用图谱(持续更新)

                                                        整体的学习思维导图如下,后续持续更新完善文章目录​​安装​​​​简介​​​​执行流程​​​​执行方式​​​​stap脚本语法​​​​探针语法​​​​API函数​​​​探针举例​​​​变量使用​​​​基本应用​​​​1. 定位函数位置​​​​2. 查看文件能够添加探针的位置​​​​3. 打印函数参数(

                                                        [转帖]s-systemtap工具使用图谱(持续更新)

                                                        整体的学习思维导图如下,后续持续更新完善文章目录​​安装​​​​简介​​​​执行流程​​​​执行方式​​​​stap脚本语法​​​​探针语法​​​​API函数​​​​探针举例​​​​变量使用​​​​基本应用​​​​1. 定位函数位置​​​​2. 查看文件能够添加探针的位置​​​​3. 打印函数参数(

                                                        [转帖]使用perf生成Flame Graph(火焰图)

                                                        https://www.cnblogs.com/keanuyaoo/p/3313378.html 具体的步骤参见这里: 《flame graph:图形化perf call stack数据的小工具》 使用SystemTap脚本制作火焰图,内存较少时,分配存储采样的数组可能失败,需要编写脚本,还要安装k

                                                        [转帖]TCP的blacklog之全连接队列与半连接队列的深入研究

                                                        文章目录 Linux内核探测工具systemtap的安装与使用backlog、半连接队列、全连接队列是什么半连接队列、全连接队列基本概念 linux 内核是如何计算半连接队列、全连接队列的半连接队列的大小的计算模拟半连接队列占满全连接队列(Accept Queue) SYN+ACK重传次数全连接队列

                                                        【转帖】linux 内核分析工具 Dtrace、SystemTap、火焰图、crash等

                                                        << System语言详解 >> 关于 SystemTap 的书。 我们在分析各种系统异常和故障的时候,通常会用到 pstack(jstack) /pldd/ lsof/ tcpdump/ gdb(jdb)/ netstat/vmstat/ mpstat/truss(strace)/iostat/s

                                                        [转帖]内核探测工具systemtap简介

                                                        https://www.bilibili.com/read/cv16655736/ systemtap是内核开发者必须要掌握的一个工具,本文我将简单介绍一下此工具,后续将会有系列文章介绍systemtap的用法。 什么是systemtap 假如现在有这么一个需求:需要获取正在运行的 Linux 系统

                                                        [转帖]小技巧!如何用 systemtap 排查问题

                                                        https://www.modb.pro/db/79444 霸爷博客,干货满满。有两篇文章现在还记得,《Linux下如何知道文件被哪个进程写》[1]和《巧用Systemtap注入延迟模拟IO设备抖动》[2],周末突然想起来,发现能看懂了:) 本文虽然说是小技巧,可是难度一点也不低 ^_^ 什么是 s

                                                        [转帖]Systemtap的另类用法

                                                        November 10th, 2010Yu Feng 原创文章,转载请注明: 转载自系统技术非业余研究 本文链接地址: Systemtap的另类用法 通常我们在做内核编程的时候,会用到内核的数据结构,比如说textsearch提供了几种算法用于支付串查找。在用于正式的项目前,我们会希望考察下他的用法

                                                        [转帖]systemtap - perf - 火焰图

                                                        https://www.cnblogs.com/hixiaowei/p/15580662.html 0. 火焰图生成框架 1. Capture stacks 2. Fold stacks 3. flamegraph.pl 1. perf (13条消息) 性能分析之profiling及火焰图_巷中人的

                                                        [转帖]Systemtap 用法

                                                        https://www.jianshu.com/p/fb4dde8baff4 霸爷博客,干货满满。有两篇文章现在还记得,《Linux下如何知道文件被哪个进程写》和《巧用Systemtap注入延迟模拟IO设备抖动》,周末突然想起来,发现能看懂了:) 什么是 systemtap Systemtap is