Огненные графики (Flame Graphs)
Для чего полезен огненный график?
Огненные графики - это способ визуализации времени процессора, потраченного на функции. Они могут помочь вам определить, где вы тратите слишком много времени на синхронные операции.
Как создать огненный график
Возможно, вы слышали, что создать огненный график для Node.js сложно, но это не так (больше). Виртуальные машины Solaris больше не нужны для огненных графиков!
Огненные графики генерируются из вывода perf
, который не является специфичным для node инструментом. Хотя это самый мощный способ визуализации времени процессора, он может иметь проблемы с тем, как код JavaScript оптимизируется в Node.js 8 и выше. См. раздел проблемы вывода perf ниже.
Использовать готовый инструмент
Если вам нужен один шаг для создания огненного графика локально, попробуйте 0x
Для диагностики производственных развертываний прочитайте эти заметки: 0x production servers.
Создать огненный график с помощью системных инструментов perf
Цель этого руководства - показать шаги, необходимые для создания огненного графика, и сохранить контроль над каждым шагом.
Если вы хотите лучше понять каждый шаг, ознакомьтесь с последующими разделами, где мы углубимся в детали.
Теперь приступим к работе.
- Установите
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
Теперь откройте файл огненного графика в своем любимом браузере и смотрите, как он горит. Он имеет цветовую кодировку, поэтому вы можете сначала сосредоточиться на самых насыщенных оранжевых полосах. Скорее всего, они представляют собой функции, интенсивно использующие ЦП.
Стоит упомянуть - если вы нажмете на элемент огненного графика, над графиком будет отображено увеличенное изображение его окружения.
Использование perf
для профилирования работающего процесса
Это отлично подходит для записи данных flame graph из уже работающего процесса, который вы не хотите прерывать. Представьте себе производственный процесс с трудновоспроизводимой проблемой.
perf record -F99 -p `pgrep -n node` -- sleep 3
Для чего нужен sleep 3
? Он нужен для того, чтобы perf продолжал работать - несмотря на то, что опция -p
указывает на другой pid, команда должна быть выполнена в процессе и завершиться вместе с ним. perf работает в течение всего времени выполнения команды, которую вы ей передаете, независимо от того, профилируете вы эту команду или нет. sleep 3
гарантирует, что perf будет работать в течение 3 секунд.
Почему -F
(частота профилирования) установлена на 99? Это разумное значение по умолчанию. Вы можете настроить его, если хотите. -F99
указывает perf делать 99 выборок в секунду, для большей точности увеличьте значение. Более низкие значения должны давать меньше выходных данных с менее точными результатами. Необходимая вам точность зависит от того, как долго на самом деле работают ваши ресурсоемкие функции. Если вы ищете причину заметного замедления, 99 кадров в секунду должно быть более чем достаточно.
После того, как вы получите 3-секундную запись perf, переходите к созданию flame graph с помощью двух последних шагов, описанных выше.
Фильтрация внутренних функций Node.js
Обычно вы просто хотите посмотреть на производительность ваших вызовов, поэтому фильтрация внутренних функций Node.js и V8 может сделать граф намного проще для чтения. Вы можете очистить свой perf-файл с помощью:
sed -i -r \
-e '/(_libc_start|LazyCompile) |v8::internal::BuiltIn|Stub|LoadIC:\\[\\[' \
-e '/^$/d' \
perf.data > perf.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
Изменения конвейера V8 в Node.js 8.x
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`_ZN2v88internal11interpreter17BytecodeGenerator15VisitStatementsEPMS0_8Zone
это означает, что Linux perf, который вы используете, был скомпилирован без поддержки demangle, см., например, https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1396654
Примеры
Попрактикуйтесь в захвате flame graph самостоятельно с помощью упражнения по созданию flame graph!