Flame Graphs
Для чего нужен flame graph?
Flame graphs - это способ визуализации процессорного времени потраченного на функции. Они могут помочь вам определить, какие синхронные операции выполняются дольше всего.
Как создать flame graph
Возможно, вы слышали о том, что в Node.js трудно создать flame graph, но это больше не так. Виртуальные машины Solaris больше не нужны для flame graph'ов!
Flame graph'ы генерируются из выходных данных perf
, который не является специфичным Node.js инструментом. Хотя это наиболее эффективный способ визуализации затраченного процессорного времени, у него могут быть проблемы с оптимизацией кода JavaScript в Node.js 8 и выше. См. проблемы вывода perf
section below.
Используйте предварительно упакованный инструмент
Если вы хотите в один ход получить flame graph, попробуйте 0x.
Для диагностики production deployments, читайте эти записи: 0x production servers
Создание flame graph'а с помощью системных инструментов
Цель это руководства - показать шаги, связанные с созданием flame graph'а и держать вас в курсе каждого шага.
Если вы хотите лучше понять каждый шаг, взгляните на следующие разделы, которые мы рассмотрим более подробно.
Теперь давайте приступим к работе.
-
установите
perf
(обычно доступен через пакетlinux-tools-common
, если он еще не установлен) -
попробуйте запустить
perf
- он может пожаловаться на отсутствующие модули ядра, установить их тоже -
запустите команду
node
с включеннымperf
(см. проблемы выводаperf
для подсказок, специфичных для разных версий Node.js)perf record -e cycles:u -g -- node --perf-basic-prof app.js
-
игнорируйте предупреждения до тех пор, пока они не скажут вам, что
perf
не может быть запущен из-за отсутствия пакетов; вы также можете получить некоторые предупреждения о невозможности доступа к примерам модулей ядра, которые вам не нужны в любом случае -
запустите
perf script > perfs.out
, чтобы сгенерировать файл с данными, который вы вот-вот визуализируете. Также полезно произвести некоторую чистку для того, чтобы получить более читабельный график -
установите
stackvis
если еще не установленnpm i -g stackvis
-
запустите
stackvis perf < perfs.out > flamegraph.htm
Теперь откройте файл flame graph'а в вашем любимом браузером и наблюдайте, как он горит. Он имеет цветовую кодировку, поэтому вы можете сосредоточиться на самых насыщенных оранжевых столбцах в первую очередь. Скорее всего, они отображают самые ресурсоемкие функции процессора.
Стоит упомянуть - если вы кликните по элементу flame graph'а, то над графиком отобразится увеличение его окружения.
perf
для замерки запущенного процесса
Использование Это отлично подходит для записи данных flame graph'а из уже запущенного процесса, который вы не хотите прерывать. Представьте себе продакшн процесс с трудновоспроизводимой проблемой.
perf record -F99 -p `pgrep -n node` -g -- sleep 3
Подождите, для чего нужна команда sleep 3
? Она нужна для того, чтобы поддерживать работу perf
. Несмотря на то, что параметр -p
указывает на другой pid
, команда должна быть выполнена в процессе и завершить его.
perf
выполняется столько, сколько выполняется команда, которую вы ему передаете, независимо от того, профилируете ли вы ее или нет. sleep 3
гарантирует, что perf
будет выполняться 3 секунды.
Почему параметр -F
(частота профилирования) установлен в 99? Это по умолчанию. Вы можете настроить его как хотите.
-F99
говорит perf
делать 99 отчетов в секунду, увеличивайте значение для большой точности. Более низкие значения должны давать меньше результатов с менее точными результатами. Точность, которая вам нужна, зависит от того, как долго выполняются ваши ресурсоемкие функции. Если вы ищете причину заметного замедления, 99 кадров в секунду должно быть более чем достаточно.
После того, как вы получите эту 3-секундную запись, приступайте к генерации flame graph'а, следуя двум последним пунктам сверху.
Фильтрация внутренних Node.js функций
Обычно вы просто хотите увидеть производительность ваших собственных функций, поэтому фильтрация внутренних функций Node.js и V8 может значительно облегчить чтение графика. Вы можете почистить свой perf
файл с помощью команды:
sed -i -r \
-e "/( __libc_start| LazyCompile | v8::internal::| Builtin:| Stub:| LoadIC:|\[unknown\]| LoadPolymorphicIC:)/d" \
-e 's/ LazyCompile:[*~]?/ /' \
perfs.out
Если вы читаете свой flame graph, и он кажется странным, как будто чего-то не хватает в ключевой функции, занимающей большую часть времени, попробуйте сгенерировать свой flame graph без фильтров - возможно, у вас возник редкий случай проблемы с самим Node.js.
Опции профилирования Node.js
--perf-basic-prof-only-functions
и --perf-basic-prof
это те две опции, которые полезны для отладки вашего JavaScript кода. Другие параметры используются для профилирования самого Node.js, что выходит за рамки данного руководства.
Опция --perf-basic-prof-only-functions
позволяет генерировать меньше вывода, поэтому это вариант с наименьшими издержками.
Зачем они мне нужны?
Ну, без этих опций вы в любом случае получите flame graph, но большинство столбцов будет с надписью v8::Function::Call
.
perf
Проблемы вывода Изменения в пайплайне Node.js 8.x V8
Node.js 8.x и выше поставляется с новыми оптимизациями для пайплайна компиляции JavaScript в движке V8, который иногда делает имена/ссылки на функции недоступными для perf
. (Это называется Turbofan)
В результате названия ваших функций может выглядеть неправильно на flame graph'е.
Вы увидите что-то вроде ByteCodeHandler:
там, где должны быть названия функций.
В 0x встроены некоторые меры пресечения этого.
Подробнее см.:
- https://github.com/nodejs/benchmarking/issues/168
- https://github.com/nodejs/diagnostics/issues/148#issuecomment-369348961
Node.js 10+
Node.js 10.x решает проблему с Turbofan с помощью флага --interpreted-frames-native-stack
.
Запустите node --interpreted-frames-native-stack --perf-basic-prof-only-functions
, чтобы получить имена функций в flame graph'е независимо от того, какой пайплайн V8 использовался для компиляции вашего JavaScript кода.
Неправильные подписи на flame graph'е
Если вы видите подписи, похожие на эту:
node`_ZN2v88internal11interpreter17BytecodeGenerator15VisitStatementsEPNS0_8ZoneListIPNS0_9StatementEEE
это означает, что используемая вами версия perf
не была скомпилирована с поддержкой demangle, см. https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1396654 для примера
Примеры
Потренируйтесь создавать flame graph'ы самостоятельно с этим заданием!