Флеймграфы: визуализация профиля производительности

Рейтинг: 68.5% · 14 голосов
Подробный курс по диагностике и производительности Linux для админов и DevOps: с чего начать, методология USE, логи (journalctl, dmesg), процессы, CPU, память и OOM, диск и IO (iostat), сеть (ss, tcpdump), системные вызовы и strace, ltrace, perf, флеймграфы, ftrace, eBPF и bpftrace, lsof, мониторинг. Разбор каждого инструмента с чтением вывода.
Ответить
Аватара пользователя
Dmitry_SRE
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Флеймграфы: визуализация профиля производительности

Сообщение Dmitry_SRE »

Оглавление курса (47)
  1. С чего начать диагностику Linux: методология вместо паники
  2. Первые 60 секунд: экспресс-диагностика нагруженного сервера
  3. Методологии диагностики: USE, RED и здравый смысл
  4. Логи systemd через journalctl: где искать причину
  5. Где лежат логи Linux: /var/log, dmesg и rsyslog
  6. Источники правды: load average, /proc и /sys
  7. Процессы Linux: ps, pstree и состояния процессов
  8. Интерактивный мониторинг: top и htop
  9. Метрики процессов во времени: pidstat
  10. Приоритеты и ограничения: nice, ionice, cgroups
  11. Сигналы и зависшие процессы: kill, и что делать с D-state
  12. Загрузка CPU: user, system, iowait, steal и контекст-свитчи
  13. Диагностика CPU по ядрам: vmstat и mpstat
  14. Профилирование CPU: perf top и поиск пожирателя
  15. Частоты, троттлинг и NUMA: turbostat и numastat
  16. Память Linux: RSS, VSZ, page cache и миф о нехватке памяти
  17. Сколько памяти занято: free, /proc/meminfo и vmstat
  18. Поиск утечек и пожирателей памяти: smem, pmap, smaps
  19. Swap и подкачка: swapon, swappiness, когда своп - это боль
  20. OOM killer: кто и за что убил процесс
  21. Память ядра и slab: slabtop и куда уходит RAM
  22. Подсистема ввода-вывода: путь запроса от приложения до диска
  23. Диагностика диска: iostat и чтение await, %util
  24. Кто грузит диск: iotop и атрибуция io процессам
  25. Файловые системы: df, du, иноды и куда делось место
  26. Глубокая диагностика блочного слоя: blktrace и biolatency
  27. Кеш страниц и грязные данные: dirty pages, fsync, drop_caches
  28. Сетевой стек Linux: путь пакета и где возникают задержки
  29. Сокеты и соединения: ss и netstat
  30. Захват трафика: tcpdump для диагностики сети
  31. Задержки и потери в сети: ping, mtr, диагностика латентности
  32. Сеть как источник проблем: nftables, conntrack, дропы
  33. Что такое системный вызов и зачем его трассировать
  34. strace: трассировка системных вызовов на практике
  35. strace в бою: почему программа висит, падает или тормозит
  36. ltrace: трассировка вызовов библиотек
  37. Накладные расходы трассировки и безопасные альтернативы
  38. perf: универсальный профайлер Linux
  39. Флеймграфы: визуализация профиля производительности (вы здесь)
  40. Трассировка ядра: ftrace и trace-cmd
  41. eBPF: революция в наблюдаемости Linux
  42. Инструменты eBPF на практике: BCC и bpftrace
  43. lsof: открытые файлы, дескрипторы, кто держит файл и порт
  44. Разбор кейса: приложение тормозит - пошаговая диагностика
  45. Непрерывный мониторинг: sar, node_exporter, Grafana, Zabbix
  46. Диагностика в контейнерах: cgroups, лимиты, Docker и Kubernetes
  47. Карта инструментов и сквозной разбор инцидента производительности
Представь: приложение тормозит, ты снимаешь профиль через perf, открываешь perf report - и видишь простыню из тысяч строк с процентами. Глаза разбегаются, непонятно, с чего начать. Знакомо? Вот ровно эту боль и решает flame graph. Один взгляд на картинку - и сразу видно, какая функция съедает CPU. Не надо листать текст, искать самые жирные проценты и складывать в голове дерево вызовов. Все уже нарисовано.

