Профилирование CPU: perf top и поиск пожирателя

Рейтинг: 61% · 6 голосов
Подробный курс по диагностике и производительности 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

Профилирование CPU: perf top и поиск пожирателя

Сообщение 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. Карта инструментов и сквозной разбор инцидента производительности
Знакомая картинка: в top одно ядро или все сразу прижаты к 100%, load average ползёт вверх, а на вопрос "чем именно занят процессор" внятного ответа нет. Видишь имя процесса - и всё. Но процесс это здоровенная программа из сотен функций. Какая из них жжёт такты прямо сейчас? top на это не ответит, ему такое не по зубам - он считает время по процессам, а не по строчкам кода. Здесь начинается профилирование linux, и главный инструмент для CPU - это perf.

В этом уроке разберём, как через perf найти ту самую функцию-пожирателя. Сначала живой профиль через perf top, потом запись со стеками вызовов (perf record и perf report), разберёмся с символами и debuginfo (почему вместо имён функций часто вылезают голые адреса), затронем актуальный на 2026 год eBPF-инструментарий и подведём к флеймграфам, которым посвящён отдельный урок 38.

Как работает perf и почему он почти ничего не стоит

Сразу о главном страхе новичка: "а не положу ли я нагруженный прод, запустив профайлер?" Нет. perf использует сэмплирование (sampling). Он не следит за каждой инструкцией - это было бы дорого. Вместо этого он много раз в секунду дёргает процессор через аппаратные счётчики (PMU) или программный таймер и спрашивает: "А что ты выполняешь прямо в этот момент? На какой функции стоит указатель инструкций?" Записал адрес и стек - отпустил. И так тысячи раз в секунду на каждом ядре.

Аналогия: чтобы понять, чем человек занят весь день, не нужна камера, пишущая 24 часа. Достаточно фотографировать его раз в минуту. Если на 80% снимков он за компьютером - вывод очевиден. perf делает ровно это с функциями. Чем чаще функция попадает в "кадр", тем больше процессорного времени она ест. Накладные расходы такого подхода на разумной частоте - доли процента, поэтому perf можно гонять и на боевых серверах.

perf - это cpu профайлер linux, который видит всё насквозь: и код твоего приложения в user space, и то, что происходит внутри ядра (системные вызовы, работа с сетью, файловой системой). Это важно: если приложение "тонет в ядре", обычные инструменты вроде top покажут только высокий %sy в общей строке, а perf покажет конкретную функцию ядра, которая ест время.

Ставится из пакета: на Debian/Ubuntu это linux-tools-$(uname -r) плюс linux-tools-generic, на RHEL/Fedora и в отечественных Astra Linux / RED OS - пакет perf. Версия perf привязана к ядру, ставь под свою (на 2026 актуально ядро 6.x).

Изображение

perf top - живой профиль: кто грузит cpu linux прямо сейчас

Когда нагрузка идёт ВОТ СЕЙЧАС, не нужно ничего записывать. Команда perf top - это как top, только не по процессам, а по функциям. Живой, обновляющийся список самых прожорливых функций в системе.

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

sudo perf top
По умолчанию perf top сэмплирует на максимально допустимой частоте (часто это 4000 Гц, упирается в kernel.perf_event_max_sample_rate). Чтобы снизить нагрузку, частоту полезно занизить, например до 99 Гц (99, а не 100 - чтобы не попадать в такт с периодическими задачами планировщика и не ловить ложные пики):

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

sudo perf top -F 99
Вывод выглядит так:

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

Samples: 41K of event 'cpu-clock', 99 Hz, Event count (approx.): 9876543210
Overhead  Shared Object        Symbol
  23.41%  myapp                [.] json_parse_value
  11.07%  [kernel]             [k] copy_user_enhanced_fast_string
   8.92%  libc.so.6            [.] __memcpy_avx_unaligned
   4.10%  myapp                [.] hash_lookup
