【问题标题】:Systemtap simple userspace example (function tracing, Ubuntu)?Systemtap 简单用户空间示例(功能跟踪,Ubuntu)?
【发布时间】:2017-10-17 15:31:04
【问题描述】:

(我花了相当长的时间让它工作,所以我想我会记录它 - 首先,将它作为一个问题正式提出):

在用户空间应用程序中是否有systemtap 探测/跟踪函数的简单示例,最好是在 C++ 中?我的系统是 Ubuntu 14.04:

$ uname -a
Linux mypc 4.2.0-42-generic #49~14.04.1-Ubuntu SMP Wed Jun 29 20:22:11 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$ g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4 ...
$ stap --version
Systemtap translator/driver (version 2.3/0.158, Debian version 2.3-1ubuntu1.4 (trusty))

【问题讨论】:

    标签: c++ ubuntu systemtap


    【解决方案1】:

    好的,所以这并不是一件小事——首先,我不知何故在 Ubuntu 14.04 上得到了一个(更新的)内核 4.2.0;显然 Ubuntu 14.04 附带的 systemtap 对于该内核来说太旧了(见下文)。这意味着我必须从源代码构建systemtap - 这是我的程序:

    cd /path/to/src
    git clone git://sourceware.org/git/elfutils.git elfutils_git
    git clone git://sourceware.org/git/systemtap.git systemtap_git
    cd systemtap_git
    ./configure --with-elfutils=/path/to/src/elfutils_git --prefix=/path/to/src/systemtap_git/local --enable-docs=no
    make
    make install
    # after this, there are `stap` executables in: 
    #  /path/to/src/systemtap_git/stap
    #  /path/to/src/systemtap_git/local/bin/stap
    

    事情是这样的:

    • 您不应该单独构建elfutils,然后再构建systemtap - 您应该将elfutils 源目录传递给systemtapconfigure--with-elfutils,然后将configure 和也构建elfutils
    • 您必须执行make install of systemtap,即使它位于非系统/私有(本地)目录中! - 否则,会发生一些错误(不幸的是,没有记录它们)

    构建后,stap 报告版本:

    $ ./stap --version
    Systemtap translator/driver (version 3.2/0.170, commit release-3.1-331-g0efba6fc74c8 + changes) ...
    

    好的,所以我找到了一个基本的斐波那契 C++ 示例进行分析,我稍作修改,并命名为/tmp/<b>fibo.cpp</b>

    // based on: http://www.cplusplus.com/articles/LT75fSEw/
    #include <iostream>
    
    using namespace std;
    
    class Fibonacci{
    public:
      int a, b, c;
      void generate(int);
      void doFibonacciStep(int);
    };
    
    void Fibonacci::doFibonacciStep(int istep){
      c = a + b;
      cout << "  istep: " << istep << " c: " << c << endl;
      a = b;
      b = c;
    }
    
    void Fibonacci::generate(int n){
      a = 0; b = 1;
      cout << " Start: a "<< a << " b " << b << endl;
      for(int i=1; i<= n-2; i++){
        doFibonacciStep(i);
      }
    }
    
    int main()
    {
      cout << "Hello world! Fibonacci series" << endl;
      cout << "Enter number of items you need in the series: ";
      int n;
      cin  >> n;
      Fibonacci fibonacci;
      fibonacci.generate(n);
      return 0;
    }
    

    首先我尝试像这样编译它:

    cd /tmp
    g++ -g fibo.cpp -o fibo.exe
    

    现在,我们要做的第一件事是确定哪些函数可用于在我们的可执行文件中进行探测;为此,我们可以使用stap -L(注意,这里我仍然使用旧的Ubuntu 14.04 系统stap):

    $ stap -L 'process("/tmp/fibo.exe").function("*").call'
    process("/tmp/fibo.exe").function("_GLOBAL__sub_I__ZN9Fibonacci15doFibonacciStepEi").call
    process("/tmp/fibo.exe").function("__static_initialization_and_destruction_0").call $__initialize_p:int $__priority:int
    process("/tmp/fibo.exe").function("doFibonacciStep@/tmp/fibo.cpp:13").call $this:class Fibonacci* const $istep:int
    process("/tmp/fibo.exe").function("generate@/tmp/fibo.cpp:20").call $this:class Fibonacci* const $n:int
    process("/tmp/fibo.exe").function("main@/tmp/fibo.cpp:28").call
    

    很好 - 所以我想探测/跟踪doFibonacciStep 及其输入参数istep。所以我从命令行尝试:

    $ sudo stap -e 'probe process("/tmp/fibo.exe").function("Fibonacci::doFibonacciStep").call { printf("stap do step: %d\n", $istep) }' -c /tmp/fibo.exe 
    WARNING: "__tracepoint_sched_process_fork" [/tmp/stap51A5tV/stap_ab5b824c79b38b5207910696c49c4e22_1760.ko] undefined!
    WARNING: "__tracepoint_sys_exit" [/tmp/stap51A5tV/stap_ab5b824c79b38b5207910696c49c4e22_1760.ko] undefined!
    WARNING: "__tracepoint_sys_enter" [/tmp/stap51A5tV/stap_ab5b824c79b38b5207910696c49c4e22_1760.ko] undefined!
    WARNING: "__tracepoint_sched_process_exec" [/tmp/stap51A5tV/stap_ab5b824c79b38b5207910696c49c4e22_1760.ko] undefined!
    WARNING: "__tracepoint_sched_process_exit" [/tmp/stap51A5tV/stap_ab5b824c79b38b5207910696c49c4e22_1760.ko] undefined!
    ERROR: Couldn't insert module '/tmp/stap51A5tV/stap_ab5b824c79b38b5207910696c49c4e22_1760.ko': Unknown symbol in module
    WARNING: /usr/bin/staprun exited with status: 1
    Pass 5: run failed.  [man error::pass5]
    Tip: /usr/share/doc/systemtap/README.Debian should help you get started.
    $ sudo stap -e 'probe process("/tmp/fibo.exe").function("Fibonacci::doFibonacciStep").call { printf("stap do step: %d\n", $istep) }' -c /tmp/fibo.exe 
    ERROR: Couldn't insert module '/tmp/stapmo60OW/stap_ab5b824c79b38b5207910696c49c4e22_1760.ko': Unknown symbol in module
    WARNING: /usr/bin/staprun exited with status: 1
    Pass 5: run failed.  [man error::pass5]
    

    哎呀,这样的错误 - 不好。帖子"__tracepoint_sched_process_fork undefined" when run systemstap script 解释说,基本上stap 版本对于我拥有的内核来说太旧了——这需要从源代码构建(上图)。现在让我们看看新的stap -L 是如何工作的:

    $ /path/to/src/systemtap_git/stap -L 'process("/tmp/fibo.exe").function("*").call'
    process("/tmp/fibo.exe").function("_GLOBAL__sub_I__ZN9Fibonacci15doFibonacciStepEi@/tmp/fibo.cpp:37").call
    process("/tmp/fibo.exe").function("__do_global_dtors_aux").call
    process("/tmp/fibo.exe").function("__libc_csu_fini").call
    process("/tmp/fibo.exe").function("__libc_csu_init").call
    process("/tmp/fibo.exe").function("__static_initialization_and_destruction_0@/tmp/fibo.cpp:37").call $__initialize_p:int $__priority:int
    process("/tmp/fibo.exe").function("_fini").call
    process("/tmp/fibo.exe").function("_init").call
    process("/tmp/fibo.exe").function("_start").call
    process("/tmp/fibo.exe").function("deregister_tm_clones").call
    process("/tmp/fibo.exe").function("doFibonacciStep@/tmp/fibo.cpp:13").call $this:class Fibonacci* const $istep:int
    process("/tmp/fibo.exe").function("frame_dummy").call
    process("/tmp/fibo.exe").function("generate@/tmp/fibo.cpp:20").call $this:class Fibonacci* const $n:int
    process("/tmp/fibo.exe").function("main@/tmp/fibo.cpp:28").call
    process("/tmp/fibo.exe").function("register_tm_clones").call
    

    很好,这已经比旧版本更冗长了。无论如何,我想探究doFibonacciStep 函数及其输入参数,这里是$istep。所以我在命令行上写了这个:

    $ sudo /path/to/src/systemtap_git/stap -e 'probe process("/tmp/fibo.exe").function("Fibonacci::doFibonacciStep").call { printf("stap do step: %d\n", $istep) }' -c /tmp/fibo.exe 
    semantic error: while processing probe process("/tmp/fibo.exe").function("Fibonacci::doFibonacciStep@/tmp/fibo.cpp:13").call from: process("/tmp/fibo.exe").function("Fibonacci::doFibonacciStep").call
    
    semantic error: No cfa_ops supplied, but needed by DW_OP_call_frame_cfa: identifier '$istep' at <input>:1:107
            source: probe process("/tmp/fibo.exe").function("Fibonacci::doFibonacciStep").call { printf("stap do step: %d\n", $istep) }
    
    Pass 2: analysis failed.  [man error::pass2]
    

    哎呀——一个令人讨厌的错误,并没有真正告诉我任何事情——关于这个错误的错误报告很少(大部分来自 2010 年)。所以我正要卡在这里,当我不知道为什么,我记得前几天我用-gdwarf-2编译了一些程序(原因我现在忘记了);所以我想我会尝试一下 - whaddayaknow,它实际上现在开始工作了:

    $ g++ -gdwarf-2 fibo.cpp -o fibo.exe
    $ sudo /path/to/src/systemtap_git/stap -e 'probe process("/tmp/fibo.exe").function("Fibonacci::doFibonacciStep").call { printf("stap do step: %d\n", $istep) }' -c /tmp/fibo.exe 
    Hello world! Fibonacci series
    Enter number of items you need in the series: 5
     Start: a 0 b 1
      istep: 1 c: 1
      istep: 2 c: 2
      istep: 3 c: 3
    stap do step: 1
    stap do step: 2
    stap do step: 3
    

    不错!请注意,stap 打印实际上是在程序完成后打印的(也就是说,它们不会与它们发生的实际程序输出交错)。

    我们可以编写一个脚本,而不是直接在命令行上指定探测点和行为 - 所以这里是check-do-step.stp - 这里有一些额外的东西:

    #!/usr/bin/env stap
    
    global stringone = "Testing String One"
    global stringtwo = "Testing String Two"
    
    probe begin {
      printf("begin: %s\n", stringone)
      #exit() # must have; else probe end runs only upon Ctrl-C if we only have `begin` and `end` probes!
    }
    
    probe process(
      "/tmp/fibo.exe"
    ).function(
      "Fibonacci::doFibonacciStep"
    ).call {
      printf("stap do step: %d\n", $istep)
    }
    
    probe end {
      newstr = "We Are " . stringtwo . " And We're Done" # string concat
      printf("%s\n", newstr)
    }
    

    ...使用此脚本,我们的调用和结果如下所示:

    $ sudo /path/to/src/systemtap_git/stap check-do-step.stp -c /tmp/fibo.exe
    Hello world! Fibonacci series
    Enter number of items you need in the series: begin: Testing String One
    6
     Start: a 0 b 1
      istep: 1 c: 1
      istep: 2 c: 2
      istep: 3 c: 3
      istep: 4 c: 5
    stap do step: 1
    stap do step: 2
    stap do step: 3
    stap do step: 4
    We Are Testing String Two And We're Done
    

    再次注意 - begin: Testing ... 字符串并没有像我们预期的那样在一开始就命中,而是在程序已经开始生成输出之后才命中。

    好吧,我想就是这样 - 对我来说当然足够了,举个简单的例子......

    【讨论】:

    • 很抱歉出现与 DWARF 相关的错误。现代 GCC 非常擅长优化函数参数,并且只在 debuginfo 中留下关于在哪里找到它们的复杂线索。 Systemtap 还没有完全掌握所有技巧。 -gdwarf2 将忽略那些复杂的线索(但作为交换,受影响的参数甚至对 stap 不可见)。
    猜你喜欢
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-09
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多