В этом уроке разберем, что такое флеймграф, как его построить из обычного perf, как читать каждый пиксель, где новички спотыкаются и что изменилось к 2026 году. Покажу классический путь через perf и набор скриптов Брендана Грегга, современный путь через eBPF, а в конце - куда все это выросло в проде (continuous profiling). Это must-have инструмент, когда надо найти узкое место в большом приложении и понять профиль linux, где десятки тысяч строк кода и непонятно, кто виноват.

Что такое flame graph linux и зачем он нужен

Flame graph (флеймграф, "пламенный график") придумал Брендан Грегг - инженер, имя которого ты будешь встречать в каждой статье про производительность Linux. Это способ нарисовать профиль так, чтобы горячий код было видно мгновенно.

Идея простая. Профайлер много раз в секунду делает снимок (сэмпл): где сейчас выполняется программа и какой у нее стек вызовов - кто кого позвал. Например: main -> handle_request -> parse_json -> memcpy. Таких снимков набираются тысячи. Флеймграф их группирует и рисует прямоугольники (их называют "фреймы"):
  • Ось X (ширина) - это НЕ время по порядку слева направо, а доля сэмплов. Чем шире прямоугольник функции, тем чаще она встречалась в стеках, то есть тем больше CPU она ела. Соседние фреймы на одном уровне отсортированы по алфавиту, а не по времени - это важно понимать, иначе будешь искать смысл там, где его нет.
  • Ось Y (высота) - глубина стека. Внизу - main или точка входа, выше - кто кого вызвал. Прямоугольник стоит на том, кто его вызвал. Высота сама по себе ничего не говорит про скорость, это просто глубина вложенности.
  • Цвет - по умолчанию случайный "теплый" (отсюда и пламя), смысловой нагрузки не несет. Но цвет можно включить осмысленный: --color=java/js/perl подсветит user/kernel/JIT-фреймы разными оттенками, --color=io пригодится для off-CPU.
Главное правило чтения: ищи широкие плато на самом верху. Широкий прямоугольник наверху - это лист дерева, функция, которая реально жгла CPU прямо сейчас, а не просто ждала вложенные вызовы. Узкие пички - это мелочь, на них не отвлекайся. Если у фрейма наверху есть широкий "потомок" под ним - значит время уходит глубже по стеку, спускайся туда взглядом.

Изображение

Как читать флеймграф: разбор по осям и взгляду

Давай разберем чтение на пальцах, потому что это половина успеха - построить картинку легко, а вот не запутаться в ней сложнее. Возьми любой флеймграф и читай его так.

Верхняя кромка - это и есть процессор прямо сейчас. Самый верхний фрейм над каждой точкой по оси X - это та функция, которая в этот момент реально исполнялась на CPU. Все, что под ней, - это ее предки, цепочка "кто кого позвал", уходящая вниз до точки входа. Поэтому правило "смотри наверх" буквально означает: смотри на ту самую кромку, по ней размазано все процессорное время. Если ты мысленно проведешь горизонтальную линию по верхушкам всех плато и сложишь их ширины - получишь 100% профиля.

Ширина - это доля, а не абсолют. Фрейм шириной в половину графика означает: эта функция (вместе со всеми, кого она вызвала ниже по стеку, то есть со всеми своими потомками выше нее на картинке) присутствовала примерно в половине сэмплов. Ключевое слово - "вместе с потомками": широкий фрейм внизу не значит, что он сам жжет CPU, он лишь суммирует все, что происходит над ним. Чтобы понять, где именно горит, спускайся взглядом по широкому столбу вверх, пока ширина не "рассыплется" на узкие пички или пока не упрешься в широкое плато на верхушке - вот оно, место, где CPU тратится непосредственно.

Высота не про скорость. Высокая узкая башня - это просто глубокая цепочка вызовов или рекурсия. Она может занимать доли процента времени и не быть проблемой вообще. Не дай высоте себя обмануть: единственное, что коррелирует с расходом CPU, - это ширина, и только у верхних фреймов.