Читаем по колонкам, это ключевое:
  • Overhead - доля сэмплов, пришедшихся на эту функцию. Грубо - сколько процентов времени CPU там провёл. 23% на одной функции это много, есть на что смотреть. Порог внимания на практике - примерно от 5%: ниже обычно шум, выше - кандидат на разбор.
  • Shared Object - откуда функция. myapp - твой бинарник. libc.so.6 - системная библиотека C. А [kernel] - это код ядра.
  • Symbol - имя функции. Метка [.] значит user space (твой код или библиотека), а [k] - kernel space (ядро).
По этому выводу уже виден диагноз: верхняя строка - json_parse_value из приложения, 23%. Значит приложение упирается в разбор JSON. А copy_user_enhanced_fast_string с меткой [k] - это ядро копирует данные между ядром и user space, типичный признак активного ввода-вывода или массы системных вызовов. Так с одного экрана понятно: приложение считает само (user) или тонет в ядре (kernel). Это базовая развилка on-CPU анализа.

Полезные клавиши прямо в perf top: стрелками встаёшь на функцию, Enter и потом Annotate - увидишь ассемблер функции с разбивкой нагрузки по конкретным инструкциям (видно, на какой строке кода затык). Клавиша E разворачивает дерево вызовов, если запущено со стеками. Выход - q.

perf record и perf report - запись со стеками вызовов

perf top отвечает "какая функция жжёт CPU". Но часто этого мало. Допустим, виновата memcpy. И что? Её зовут из ста мест. Нужен контекст: КТО её вызывает. Для этого нужна запись со стеками вызовов (call graph). Тут на сцену выходит perf record.

Ключевой флаг - -g, он включает запись стека вызовов на каждом сэмпле. То есть perf фиксирует не только текущую функцию, но и всю цепочку: кто кого позвал.

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

sudo perf record -F 99 -g -p 12345 -- sleep 30
Разберём: -F 99 - частота, -g - стеки, -p 12345 - профилируем конкретный PID, а sleep 30 задаёт длительность 30 секунд (perf завершится, когда завершится команда после --). Профилировать всю систему - флаг -a вместо -p. Результат пишется в файл perf.data в текущем каталоге, поэтому запускай там, где есть место на диск.

Теперь разбираем запись:

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

sudo perf report
Откроется интерактивный отчёт (TUI). Главная фишка - две колонки процентов:
  • Self - сколько времени потрачено в самой этой функции (её собственный код, без вызванных).
  • Children - сколько времени потрачено в ней И во всех функциях, которые она вызвала, суммарно.
Это разделение и есть соль анализа. Функция main почти всегда имеет Children близко к 100% (из неё растёт всё дерево), но Self у неё крошечный - сама она ничего не считает. А вот функция с большим Self и есть реальный пожиратель тактов. Внутри отчёта встаёшь на строку, жмёшь Enter и разворачиваешь дерево вызовов (клавиша + или E) - видно, через какую цепочку программа дошла до тяжёлой функции. Полезно: perf report --stdio выгружает то же самое текстом, удобно сложить в тикет или сравнить два прогона через diff.

Символы и debuginfo: почему видишь адреса вместо имён

Очень частая боль новичка. Открываешь perf report, а там вместо имён функций - такое:

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

  18.22%  myapp  [.] 0x000000000004a1f0
   9.05%  myapp  [.] 0x0000000000051b3c
Голые шестнадцатеричные адреса. Профиль есть, а смысла ноль. Причина: perf знает АДРЕС горячего кода, но не знает, какому имени функции этот адрес соответствует. Таблица соответствия (символы, symbols) либо вырезана из бинарника при сборке (stripped релиз), либо лежит в отдельных debuginfo-пакетах, которых на машине нет.

