DTrace — это такая штука, присутствующая во FreeBSD, NetBSD, MacOS, Solaris и Linux . DTrace предназначен для динамической трассировки ядра системы и приложений в реальном времени, главным образом с целью их профайлинга и отладки. Сегодня мы попробуем поработать с DTrace во FreeBSD. Кроме того, мы установим DTrace и в Ubuntu, хотя по поводу стабильности такой конфигурации и остаются вопросы.
Дополнение: Спустя пару лет после публикации этого поста поддержка DTrace была добавлена в Windows . В Linux теперь есть полноценный аналог DTrace под названием bpftrace.
Чем так примечателен именно DTrace?
DTrace является не единственным решением в своем роде. Из достойных аналогов можно назвать, например, SystemTap . По сравнению с аналогами, DTrace обладает следующими примечательными свойствами:
- Во многих системах DTrace есть из коробки и просто работает, не нужно ничего устанавливать;
- DTrace не тормозит по 10 секунд перед запуском трассировки;
- Технология появилась давно и является вполне зрелой, ее не страшно использовать прямо на проде;
- Имеется подробнейшая документация;
- В первом приближении функционал выглядит более удобно и богато, чем у аналогов. Стабильные и документированные пробы есть прямо на все события в системе — в стиле TCP-соединение установлено, TCP-соединение закрыто, и такого рода вещи. А не так, что нужно знать, за какой процедурой в коде этой версии ядра нужно следить;
Приведенные далее примеры были проверены на FreeBSD версии 10.3, но по идее должны без изменений работать и на других версиях системы.
Примеры трассировки ядра системы
Как уже отмечалось, установка не требуется, так как DTrace является частью ядра FreeBSD начиная с версии 9.2 и по умолчанию включен в GENERIC. Если вы используете старое ядро, или новое, но собранное с кастомными параметрами, здесь описывается, как включить поддержку DTrace. Описание процесса сборки ядра FreeBSD вы найдете в заметке Собираем ядро и мир FreeBSD из исходников .
Загружаем все необходимые модули:
Смотрим список доступных пробов (probe, еще можно перевести как «зонд» или даже «щуп»):
sudo dtrace -l | grep entry | wc -l
Как видите, пробов существует великое множество. У меня в системе их доступно более 28 тысяч!
В качестве примера попробуем логировать создание всех новых процессов:
Пример вывода:
dtrace: buffer size lowered to 2m
CPU ID FUNCTION:NAME
0 51735 none:exec-success /usr/sbin/sshd -R
0 51735 none:exec-success -csh
0 51735 none:exec-success /usr/games/fortune freebsd-tips
^C
Другой пример — трейсим все, что как-то связано с TCP:
Вывод будет выглядеть как-то так:
dtrace: buffer size lowered to 2m
dtrace: aggregation size lowered to 2m
^C
connect-established 1
connect-request 1
state-change 5
send 62
receive 107
Еще вас может заинтересовать утилита dtruss:
Эта утилита трейсит используемое программой системные вызовы. То есть, делает то же самое, что и truss (или strace в Linux), только через DTrace.
Если нужно потрейсить определенные системные вызовы, совершаемые конкретным процессом, можно сделать это при помощи такого скрипта:
syscall:freebsd:poll:entry /execname == «postgres»/
{
printf(«fds = %p, nfds = %d, timeout = %d», arg0, arg1, arg2);
}
syscall:freebsd:poll:return /execname == «postgres»/
{
printf(«result = %p», arg1)
}
Как видите, DTrace позволяет нам залезть своими грязными лапками практически в любое место системы, будь то системные вызовы, сетевой стек, или что-то еще (см полный список доступных пробов). Довольно круто, не так ли?
Пример трассировки приложения
DTrace позволяет трейсить не только ядро операционной системы, но и пользовательские приложения. В качестве примера приложения, снабженного пробами для DTrace, рассмотрим мой любимый PostgreSQL . Чтобы активировать пробы, придется собрать PostgreSQL из исходников с соответствующими флагами. Вопрос сборки PostgreSQL подробнейшим образом рассмотрен в заметке PostgreSQL: сборка из исходников и настройка под Linux . Далее предполагается, что процесс этот вам прекрасно знаком.
Нам понадобится пакет libelf:
Непосредственно сборка PostgreSQL (я лично проверял на версии 9.6, но по идее должно работать и на других):
CFLAGS = «-O0 -g» LDFLAGS = «-lelf»
. / configure —enable-cassert —enable-debug —enable-dtrace
—prefix = / home / afiskon / postgresql-install &&
echo ‘#undef HAVE_SETPROCTITLE’ >> . / src / include / pg_config.h &&
gmake clean && gmake -j2 -s
Для быстрой локальной установки и конфигурации PostgreSQL можно воспользоваться скриптами из этого репозитория на GitHub .
При компиляции сыпется довольно много варнингов. Насколько я смог выяснить, так и должно быть, этому багу уже лет десять . Еще я обнаружил, что Clang версий 3.7 и 3.8 почему-то не могут скомпилировать проект с такими флагами, выдавая ошибку undefined reference to `bort'
. Clang версии 3.4, идущий в системе по умолчанию, собирает все без проблем. Разработчики Clang говорят, что проблема не на их стороне .
Список пробов, доступных в PostgreSQL, можно подсмотреть в исходниках:
less . / src / backend / utils / probes.h
В общем случае список пробов для конкретного процесса можно посмотреть так:
… или, если нам известны названия пробов:
sudo dtrace -l -P postgresql79470
Пример трассировки локов в PostgreSQL:
‘, mode = %d», copyinstr(arg0), arg1, arg2) } :postgres:LWLockRelease’
‘: { printf(«name = %s», copyinstr(arg0)) }’ -p 79670
В общем и целом, принцип тот же, что и в случае с ядром.
Профилирование с использованием DTrace
Как выяснилось, DTrace также легко справляется с профилированием кода:
-n ‘profile-4999 /execname == «postgres»/ { @[ustack(1)] = count() }’
sudo dtrace
-n ‘profile-4999 /pid == 1234/ { @[ustack()] = count() }’
-o out.dtrace
Идея заключается в том, чтобы снимать стектрейсы приложения с заданной частотой, в данном случае 4999 Гц, а затем смотреть, какие стектрейсы были сняты чаще других. Все гениальное — просто.
Если генерируемый вывод кажется вам не очень читаемым, скажите:
perl . / FlameGraph / stackcollapse.pl out.dtrace > out_folded.dtrace
perl . / FlameGraph / flamegraph.pl . / out_folded.dtrace > fg.svg
В результате по собранным стектрейсам будут построены красивые флеймграфы, ничем не уступающие тем, что нам доводилось строить при помощи perf в заметке Профилирование кода на C/C++ в Linux и FreeBSD .
Использование DTraceToolkit
С моей стороны было бы большим упущением не рассказать про DTraceToolkit:
Это такая коллекция из нескольких сотен полезных скриптов на базе DTrace:
Рассмотрим некоторые из них.
Скрипт hotkernel позволяет определить, в каких процедурах ядро проводит больше всего времени:
FUNCTION COUNT PCNT
zfs.ko`arc_read_done 1 0.0%
kernel`hpet_get_timecount 1 0.0%
kernel`in_lltable_lookup 1 0.0%
kernel`ata_end_transaction 1 0.0%
zfs.ko`zio_buf_free 1 0.0%
kernel`DELAY 1 0.0%
kernel`ata_pci_dmastart 1 0.0%
zfs.ko`txg_all_lists_empty 1 0.0%
kernel`ata_generic_command 2 0.1%
kernel`ata_tf_write 2 0.1%
kernel`spinlock_exit 2 0.1%
dtrace.ko`dtrace_dynvar_clean 3 0.1%
kernel`ata_pci_dmastop 4 0.1%
kernel`acpi_cpu_c1 2984 99.3%
Скрипт opensnoop позволяет следить, кто какие файлы открывает:
UID PID COMM FD PATH
1001 9147 id 3 /etc/nsswitch.conf
1001 9147 id 3 /etc/pwd.db
1001 9147 id 3 /etc/group
1001 9147 id 3 /etc/group
1001 9147 id 3 /etc/group
Скрипт procsystime позволяет определить, сколько времени процессы проводят в каких системных вызовах:
Elapsed Times for all processes,
SYSCALL TIME (ns)
sigaction 2827
mmap 3400
sigreturn 4137
getpid 5032
__sysctl 9839
madvise 16798
ioctl 24210
sigprocmask 56221
read 115654
write 658697
select 998609958
_umtx_op 998813579
Скрипт shellsnoop следит за тем, кто какие команды выполняет в шелле (по ширине вывод обрезан мной):
PID PPID CMD DIR TEXT
9175 799 id W uid=1001(afiskon) gid=1001(afiskon) groups…
9176 799 pwd W /home/afiskon
9177 799 uname W FreeBSD 10.3-RELEASE-p4 FreeBSD 10.3-RELE…
Детально рассмотреть все скрипты со всеми доступными флагами, увы, не представляется возможным. На эту тему можно вести целый отдельный блог!
Установка DTrace в Ubuntu Linux
Существует проект по портированию DTrace на ядро Linux под названием dtrace4linux.
Устанавливается dtrace4linux таким образом (я тестил на Ubuntu 16.04):
sudo . / tools / get-deps.pl
make all
sudo make install
sudo make load
Проверяем:
sudo dtrace -n ‘syscall:::entry { @[probefunc] = count(); }’
Вроде, работает. Впрочем, если судить по ишьюсам на GitHub , использовать dtrace4linux следует с некоторой осторожностью.
Заключение
Надеюсь, мне удалось убедить вас в том, что DTrace — крутейшая штука, заслуживающая самого пристального внимания с вашей стороны. В качестве источников дополнительной информации можно рекомендовать:
- Официальный блог и мейлинг лист проекта;
- Список рассылки freebsd-dtrace@freebsd.org ;
- В хэндбуке FreeBSD есть глава, посвященная DTrace , плюс туториал и однострочники можно найти на wiki.freebsd.org ;
- Из книг можно посоветовать Systems Performance: Enterprise and the Cloud и DTrace Dynamic Tracing in Oracle Solaris, Mac OS X and FreeBSD ;
- Исходники DTrace из Oracle Linux , имеющего мало общего с проектом dtrace4linux ;
А пользуетесь ли вы DTrace и если да, то какие его возможности находите наиболее ценными?
Дополнение: См также заметки Трассировка и профайлинг в Linux с помощью bcc/eBPF и Основы трассировки с помощью bpftrace .