Алфавитная раскладка по X - это фича, а не баг. Соседние фреймы на одном уровне отсортированы по имени функции слева направо. Зачем? Чтобы одинаковые поддеревья всегда оказывались рядом и сливались в одно широкое плато, а не были раскиданы по графику. Побочный эффект: положение фрейма слева или справа не значит "раньше" или "позже" по времени. Если тебе нужен именно порядок во времени (что за чем исполнялось), это другой инструмент - flame chart (с осью X = время) или perf timechart, не путай их.

Алгоритм осмотра за 30 секунд. Открыл картинку - не вчитывайся в имена сразу. Сначала глазами найди 2-3 самых широких столба от низа до верха. Поднимись по каждому до верхней кромки, прочитай имя функции на верхушке широкого плато - это твои кандидаты в горячие точки, обычно их 1-3 штуки и они дают 60-90% времени. Все остальное (лес узких пичков) игнорируй на первом проходе: даже если ускорить узкий фрейм в два раза, общий выигрыш будет в пределах его доли, то есть копейки. Это и есть смысл флеймграфа - он за секунды показывает, куда стоит вкладывать силы, а куда нет.

Поиск как лупа. Не доверяй только глазам: используй Ctrl+F (поиск в SVG) по имени функции или библиотеки. Он подсветит фиолетовым все вхождения, даже размазанные по разным веткам, и покажет суммарный процент справа вверху (надпись Matched). Так ты увидишь, например, что malloc сам по себе нигде не широкий, но в сумме по всему графику ест 12% - такое "размазанное" потребление глаз пропускает, а поиск ловит.

Как построить perf flamegraph: три шага

Классический путь Brendan Gregg flamegraph - это связка perf и набора скриптов FlameGraph. Скрипты - это несколько perl-файлов, ставятся клонированием с гитхаба, компилировать ничего не надо.

Код: Выделить всё

git clone https://github.com/brendangregg/FlameGraph
cd FlameGraph
Шаг 1 - снять профиль. Записываем стеки с частотой 99 раз в секунду по всей системе в течение 30 секунд:

Код: Выделить всё

sudo perf record -F 99 -a -g -- sleep 30
Разберем флаги по буквам, потому что новичку тут легко запутаться:
  • -F 99 - частота сэмплирования, 99 Гц (99 снимков в секунду на каждый CPU). Почему не 100? Чтобы не попасть в резонанс с таймерами и периодическими задачами, которые часто тикают ровно на круглых числах (100, 1000 Гц), и не словить систематическое искажение - так называемый lockstep sampling. По той же причине берут нечетные 99, 199, 997 Гц, а не 100/200/1000.
  • -a - снимать со всех CPU (вся система). Для одного процесса вместо -a используй -p PID, для одной команды от старта - perf record ... -- ./myapp.
  • -g - вот это ключевое: записывать стеки вызовов (call graph). Без -g флеймграфа не будет, останутся только верхушки без предков. -g - это сокращение для --call-graph fp (разворот по фрейм-поинтеру).
  • -- sleep 30 - perf будет писать ровно пока живет команда sleep, то есть 30 секунд. Удобный трюк задать длительность, не нажимая Ctrl+C руками.
Шаг 2 и 3 - превратить бинарный perf.data в текст, "свернуть" одинаковые стеки и нарисовать SVG:

Код: Выделить всё

perf script | ./stackcollapse-perf.pl > out.folded
./flamegraph.pl out.folded > profile.svg
perf script разворачивает бинарную запись в построчный текст стеков. stackcollapse-perf.pl схлопывает одинаковые стеки в одну строку со счетчиком (формат "стек;через;точку;с;запятой ЧИСЛО"). flamegraph.pl из этого рисует интерактивный SVG. Открой profile.svg в браузере - по прямоугольникам можно кликать, чтобы зумиться, есть поиск по Ctrl+F (он подсветит совпадения фиолетовым и покажет суммарный процент), а кнопка Reset Zoom возвращает обзор.

Важная тонкость: запускай perf script на той же машине и желательно с теми же бинарями, где делал perf record, иначе perf не сможет разрезолвить адреса в имена функций и ты увидишь голые шестнадцатеричные адреса вместо названий.

Вот как выглядит промежуточный свернутый (folded) формат - то, что отдает stackcollapse:

Код: Выделить всё