Что делать:
  • Для своего приложения - собирай с флагом -g (отладочная информация) и не делай strip для профилируемой сборки. Для интерпретируемых/JIT-языков (Java, Node.js, .NET) нужны perf-map-агенты, чтобы JIT-адреса превращались в имена.
  • Для системных библиотек и ядра - доставь debuginfo. На RHEL/Fedora: sudo dnf debuginfo-install glibc. На Debian/Ubuntu подключают репозиторий ddebs и ставят пакеты вида libc6-dbg. В Astra Linux / RED OS ищи пакеты с суффиксом -dbgsym или -debuginfo в штатных репозиториях.
  • Символы ядра обычно доступны через /proc/kallsyms (нужен sudo, иначе адреса занулены настройкой kernel.kptr_restrict).
Отдельная засада - неполные или рваные стеки. По умолчанию perf разворачивает стек по frame pointer (метод fp), и это почти бесплатно. Хорошая новость на 2026: фрейм-пойнтеры вернулись. Ubuntu начиная с 24.04 LTS и Fedora начиная с 38 собирают пакеты с -fno-omit-frame-pointer по умолчанию, поэтому fp-стеки в системных библиотеках снова честные из коробки (раньше их массово вырезали ради скорости, и стеки врали). Исключения остались - например интерпретатор Python местами собирают без фрейм-пойнтеров.

Если же код собран с -fomit-frame-pointer, стек получится обрезанным или фальшивым. Тогда переключаются на DWARF-разворачивание:

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

sudo perf record -F 99 --call-graph dwarf -p 12345 -- sleep 30
DWARF честно восстанавливает стек по отладочным секциям, но он заметно дороже: копирует кусок стека на каждом сэмпле, накладные расходы выше, а perf.data раздувается в разы (на длинной записи легко на гигабайты). Поэтому dwarf берут точечно, когда fp-стеки врут. Третий вариант - --call-graph lbr (аппаратный Last Branch Record): дёшево и точно, но глубина стека ограничена железом и LBR часто недоступен в облаке/виртуалках. Практика 2026: на современных Ubuntu/Fedora начинай с дефолтного fp, к dwarf переходи только при рваных стеках.

Где сейчас уместен eBPF, а где perf (актуально на 2026)

perf - не единственный и не всегда лучший инструмент. К 2026 году eBPF дозрел и во многих задачах оттеснил классику.
  • profile из bcc - eBPF-профайлер, который агрегирует стеки прямо в ядре и отдаёт уже свёрнутые счётчики, минуя огромный perf.data. Для быстрого on-CPU снимка часто удобнее: profile -F 99 -p 12345 30. Под капотом то же сэмплирование, но дешевле по диску.
  • bpftrace - однострочники для точечной трассировки. Заменил многие сценарии strace там, где важен минимальный overhead. Пример снимка стеков: bpftrace -e 'profile:hz:99 /pid==12345/ { @[ustack] = count(); }'.
  • Continuous profiling - Parca и Grafana Pyroscope (проекты слились) собирают eBPF-профили всего парка машин непрерывно и хранят историю. Это уже не "запустил руками раз в день", а постоянный поток профилей, по которому видно регрессии после релиза. Для прода 2026 - стандарт де-факто.
Когда что: разовая диагностика "горит сейчас" - perf top или profile; глубокий разбор со стеками - perf record/report; постоянное наблюдение за регрессиями - continuous profiling.

Типичные грабли и заблуждения
  • "Запущу perf и убью сервер". Нет. Сэмплирование на 99 Гц - копейки. Бойся не overhead, а размера perf.data при --call-graph dwarf на длинной записи (следи за местом на диске).
  • "Высокий Self у main - вот и виновник". Перепутаны Self и Children. У контейнерных функций (main, циклы событий) Self почти всегда мал. Ищи большой Self, а не Children.
  • "Адреса вместо имён - perf сломан". perf в порядке, не хватает символов/debuginfo. Лечится пакетами, не переустановкой perf.
  • "Стек обрывается на двух кадрах". Это обрезанные fp-стеки из пакета без фрейм-пойнтеров. На свежих Ubuntu/Fedora это уже редкость, но для старых сборок спасает --call-graph dwarf.
  • Профилирование меньше пары секунд даёт слишком мало сэмплов, статистика врёт. Дай записи хотя бы 10-30 секунд под нагрузкой.
  • Забыл sudo или упёрся в paranoid. Доступ к событиям регулирует /proc/sys/kernel/perf_event_paranoid. Уровни: 2 - запрещено профилирование ядра непривилегированным, 1 - запрещены CPU-события, 0 - разрешены tracepoint'ы, -1 - можно почти всё. Апстрим-дефолт 2, многие дистрибутивы ставят 1. Если perf ругается на права: sudo sysctl kernel.perf_event_paranoid=1 (временно, до перезагрузки).
