Операционные системы

Отладка и профилирование

Production система крашится раз в неделю без объяснений. Логи чистые. Метрики нормальные. Воспроизвести локально не удаётся. Это кошмар каждого инженера. Но у FAANG-компаний есть секретное оружие: strace, perf, eBPF, ASan. Эти инструменты находят баги, которые невидимы обычными методами. Они работают на production без остановки сервиса. Они показывают то, что скрыто от debugger. Освоение этих инструментов - возможность дебажить системы любой сложности.

  • **Netflix обслуживает 200+ млн пользователей.** Любой performance regression = миллионы долларов потерь. Используют perf + flame graphs для поиска CPU bottleneck. Результат: находят что 25% CPU уходит на устаревший кодек, переходят на новый → экономят 10M/год на серверах.
  • **Cloudflare отражает DDoS атаки 46 млн requests/sec.** Используют eBPF/XDP для фильтрации пакетов прямо в ядре. Скорость: 24 млн packets/sec на одном ядре - в 10x быстрее чем iptables. eBPF защищает половину интернета от атак.
  • **Facebook запускает ASan на 1% production серверов (canary).** Ловят use-after-free и buffer overflow ДО широкого rollout. Один пойманный баг спас от potential RCE exploit, который мог скомпрометировать миллионы аккаунтов.

Цели урока

  • strace для трассировки syscalls процесса в реальном времени
  • perf: CPU profiling, flame graphs (Brendan Gregg), hardware counters
  • eBPF (BCC, bpftrace): observability one-liners без kernel modules
  • Valgrind для memory errors (медленный, ~50x slowdown)
  • AddressSanitizer (ASan, ~2x slowdown), TSan, UBSan для production hardening

Strace и Ltrace: рентген системных вызовов

**Strace** - инструмент, показывающий ВСЕ системные вызовы (syscalls) программы в реальном времени. Это как рентген процесса: видишь каждое взаимодействие с ядром - открытие файлов, сетевые запросы, выделение памяти, сигналы.

Production кейс: почему сервис тормозит?

Сервис API начал отвечать за 2 секунды вместо 50мс. Логи ничего не показывают. Запускаешь `strace -p <PID>` на production и видишь: ``` open("/etc/hosts", O_RDONLY) = 3 read(3, "127.0.0.1 localhost\n...", 4096) = 180 close(3) = 0 ``` Повторяется 1000 раз в секунду! Оказалось, библиотека DNS резолвинга читала `/etc/hosts` при каждом запросе вместо кеширования. Одна строка strace - час дебага сэкономлен.

**Ltrace** - аналог strace, но показывает вызовы библиотечных функций (libc, libssl, etc), а не системные вызовы. Используй когда проблема не в ядре, а в userspace библиотеках.

**Разница strace vs ltrace:** - **strace** → системные вызовы (ядро): `open()`, `read()`, `socket()`, `fork()` - **ltrace** → библиотечные функции (userspace): `malloc()`, `strlen()`, `SSL_connect()` Пример цепочки: ``` program → fopen() [ltrace видит] → внутри вызывает open() [strace видит] → ядро открывает файл ```

Debugging: программа зависает на старте

Запускаешь `./myserver`, он виснет без вывода. GDB не помогает (нет символов). Используешь strace: ```bash strace ./myserver ... connect(3, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("192.168.1.100")}, 16) = -1 ETIMEDOUT (Connection timed out) ``` БАМ! Сервер пытается подключиться к MySQL по IP 192.168.1.100, который недоступен. Таймаут 30 секунд. Проблема найдена за 5 секунд.

**Когда использовать strace/ltrace:** 1. Программа зависает - ищем на чём застряла (ждёт сети? диска? lock?) 2. Медленная работа - профилируем syscalls, смотрим где время 3. "Permission denied" - strace покажет КАКОЙ файл недоступен 4. Утечки файловых дескрипторов - считаем `open()` vs `close()` 5. Проблемы с DNS/network - видим все `connect()`, `sendto()`, `recvfrom()`