main;handle_request;parse_json;memcpy 5412
main;handle_request;db_query;recv 1830
main;event_loop 540
Читается так: путь main -> handle_request -> parse_json -> memcpy встретился 5412 раз, ветка с db_query -> recv - 1830 раз. Видно сразу: parse_json/memcpy жрет втрое больше. На картинке это и будет самым широким плато. Кстати, folded - это просто текст, его удобно diff-ить между двумя замерами, чем мы воспользуемся ниже для дифф-флеймграфов. И еще: folded можно фильтровать обычным grep - например, grep db_query out.folded оставит только ветки через базу, а потом нарисуешь флеймграф только по ним.

Почему именно folded в середине. Этот текстовый формат - универсальный "мостик". Любой профайлер (perf, DTrace, SystemTap, async-profiler для Java, py-spy для Python, eBPF-инструменты) умеет отдавать стеки, а в репозитории FlameGraph под каждый есть свой свертыватель: stackcollapse-perf.pl для perf, stackcollapse.pl для DTrace, stackcollapse-stap.pl для SystemTap, stackcollapse-java-exceptions.pl и так далее. Все они приводят данные к одному виду "стек ЧИСЛО", а дальше flamegraph.pl уже не важно, откуда стеки пришли. Поэтому, освоив один формат, ты строишь флеймграфы для чего угодно.

Актуально на 2026. Современный perf (ядра 6.x) умеет отдавать folded-формат сам, без perl-скрипта: perf script -F +pid --no-inline для контроля полей, а связка для одной трубы выглядит так же. Более того, в perf встроили команду perf script flamegraph, которая сразу рисует интерактивный HTML-флеймграф на базе d3-flame-graph (нужен шаблон d3, perf предложит скачать его с CDN при первом запуске). Но stackcollapse-perf.pl по-прежнему стандарт - он чистит мусорные кадры и оффсеты лучше, а SVG-вывод flamegraph.pl легче встроить в отчет. Для Rust/C++ удобнее обертки: cargo flamegraph (crate flamegraph) или samply - последний не требует perl и открывает результат прямо в Firefox Profiler (это полноценный flame chart с таймлайном плюс обычный флеймграф). Учти нюанс: если бинарь слинкован новым линкером lld (он стал дефолтным в Rust 1.90) или mold, для корректных стеков нужен флаг --no-rosegment, иначе perf не размотает стек.

Fail быстро: фрейм-поинтеры против DWARF

Самая частая причина "сломанного" флеймграфа - не размотался стек, потому что в бинаре нет фрейм-поинтеров (компилятор выкинул регистр RBP оптимизацией -O2). Есть два пути разворота:
  • --call-graph fp (он же -g) - быстрый и почти бесплатный, но требует, чтобы код был собран с -fno-omit-frame-pointer. Актуально на 2026: Ubuntu 24.04 и Fedora пересобрали системные пакеты с фрейм-поинтерами по умолчанию именно ради профилирования, так что на свежих дистрибутивах fp чаще просто работает.
  • --call-graph dwarf - разматывает стек по DWARF/.eh_frame даже без фрейм-поинтеров. Работает с любым релизным бинарем, но дороже: perf копирует кусок стека на каждый сэмпл (по умолчанию 8 КБ, регулируется --call-graph dwarf,16384), perf.data распухает, и при глубокой рекурсии стек может обрезаться. Это плата за удобство.
  • --call-graph lbr - аппаратный разворот через Last Branch Record на Intel/AMD, очень дешевый, но глубина ограничена железом (десятки кадров).
Практическое правило: сначала пробуй -g (fp), не получилось - бери dwarf. Для своего кода правильнее пересобрать с фрейм-поинтерами.

Символы - отдельная боль. Даже если стек размотался, без таблицы символов вместо имен будут голые адреса 0x7f.... Что нужно:
  • Для системных библиотек - поставить пакеты с debug-символами (имена -dbg, -dbgsym или -debuginfo в зависимости от дистрибутива) либо настроить debuginfod - сервис, который подтягивает символы по сети на лету (переменная DEBUGINFOD_URLS, на современных дистрибутивах уже преднастроена).
  • Для своего бинаря - не запускать stripped-версию, держать символы при себе.
  • Для JIT-языков символы генерятся в рантайме. Java: запусти JVM с -XX:+PreserveFramePointer (иначе стек обрывается на JIT-кадрах) и подключи perf-map-agent, который кладет таблицу /tmp/perf-PID.map - perf ее читает автоматически. Node.js: флаг --perf-basic-prof делает то же самое. Альтернатива для Java - async-profiler, он сам строит флеймграф без perf и без правки фрейм-поинтеров.
