Flame Graphs
Для чего полезны flame graphs?
Flame graphs — это способ визуализации времени ЦП, затраченного на выполнение функций. Они могут помочь определить, где вы тратите слишком много времени на синхронные операции.
Как создать flame graph
Вы могли слышать, что создание flame graph для Node.js — сложная задача, но это уже не так (больше). Solaris vms больше не нужны для flame graphs!
Flame graphs генерируются из вывода perf
, который не является инструментом, специфичным для Node.js. Хотя это самый мощный способ визуализации времени ЦП, могут возникнуть проблемы с тем, как JavaScript-код оптимизирован в Node.js 8 и выше. См. раздел проблемы с выводом perf ниже.
Использование предварительно упакованного инструмента
Если вам нужен один шаг, который создает flame graph локально, попробуйте 0x
Для диагностики рабочих развертываний ознакомьтесь со следующими заметками: 0x production servers.
Создание flame graph с помощью системных инструментов perf
Цель этого руководства — показать шаги, необходимые для создания 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` -- sleep 3
Зачем нужен sleep 3
? Он нужен, чтобы perf
продолжал работать — несмотря на то, что опция -p
указывает на другой PID, команда должна быть выполнена в процессе и завершиться вместе с ним. perf
работает в течение времени жизни команды, которую вы ему передаёте, независимо от того, профилируете ли вы фактически эту команду. sleep 3
гарантирует, что perf
будет работать в течение 3 секунд.
Почему -F
(частота профилирования) установлена на 99? Это разумное значение по умолчанию. Вы можете его изменить, если хотите. -F99
говорит perf
брать 99 выборок в секунду; для большей точности увеличьте значение. Меньшие значения должны давать меньший объём вывода с меньшей точностью результатов. Необходимая точность зависит от того, как долго действительно работают ваши ресурсоёмкие функции ЦП. Если вы ищете причину заметного замедления, 99 кадров в секунду должно быть более чем достаточно.
После получения записи perf
за 3 секунды перейдите к созданию 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!