Привет, Хабр!

Процесс висит — и непонятно, что он делает. Потребляет CPU, но не пишет в лог. Или наоборот — не потребляет ничего, но и не отвечает. top показывает состояние D или S, но это мало что говорит. Логов нет, дебаггер подключать нельзя (прод), исходников нет (чужой бинарник).

Есть strace. Утилита, которая перехватывает системные вызовы процесса и показывает их в реальном времени. Процесс пытается открыть файл? Вы увидите openat(). Ждёт данные из сокета? recvfrom(). Спит? nanosleep().

Первый запуск: что происходит внутри

strace ls /tmp

Выводом получаем стену текста. Каждая строка как один системный вызов:

execve("/usr/bin/ls", ["ls", "/tmp"], 0x7fff...) = 0
brk(NULL)                               = 0x55a8c2d3e000
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
read(3, "\x7fELF..."..., 832)           = 832
...
openat(AT_FDCWD, "/tmp", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 3
getdents64(3, /* 15 entries */, 32768)   = 488
write(1, "file1.txt\nfile2.log\n...", 87) = 87
close(3)                                 = 0
exit_group(0)                            = ?

Первым делом — execve (запуск). Потом загрузка динамических библиотек (openat + read для .so файлов). Потом собственно работа: открыть директорию, прочитать записи, вывести в stdout, закрыть, выйти.

Формат строки: имя_вызова(аргументы) = результат. Результат -1 — ошибка, и strace покажет код ошибки: ENOENT (файл не найден), EACCES (нет прав), ECONNREFUSED (соединение отклонено).

Подключиться к работающему процессу

# По PID
strace -p 12345

# Найти PID и подключиться
strace -p $(pidof nginx)

Не нужно перезапускать процесс и не нужен дебаггер. Подключились к проду, посмотрели что происходит, отключились. Для отключения — Ctrl+C, процесс продолжит работать.

Фильтрация

По умолчанию strace показывает всё. Для типичного процесса сотни вызовов в секунду. Есть фильтры:

# Только файловые операции
strace -e trace=file -p 12345
# openat, stat, access, unlink, rename...

# Только сетевые
strace -e trace=network -p 12345
# socket, connect, bind, listen, accept, send, recv...

# Только чтение/запись
strace -e trace=read,write -p 12345

# Конкретные вызовы
strace -e trace=openat,connect -p 12345

Категории: file (файловый ввод-вывод), network (сеть), process (fork, exec, exit), signal (сигналы), memory (mmap, brk), ipc (межпроцессное взаимодействие).

Кейс: «приложение не стартует»

Сервис не запускается. В логах пусто. Systemd говорит exit code 1.

strace -f -e trace=file ./myapp 2>&1 | grep -i "no such\|permission"
openat(AT_FDCWD, "/etc/myapp/config.yaml", O_RDONLY) = -1 ENOENT (No such file or directory)

Готово. Приложение ищет конфиг по пути /etc/myapp/config.yaml, файла нет. Без strace можно было бы часами гадать.

Ещё вариант — проблемы с правами:

openat(AT_FDCWD, "/var/lib/myapp/data.db", O_RDWR) = -1 EACCES (Permission denied)

Или динамическая библиотека не найдена:

openat(AT_FDCWD, "/usr/lib/libcustom.so", O_RDONLY) = -1 ENOENT

Кейс: «приложение тормозит»

Сервер отвечает медленно. CPU не загружен. Диск не загружен. Что-то ждёт.

strace -T -p 12345

Флаг -T добавляет время выполнения каждого вызова:

recvfrom(5, "HTTP/1.1 200 OK\r\n...", 8192, 0, NULL, NULL) = 347 <2.103452>
write(1, "Processing response...\n", 23) = 23 <0.000041>
connect(6, {sa_family=AF_INET, sin_addr=10.0.0.50, sin_port=3306}, 16) = 0 <0.503221>

<2.103452> — два секунды на получение HTTP-ответа. <0.503221> — полсекунды на коннект к MySQL. Узкое место найдено.

Или посчитать суммарно:

strace -c -p 12345
# Ctrl+C через 10 секунд

# % time     seconds  usecs/call     calls    errors syscall
# ------ ----------- ----------- --------- --------- ----------------
#  89.12    4.523100      150770        30           poll
#   5.23    0.265400        8846        30           recvfrom
#   3.01    0.152800        5093        30           sendto
#   ...

-c — статистика. 89% времени процесс проводит в poll — ждёт данных. Не CPU-bound, а I/O-bound. Смотрите, кого он ждёт.

Кейс: «куда пишет логи?»

Достался чужой бинарник. Документации нет. Куда он пишет?

strace -e trace=write,openat -p 12345 2>&1 | grep -E "open.*O_WRONLY|open.*O_RDWR|write\("
openat(AT_FDCWD, "/var/log/mystery/app.log", O_WRONLY|O_APPEND|O_CREAT, 0644) = 4
write(4, "2026-03-23 10:15:02 INFO Start...", 52) = 52

Файловый дескриптор 4 — это /var/log/mystery/app.log. Теперь знаете.

Кейс: «DNS-резолвинг тормозит»

Приложение медленно подключается к внешним сервисам. Подозрение на DNS.

strace -e trace=network -T -p 12345 2>&1 | grep connect
connect(3, {sa_family=AF_INET, sin_addr=8.8.8.8, sin_port=53}, 16) = 0 <0.000045>
...
recvfrom(3, ...) = 52 <3.001234>

Три секунды на ответ от DNS-сервера. Проблема не в приложении — DNS тормозит.

Многопоточные приложения: -f

По умолчанию strace трассирует только основной поток. Для многопоточных приложений:

strace -f -p 12345

-f — следить за всеми потоками и дочерними процессами. Каждая строка будет префиксирована PID:

[pid 12345] poll([{fd=5, events=POLLIN}], 1, 5000) = 1
[pid 12346] read(7, "data...", 4096) = 128
[pid 12347] write(3, "log entry\n", 10) = 10

Вывод в файл

Для анализа позже:

# Всё в файл
strace -o trace.log -f -T -p 12345

# Каждый поток/процесс в отдельный файл
strace -ff -o trace -p 12345
# Создаст trace.12345, trace.12346, trace.12347...

-ff с -o — по файлу на каждый PID. Удобно для многопоточных: каждый поток — свой файл, нет перемешивания.

Строки: больше контекста

По умолчанию strace обрезает строки до 32 символов. Для отладки часто мало:

# Показывать до 1024 символов в строковых аргументах
strace -s 1024 -p 12345

# Показывать пути полностью
strace -y -p 12345
# write(4</var/log/app.log>, "data", 4) = 4

-y — рядом с файловым дескриптором показывает путь. Не нужно вручную сопоставлять fd 4 с файлом.

Timestamps

# Абсолютное время
strace -t -p 12345
# 10:15:02 openat(...) = 3

# С микросекундами
strace -tt -p 12345
# 10:15:02.345678 openat(...) = 3

# Относительное (от предыдущего вызова)
strace -r -p 12345
#      0.000123 openat(...) = 3
#      2.103456 read(...) = 128   ← 2 секунды между вызовами

-r — самый полезный для поиска задержек. Видите большое число — между этими двумя вызовами процесс ждал.

Краткая шпаргалка

strace -p PID                    # Подключиться к процессу
strace -f -p PID                 # + потоки и дочерние
strace -e trace=file             # Только файловые операции
strace -e trace=network          # Только сетевые
strace -T                        # Время каждого вызова
strace -c                        # Статистика по вызовам
strace -r                        # Время между вызовами
strace -y                        # Показать пути для fd
strace -s 1024                   # Длинные строки
strace -o file.log               # В файл

Десять флагов, c ними вы точно покроете 95% случаев.

Если в какой-то момент вам стало тесно на уровне «посмотреть вывод утилиты и починить симптом», значит дальше уже нужен разбор того, как система устроена изнутри. На курсе по разработке ядра Linux как раз идут в эту глубину: архитектура ядра, модули, память, прерывания, синхронизация и отладка — то есть те вещи, которые помогают не просто диагностировать поведение системы, а понимать его причины.

Немного практики в тему — пройдите вступительный тест и узнаете, есть ли пробелы в знаниях. ➦ Пройти тест.

А чтобы узнать больше о формате обучения и задать вопросы экспертам, приходите на бесплатные уроки:

  • 9 апреля в 20:00. «Платформенные драйверы. Как управлять периферией систем на кристалле». Записаться

  • 21 апреля в 20:00. «Связанные списки в ядре Linux: от базовых средств до реального кода». Записаться