On-CPU и off-CPU флеймграф: где жжем и где ждем

То, что мы построили выше, - это on-CPU флеймграф. Он показывает, где процессор реально считал. Но огромный класс тормозов - это не "считаем много", а "ждем". Ждем диск, сеть, блокировку (lock), ответ базы, планировщик. На on-CPU профиле этого ожидания не видно вообще - процесс в это время не на CPU, профайлер его не сэмплит. Симптом: приложение "висит", отвечает медленно, а top показывает простаивающий CPU. on-CPU граф тут будет почти пустой и бесполезный.

Тут на помощь приходит off-CPU флеймграф. Он показывает обратную картину: где поток заблокировался и сколько времени проспал вне CPU. Снять его обычным perf record неудобно (нужно ловить каждый sched-switch), поэтому используют eBPF. eBPF - это технология, которая позволяет безопасно цеплять мини-программы к событиям ядра почти без накладных расходов. Для off-CPU есть готовый инструмент из пакета bcc - offcputime (в Debian/Ubuntu ставится как bpfcc-tools, бинарь зовется offcputime-bpfcc).

Код: Выделить всё

sudo offcputime-bpfcc -df -p $(pgrep -n myapp) 30 > off.folded
./flamegraph.pl --color=io --countname=us --title="Off-CPU" off.folded > offcpu.svg
Флаг -f выдает сразу свернутый (folded) формат, -d вставляет разделитель между user- и kernel-стеками, число 30 - длительность в секундах. На выходе ширина прямоугольника = суммарное время ожидания в микросекундах, а не доля CPU. Самое широкое плато off-CPU графа - твой главный источник задержки: чаще всего там окажется чтение с диска, recv из сокета, ожидание мьютекса или page fault. offcputime ловит все типы блокировки: disk I/O, network I/O, locks, page faults и принудительные переключения контекста.

Важная разница в смысле ширины. У on-CPU графа ширина = доля сэмплов = доля процессорного времени. У off-CPU графа ширина = суммарное время сна в микросекундах. Это меняет интерпретацию: широкое плато на off-CPU может быть нормой (например, поток-воркер большую часть времени честно спит на epoll_wait в ожидании запросов - это здоровое ожидание, а не проблема). Поэтому off-CPU читают с пониманием контекста: ищи не просто широкое, а широкое ожидание на горячем пути запроса, которое бьет по latency. Блокировка внутри обработки запроса - проблема; сон idle-воркера - нет.

Полезный сосед - offwaketime-bpfcc: он показывает не только где заснули, но и кто разбудил поток (wakeup-стек), что помогает распутать цепочки "кто кого ждет" на блокировках. А если наложить on-CPU и off-CPU на один граф, получится hot/cold флеймграф - полная картина жизни потока: и где он жег CPU (hot), и где спал (cold), в сумме они дают все 100% wall-clock времени. Это самый честный, но и самый перегруженный вид: новичку проще держать два отдельных графа, а hot/cold доставать, когда надо доказать, что "потерянное" время именно в ожидании, а не в счете.

Про версии ядра. eBPF-разворот стеков появился в Linux 4.8, offcputime работает на 4.6+ (полноценно с eBPF - с 4.8). На современных дистрибутивах с systemd (Ubuntu 22.04/24.04, свежие Debian, RHEL/Fedora) все из коробки. На отечественных Astra Linux и RED OS ядра достаточно новые, bcc-инструменты ставятся из репозиториев. Для разовых замеров вместо bcc удобнее bpftrace - однострочники короче, а для on-CPU есть штатный bcc-инструмент profile (profile-bpfcc), который начиная с Linux 4.9 сэмплит и считает стеки прямо в ядре, не создавая огромный perf.data. Это и есть тот самый эффективный BPF-профайлер: меньше накладных расходов, сразу folded на выходе.

Зоопарк флеймграфов: какой когда брать

