Systemtap简单的用户空间示例(函数跟踪,Ubuntu)?

sda*_*aau 1 c++ ubuntu systemtap

(我花了很多时间使它生效,所以我想我要记录下来-首先,将其正式提出来作为一个问题):

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))
Run Code Online (Sandbox Code Playgroud)

sda*_*aau 5

好的,所以这并不是一件容易的事-首先,我以某种方式最终在Ubuntu 14.04上获得了(较新的)内核4.2.0。而且显然systemtapUbuntu 14.04附带的版本对于该内核而言太旧​​了(请参阅下文)。这意味着我必须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
Run Code Online (Sandbox Code Playgroud)

这是东西:

  • 你不应该建立elfutils独立,然后systemtap-你应该改为通过elfutils源目录--with-elfutilssystemtapconfigure,由它来configure和构建elfutils为好。
  • 你必须做make installsystemtap,即使是在非系统/私有(本地)目录!-否则,会发生一些错误(不幸的是,没有记录这些错误)

构建后,stap报告版本:

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

好的,所以我找到了一个基本的Fibonacci C ++示例进行分析,并对其进行了稍微修改,并称为:/tmp/fibo.cpp

// 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;
}
Run Code Online (Sandbox Code Playgroud)

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

cd /tmp
g++ -g fibo.cpp -o fibo.exe
Run Code Online (Sandbox Code Playgroud)

现在,我们要做的第一件事是弄清楚哪些函数可用于在可执行文件中进行探测。为此,我们可以使用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
Run Code Online (Sandbox Code Playgroud)

很好-所以我想探查/追踪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]
Run Code Online (Sandbox Code Playgroud)

哎呀,像这样的错误-不好。后“__tracepoint_sched_process_fork未定义”运行时systemstap脚本解释说,基本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
Run Code Online (Sandbox Code Playgroud)

很好,这已经比旧版本更详细了。无论如何,我想在这里探讨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]
Run Code Online (Sandbox Code Playgroud)

糟糕-一个令人讨厌的错误,并且什么都没告诉我-几乎没有关于此错误的错误报告(大部分来自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
Run Code Online (Sandbox Code Playgroud)

真好!请注意,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)
}
Run Code Online (Sandbox Code Playgroud)

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

#!/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)
}
Run Code Online (Sandbox Code Playgroud)

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

好吧,我想是这样-对于一个简单的例子,对我来说肯定足够好...