Production сервис начал медленно отвечать. `top` показывает низкую загрузку CPU (5%), но latency выросла с 10мс до 1 секунда. Какой инструмент поможет найти проблему?

Perf: CPU профилирование и flame graphs

**Perf** - стандартный Linux профайлер, использующий аппаратные счётчики процессора (PMU - Performance Monitoring Unit). Показывает где именно программа тратит CPU циклы - вплоть до отдельных инструкций и cache misses.

Real-world: оптимизация в Dropbox

Инженеры Dropbox использовали perf для оптимизации Python кода. Профилировали production серверы: ```bash perf record -g -p $(pgrep python) sleep 60 perf script | flamegraph.pl > flame.svg ``` Flame graph показал: 40% CPU времени уходит на парсинг JSON. Заменили стандартный `json` модуль на `ujson` (C-библиотека) - скорость выросла в 2 раза. Одна оптимизация сэкономила тысячи серверов.

**Flame Graphs (графы пламени)** - визуализация профиля. Ось X - алфавитный порядок (не время!), ось Y - глубина стека, ширина - сколько времени функция занимала CPU.

**Perf events: не только CPU циклы** Perf может мониторить аппаратные события: - `perf stat ./myapp` - базовая статистика (инструкции, cache misses, branch mispredictions) - `perf record -e cache-misses` - профиль L1/L2/L3 cache промахов - `perf record -e page-faults` - где происходят page faults (память свапается) - `perf record -e context-switches` - частота переключения контекста Пример: ```bash perf stat ./myapp 10,523,456,789 instructions # 10 млрд инструкций 512,345,678 cycles # 512 млн циклов 0.54 IPC # Instructions Per Cycle (эффективность) 12,345,678 cache-misses # Много! → оптимизировать locality ```

Netflix: perf для поиска CPU bottleneck

Netflix обслуживает 200+ млн пользователей. Любая оптимизация = миллионы экономии. Используют perf на production: 1. `perf record -ag -F 99 sleep 60` - профиль всех серверов 2. Генерируют flame graph → находят что 25% CPU уходит на сжатие видео устаревшим кодеком 3. Переходят на новый кодек (SVT-AV1) → CPU использование упало на 20% 4. Сэкономили ~10M/год на серверах Все благодаря одному flame graph.

**Когда использовать perf:** 1. Программа медленная, но непонятно где bottleneck 2. Оптимизация: найти функции, съедающие больше всего CPU 3. Анализ cache efficiency (много cache misses?) 4. Проверка после оптимизации (стало быстрее?) 5. Production profiling - можно запускать на живых серверах (overhead ~1-5%)

После запуска perf сгенерирован flame graph. На верхнем уровне (leaf) виден широкий блок функции `hash_table_lookup()`. Что это означает?

eBPF: программирование ядра без kernel modules

**eBPF (extended Berkeley Packet Filter)** - революция в Linux мониторинге. Позволяет запускать безопасный код прямо в ядре, без компиляции kernel modules. Трейсишь что угодно: syscalls, функции ядра, network packets, filesystem операции - всё в реальном времени с минимальным overhead.

**Почему eBPF безопасен?** Код проходит верификатор перед загрузкой: нет бесконечных циклов, нет обращений к невалидной памяти, ограниченный стек. Если верификатор отклоняет - программа не загрузится. Это как JIT в V8/JVM, но для ядра.

Production кейс: необъяснимые задержки в Kubernetes

Кластер Kubernetes начал показывать random latency спайки (99th percentile 500мс → 5 секунд). Логи чистые, метрики нормальные. Запускаем bpftrace на node: ```bash # Трейсим scheduler задержки (сколько процесс ждал CPU) sudo bpftrace -e ' tracepoint:sched:sched_switch { @qtime[args->next_pid] = nsecs; } tracepoint:sched:sched_wakeup { $wait = nsecs - @qtime[args->pid]; if ($wait > 1000000) { // > 1ms printf("PID %d waited %d ms\n", args->pid, $wait/1000000); } }' ``` Выясняется: один Pod жрёт 100% CPU, вызывая scheduler thrashing для соседей. cgroup limits не работали из-за бага в kernel 4.14. Обновили kernel → проблема ушла.

