Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Руководство для начинающих пользователей SystemTap. Принцип работы SystemTap

Оригинал: SystemTap Beginners Guide
Авторы: Don Domingo, William Cohen
Дата публикации: 20 июля 2009 г.
Перевод: А.Панин
Дата перевода: 30 сентября 2014 г.

Глава 3. Принцип работы SystemTap

SystemTap позволяет пользователям разрабатывать и повторно использовать простые сценарии для осуществления глубокого анализа операций, выполняющихся в рамках функционирующей системы на основе ядра Linux. Эти сценарии могут быть разработаны с целью извлечения значений параметров системы, их фильтрации и быстрого (а также безопасного) накопления, что позволяет производить диагностику сложных проблем с производительностью (или даже функциональных проблем) системы.

Основная идея, положенная в основу механизма сценариев SystemTap, заключается в именовании событий (events) и связи с ними обработчиков (handlers). В момент, когда SystemTap начинает исполнение сценария, на самом деле средствами SystemTap инициируется мониторинг события; как только событие наступает, ядро Linux выполняет связанный с ним обработчик как быструю подпрограмму, после чего продолжает работу в обычном режиме.

Существует несколько видов событий; вход в функцию/выход из функции, истечение времени таймера, завершение сессии, и.т.д. Обработчик является набором объявлений языка сценариев, устанавливающих операции, которые необходимо выполнить при наступлении заданного события. Эти операции обычно заключаются в извлечении данных из контекста события, сохранении этих данных во внутренних переменных, а также в выводе результатов.

3.1. Архитектура

Сессия SystemTap начинается в момент, когда вы инициируете исполнение сценария SystemTap. При этом сессия делится на следующие этапы:

Процедура 3.1. Сессия SystemTap
  1. В первую очередь SystemTap проверяет, используются ли в рамках сценария какие-либо тапсеты из библиотеки тапсетов (обычно расположенной в директории /usr/share/systemtap/tapset/). В результате SystemTap заменит все найденные тапсеты на соответствующие им объявления из библиотеки тапсетов.
  2. После этого SystemTap преобразует сценарий в код на языке программирования C и задействует компилятор языка C для создания модуля ядра ОС на его основе. Инструменты, необходимые для выполнения этой операции, находятся в пакете systemtap (обратитесь к Разделу 2.1.1, "Установка SystemTap" для получения дополнительной информации).
  3. SystemTap загрузит созданный модуль, после чего активирует все зонды (использующие события и обработчики) сценария. Данные функции реализованы в рамках утилиты staprun из пакета systemtap-runtime (обратитесь к Разделу 2.1.1, "Установка SystemTap" для получения дополнительной информации).
  4. В момент наступления события будут выполнены соответствующие ему обработчики.
  5. После завершения сессии SystemTap деактивирует все зонды и выгрузит модуль из адресного пространства ядра ОС.

Данная последовательность действий выполняется с помощью единственной программы с интерфейсом командной строки: stap. Эта программа является основным инструментом для управления работой SystemTap. Для получения дополнительной информации о программе stap обратитесь к странице руководства данной программы с помощью команды man stap (страница руководства будет доступна только после того, как система SystemTap будет корректно установлена на вашей машине).

3.2. Сценарии SystemTap

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

Как говорилось в Главе 3, "Принцип работы SystemTap", сценарии SystemTap состоят из двух компонентов: событий (events) и обработчиков (handlers). После открытия сессии SystemTap начинает мониторинг операционной системы в ожидании наступления заданных событий и выполняет обработчики в моменты их наступления.

Примечание

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

Обработчик из состава зонда обычно также называется телом зонда (probe body).

Говоря на языке разработчиков приложений, использование событий и обработчиков аналогично диагностике кода путем включения вызовов функций вывода данных в последовательность команд программы. Эти диагностические вызовы функций вывода данных позволяют вам ознакомиться с историей исполнения команд после запуска программы.

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

Формат сценариев

Сценарии SystemTap используют расширение .stp и содержат реализации зондов, записанные в следующем формате:
probe событие {операторы}

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

Каждый зонд имеет соответствующий ему блок операторов (statement block). Этот блок операторов ограничивается с помощью фигурных скобок ({ }) и содержит операторы, которые должны выполняться при наступлении события. SystemTap исполняет упомянутые операторы последовательно; при использовании множества операторов специальные разделители и ограничители в общем случае не являются необходимыми.