Брендан Грегг развел целое семейство визуализаций на одной идее. Держи в голове карту, чтобы выбирать инструмент под задачу, а не строить on-CPU граф на любую проблему.
  • On-CPU (CPU flame graph). Базовый. Ширина = доля процессорного времени. Берешь, когда CPU загружен под завязку и надо понять, на что он тратится. Источник - perf или profile-bpfcc.
  • Off-CPU. Ширина = время вне CPU (ожидание). Берешь, когда latency большая, а CPU простаивает. Источник - offcputime-bpfcc.
  • Hot/cold. On-CPU и off-CPU на одном графике, в сумме весь wall-clock. Берешь, когда надо показать полную картину "где время вообще".
  • Icicle graph (ледяной/сосулька). Тот же флеймграф, но перевернутый вверх ногами: корень (точка входа) сверху, а листья-горячие функции свисают вниз, как сосульки. Строится флагом --inverted у flamegraph.pl. Зачем переворачивать? Так удобнее, когда тебя интересует не "кто жжет", а "кто вызывает горячий код" - агрегация идет от листьев, и одинаковые горячие функции, вызванные из разных мест, собираются в широкие верхние (теперь нижние) плато. Многие continuous-profiling UI рисуют именно icicle по умолчанию, потому что так привычнее читать сверху вниз.
  • Differential (дифференциальный). Сравнение двух профилей цветом: красный - стало хуже, синий - стало лучше. Незаменим для поиска регрессий. Подробно ниже.
  • Memory flame graph. Ширина = не сэмплы, а байты выделенной/живой памяти, цвет обычно зеленый. Строится из трейсинга malloc/free, brk, mmap или page faults (например, через bcc или ловлю аллокаций). Берешь, когда расследуешь утечку или раздувшийся RSS: широкое плато покажет стек, по которому льется память.
  • I/O flame graph. Ширина = байты или количество операций ввода-вывода по стекам. Полезен, чтобы увидеть, какой код инициирует диск или сеть.
Общий принцип: меняется только метрика по ширине (сэмплы CPU, микросекунды ожидания, байты памяти, операции I/O) и иногда направление по Y, но техника чтения одна и та же - ищи широкое наверху.

Дифф-флеймграфы и continuous profiling (2026)

Два приема, которые отличают новичка от практика.

Дифференциальные флеймграфы. Сняли folded "до" и "после" релиза - и сравнили скрипт-ом из того же репозитория:

Код: Выделить всё

./stackcollapse-perf.pl out.stacks1 > before.folded
./stackcollapse-perf.pl out.stacks2 > after.folded
./difffolded.pl before.folded after.folded | ./flamegraph.pl > diff.svg
Красным подсветятся пути, где стало хуже (выросло), синим - где улучшилось. Насыщенность цвета = величина изменения. Незаменимо при поиске регрессии после деплоя: вместо того чтобы глазами сравнивать две картинки, ты сразу видишь, что именно покраснело.

Несколько тонкостей, на которых спотыкаются:
  • Ширина берется из второго (after) профиля. То есть форма графика - это состояние "после", а цвет накладывается как дельта. Поэтому если функция в "после" исчезла совсем, ей негде нарисоваться и ты не увидишь, что она пропала. Чтобы поймать и такие пропажи, есть зеркальный режим: difffolded с флагом --negate плюс flamegraph - он рисует по форме "до" и подсвечивает то, что ушло. На практике строят оба и смотрят вместе.
  • Нормализация -n. Если "до" и "после" снимали разное время или под разной нагрузкой, абсолютные счетчики не сравнить честно. Флаг -n у difffolded приводит первый профиль к масштабу второго, чтобы дельта отражала структуру, а не просто разную длительность замера.
  • Срезать адреса -x. Если между сборками поехали адреса и в стеках остались куски hex, флаг -x их вычистит, иначе одинаковые по смыслу кадры не совпадут и diff будет шумным.
