Бывают моменты, когда программа работает неправильно, а журналы молчат. Исходников под рукой нет, документация скудна, разработчик давно сменил работу. Классическая ситуация для любого, кто хоть раз поддерживал корпоративное ПО. В такой момент привычные инструменты вроде gdb или профилировщиков бессильны. Зато есть утилита, которая не требует ни исходников, ни пересборки, ни специальных прав. Она просто подключается к процессу и показывает абсолютно всё, что он говорит ядру. Имя этой утилиты - strace.

Зачем системному администратору нужно уметь работать с strace и где этот инструмент выручает лучше всего

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

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

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

Синтаксис утилиты минималистичен до предела.

strace [OPTIONS] command

Официальное описание из man-страницы раскрывает возможности инструмента подробнее.

       In  the simplest case strace runs the specified command until it exits.
       It intercepts and records the  system  calls  which  are  called  by  a
       process  and  the signals which are received by a process.  The name of
       each system call, its arguments and its return  value  are  printed  on
       standard error or to the file specified with the -o option.

       strace is a useful diagnostic, instructional, and debugging tool.  Sys?
       tem administrators, diagnosticians and trouble-shooters  will  find  it
       invaluable  for  solving problems with programs for which the source is
       not readily available since they do not need to be recompiled in  order
       to trace them.  Students, hackers and the overly-curious will find that
       a great deal can be learned about a system  and  its  system  calls  by
       tracing  even  ordinary programs.  And programmers will find that since
       system calls and signals are events  that  happen  at  the  user/kernel
       interface,  a close examination of this boundary is very useful for bug
       isolation, sanity checking and attempting to capture race conditions.

Формулировка из документации точна и ёмка. Утилита полезна трём категориям пользователей. Системным администраторам - для решения проблем с программами, исходники которых недоступны. Студентам и любознательным - для изучения устройства операционной системы через наблюдение за обычными программами. Разработчикам - для изоляции ошибок, проверки корректности и поиска состояний гонки.

Установка strace на дистрибутивах Debian и Ubuntu и первая команда для знакомства с утилитой

Особенность strace в том, что далеко не во всех дистрибутивах она входит в базовую установку. Это логично - обычному пользователю утилита не требуется, а для разработчика и администратора ставится тривиально.

sudo apt-get install strace

На системах из семейства Red Hat команда будет слегка другой - sudo dnf install strace или sudo yum install strace для совсем старых версий. В Arch Linux - sudo pacman -S strace. Во всех случаях это один пакет размером в несколько сотен килобайт без объёмных зависимостей.

Базовое использование предельно простое. Достаточно написать strace и после него любую команду, работу которой хочется проанализировать.

strace ls

Вывод даже для такой простой команды как ls оказывается внушительным. На экран сыпятся десятки строк с системными вызовами - и это только для получения списка файлов в текущей директории. Обычный пользователь такого объёма никогда не видит, поскольку все эти детали скрыты за слоями абстракций. Strace срывает покровы и показывает реальную картину работы программы.

Первая реакция начинающего - некоторое ошеломление от количества информации. Это нормально. Понимание приходит с практикой, а пока важно усвоить общий принцип. Каждая строка вывода - один системный вызов со всеми подробностями.

Структура вывода strace и способы читать потоки информации о системных вызовах

Разбор формата вывода - ключевой навык для продуктивной работы с утилитой. Без понимания структуры строк любая попытка осмысленного анализа превращается в бесполезное чтение иероглифов. К счастью, формат простой и последовательный.

Man-страница объясняет его так.

       Each line in the trace contains the system call name, followed  by  its
       arguments  in parentheses and its return value.  An example from strac?
       ing the command "cat /dev/null" is:

           open("/dev/null", O_RDONLY) = 3

       Errors (typically a return value of -1) have the errno symbol and error
       string appended.

           open("/foo/bar", O_RDONLY) = -1 ENOENT (No such file or directory)

       Signals are printed as signal symbol and decoded siginfo structure.  An
       excerpt from stracing and interrupting the command "sleep 666" is:

           sigsuspend([] <unfinished ...>
           --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=...} ---
           +++ killed by SIGINT +++

Структура каждой строки состоит из нескольких элементов. Сначала идёт имя системного вызова - open, read, write, close, mmap и десятки других. Затем в круглых скобках перечисляются аргументы, которые были переданы вызову. После знака равенства следует возвращаемое значение. Для успешных вызовов это обычно положительное число - номер файлового дескриптора, количество прочитанных байтов, идентификатор процесса. Для неудачных - минус единица с указанием конкретного кода ошибки.

