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'а и держать вас в курсе каждого шага.

Если вы хотите лучше понять каждый шаг, взгляните на следующие разделы, которые мы рассмотрим более подробно.

Теперь давайте приступим к работе.

  1. установите perf (обычно доступен через пакет linux-tools-common, если он еще не установлен)

  2. попробуйте запустить perf - он может пожаловаться на отсутствующие модули ядра, установить их тоже

  3. запустите команду node с включенным perf (см. проблемы вывода perf для подсказок, специфичных для разных версий Node.js)

    perf record -e cycles:u -g -- node --perf-basic-prof app.js
    
  4. игнорируйте предупреждения до тех пор, пока они не скажут вам, что perf не может быть запущен из-за отсутствия пакетов; вы также можете получить некоторые предупреждения о невозможности доступа к примерам модулей ядра, которые вам не нужны в любом случае

  5. запустите perf script > perfs.out, чтобы сгенерировать файл с данными, который вы вот-вот визуализируете. Также полезно произвести некоторую чистку для того, чтобы получить более читабельный график

  6. установите stackvis если еще не установлен npm i -g stackvis

  7. запустите 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 встроены некоторые меры пресечения этого.

Подробнее см.:

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'ы самостоятельно с этим заданием!

Вверх