Continuous profiling. Раньше флеймграф снимали руками во время инцидента, но к моменту запуска perf пик уже прошел. В 2026 норма - непрерывное профилирование в проде через eBPF с почти нулевым оверхедом: агент постоянно собирает стеки со всех процессов, а UI рисует флеймграф (обычно в виде icicle) за любой прошлый интервал. Лидеры стека:
  • Grafana Pyroscope. Версия 2.0 вышла в апреле 2026, переписана под масштаб: единый путь записи профилей и stateless-обработка запросов, что снизило стоимость хранения и ускорило выборки. Интегрируется с остальной экосистемой Grafana, понимает OTLP.
  • Parca от Polar Signals. Полностью open-source, сильная eBPF-сторона, чистая интеграция в стиле Prometheus (метки, scrape-модель).
  • OpenTelemetry eBPF profiler. Системный профайлер, который цепляет eBPF-пробы и снимает стеки со всех процессов хоста (порядка 97 Гц), складывая профили в формат OTLP. SIG по профилированию в OpenTelemetry стандартизирует профиль как четвертый сигнал телеметрии (рядом с трейсами, метриками и логами), так что профили потекут в общий OTel Collector наравне с остальным.
Их общая фишка: они умеют разматывать стек без фрейм-поинтеров, читая .eh_frame/DWARF прямо в рантайме, и работают для любого языка system-wide, не трогая код приложения. Идея флеймграфа осталась той же, но теперь это не разовая картинка, а постоянная карта производительности: можно выбрать диапазон "вчера с 14:00 до 14:05, когда был всплеск latency" и получить флеймграф ровно того окна, а еще наложить два окна друг на друга и получить тот самый дифф - регрессию видно ровно в момент деплоя, без ручного perf record.

Куда смотреть дальше. Если зацепило: страница brendangregg.com/flamegraphs.html - первоисточник со всеми вариантами; репозиторий brendangregg/FlameGraph - скрипты и примеры; для языков - async-profiler (JVM), py-spy (Python), samply и cargo flamegraph (Rust/native); для прода - подними Pyroscope или Parca на тестовом сервисе и поживи с непрерывным профилем неделю, это меняет привычки сильнее любой статьи.

Типичные грабли и заблуждения
  • "Ширина по X - это время выполнения слева направо." Нет. По X соседние фреймы отсортированы по алфавиту, а ширина - это доля сэмплов. Флеймграф не таймлайн, порядок слева направо ничего не значит. Нужен таймлайн - смотри flame chart (другой инструмент) или perf timechart.
  • "Цвет что-то означает." По умолчанию нет, теплые цвета случайны и нужны только чтобы соседние фреймы визуально различались. Смысл у цвета появляется лишь в спец-режимах: --color=java/js (тип кода), --color=io, дифференциальный (красный/синий), memory (зеленый). Не ищи смысл в случайной палитре обычного графа.
  • Пустой или плоский график, все стеки глубиной 1. Почти всегда забыли -g у perf record, либо нет фрейм-поинтеров. Собери с -fno-omit-frame-pointer или используй perf record --call-graph dwarf.
  • Вместо имен функций - адреса вида 0x7f... Нет символов. Поставь debug-символы (пакеты -dbg/-debuginfo или debuginfod), не запускай stripped-бинарь, резолвь на той же машине. Для JIT-языков (Java, Node) нужен perf-map-agent или -XX:+PreserveFramePointer, иначе стек обрывается.
  • "Узкая высокая башня = проблема." Не обязательно. Высота - это глубина рекурсии или вложенности вызовов, она не про CPU. Смотри на ширину верхушек, а не на высоту башни.
  • Ищут тормоза ожидания на on-CPU графе. Если приложение висит, но CPU простаивает - on-CPU граф пустой. Бери off-CPU (offcputime).
  • Слишком короткий замер. 1-2 секунды на 99 Гц - это сотня-другая сэмплов, статистика шумная, узкие фреймы случайны. Снимай хотя бы 15-30 секунд под стабильной нагрузкой.
  • Принимают широкое плато на off-CPU за проблему. Idle-воркер, который честно спит на epoll_wait или futex в ожидании работы, даст широченное плато - и это норма, а не баг. На off-CPU графе смотри на ожидание внутри горячего пути запроса, а не на здоровый сон простаивающих потоков.
Мини-лаба: построй свой первый флеймграф

