Systemtap простой пример пользовательского пространства (трассировка функций, Ubuntu)?

(Я потратил довольно много времени на то, чтобы заставить это работать, поэтому я подумал, что задокументирую это - во-первых, чтобы поставить это формально как вопрос):

Есть ли простой пример 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))

person sdaau    schedule 17.10.2017    source источник


Ответы (1)


Итак, это оказалось не тривиально - во-первых, я каким-то образом получил (более новое) ядро ​​​​4.2.0 на Ubuntu 14.04; и, очевидно, systemtap, поставляемый с Ubuntu 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

Вот в чем дело:

  • вы не должны собирать elfutils отдельно, а затем systemtap - вместо этого вы должны передать исходный каталог elfutils в --with-elfutils из configure systemtap, который затем будет configure и также будет собирать elfutils.
  • вы ДОЛЖНЫ выполнить make install из systemtap, даже если он находится в несистемном/частном (локальном) каталоге! - в противном случае возникают некоторые ошибки (к сожалению, не записал их)

После сборки stap сообщает версию:

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

Итак, я нашел базовый пример Фибоначчи 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;
}

Сначала я попытался скомпилировать его так:

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 при запуске скрипта 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

Хорошо, это уже немного более многословно, чем старая версия. В любом случае, я хотел бы проверить функцию 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 (по причинам, которые я уже забыл); так что я подумал, что попробую - и, черт возьми, теперь это действительно заработало:

$ 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 ... не попадает в самом начале, как можно было бы ожидать, а только после того, как программа уже начала генерировать вывод.

Ну, я думаю, это все - конечно, достаточно хорошо для меня, для простого примера...

person sdaau    schedule 17.10.2017
comment
Извините за ошибки, связанные с DWARF. Современный GCC очень хорошо оптимизирует параметры функций и оставляет в отладочной информации только сложные подсказки о том, где их найти. Systemtap не совсем уловил все уловки. -gdwarf2 пропустит эти сложные подсказки (но взамен, затронутые параметры не будут видны даже при stap). - person fche; 19.10.2017