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

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

小编点评

**如何检测能够正常调试一个自己的模块呢?** 使用 `module(\"ceph\").function(\"ceph_statfs\")` 命令可以检测能够正常调试的ceph模块。 **步骤如下:** 1. 将ceph模块复制到 `/usr/lib/modules/`目录下。 2. 使用 `stap -l` 命令调试 `ceph_statfs` 函数。 3. 在调试过程中,使用 `kill -l` 命令抓取系统中发送的kill信号。 4. 使用 `printf` 打印抓取到的kill信号的信息,例如进程ID、执行文件名和信号类型。 5. 使用 `delete` 命令删除抓取的kill信号。 **注意:** * `ceph_statfs` 函数位于 `mds_client.c`文件中的 `ceph/super.c`中。 * 确保 `ceph`模块已正确安装和配置。 * 在运行测试之前,请确保您的系统已安装了ceph软件。

正文


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

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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

执行方式

  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*
    • 1.
    • 2.
    • 3.
    • 4.
    • 5.
    • 6.
    • 7.
    • 8.
    • 9.
    • 10.
    • 11.
    • 12.
    • 13.
    • 14.
    • 15.
    • 16.
    • 17.
    • 18.
    • 19.
    • 20.
    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
        • 1.
        • 2.
        • 3.
        • 4.
        • 5.
        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
            ...
            • 1.
            • 2.
            • 3.
            • 4.
            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;
                }
                • 1.

                这里我们想要查看一下函数返回时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
                  • 1.
                  • 2.
                  • 3.
                  • 4.
                  • 5.
                  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
                    ----------------------------------
                    • 1.
                    • 2.
                    • 3.
                    • 4.
                    • 5.
                    • 6.
                    • 7.
                    • 8.
                    • 9.

                    如果想要通过​​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
                          • 1.
                          • 2.
                          • 3.
                          • 4.
                          • 5.
                          • 6.
                          • 7.

                          发现​​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