Повтори прямо сейчас, минут на десять:
  • Клонируй FlameGraph: git clone https://github.com/brendangregg/FlameGraph и зайди в каталог.
  • Запусти любую нагрузку, например в соседнем терминале: yes > /dev/null (одно ядро в полку) или реальную сборку проекта.
  • Сними профиль: sudo perf record -F 99 -a -g -- sleep 15
  • Построй SVG одной трубой: perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > profile.svg
  • Открой profile.svg в браузере. Найди самое широкое плато наверху, кликни по нему - оно развернется на весь экран. Это твое узкое место. Попробуй Ctrl+F и поиск по имени функции - увидишь ее суммарную долю.
  • Переверни тот же профиль в icicle: ./flamegraph.pl --inverted out.folded > icicle.svg и сравни, как читается сверху вниз.
  • Бонус: сними второй профиль под другой нагрузкой в out2.folded и сделай difffolded.pl out.folded out2.folded | flamegraph.pl > diff.svg - посмотри, что покраснело.
Контрольные вопросы
  • Что означает ширина прямоугольника на флеймграфе и почему это НЕ время по порядку слева направо?
  • Почему смотреть надо именно на верхнюю кромку графика, а широкий фрейм внизу сам по себе не значит, что он жжет CPU?
  • Какой флаг perf record отвечает за запись стеков вызовов и что будет, если его забыть?
  • Чем off-CPU флеймграф принципиально отличается от on-CPU, что означает у него ширина и каким инструментом его снимают?
  • Что такое icicle graph и дифференциальный флеймграф, и в каких ситуациях каждый из них берут?
  • Ты видишь вместо имен функций шестнадцатеричные адреса. В чем причина и как починить? Назови хотя бы две.
Что запомнить

Флеймграф - это профиль, превращенный в картинку, где ширина = доля сэмплов (времени), а высота = глубина стека. Ось X - алфавитная раскладка стеков, а не время; цвет по умолчанию случаен. Читаешь по верхней кромке: самые широкие плато на верхушках - горячие точки, и их обычно всего 1-3. Цепочка постройки проста: perf record -F 99 -a -g -> perf script -> stackcollapse-perf.pl -> flamegraph.pl -> SVG, а посередине универсальный folded-формат, который можно фильтровать и дифить. Жжешь CPU - бери on-CPU граф; висишь в ожидании - бери off-CPU через eBPF (offcputime), там ширина = время сна; нужна полная картина - hot/cold; удобнее читать сверху - icicle (--inverted); ищешь регрессию - дифф-флеймграф (красный хуже, синий лучше). Не размотался стек - проверь -g, фрейм-поинтеры или --call-graph dwarf; вместо имен адреса - поставь debug-символы или debuginfod, для JIT нужен perf-map-agent / -XX:+PreserveFramePointer. В 2026 это уже не только разовая картинка, а непрерывное профилирование в проде (Pyroscope 2.0, Parca, OTel eBPF profiler), где флеймграф рисуется за любой прошлый интервал с почти нулевым оверхедом. Флеймграф остается самым наглядным способом найти узкое место в большом приложении - от десктопа до прода.
👍4 ❤️1 🔥1 😄 🤔2
Аватара пользователя
nacherba
Сообщения: 1
Зарегистрирован: 06 июн 2026, 23:36

Re: Флеймграфы: визуализация профиля производительности

Сообщение nacherba »

Спасибо, наконец дошло почему мой график был плоский - забывал -g у perf record. Добавил и сразу все нарисовалось.
👍2 ❤️1 🔥 😄 🤔1
Аватара пользователя
redis_nerd
Сообщения: 1
Зарегистрирован: 23 май 2026, 08:05

Re: Флеймграфы: визуализация профиля производительности

Сообщение redis_nerd »

А раздел про fp vs dwarf прямо в точку. У меня релизный бинарь без фрейм-поинтеров давал обрывки стеков, --call-graph dwarf починил. Дороговато по размеру perf.data, но работает.
👍2 ❤️ 🔥1 😄 🤔
Ответить
← Предыдущая глава
perf: универсальный профайлер Linux
Следующая глава →
Трассировка ядра: ftrace и trace-cmd

Все главы курса «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Поделиться темой: ✈ Telegram VK
Похожие запросы: perf top как найти что грузит процессорс чего начать диагностику linux когда сервер тормозит

Вернуться в «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость