V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sherryxueli
V2EX  ›  推广

SystemTap 使用技巧之一

  •  
  •   sherryxueli · 2017-08-21 18:29:59 +08:00 · 3631 次点击
    这是一个创建于 2437 天前的主题,其中的信息可能已经有所发展或是发生改变。

    1.简介

     SystemTap 是一个 Linux 非常有用的调试(跟踪 /探测)工具,常用于 Linux
     内核或者应用程序的信息采集,比如:获取一个函数里面运行时的变
     量、调用堆栈,甚至可以直接修改变量的值,对诊断性能或功能问题非
     常有帮助。SystemTap 提供非常简单的命令行接口和很简洁的脚本语
     言,以及非常丰富的 tapset 和例子。  
    

    2.何时使用

    定位(内核)函数位置
    查看函数被调用时的调用堆栈、局部变量、参数
    查看函数指针变量实际指的是哪个函数
    查看代码的执行轨迹(哪些行被执行了)
    查看内核或者进程的执行流程
    调试内存泄露或者内存重复释放
    统计函数调用次数
    ......
    

    3.原理

    在网上找了个原理图:

    systemtap

    SystemTap 的处理流程有 5 个步骤:解析 script 文件(parse)、细化( elaborate )、script 文件翻译成 C 语言代码( translate )、编译 C 语言代码(生成内核模块)( build )、加载内核模块( run )

    systemtap_phase

    4.安装

    SystemTap 依赖的 package: elfutils、gcc、kernel-devel、kernel-debuginfo 如果调用用户态进程,还需要该程序有调试符号,否则无法调试。 推荐使用最新稳定版的 SystemTap,目前最新稳定版为:systemtap-2.9.tar.gz

    5.入门

    5.1 stap 命令

    stap [OPTIONS] FILENAME [ARGUMENTS]
    stap [OPTIONS] - [ARGUMENTS]
    stap [OPTIONS] – e SCRIPT [ARGUMENTS]
    
    比较常用和有用的参数:
    -e SCRIPT               Run given script.
    -l PROBE                List matching probes.
    -L PROBE                List matching probes and local variables.
    -g                      guru mode 
    -D NM=VAL               emit macro definition into generated C code
    -o FILE                 send script output to file, instead of stdout.
    -x PID                  sets target() to PID
    

    Hello World:

    root@j9 ~/stp# cat hello-world.stp
    probe begin {
        print("===Hello World===\n")
    }
    
    probe end {
        print("===GunLe===\n")
    }
    root@j9 ~/stp# stap hello-world.stp 
    ===Hello World===
    ^C===GunLe===
    root@j9 ~/stp# stap -e 'probe begin { printf("Hello World!\n") exit() }'   
    Hello World!
    root@j9 ~/stp#
    

    5.2 staprun 命令

    staprun [OPTIONS] MODULE [MODULE-OPTIONS]
    

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

    6.脚本语言

    6.1 probe

    “ probe ” <=> “探测”, 是 SystemTap 进行具体地收集数据的关键字。 systemtap_probe

    “ probe point ” 是 probe 动作的时机,也称探测点。也就是 probe 程序监视的某事件点,一旦侦测的事件触发了,则 probe 将从此处插入内核或者用户进程中。 “ probe handle ” 是当 probe 插入内核或者用户进程后所做的具体动作。

    probe 用法:

    probe probe-point { statement }
    

    在 Hello World 例子中 begin 和 end 就是 probe-point,statement 就是该探测点的处理逻辑,在 Hello World 例子中 statement 只有一行 print,statement 可以是复杂的代码块。 探测点语法:

    kernel.function(PATTERN)
    kernel.function(PATTERN).call
    kernel.function(PATTERN).return
    kernel.function(PATTERN).return.maxactive(VALUE)
    kernel.function(PATTERN).inline
    kernel.function(PATTERN).label(LPATTERN)
    module(MPATTERN).function(PATTERN)
    module(MPATTERN).function(PATTERN).call
    module(MPATTERN).function(PATTERN).return.maxactive(VALUE)
    module(MPATTERN).function(PATTERN).inline
    kernel.statement(PATTERN)
    kernel.statement(ADDRESS).absolute
    module(MPATTERN).statement(PATTERN)
    process(PROCESSPATH).function(PATTERN)
    process(PROCESSPATH).function(PATTERN).call
    process(PROCESSPATH).function(PATTERN).return
    process(PROCESSPATH).function(PATTERN).inline
    process(PROCESSPATH).statement(PATTERN)
    

    PATTERN 语法为:

    func[@file]
    func@file:linenumber
    

    例如:

    kernel.function("*init*")
    module("ext3").function("*")
    kernel.statement("*@kernel/time.c:296")
    process("/home/admin/tengine/bin/nginx").function("ngx_http_process_request")
    

    在 return 探测点可以用$return 获取该函数的返回值。 inline 函数无法安装.return 探测点,也无法用$return 获取其返回值。

    6.2 基本语法

    SystemTap 脚本语法比较简单,与 C 语言类似,只是每一行结尾";"是可选的。主要语句如下: if/else、while、for/foreach、break/continue、return、next、delete、try/catch 其中: next:主要在 probe 探测点逻辑处理中使用,调用此语句时,立刻从调用函数中退出。不同于 exit()的是,next 只是退出当前的调用函数,而此 SystemTap 并没有终了,但 exit()则会终止 SystemTap。

    6.2.1 变量

    不需要明确声明变量类型,脚本语言会根据函数参数等自动判断变量是什么类型的。 局部变量:在声明的 probe 和 block (”{ }“范围内的部分)内有效。 全局变量:用” global “声明的变量,在此 SystemTap 的整个动作过程中都有效。全局变量的声明位置没有具体要求。需要注意的是,全局变量默认有锁保护,使用过多会有性能损失,如果用全局变量保存指针,可能出现指针所指的内容被进程修改,在探测点中拿不到真正的数据。 获取进程中的变量(全局变量、局部变量、参数)直接在变量名前面加$即可(后面会有例子)

    6.2.2 注释

    # ...... :Shell 语言风格    
    //...... :C++语言风格    
     /*......*/ :C 语言风格
    

    6.2.3 操作符

    比较运算符、算数运算符基本上与 C 语言一样,需要特别指出的是: (1)、.操作符:连接两个字符串,类似于 php ; (2)、=~和!~:正则匹配和正则不匹配;

    6.2.4 函数

    函数定义例子:

    function indent:string (delta:long){
      return _generic_indent(-1, "",  delta)
    }
    
    function _generic_indent (idx, desc, delta)
    {
      ts = __indent_timestamp ()
      if (! _indent_counters[idx]) _indent_timestamps[idx] = ts
      depth = _generic_indent_depth(idx, delta)
      return sprintf("%6d (%d:%d) %s:%-*s", (ts - _indent_timestamps[idx]), depth, delta, desc, depth, "")
    }  
    
    function strlen:long(s:string) %{
        STAP_RETURN(strlen(STAP_ARG_s));
    %}
    

    官方有很多很有用的函数,详情请参考: https://sourceware.org/systemtap/tapsets/ 以及在本机安装了 SystemTap 之后在目录 /usr/local/share/systemtap/tapset/下也可以看具体函数的实现以及一些奇特的用法。

    本文是阿里云 CDN 安防专家金九所写,下次会发布 SystemTap 使用技巧之二,欢迎沟通交流~

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5263 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 09:05 · PVG 17:05 · LAX 02:05 · JFK 05:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.