Первый пример из документации показывает успешное открытие устройства /dev/null в режиме только для чтения. Ядро вернуло дескриптор номер 3. Номера 0, 1 и 2 зарезервированы за стандартными потоками - ввода, вывода и ошибок. Поэтому новые дескрипторы начинаются с тройки.

Второй пример демонстрирует ошибку. Попытка открыть несуществующий файл /foo/bar вернула -1 с кодом ENOENT. Именно такие строки представляют первостепенный интерес при отладке. Когда программа ведёт себя странно, поиск строк с -1 в выводе strace часто сразу указывает на причину.

Третий блок показывает обработку сигналов. Если программа получает сигнал во время выполнения, это тоже отражается в выводе. Линии с минусами и плюсами обозначают получение и обработку сигналов соответственно. Такое подробное логирование делает strace незаменимой при отладке проблем с многопоточными приложениями, где гонка за ресурсы и неожиданные прерывания могут приводить к непредсказуемому поведению.

Режим с указателем инструкции через параметр -i для низкоуровневой отладки

Иногда одних только системных вызовов недостаточно. Нужно понять, из какого именно места программы был сделан тот или иной вызов. Для этого существует параметр -i, который добавляет в вывод значение указателя инструкции на момент каждого системного вызова.

strace -i ls

В результирующем выводе в начале каждой строки появляется адрес в квадратных скобках - это тот самый указатель инструкции. Шестнадцатеричное число показывает позицию в коде программы, откуда был вызван системный вызов. Если у администратора есть доступ к символам отладки через objdump или addr2line, по этому адресу можно найти конкретную функцию или даже строку исходного кода.

Практическое применение режима -i встречается в сложной отладке. Когда из сотен одинаковых open или read нужно выделить конкретный проблемный вызов, адрес в коде становится единственной зацепкой. В обычных ситуациях такая детализация избыточна и только засоряет вывод, но в специфических случаях оказывается незаменимой.

Относительные метки времени через параметр -r для анализа задержек между вызовами

Временные характеристики системных вызовов часто играют решающую роль в диагностике производительности. Параметр -r добавляет относительную метку времени, показывающую разницу между моментами начала последовательных системных вызовов.

strace -r ls

В выводе появляется колонка с временными интервалами. Каждое число показывает, сколько секунд прошло с момента предыдущего системного вызова. Для быстрых операций значения измеряются в микросекундах, для медленных могут достигать секунд и больше.

Анализ таких данных особенно полезен для поиска узких мест. Если между двумя вызовами виден подозрительный разрыв в сотни миллисекунд или секунды, значит, что-то тормозит именно в этой точке. Типичные причины - сетевые задержки, ожидание блокировок, медленный диск, системные исключения. Без временных меток такие проблемы были бы невидимы - все строки сливаются в единый поток без ощущения временных масштабов.

Абсолютное время через параметры -t -tt и -ttt с разной детализацией до миллисекунд

Относительное время удобно для измерения интервалов, но не даёт ответа на вопрос "когда именно это произошло". Для абсолютных меток времени существует отдельный параметр -t.

strace -t ls

Каждая строка вывода теперь начинается со времени в формате часы:минуты:секунды. Такая разметка удобна для сопоставления событий с логами других программ. Если приложение записало ошибку в лог в 15:23:47, поиск соответствующих системных вызовов в выводе strace по времени даёт полную картину происходящего.

Для более точной временной привязки есть два расширенных варианта.

-tt         
If given twice, the time printed will include the microseconds.

-ttt        
If given thrice, the  time  printed  will  include  the microseconds and the leading portion will 
be printed as the number of seconds since the epoch.

Двойное t добавляет к времени микросекунды, что важно при анализе очень быстрых операций. На современных серверах обычные системные вызовы укладываются в десятки микросекунд, и без такой точности отличить быстрые от медленных невозможно.

Тройное t превращает временную метку в формат Unix-времени - количество секунд с 1 января 1970 года. Такой формат менее читаем глазами, но идеален для программной обработки. Логи в Unix-формате легко фильтровать, сортировать и сопоставлять скриптами без необходимости парсить человекочитаемые строки.

Измерение длительности системных вызовов через параметр -T для поиска медленных операций

Отдельная задача - понять, сколько времени занимает каждый конкретный системный вызов. Параметр -T добавляет в конец каждой строки продолжительность вызова.

strace -T ls

В конце строк появляются значения в угловых скобках, обозначающие время выполнения в секундах. Для обычных быстрых вызовов это будут числа вроде 0.000012 - двенадцать микросекунд. Для медленных операций вроде открытия файла на сетевом хранилище или выполнения сложной проверки прав значения могут быть на порядки больше.

