Это больше руководство для самого себя по теме systemtap, я это средство отладки недавно обнаружил и решил задокументировать все, что по нему узнал. Поэтапно и постепенно, какие примеры разбирал. Эти меморизы должны будут побороть мой склероз и пригодиться мне в дальнейшем использовании этого мощного средства.

И так, что такое systemtap?

SystemTap — это средство, которое позволяет собирать и анализировать информацию о работающей Linux системе. В отличие от встроенных средств, таких как netstat, ps, top, SystemTap был разработан с целью предоставить больше возможностей для сбора и представления информации.

SystemTap представляет из себя интерфейс командной строки и скриптовый язык программирования.

Системные администраторы могут использовать SystemTap для мониторинга и анализа производительности системы, а разработчики программного обеспечения могут использовать SystemTap для анализа поведения приложения в работающей системе.

В разработке проекта SystemTap участвуют такие компании как Red Hat, IBM, Oracle Corporation, Hitachi.

Установка на CentOS 7

Воспользовавшись командами из мануала, получаю:

yum install systemtap systemtap-runtime
stap-prep

скажу лишь, то что имел вот такие подключенные репозитории на машине:

repo id repo name
!base/7/x86_64 CentOS-7 - Base
!base-debuginfo/x86_64 CentOS-7 - Debuginfo
!epel/x86_64 Extra Packages for Enterprise Linux 7 - x86_64
!epel-debuginfo/x86_64 Extra Packages for Enterprise Linux 7 - x86_64 - Debug
!home_bayrepo home:bayrepo (CentOS_7)
!extras/7/x86_64 CentOS-7 - Extras
!updates/7/x86_64 CentOS-7 - Updates

Все прошло без особых проблем, все поставилось без дополнительных нареканий.

Инстукция, grep CONFIG_UPROBES /boot/config-$(uname -r) показала: CONFIG_UPROBES=y

Проверка тестовой программки:

[~]# stap -v -e 'probe vfs.read {printf("read performed\n"); exit()}'
Pass 1: parsed user script and 123 library scripts using 227624virt/40728res/3260shr/37660data kb, in 400usr/20sys/732real ms.
Pass 2: analyzed script: 1 probe, 1 function, 4 embeds, 0 globals using 359016virt/173172res/4464shr/169052data kb, in 1830usr/450sys/5222real ms.
Pass 3: using cached /root/.systemtap/cache/ab/stap_ab882d45e2b778be682005a486a8a6be_1675.c
Pass 4: using cached /root/.systemtap/cache/ab/stap_ab882d45e2b778be682005a486a8a6be_1675.ko
Pass 5: starting run.
read performed
Pass 5: run completed in 10usr/50sys/617real ms.

Первая программа

Пример первой программы взят из учебника SystemTap по синтаксису языка. Ниже приведен код программы размещенный в файле first.stp:

1-й вариант:

probe begin {
printf("%d, %s\n", $1, @2)
exit()
}

2-й вариант с точкой с запятой после каждой строки, как в Си:

probe begin {
printf("%d, %s\n", $1, @2);
exit();
}

3-й вариант в одну строку:

probe begin { printf("%d, %s\n", $1, @2); exit(); }

Далее выполняем:

[probes]# stap first.stp "5+5" mystring
10, mystring

Из примера видно, что для обращения к агругментам командной строки необходимо использовать литералы: $1, $2, $3 ... $N для приведения к целому, в нашем случае "5+5" трактуется как int и @1, @2 ... для приведения к строке. В нашем случае это "mystring". Из примера видно, что литералы можно смешивать, т.е $1, @2, $3. Если обратиться к несуществующему аргументу, то возникнет ошибка.

stap преобразует stp код в С и компилирует его. С результатом промежуточным преобразования можно ознакомиться, запустив команду с параметром -k - stap -k first.stp "5+5" mystring

В первую очередь хотелось бы описать userspace probing

Начнем с того, что systemtap позволяет работать как с функциями ядра, так и с userspace. Задокументирую в первую очередь userspace, как более понятную для меня область.

Описание синтаксиса я приводить не буду, при желании его легко выудить из мануала. Все что необходимо разобрать, буду разбирать по мере необходимости при написании тестовых пробов.

begin и end

Примеры использования:

process.begin
process("PATH").begin
process(PID).begin

process.thread.begin
process("PATH").thread.begin
process(PID).thread.begin

process.end
process("PATH").end
process(PID).end

process.thread.end
process("PATH").thread.end
process(PID).thread.end

.begin - вызывается когда процесс описанный PID или PATH создан. Если ни PID ни PATH не заданы, то .begin вызывается для всех создающихся и отпочковавшихся процессов. .thread.begin то же самое, что и выше, только для потоков созданных в рамках указанного процесса. .end и .thread.end вызываются, когда процесс умирает.

В документации написанно, что именно когда умирает , а не умер. Т.е пока он еще существует. Я так понимаю.

Я для понимания и осознания наваял вот такую прожку и собрал ее gdb -o memory memory.c -ggdb3:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void memory_fill(char *src, char *dest, int len){
    char buff[1024] = {0};
    memcpy((char *)buff, src, len);
    memcpy(dest, buff, len);
}

int main(){
    char test[1024] = {0};
    char *data = (char *)malloc(sizeof test);
    if (!data){
        printf("Ooops\n");
        return 1;
    }
    memory_fill(test, data, sizeof test);
    free(data);
    return 0;
}

Не несет абсолютно никакой смысловой нагрузки, сделана для исследования. И первым делом сделаем probe для отслеживания ее запуска:

#! /usr/bin/env stap

# Здесь я прошу при детекте вызова программы вывести ее pid
probe process("./memory").begin {
    printf("Test process started %d\n", pid())
}

# Здесь то же самое, только при завершении программы
probe process("./memory").end {
    printf("Test process ended %d\n", pid())
}

#А здесь говорю висеть и ждать Ctrl+C, если добавлю exit() то получу мгновенный останов
probe begin {
    printf("Waiting for even or Ctrl+C\n")
}

Запускаю, вариант 1:

[~]# stap malloc.stp -c "./memory"
Waiting for even or Ctrl+C
Test process started 14360
Test process ended 14360

Весьма недурно, отследил такую быстро завершающуюся программу. А теперь запущу без параметров и в другом терминале буду запускать программу:

[~]# stap malloc.stp
Waiting for even or Ctrl+C
Test process started 14435
Test process ended 14435
Test process started 14436
Test process ended 14436
Test process started 14437
Test process ended 14437
^C

Неплохо, до прерывания были выловлены все запуски.

.syscall и .function и .statement

Если заменить .begin на .syscall, а .end на .syscall.return, то получим probe для начала и конца системного вызова, где в переменной $syscall можно плучить номер системного вызова, а в первых шести аргументах его параметры: $arg1 ... $arg6.

.function, так же аналогичен .begin, а .function.return = .end. Только в этом случае, еще задается имя функции:

process("PATH").function("NAME")
process("PATH").statement("*@FILE.c:123")
process("PATH").function("*").return
process("PATH").function("myfun").label("foo")

Разберем код для извлечения имен функций из программы, первый пример для программы собранной с -g:

[~]# stap -e 'probe process("./memory").function("*").call { log (probefunc()." ".$$parms)}' -c './memory'
_start 
__libc_csu_init 
_init 
frame_dummy 
register_tm_clones 
main 
memory_fill src=0x7fff10cba010 dest=0x1d37010 len=0x400
__do_global_dtors_aux 
deregister_tm_clones 
_fini 

и второй пример, для этой же программы, но собранной без -g:

[~]# stap -e 'probe process("./memory").function("*").call { log (probefunc()." ".$$parms)}' -c './memory'
_start 
__libc_csu_init 
_init 
frame_dummy 
register_tm_clones 
main 
memory_fill 
__do_global_dtors_aux 
deregister_tm_clones 
_fini 

Идентично!!!

В заключении главы приведу список в стиле, что пробировать, что печатать: готовые функции и идентификаторы.

  • tid() id текущего потока.
  • pid() id текущего процесса текущего потока.
  • uid() id текущего пользователя.
  • execname() имя текущего процесса.
  • cpu() номер текущего процессора.
  • gettimeofday_s() аналог time().
  • get_cycles() число циклов.
  • pp() указатель на текущий probe, который выполняется.
  • ppfunc() имя функции, в которую текущий probe был помещен, если доступно.
  • $$vars список перемнных локальных в области видимости.
  • print_backtrace() распечатать kernel backtrace.
  • print_ubacktrace() распечатать user-space backtrace.

Ну и ссылка на документацию обязательно к прочтению, хоть и на английском

Вырезка оттуда, думаю важная, как итог главы:

  • begin The startup of the systemtap session.
  • end The end of the systemtap session.
  • kernel.function("sys_open") The entry to the function named sys_open in the kernel.
  • syscall.close.return The return from the close system call.
  • module("ext3").statement(0xdeadbeef) The addressed instruction in the ext3 filesystem driver.
  • timer.ms(200) A timer that fires every 200 milliseconds.
  • timer.profile A timer that fires periodically on every CPU.
  • perf.hw.cache_misses A particular number of CPU cache misses have occurred.
  • procfs("status").read A process trying to read a synthetic file.
  • process("a.out").statement("*@main.c:200") Line 200 of the a.out program.

Добавить комментарий

Blog Comments powered by Disqus.

Следующая запись