Примечание
В блоках операторов сценариев SystemTap используются те же синтаксис и семантики, что и в языке программирования C. Блок операторов может помещаться внутрь другого блока операторов.
SystemTap позволяет разрабатывать функции для обособленного размещения кода, который будет использоваться во множестве зондов. Таким образом, вместо постоянного написания одних и тех же наборов операторов при разработке множества зондов, вы можете просто разместить повторяющиеся инструкции в рамках функции (function) также, как показано ниже:
function имя_функции(аргументы) {операторы}
probe событие {имя_функции(аргументы)}

Операторы, объявленные в рамках функции с заданным именем, будут выполняться тогда, когда будет задействован зонд соответствующего события. Аргументы являются необязательными данными, передаваемыми в функцию.

Важная информация
Раздел 3.2, "Сценарии SystemTap" составлен главным образом для ознакомления читателей с базовыми понятиями, относящимися к сценариям SystemTap. Для лучшего понимания принципа работы сценариев SystemTap рекомендуется обратиться к Главе 5, "Полезные сценарии SystemTap"; в каждом разделе данной главы предоставлено детальное описание сценария, его событий, обработчиков и ожидаемого вывода.

3.2.1. События

События в рамках SystemTap могут быть грубо разделены на два типа: синхронные и асинхронные.

Синхронные события

Синхронное событие наступает в момент, когда какой-либо процесс выполняет инструкцию в определенном фрагменте кода ядра ОС. Оно позволяет передать другим событиям ссылку на точку, из который могут быть получены дополнительные данные контекста.

Примерами синхронных событий являются:

syscall.system_call

Вход в системный вызов system_call. В том случае, если желательно отследить выход из системного вызова, следует добавить суффикс .return к описанию события для его отслеживания. Например, для отслеживания входа и выхода из системного вызова close, следует использовать события syscall.close и syscall.close.return соответственно.

vfs.file_operation

Вход в операцию file_operation виртуальной файловой системы (VFS). Аналогично событию syscall, добавление суффикса .return к описанию события позволяет отследить выход из операции file_operation.

kernel.function("function")

Вход в функцию ядра ОС с именем function. Например, описание события kernel.function("sys_open") соответствует "событию", наступающему в момент вызова функции sys_open ядра ОС из-какого-либо программного потока, выполняющегося в системе. Для указания на необходимость отслеживания события выхода из функции sys_open ядра ОС, к описанию события должке быть добавлен суффикс return; таким образом, в результате будет получено описание события kernel.function("sys_open").return.

При описании событий зондов для формирования шаблонов может использоваться символ звездочки (*). Также вы можете отслеживать вход в функцию и выход из функции, объявленной в рамках файла исходного кода ядра ОС. Рассмотрите следующий пример:

Пример 3.1. wildcards.stp
probe kernel.function("*@net/socket.c") { }
probe kernel.function("*@net/socket.c").return { }

В примере выше первое событие зонда описывает вход во ВСЕ функции из файла исходного кода ядра ОС net/socket.c. Второй зонд описывает выход из всех этих функций. Учтите, что в этом примере в рамках обработчиков не были использованы операторы; по этой причине в случае задействования данного сценария не будет осуществляться сбор и вывод какой-либо информации.

kernel.trace("tracepoint")

Статический зонд для точки трассировки tracepoint. Новые версии ядер Linux (2.6.30 и более поздние) включают инструменты для обработки определенных событий в рамках ядра. Эти события статически отмечены с помощью точек трассировки. Одним из примеров использования точки трассировки в рамках systemtap является событие, описываемое как kernel.trace("kfree_skb"), которое наступает каждый раз при очищении буфера сетевого стека ядра ОС.

module("module").function("function")

Позволяет отслеживать вызовы функций, реализованных в рамках модулей ядра ОС. Например:

Пример 3.2. moduleprobe.stp
probe module("ext3").function("*") { }
probe module("ext3").function("*").return { }

Первый зонд из Примера 3.2, "moduleprobe.stp" отслеживает все входы во все функции модуля ext3 ядра ОС. Второй зонд отслеживает все выходы из функций того же модуля; суффикс .return используется точно также, как и в описании события kernel.function(). Учтите, что зонды в Примере 3.2, "moduleprobe.stp" не содержат операторов в обработчиках и по этой причине не будут выводить какие-либо полезные данные в случае использования (как и в Примере 3.1, "wildcards.stp").

Установленные в системе модули ядра ОС обычно расположены в директории /lib/modules/версия_ядра, где вместо строки версия_ядра используется загруженная на данный момент версия ядра ОС. Для файлов модулей используется расширение .ko.

Асинхронные события

Асинхронные события не привязываются к определенной инструкции или определенному фрагменту кода. Это семейство событий состоит главным образом из счетчиков, таймеров и аналогичных механизмов.

Примерами асинхронных событий являются:

begin