Мини-лаба: сделай прямо сейчас
  • Создай нагрузку в отдельном терминале: запусти yes > /dev/null или поставь stress-ng --cpu 1.
  • Запусти sudo perf top -F 99. Найди в верхушке свою нагрузку. Посмотри на колонку Overhead и метки [.] / [k].
  • Возьми PID нагрузки (через pidof или top) и сними запись: sudo perf record -F 99 -g -p ПИД -- sleep 15.
  • Разбери: sudo perf report. Найди функцию с самым большим Self. Разверни дерево через Enter и +, посмотри, кто её вызвал.
  • Сравни с eBPF (если есть bcc): sudo profile -F 99 -p ПИД 15 - получишь свёрнутые стеки без огромного perf.data.
  • Бонус к уроку про флеймграфы: sudo perf record -F 99 -ag -- sleep 30, затем perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > cpu.svg (скрипты из репозитория FlameGraph Брендана Грегга). Открой SVG в браузере - и весь профиль виден одной картинкой.
Контрольные вопросы
  • Почему perf можно запускать на боевом сервере и чем сэмплирование отличается от трассировки каждой инструкции?
  • Чем отличаются колонки Self и Children в perf report и в какой из них искать настоящего пожирателя CPU?
  • Ты видишь в отчёте шестнадцатеричные адреса вместо имён функций. В чём причина и как это починить для своего приложения и для системных библиотек?
  • Когда стоит переключиться с разворачивания стека по frame pointer на --call-graph dwarf, какой ценой и почему на свежих Ubuntu/Fedora это теперь нужно реже?
Что запомнить

perf - это профайлер, который через сэмплирование почти бесплатно показывает, какие функции жгут CPU, и в приложении, и в ядре. perf top - живой профиль для "горит прямо сейчас". perf record -g плюс perf report - запись со стеками, чтобы понять не только ЧТО, но и КТО это вызвал. Голые адреса вместо имён лечатся символами и debuginfo, а не переустановкой. Смотри на Self, чтобы найти виновника, и на Children, чтобы понять путь. На 2026 фрейм-пойнтеры в дистрибутивах вернулись, а для постоянного наблюдения за CPU всё чаще берут eBPF (profile из bcc, bpftrace, continuous profiling через Parca/Pyroscope). А когда профиль большой и хочется увидеть всю картину разом - превращаем его во флеймграф, об этом в уроке 38.
👍5 ❤️3 🔥3 😄 🤔
Аватара пользователя
k8s91
Сообщения: 1
Зарегистрирован: 20 май 2026, 00:55

Re: Профилирование CPU: perf top и поиск пожирателя

Сообщение k8s91 »

Спасибо, наконец понял разницу Self и Children, всегда тыкал в main и удивлялся почему там 99 процентов. Теперь ясно куда смотреть.
👍1 ❤️1 🔥 😄 🤔
Аватара пользователя
mjg123
Сообщения: 1
Зарегистрирован: 11 май 2026, 18:44

Re: Профилирование CPU: perf top и поиск пожирателя

Сообщение mjg123 »

О, не знал что в 24.04 фрейм-пойнтеры по умолчанию вернули. У меня на старой убунте стеки в libc вечно обрывались, теперь понятно почему dwarf спасал. А profile из bcc реально удобнее, perf.data на проде задолбал место жрать.
👍 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
Диагностика CPU по ядрам: vmstat и mpstat
Следующая глава →
Частоты, троттлинг и NUMA: turbostat и numastat

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

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

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

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

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