**BCC (BPF Compiler Collection)** - набор готовых eBPF инструментов: - `execsnoop` - логирует все запуски процессов (exec) - `opensnoop` - все открытия файлов - `tcpconnect` - все TCP подключения - `biolatency` - histogram disk I/O latency - `funccount` - счётчик вызовов функции (kernel или userspace) - `trace` - универсальный трейсер (аналог SystemTap) Пример: ```bash # Смотрим кто подключается по сети sudo tcpconnect PID COMM SADDR DADDR DPORT 1234 chrome 127.0.0.1 1.2.3.4 443 5678 ssh 10.0.0.5 20.0.0.10 22 ```

Cloudflare: eBPF для защиты от DDoS

Cloudflare обрабатывает 46 млн HTTP requests/sec. Используют eBPF для фильтрации DDoS атак прямо в ядре: 1. eBPF программа на XDP (eXpress Data Path) проверяет пакеты ДО стека TCP/IP 2. Блокирует вредоносные пакеты на скорости 24 млн packets/sec на ОДНОМ ядре 3. Обычный iptables/netfilter: ~2 млн pps В 10+ раз быстрее! eBPF работает на уровне сетевого драйвера, минуя весь networking stack.

**Когда использовать eBPF:** 1. Production debugging без перезагрузки - инжектишь код в ядро на лету 2. Performance анализ: где задержки? где contention? какие syscalls? 3. Security monitoring: трейсить все exec, file access, network connections 4. Network performance: XDP для packet filtering/load balancing 5. Kernel development: дебаг новых фич без пересборки ядра

Чем eBPF принципиально отличается от традиционных kernel modules (loadable kernel modules)?

Memory debugging: Valgrind, ASan, leak detection

**Memory bugs** - самые коварные: use-after-free, double-free, buffer overflow, memory leaks. Проявляются непредсказуемо: программа может работать месяцы, потом внезапно крашнуться. Инструменты memory debugging находят баги ДО того как они попадут в production.

**Типы memory багов:** 1. **Memory leak** - выделил память, забыл освободить (`malloc` без `free`) 2. **Use-after-free** - используешь память после `free()` (данные могут быть перезаписаны) 3. **Double-free** - вызвал `free()` дважды на один адрес (corruption heap) 4. **Buffer overflow** - запись за границы массива (`arr[100]` когда размер 50) 5. **Uninitialized memory** - читаешь переменную до инициализации (случайный мусор) 6. **Stack overflow** - бесконечная рекурсия или огромный stack array

**Valgrind минусы:** замедляет программу в 10-50 раз. Не подходит для production. Но отлично для testing: запускаешь тесты под Valgrind → находишь все memory issues.

Production кейс: intermittent crash в Redis

Redis периодически крашился раз в неделю без паттерна. Core dump не помогал (corrupted heap). Собрали Redis с ASan: ```bash make SANITIZER=address ./redis-server --sanitizer ``` Через 2 дня ASan поймал: ``` heap-use-after-free in module API callback ``` Оказалось: сторонний Redis модуль держал указатель на строку, которую Redis уже освободил. Одна строка - миллионы пользователей спасены от крашей.

**Сравнение инструментов memory debugging:**

ИнструментСкоростьЧто находитИспользование
**Valgrind**10-50x медленнееLeaks, use-after-free, overflows, uninit readsDevelopment/Testing
**ASan**2x медленнееOverflows, use-after-free, double-freeDevelopment/Testing/Canary prod
**TSan**5-15x медленнееData races, deadlocksDevelopment/Testing
**MSan**3x медленнееUninitialized memory readsDevelopment
**eBPF (bcc memleak)**<5% overheadMemory leaks в productionProduction

На практике: ASan в CI/CD, eBPF на production для мониторинга.

Facebook: ASan на production canary