Событие открытия сессии SystemTap; оно наступает настолько быстро, насколько быстро начинает исполняться сценарий SystemTap.

end

Событие завершения сессии SystemTap.

События таймеров

Это события, позволяющие указать функцию, которая должна периодически исполняться. Например:

Пример 3.3. timer-s.stp
probe timer.s(4)
{
	printf("hello world\n")
}
В Примере 3.3, "timer-s.stp" представлен зонд, который выводит строку hello world через каждые четыре секунды. Также имеется возможность использования следующих описаний событий таймеров:
  • timer.ms(миллисекунды)
  • timer.us(микросекунды)
  • timer.ns(наносекунды)
  • timer.hz(герцы)
  • timer.jiffies(тики)

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

Важная информация
SystemTap позволяет использовать описания событий из коллекции большого объема. Для получения дополнительной информации о поддерживаемых событиях обратитесь к странице руководства man stapprobes. Кроме того, раздел "Смотрите также" страницы руководства man stapprobes содержит ссылки на другие страницы руководств man, на которых обсуждаются поддерживаемые события, связанные с отдельными подсистемами и программными компонентами.

3.2.2. Обработчики/тела зондов SystemTap

Рассмотрите следующий пример сценария:

Пример 3.4. helloworld.stp
probe begin
{
    printf ("hello world\n")
    exit ()
}

В Примере 3.4, "helloworld.stp" событие begin (то, которое наступает при открытии сессии) активирует обработчик, помещенный между фигурными скобками { }, который просто печатает строку hello world с символом перехода на новую строку в конце, после чего завершает работу.

Примечание
Исполнение сценариев SystemTap продолжается до момента исполнения функции exit(). В том случае, если пользователь желает остановить исполнение сценария, он также может быть остановлен вручную с помощью сочетания клавиш Ctrl+C.

Функции printf ()

Функция printf () является одним из простейших механизмов для вывода данных. Также printf () может использоваться для вывода данных, полученных с помощью большого количества разнообразных функций SystemTap, причем в этом случае должен использоваться следующий формат записи:
    printf ("format string\n", arguments)

Строка форматирования format string устанавливает способ вывода аргументов arguments. Строка форматирования в Примере 3.4, "helloworld.stp" сообщает SystemTap о необходимости вывода строки hello world и не содержит спецификаторов форматирования.

Вы можете использовать спецификаторы форматирования %s (для строк) и %d (для чисел) в рамках строк форматирования в зависимости от списка ваших аргументов. Строки форматирования могут содержать множество спецификаторов форматирования, каждый из которых должен соответствовать определенному аргументу; аргументы разделяются с помощью символа запятой (,).

Примечание
В плане семантики функция printf SystemTap очень похожа на аналогичную функцию языка C. Описанные выше синтаксис и форматирование, используемые при работе с функцией printf SystemTap идентичны тем, что используются при работе с функцией printf языка C.

Для иллюстрации этих утверждений рассмотрим следующий пример реализации зонда:

Пример 3.5. variables-in-printf-statements.stp
probe syscall.open
{
    printf ("процесс %s(%d) открыл файл\n", execname(), pid())
}
Сценарий из Примера 3.5, "variables-in-printf-statements.stp" сообщает SystemTap о необходимости отслеживания всех входов в системный вызов open; для каждого события он будет выводить значение, возвращаемое функцией execname() (строку, содержащую имя исполняемого файла) и pid() (текущее значение идентификатора процесса), а также строку "открыл файл". Фрагмент данных, выводимых приведенным выше зондом, будет выглядеть следующим образом:
процесс vmware-guestd(2206) открыл файл
процесс hald(2360) открыл файл
процесс hald(2360) открыл файл
процесс hald(2360) открыл файл
процесс df(3433) открыл файл
процесс df(3433) открыл файл
процесс df(3433) открыл файл
процесс hald(2360) открыл файл

Функции SystemTap

SystemTap поддерживает широкий спектр функций, которые могут использоваться и принимать аргументы аналогично функции printf (). В Примере 3.5, "variables-in-printf-statements.stp" используется функция execname() (возвращающая имя процесса, который осуществил вызов функции ядра ОС/выполнил системный вызов) и функция pid() (возвращающая идентификатор текущего процесса).

Ниже приведен список часто используемых функций SystemTap:

tid()

Возвращает идентификатор текущего программного потока.

uid()

Возвращает идентификатор текущего пользователя.

cpu()

Возвращает номер используемого в данный момент центрального процессора.

gettimeofday_s()

Возвращает количество секунд с начала эпохи UNIX (1 января 1970 г.).

ctime()

Преобразует количество секунд с начала эпохи UNIX в системную дату.

pp()

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

thread_indent()

