Когда программа отказывается стартовать, молча падает или виснет, первое, что хочется узнать, — какие системные вызовы она выполняет и на каком шаге всё ломается. Именно для этого существуют strace и ltrace: первый перехватывает обращения процесса к ядру, второй показывает вызовы функций из разделяемых библиотек. Оба инструмента входят в набор дистрибутива, не требуют пересборки приложения и за пару минут дают ответ там, где просмотр логов и тяжёлый отладчик выглядят избыточно.

Как strace работает под капотом

strace построен на механизме ptrace: трассировщик присоединяется к целевому процессу, и ядро останавливает его перед каждым системным вызовом, передавая управление наблюдателю. Приложение замедляется в десятки и сотни раз — это цена наблюдаемости. В вывод попадают имена вызовов, аргументы и результаты: openat, read, futex, clone, connect. Стандартная запись выглядит так: сначала вызов с аргументами, затем результат после знака равенства; у неудачных вызовов там код ошибки с расшифровкой.

Доступ к трассировке контролирует политика Yama. Значение 1 в файле /proc/sys/kernel/yama/ptrace_scope разрешает отладку только собственных дочерних процессов, поэтому подключение к чужому сервису требует root либо повышения уровня. Проверить состояние и назначить его можно так:

cat /proc/sys/kernel/yama/ptrace_scope
sudo sysctl -w kernel.yama.ptrace_scope=1

На рабочем сервере держите уровень 1 и запускайте трассировку под root: это ограничение защищает от чужого чтения памяти вашего процесса.

Установка и первый запуск

sudo apt install strace ltrace
strace -o /tmp/ls.trace ls /etc
head -20 /tmp/ls.trace
strace -c ls /tmp

Флаг -c заменяет подробный вывод сводной таблицей: сколько раз вызов встретился, сколько завершилось ошибкой и сколько времени он занял. Колонка errors отдельно показывает неудачные попытки — по ней сразу видно, ищет ли программа несуществующие файлы или дёргает сеть вхолостую. Это первый шаг, когда непонятно, куда смотреть, — цифры сразу указывают на подозрительные вызовы вроде массовых openat или connect к адресу, который никто не открывал.

Фильтры: смотреть только нужное

Полная трасса даже короткой команды уходит в тысячи строк, поэтому класс вызовов ограничивают явно:

strace -f -e trace=%file ./myapp
strace -e trace=network curl -s https://example.com
strace -e trace=process,desc ./myapp

Шаблон %file собирает все операции с файлами, network — сокеты, desc — файловые дескрипторы, process — fork и execve, memory — управление памятью. Несколько классов перечисляются через запятую, отрицание задаётся префиксом %. Спутники вывода расширяют картину: -s 200 удлиняет строки аргументов, -yy подписывает дескрипторы типами (socket, pipe, anon_inode), а метки времени и длительность вызова задают опции -tt и -T. Длинную трассу удобно сохранять в файл и искать по нему:

strace -s 200 -tt -yy -T -e trace=desc ./myapp > /tmp/trace.log 2>&1
grep -n 'EAGAIN' /tmp/trace.log | head
wc -l /tmp/trace.log

Разбор отказа в запуске

Самый частый сценарий — бинарник падает сразу после запуска или жалуется, что файла нет, хотя он на месте. Трассировка выдаёт цепочку попыток поиска:

strace -f -e trace=file ./myapp 2>&1 | grep -E 'ENOENT|EACCES|EPERM'
ldconfig -p | grep libfoo

Код ENOENT на открытии библиотеки означает, что загрузчик искал её в кэше /etc/ld.so.cache и не нашёл: добавьте каталог в конфиг из /etc/ld.so.conf.d и выполните ldconfig. Код EACCES указывает на права — реальный файл нечитаем от имени запускающего пользователя, что часто встречается у сервисов под системной учётной записью. Порядок открытых путей в выводе подсказывает, какой именно каталог приложение считает штатным.

Ловим падение сервиса при старте

Подключиться к работающему демону легко, а поймать момент падения при запуске сложнее: процесс живёт доли секунды и исчезает вместе с выводом. Надёжный приём — временно завернуть исполняемый файл в strace через переопределение юнита. Команда systemctl edit откроет drop-in в каталоге /etc/systemd/system/myservice.service.d/:

sudo systemctl edit myservice
[Service]
ExecStart=
ExecStart=/usr/bin/strace -f -tt -T -o /tmp/svc.trace /usr/sbin/myservice --config /etc/myservice/config.yml
sudo systemctl restart myservice
tail -50 /tmp/svc.trace

Пустая строка ExecStart= обязательна: без неё systemd откажется заменять команду из основного юнита. После разбора выполните systemctl revert myservice и перезапустите сервис — в трассе останется точная последовательность вызовов до места падения вместе с кодами ошибок.

Диагностика зависшего сервиса

Когда сервис не отвечает, но процесс жив, пригодится подключение к уже запущенному процессу:

pid=$(systemctl show --property MainPID --value nginx)
sudo strace -p "$pid" -f -tt -T
sudo strace -c -p "$pid"

Частая картина: потоки сидят в futex_wait_queue_me — ждут блокировку, которую никто не отдаёт; epoll_wait — ждут сетевые события, что нормально для сервера; read на дескрипторе — ждут данные от внешнего сервиса. Если строк нет несколько секунд подряд, процесс вообще не делает вызовов: смотрите стек через perf или gdb, а не strace.

ltrace: разговор с библиотеками

ltrace работает на уровне разделяемых библиотек: он разрешает символы через загрузчик и печатает вызовы printf, malloc, strlen, даже если они идут из libc. Для динамически слинкованных программ это короткий путь к пониманию логики:

ltrace -C -S ./myapp 2>&1 | head -50
ltrace -e 'malloc+free+strlen' ./myapp
ltrace -p 4321

Флаг -C включает деманглинг C++-имён, -S добавляет системные вызовы в тот же поток, -e перечисляет нужные функции через плюс. Ограничения жёсткие: статически слинкованный или полностью очищенный от символов бинарник ltrace не увидит, а собственные библиотеки без отладочной информации покажет лишь как адреса. В таких случаях помогает eBPF-трассировка или собственный перехват через LD_PRELOAD.

Практический чек-лист

  1. Начните со strace -c на процесс: сводка экономит десятки строк.
  2. Сузьте вывод фильтром %file или network, чтобы убрать шум.
  3. Добавляйте -f, если приложение порождает дочерние процессы и воркеры.
  4. Читайте коды ошибок: ENOENT, EACCES, EAGAIN — это и есть ответы.
  5. Для библиотек переходите к ltrace, для статической сборки — к eBPF.
  6. Помните об оверхеде: тяжёлые продакшен-нагрузки в трассировку не берут.

Итог

strace и ltrace — набор микроскопов для процесса: один показывает границу с ядром, второй — разговор с библиотеками. Работайте в правильном порядке: сводная статистика, фильтр по классу вызовов, разбор кодов ошибок и только потом детальный вывод. С таким порядком диагностика «не запускается» и «висит» превращается в пятиминутную задачу, а не в часы перебора гипотез.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте