Диагностика процессов: strace, lsof, perf и eBPF

↑ Этап 1 · Фундамент: Linux, Bash, сети

Желательно~7 мин чтенияУровень: middle

Зачем это на собесе

«Программа зависла / не находит файл / тормозит, а логов нет. Что делаете?» Ждут последовательность: посмотреть, что процесс делает прямо сейчас (strace), какие файлы и сокеты открыты (lsof, ss), куда уходит процессор (perf), и знать, что современная замена всему этому eBPF.

Подтемы

  • Системные вызовы: как программа общается с ядром
  • strace
  • lsof и ss
  • perf и профилирование
  • eBPF: bpftrace и готовые утилиты
  • Диагностика внутри контейнеров

Объяснение

Системные вызовы

Программа в пользовательском пространстве не может сама читать файлы, открывать сокеты или создавать процессы. Она просит об этом ядро через системные вызовы (openat, read, write, connect, accept, futex). Если видеть, какие вызовы делает процесс и чем они заканчиваются, поведение программы становится понятным без исходников.

flowchart LR
    A["Приложение<br/>(user space)"] -- "openat(), read(), connect()" --> K["Ядро Linux<br/>(kernel space)"]
    K --> D["Диск, сеть, память"]
    S["strace / eBPF"] -. "наблюдает вызовы" .-> K

strace

Показывает системные вызовы процесса и их результат.

strace -f -o trace.txt ./app          # запустить и писать в файл, -f: следить за потоками и дочерними процессами
strace -p 1234                        # подключиться к работающему процессу
strace -e trace=openat,connect -p 1234  # только нужные вызовы
strace -c ./app                       # сводка: сколько вызовов и времени на каждый
strace -tt -T -p 1234                 # метки времени и длительность каждого вызова

Пример поиска причины «не находит файл»:

$ strace -e trace=openat cat /nonexistent
openat(AT_FDCWD, "/nonexistent", O_RDONLY) = -1 ENOENT (No such file or directory)

ENOENT означает «файла нет»; EACCES «нет прав»; ECONNREFUSED «порт никто не слушает»; ETIMEDOUT «нет ответа». Зависшую программу видно по последнему вызову: futex(...) ожидание блокировки, read(3, ...) ожидание данных из сокета 3, poll/epoll_wait ожидание событий.

Накладные расходы

strace заметно замедляет процесс, потому что перехватывает каждый вызов. На нагруженном продакшене используйте его кратко или берите eBPF-инструменты.

lsof и ss

lsof -p 1234                 # открытые файлы и сокеты процесса
lsof -i :8080                # кто держит порт 8080
lsof +L1                     # удалённые, но ещё открытые файлы (типичная причина «диск полон, а du показывает мало»)
ss -tlnp                     # слушающие TCP-порты и процессы
ss -tnp state established    # установленные соединения

Подробнее о сокетах: запрос по TCP руками.

perf: куда уходит процессор

perf top                          # живой список «горячих» функций в системе
perf record -g -p 1234 -- sleep 30   # снять профиль процесса за 30 секунд
perf report                       # посмотреть результат

С -g собираются стеки вызовов, по ним строят flame graph: ширина блока равна доле времени. Для .NET есть свои инструменты (dotnet-trace и dotnet-counters).

eBPF

eBPF позволяет безопасно запускать небольшие программы внутри ядра и собирать данные о событиях (системные вызовы, сеть, планировщик) с малыми накладными расходами и без остановки процесса.

# bpftrace: печатать, какие процессы открывают какие файлы
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'

Готовые утилиты из набора BCC и bpftrace-инструментов:

УтилитаЧто показывает
opensnoopкакие файлы открываются
execsnoopкакие процессы запускаются
tcpconnect, tcpacceptисходящие и входящие TCP-соединения
biolatencyраспределение задержек дисковых операций
runqlatзадержки планировщика процессора

Требуют root, достаточно новое ядро и установленных инструментов. Это основа современных средств наблюдаемости и безопасности (Cilium, Falco, Tetragon), см. eBPF в Kubernetes.

Внутри контейнеров

docker run --cap-add SYS_PTRACE ...          # разрешить strace внутри контейнера
kubectl debug -it pod/app --image=busybox --target=app   # эфемерный контейнер в поде
nsenter -t <pid-контейнера> -n ss -tlnp      # зайти в сетевое пространство имён контейнера с хоста

По умолчанию в контейнере нет strace; безопаснее подключаться с хоста или через эфемерный контейнер с нужными инструментами.

Примеры

Алгоритм «процесс завис»

flowchart TD
    A["Процесс завис или тормозит"] --> B["top / htop:<br/>грузит ли CPU?"]
    B -- "CPU занят" --> C["perf top / perf record:<br/>какие функции"]
    B -- "CPU свободен" --> D["strace -p PID:<br/>на каком вызове стоит?"]
    D -- "read/recv на сокете" --> E["ss, lsof -i:<br/>кто на другом конце?"]
    D -- "futex" --> F["блокировка: дамп потоков,<br/>dotnet-dump"]
    D -- "openat ENOENT" --> G["неверный путь или права"]

Почему диск «полон», хотя файлов мало

df -h /var                      # раздел заполнен
du -sh /var/* | sort -h | tail  # а файлов на такой объём нет
lsof +L1 | grep /var            # процесс держит удалённый файл (например, лог)
# решение: перезапустить процесс, чтобы он отпустил файл, или очистить его через /proc/PID/fd/N

Нюансы и подводные камни

  • Права. strace -p чужого процесса требует root или CAP_SYS_PTRACE; в части систем запрещено настройкой ptrace_scope.
  • Накладные расходы strace. Заметное замедление, не запускайте на горячем продакшене надолго.
  • Шум. Фильтруйте вызовы (-e trace=), иначе вывод не прочитать.
  • Стеки без символов. Для perf нужны символы отладки, для JIT-языков (.NET, Java) нужны дополнительные настройки.
  • Безопасность. Трассировка может показать пароли и токены из буферов (read, write): не публикуйте сырые дампы.
  • eBPF требует новых ядер и прав. На старых и сильно ограниченных системах недоступен.

Вопросы с ответами

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