Данная функция может оказаться особенно полезной, так как предоставляет возможность лучшей организации выводимых результатов. Функция принимает один аргумент, отражающий разницу в отступах и устанавливающий, насколько нужно увеличить или уменьшить значение "счетчика отступов" программного потока, на основании которого к строке будут добавлены пробелы при выводе. После этого она возвращает строку со обобщенной информацией трассировки, которая отформатирована с использованием необходимого количества отступов.

Обобщенная информация, переданная в рамках возвращаемой строки, содержит метку времени (количество микросекунд, прошедших момента с первого вызова функции thread_indent() из программного потока), имя процесса и идентификатор программного потока. Эта информация позволяет идентифицировать вызванные функции, процесс, вызывающий эти функции и продолжительность каждого из вызовов функций.

В том случае, если события входов в функции и выходов из них следуют друг за другом, их очень просто сопоставить. Однако, в большинстве случаев, после осуществления входа в первую функцию может быть осуществлено множество входов в другие функции и выходов из них перед тем, как будет осуществлен выход из первой функции. Счетчик отступов поможет вам сопоставить событие входа в функцию с соответствующим ему событием выхода из этой функции, отделяя следующее событие вызова функции в том случае, если на его месте не находится событие выхода из предыдущей функции.

Рассмотрите следующий пример использования функции thread_indent():

Пример 3.6. thread_indent.stp
probe kernel.function("*@net/socket.c").call
{
    printf ("%s -> %s\n", thread_indent(1), probefunc())
}
probe kernel.function("*@net/socket.c").return
{
    printf ("%s <- %s\n", thread_indent(-1), probefunc())
}			
			
Сценарий, приведенный в Примере 3.6, "thread_indent.stp" выводит данные, возвращаемые функциями thread_indent() и probefunc() для каждого события в следующем формате:
0 ftp(7223): -> sys_socketcall
1159 ftp(7223):  -> sys_socket
2173 ftp(7223):   -> __sock_create
2286 ftp(7223):    -> sock_alloc_inode
2737 ftp(7223):    <- sock_alloc_inode
3349 ftp(7223):    -> sock_alloc
3389 ftp(7223):    <- sock_alloc
3417 ftp(7223):   <- __sock_create
4117 ftp(7223):   -> sock_create
4160 ftp(7223):   <- sock_create
4301 ftp(7223):   -> sock_map_fd
4644 ftp(7223):    -> sock_map_file
4699 ftp(7223):    <- sock_map_file
4715 ftp(7223):   <- sock_map_fd
4732 ftp(7223):  <- sys_socket
4775 ftp(7223): <- sys_socketcall			
Данный вывод содержит следующую информацию:
  • Время (в микросекундах) с момента первого вызова функции thread_indent() в рамках программного потока (в составе строки, возвращаемой функцией thread_indent()).
  • Имя процесса (и соответствующий идентификатор), который выполнил вызов функции (в составе строки, возвращаемой функцией thread_indent()).
  • Стрелку, указывающую на то, был ли осуществлен вход в функцию (<-) или выход из нее (->); отступы помогут вам сопоставить входы в определенные функции с соответствующими им выходами из функций.
  • Имя функции, вызванной процессом.
name

Идентифицирует имя определенного системного вызова. Данная переменная может использоваться исключительно в зондах, предназначенных для отслеживания события задействования системного вызова syscall.system_call.

target()

Используется вместе с командой stap сценарий -x идентификатор_процесса или stap сценарий -c команда. Если вы хотите, чтобы сценарий принимал идентификатор процесса или команду в качестве аргумента, используйте функцию target() в качестве переменной в рамках сценария для ссылки на эти данные. Например:

Пример 3.7. targetexample.stp
probe syscall.* {
    if (pid == target())
        printf("%s\n", name)
}			
			

При запуске сценария из Примера 3.7, "targetexample.stp" с аргументом -x идентификатор_процесса, он будет отслеживать системные вызовы (как и указано в рамках описания события syscall.*) и выводить имена системных вызовов, осуществленных процессом с заданным идентификатором.

Аналогичный эффект будет достигаться каждый раз, когда вы будете использовать условный переход if (pid() == идентификатор_процесса) при необходимости работы с определенным процессом. Однако, использование функции target() упрощает повторное использование сценария, так как у вас появляется возможность передачи идентификатора процесса в качестве аргумента при каждом использовании сценария (а именно, при использовании команды stap targetexample.stp -x идентификатор_процесса).

Для получения дополнительной информации о функциях, поддерживаемых SystemTap, обратитесь к странице руководства man stapfuncs.


Следующий раздел : 3.3. Базовые конструкции обработчиков SystemTap.