Профайлинг кода на C/C++ во FreeBSD с помощью pmcstat

31 августа 2016

Ранее в заметке Профилирование кода на C/C++ в Linux и FreeBSD вскользь упоминалось, что аналогом perf из мира Linux во FreeBSD является утилита pmcstat. Однако не сообщалось, как именно этим pmcstat пользоваться, просто потому что на тот момент я этого и не умел. Не так давно, благодаря помощи со стороны Федора Сигаева, мне все-таки удалось осилить pmcstat. А теперь, благодаря помощи с моей стороны, осилить удастся и вам!

Примечания: (1) Также вас могут заинтересовать статьи Использование DTrace на примере FreeBSD и Linux, Установка и простые примеры использования SystemTap и Трассировка и профайлинг в Linux с помощью bcc/eBPF. (2) Насколько мне известно, в MacOS утилиты pmcstat нет.

По традиции, сначала немного матчасти. Performance Monitoring Counters, или PMC, называют набор регистров специального назначения, встроенный в современные CPU для хранения счетчиков связанных с железом событий, происходящих в системе. В качестве примера такого события можно привести промах мимо кешей процессора при обращении к оперативной памяти и, как следствие, вынужденное более дорогое обращение к оперативной памяти напрямую.

PMC устроены по-разному на разных CPU. Роль уровня абстракции над этими различиями во FreeBSD играет модуль ядра hwpmc.ko. Пользовательские приложения, такие, как pmccontrol и pmcstat, общаются с hwpmc.ko при помощи библиотеки libpmc. (В случае с Linux и perf, подозреваю, принцип аналогичный.)

От теории плавно переходим к практике.

Первым делом загружаем уже упомянутый hwpmc.ko:

sudo kldload hwpmc

Смотрим список доступных хардверных счетчиков:

sudo pmccontrol -l

Активируем все счечтики:

sudo pmccontrol -e*

Теперь мы можем посмотреть самый «горячие» процедуры конкретного процесса (аналог perf top) таким образом:

pmcstat -Pinstructions -T -t 19843

Пример вывода:

Топ самых горячих процедур в pmcstat

Не особо уступает perf, не так ли? Хотя постойте-ка, ведь perf позволяет нам «проваливаться» в процедуры и смотреть, какие именно строчки кода тормозят. Можно ли сделать то же самое при помощи pmcstat? Оказывается, что можно:

pmcstat -Pinstructions -t 20171 -O out.dat
pmcannotate out.dat /path/to/bin/postgres > annotate.txt
less -x8 annotate.txt

Пример вывода (по ширине обрезано мной):

CONVERSION STATISTICS:
 #samples/total                           875683
Profile trace for function: strcmp() [10.18%]

Profile trace for function: _bt_compare() [7.59%]
0.90%| _bt_compare(Relation rel,
     |             int keysz,
     |             ScanKey scankey,
     |             Page page,
     |             OffsetNumber offnum)
     | {
0.33%|   TupleDesc    itupdesc = RelationGetDescr(rel);
1.98%|   BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer...
4.70%|
     |   /*
     |    * Force result ">" if target item is first data item on a...
     |    * --- see NOTE above.
     |    */
     |   if (!P_ISLEAF(opaque) && offnum == P_FIRSTDATAKEY(opaque))
0.30%|     return 1;
8.19%|
     |   itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, ...
0.81%|    * We don't test for violation of this condition here, how...
     |    * initial setup for the index scan had better have gotten...
     |    * _bt_first).
     |    */
     |
     |   for (i = 1; i <= keysz; i++)
9.51%|   {
     |     Datum           datum;
     |     bool            isNull;
     |     int32           result;
...

И снова ничем не уступает perf! Но постойте-ка, а как на счет построения флеймграфов? Никаких проблем, при помощи pmcstat их тоже можно строить:

pmcstat -Pinstructions -t 22456 -O pmc.out
pmcstat -R pmc.out -z16 -G pmc.graph
git clone https://github.com/brendangregg/FlameGraph
perl ./FlameGraph/stackcollapse-pmc.pl pmc.graph > pmc.stack
perl ./FlameGraph/flamegraph.pl ./pmc.stack > pmc.svg

Ну и напоследок мне хотелось бы показать еще одну клевую штуку, которую умеет pmcstat — построение графа вызовов подобно тому, что мы ранее строили при помощи gprof:

sudo pkg install graphviz
sudo pip install gprof2dot
pmcstat -R pmc.out -g
gprof /path/to/bin/postgres INSTR_RETIRED_ANY/postgres.gmon > gprof.out
gprof2dot gprof.out | dot -Tsvg -o gprof.svg

Пример куска графа, полученного таким образом:

Граф вызовов, полученный с помощью pmcstat

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

Также pmcstat может считать уже упомянутые кэш-мисы, и, подозреваю, делать множество других умопомрачительных штук, про большинство из которых я пока даже не догадываюсь. Но, честно говоря, особой потребности в этих самых штуках у меня пока что нет, вот и поведать я о них не могу. Подробности заинтересовавшиеся читатели могут найти в man pmcstat, и далее по ссылкам.

А пользуетесь ли вы pmcstat, и если да, то как именно?

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.