Введение
Часто бывают ситуации когда надо понять насколько оптимально работает приложение или какие функции или системные вызовы оно использует и как это соотносится с ресурсами. Эта задача называется профилированием. Для профилирования в Linux есть несколько вариантов, самый распространенный из которых - утилита perf, но для пользователей MacOS она недоступна, так как напрямую связан с ядром Linux. К счастью, для их операционной системы есть другие способы профилирования, о которых я расскажу ниже.
Обзор способов профилирования на MacOS
Для профилирования в MacOS фреймворк DTrace. Но использоваться он может 2-мя способами:
- С помощью набора утилит для профилирования Instruments.app
- Консольная утилита
dtrace
.
Схематично верхнеуровневая архитектура выглядит так:

Профилирование при помощи Instruments
Чтобы профилирвать приложение таким способом необходимо запусть Instruments и выбрать Time profiler:

Задать настрокйи профилирования (это тема для отдельной статьи и тут я их описывать не буду):

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

Однако, такое представлени не очень информативно, но улучшить ситуацию можно использованием FlameGraph, который наглядно отображает информацию о стеке вызовов и времени их выполения в процентах.
Чтобы отобразить информацю с помощью FlameGraph нужно сделать 2 дополнительных действия:
Сначала нужно выгрзуть стек вызовов из Instrument (выбрать процесс и сделать Edit->Deep Copy
):

Потом вставить это в любой текстовый документ (в моем случае emacs.stack
):
Weight Self Weight Symbol Names
13.00 ms 100,0 % 0 s Emacs-x86_64-10_14 (2465)
11.00 ms 84,6 % 0 s Main Thread 0x5c9b
11.00 ms 0,0 % 0 s start
11.00 ms 0,0 % 0 s main
11.00 ms 0,0 % 0 s Frecursive_edit
11.00 ms 0,0 % 0 s recursive_edit_1
11.00 ms 0,0 % 0 s command_loop
11.00 ms 0,0 % 0 s internal_catch
...
Дальше загружаем FlameGraph:
git clone https://github.com/brendangregg/FlameGraph.git
Далее нужно свернуть стек вызовов в линии:
./flamegraph-instruments.pl emacs.stack > emacs.stack_folded
Важно: чтобы команда отработала без ошибок необходимо применить патч #349
После “свертки” можно построить итоговый граф:
./flamegraph.pl emacs.stack_folded > emacs.svg
В итоге получаем следующую картинку:
Профилирование при помощи dtrace
Важно: для полноценной работы dtrace
нужно добавить его в исключения [SIP(https://support.apple.com/ru-ru/102149). Для этого нужно сделать следующее
- При загрузке зажать
cmd+r
чтобы попасть в меню восстановления системы - Открыть терминал
- Выполнить команду
csrutil enable --without dtrace
- Перезагрузиться
Этот способ гораздо сложнее предущего, так как тут надо изучать дополнительный язык сценариев.
Для получения стека вызовов для профилирования можно использовать “однострочник”:
dtrace -x ustackframes=100 -n 'profile-97 /pid == 12345 && arg1/ { @[ustack()] = count(); } tick-60s { exit(0); }' -o out.user_stacks
В этой команде устанавливается размер стека в 100 кадров (ustackframes=100). Функция ustack()
профилирует функции в пространтве пользователя. Аргумет pid задает id процесса, который профилируется. tick-60s значит что через 60 сек будет выполнен выход и скрипта профилирования.
Можно написать отдельный скрипт (profiling.d
):
profile-997
/pid == "json2mpack"/
{
@[ustack(100)] = count();
}
tick-5s
{
exit(0);
}
Как видно тут у функции есть первый параметр, он также отвечает за рамер стека и может передаваться в качестве агрумента.
Запустить его через dtrace
можно так:
dtrace -s profiling.d -o out.user_stacks
В целом по отладке с помощью скриптов dtrace есть целые книги, с которыми нужно будет хотя бы мельком ознакомиться в отличии от предыдцщего способа. Но такая отладка имеет гораздо больше применения нежели способ с Instruments.
На выходе получится файл вида (out.user_stacks):
json2mpack`DYLD-STUB$$fcntl
json2mpack`main+0x2c
dyld`start+0x796
1
libsystem_kernel.dylib`__exit+0xa
1
Теперь можно построить flamegraph:
./stackcollapse.pl out.user_stacks | ./flamegraph.pl > app.svg
В итоге получаем SVG:
Заключение
В статье я постарался описать базовые принципы профилирования приложений в MacOS, есть и другие способы, но они являются производными от тех, которые описаны выше. По теме профилирования приложений рекомендую блог Брендана Грега.
Так же есть еще утилита xctrace
, котрая делает тоже самое что и Instuments, но из консоли. Например записать профилирование процесса можно через команду:
xctrace record --template 'Time Profiler' --attach <pid процесса>