Facebook запускает 1% production серверов с включённым ASan (canary deployments). Overhead 2x приемлем для небольшой части флота. Результат: - Ловят use-after-free и overflows ДО широкого rollout - Один пойманный баг спас от потенциального security incident (RCE exploit через buffer overflow) - ASan стал частью deployment pipeline: canary → ASan clean → rollout 100%

Ключевые идеи

  • **strace/ltrace - рентген процесса:** показывают каждый syscall и library call в реальном времени. Незаменимы для debugging: программа виснет? strace покажет на чём. Permission denied? strace покажет какой файл. Работают на production с низким overhead.
  • **perf - CPU профилирование через hardware counters:** записывает где программа тратит циклы процессора. Flame graphs визуализируют hotspots. Netflix/Dropbox оптимизируют production используя perf - находят функции, съедающие CPU, и ускоряют их в разы.
  • **eBPF - программирование ядра без риска:** безопасно инжектишь код в kernel для трейсинга syscalls, network, scheduler. Верификатор гарантирует что не крашнет систему. Cloudflare использует для DDoS защиты, Uber - для performance мониторинга.
  • **ASan/Valgrind - memory debugging:** находят use-after-free, buffer overflow, leaks. ASan в 2x медленнее (подходит для canary prod), Valgrind в 10-50x (только dev/test). Facebook/Google запускают ASan на production canary - ловят critical bugs до широкого rollout.

Связанные темы

Debugging и профилирование связаны со всеми аспектами системного программирования:

  • Системные вызовы (syscalls) — strace показывает каждый syscall - понимание как программа взаимодействует с ядром критично для debugging
  • Управление памятью — ASan/Valgrind находят memory corruption - нужно понимать как работает heap, stack, virtual memory
  • Scheduling и multithreading — perf показывает context switches и CPU time - помогает оптимизировать многопоточные программы
  • Networking — eBPF/XDP используют для packet filtering и network debugging - понимание TCP/IP стека обязательно

Вопросы для размышления

  • Production сервис с latency 10мс, но раз в минуту происходит спайк до 5 секунд. Логи не показывают ошибок. Какие инструменты (strace/perf/eBPF) использовать и в какой последовательности для диагностики?
  • Программа на C++ медленно жрёт память (1GB за сутки), но Valgrind показывает "no leaks". Как такое возможно? (Подсказка: reachable vs lost memory) Какими инструментами искать проблему?
  • Почему eBPF считается безопасным для production, а обычный kernel module - нет? Какие гарантии даёт eBPF верификатор и какие баги он не может предотвратить?

Связанные уроки

  • arch-01-binary
Отладка и профилирование

0

1

Войти

**Best practices memory debugging:** 1. **Development:** Компилируй с ASan/UBSan всегда (`-fsanitize=address,undefined`) 2. **CI/CD:** Запускай тесты под Valgrind/ASan (находи баги до merge) 3. **Production:** eBPF `memleak` для мониторинга утечек, ASan на canary серверах 4. **Core dumps:** Включай `ulimit -c unlimited` + анализируй через gdb при крашах 5. **Fuzz testing:** AFL, libFuzzer с ASan - автоматический поиск багов через random inputs

Memory leaks не критичны - современные ОС освобождают всю память при завершении процесса

Memory leaks критичны для long-running процессов: серверы, демоны, desktop приложения работают дни/месяцы без перезапуска

Да, OS освободит память при завершении процесса. Но сервер, работающий 24/7, будет жрать всё больше RAM пока не упрётся в лимит и не крашнется (OOM killer). Пример: leak 1KB/sec → за сутки 86MB → за месяц 2.5GB → через полгода сервер умрёт. А в микросервисной архитектуре leak в одном сервисе может уронить весь кластер через cascade failure. Поэтому memory leaks КРИТИЧНЫ, особенно в production системах.

Программа на C++ периодически крашится в production (раз в неделю), но воспроизвести локально не удаётся. Core dump показывает corrupted heap. Что поможет найти причину?