Анализ таких данных - ключевой инструмент для оптимизации производительности. Типичный сценарий выглядит так. Администратор замечает, что приложение отвечает медленнее обычного. Strace с параметром -T показывает, что большую часть времени программа проводит в ожидании ответа от одного конкретного сетевого вызова. Дальше уже дело техники - проверить состояние сети, проверить удалённый сервис, возможно, заменить синхронный вызов на асинхронный.

Стоит упомянуть типичные значения времени для разных категорий вызовов. Операции с памятью вроде mmap или brk занимают единицы микросекунд. Чтение из кэша файловой системы - десятки микросекунд. Реальный доступ к диску - миллисекунды. Сетевые операции - от миллисекунд до секунд в зависимости от удалённости сервера и качества соединения.

Сводная статистика через параметр -c для быстрого общего представления о программе

Когда конкретных деталей не нужно, а нужна общая картина - сколько каких вызовов сделала программа и сколько времени они заняли - на помощь приходит параметр -c. Он переводит strace в режим сводки.

strace -c ls

Вывод полностью меняется. Вместо потока отдельных строк появляется аккуратная таблица со статистикой.

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 93.66    0.000133           5        28           write
  6.34    0.000009           1        11           close
  0.00    0.000000           0         7           read
  0.00    0.000000           0        10           fstat
  0.00    0.000000           0        17           mmap
  0.00    0.000000           0        12           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         2           ioctl
  0.00    0.000000           0         8         8 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         2           getdents
  0.00    0.000000           0         2         2 statfs
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         9           openat
  0.00    0.000000           0         1           set_robust_list
  0.00    0.000000           0         1           prlimit64
------ ----------- ----------- --------- --------- ----------------
100.00    0.000142                   120        10 total

Разбор колонок даёт много полезной информации. Процент времени показывает, какая доля общего времени ушла на каждый тип вызовов. Секунды - абсолютное время, проведённое в каждом вызове суммарно. Микросекунды на вызов - среднее время одной операции. Число вызовов говорит о частоте использования. Колонка errors отмечает количество неудачных вызовов.

Из приведённой сводки видна типичная картина для ls. Больше 93 процентов времени ушло на write - команда выводит результаты на экран, и это оказалось самой затратной операцией. Close забирает оставшиеся 6 процентов. Восемь вызовов access завершились ошибкой - программа проверяла доступность каких-то файлов, которых не оказалось. Такие детали дают моментальное представление о характере работы программы.

Полный набор практических приёмов работы с strace в реальных задачах

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

  1. Подключение к уже запущенному процессу через параметр -p с указанием PID позволяет анализировать работу программ без их перезапуска
  2. Сохранение вывода в файл через параметр -o освобождает терминал и даёт возможность последующего анализа большими объёмами данных
  3. Фильтрация системных вызовов через параметр -e trace= позволяет отсекать шум и смотреть только на интересующие категории вроде network или file
  4. Трассировка дочерних процессов через параметр -f включает отслеживание форков и потоков, без чего многие современные приложения остаются частично невидимыми
  5. Отображение строковых аргументов полностью через параметр -s с указанием длины помогает видеть реальное содержимое данных вместо обрезанных версий
  6. Подсветка конкретных ошибок через параметр -Z позволяет быстро находить проблемные вызовы среди массы успешных
  7. Ограничение длительности трассировки через таймеры скриптов полезно для программ, которые должны работать длительное время

Подключение к живому процессу заслуживает отдельного упоминания. Типичная ситуация - на сервере висит процесс, который чем-то занят и не отвечает на запросы. Перезапуск приложения нежелателен, поскольку потеряются данные в памяти. В таком случае strace -p PID моментально показывает, что именно делает процесс прямо сейчас. Часто выясняется, что программа бесконечно ждёт ответа от недоступного сервиса или заблокирована на чтении пустого сокета.

Фильтрация системных вызовов - критически важный навык при работе с шумными приложениями. Современные программы генерируют тысячи системных вызовов в секунду, и попытка проанализировать их все вручную бессмысленна. Конструкции вроде strace -e trace=network для отслеживания только сетевых операций или strace -e trace=file для работы с файловой системой отсеивают ненужное и оставляют суть.

Strace - один из тех инструментов, глубокое освоение которых окупается многократно в самых неожиданных ситуациях. Ценность утилиты раскрывается не сразу. Поначалу кажется, что все эти open, read и write - лишь техническая мелочь, не заслуживающая внимания. Но рано или поздно наступает момент, когда стандартных средств недостаточно, логов мало, а проблема требует решения. Именно в этот момент знакомство со strace отличает грамотного инженера от растерянного пользователя.