Поиск по сайту:

Как отследить системные вызовы, выполненные процессом с помощью strace в Linux


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

В этом уроке вы узнаете:

  • Как установить strace
  • Использование strace для трассировки системных вызовов, выполненных процессом
  • Как фильтровать специфические системные звонки
  • Как подключиться к уже запущенному процессу
  • Как создать сводку системных звонков

Как отследить системные вызовы, выполненные процессом с помощью strace в Linux

Требования к программному обеспечению и используемые условные обозначения

Установка

Несмотря на то, что утилита strace не установлена по умолчанию, она доступна в официальных репозиториях всех основных дистрибутивов Linux; это означает, что мы можем очень легко установить ее с помощью нашего любимого менеджера пакетов.

Например, если мы работаем на Fedora (или любом другом дистрибутиве семейства Red Hat), мы должны использовать dnf:

sudo dnf install strace

Если нам удобнее использовать Debian или дистрибутивы на основе Debian, такие как Ubuntu или Linux Mint, мы можем использовать apt для достижения того же результата:

sudo apt install strace

Если мы предпочитаем Arch Linux, мы можем использовать pacman для установки приложения, которое доступно в дополнительном репозитории:

sudo pacman -S strace

Установив программное обеспечение, мы можем двигаться дальше и увидеть некоторые примеры его использования.

Знакомство с strace

Как мы уже говорили, strace — это инструмент, используемый для отслеживания системных вызовов, совершаемых запущенным процессом, и получаемых им сигналов. Системные вызовы являются фундаментальным интерфейсом между приложением и ядром Linux; Когда мы используем Strace, имена вызовов, выполненных процессом, вместе с их аргументами и возвращаемыми значениями отображаются в STDERR (стандартный дескриптор файла ошибок).

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

strace cp ~/.bashrc bashrc

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

  • Имя системного вызова
  • Аргументы, передаваемые системе, заводятся в круглых скобках
  • Возвращаемое значение системного вызова

Первый системный вызов, который мы видим в выводе, — execve. Этот вызов используется для выполнения программы с указанным массивом аргументов. Первый аргумент, принимаемый execv, — это путь к файлу, который мы хотим выполнить; второй — массив строк, представляющий аргументы, которые будут переданы программе (первый аргумент, по соглашению, является именем самой программы).

В нашем случае, как и ожидалось, вызывается двоичный файл /usr/bin/cp, а массив аргументов, передаваемых вызову: имя программы (cp), пути к источнику и назначению:

execve("/usr/bin/cp", ["cp", "/home/egdoc/.bashrc", "bashrc"], 0x7fff53d4e4c0 /* 46 vars */) = 0

Нотация /* 46 vars */ означает, что 46 переменных были унаследованы от вызывающего процесса (в функции execv окружение берется из переменной внешней среды). Наконец, у нас есть возвращаемое значение, которое в данном случае равно 0 (на самом деле семейство функций exec возвращает значение только в случае возникновения ошибки).

Фильтрация только определенных системных вызовов

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

strace -e read cp ~/.bashrc bashrc

Как и ожидалось, выдаются только вызовы чтения:

Вывод команды "strace -e read cp ~/.bashrc bashrc " Кстати, системный вызов read системный вызов принимает три аргумента: первый - это style="font-size: inherit; ">файловый дескриптор связанный с файлом, который следует прочитать; второй - the style="font-size: inherit;">buffer в который следует читать файл, и третий - это style="font-size: inherit;">количество байтов , который следует прочитать. При успешном выполнении функция возвращает количество байт read из файла, как мы можем наблюдать в выводе выше.

Присоединение strace к выполняющемуся процессу

До сих пор мы вызывали strace, передавая ему команду для выполнения и сохранения trace; что, если мы хотим отследить существующий и уже запущенный процесс? В этом случае мы должны вызвать strace с параметром -p (или --attach) и передать PID (Process Id) процесса, к которому мы хотим его присоединить.

Чтобы найти PID программы, среди прочих решений, можно воспользоваться утилитой pidof. Для примера мы прикрепим strace к работающему экземпляру gnome-terminal-server:

pidof gnome-terminal-server
121316

Команда pidof вернула 121316, который является PID gnome-terminal-server. Зная это, мы можем присоединить strace к процессу:

strace -p 121316

Приведенная выше команда изначально вернет что-то вроде:

Вывод команды "strace -p 121316" Вышеуказанный (усеченный) вывод будет обновляться "на лету" по мере выполнения системных вызовов. Чтобы "отсоединить" style="font-size: inherit; ">strace мы можем просто нажать Ctrl+C на клавиатуре; мы получим уведомление о the "detach ", но отслеживаемый процесс продолжит выполняться:

strace: Process 121316 detached

Сигналы трассировки

Благодаря strace мы также можем наблюдать, когда процесс получает сигнал и как он реагирует на него. Позвольте мне это продемонстрировать. Сначала мы запускаем в качестве top долго работающий процесс, который представляет собой монитор процесса:

top

Затем мы прикрепляем к нему strace после получения его PID, который в данном случае равен 44825:

strace -p 44825

На этом этапе strace начинает отслеживать не только системные вызовы, но и принимаемые им сигналы. Чтобы доказать это, мы отправляем SIGTERM на PID 44825:

kill 44825

Как и ожидалось, событие сообщается в выходных данных strace:

--- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=44888, si_uid=1000} ---

В приведенном выше выходном si_signo указан номер доставляемого сигнала (SIGTERM=15), si_code содержит код, определяющий причину сигнала (SI_USER=0): в данном случае сигнал был сгенерирован пользовательским процессом. Поля si_pid и si_uid сообщают, соответственно, PID процесса отправки и его UID.

Сохранение выходных данных strace в файл

Если мы используем опцию -o (сокращение от --ouput) при запуске strace, мы можем перенаправить его вывод в файл, передав путь в качестве аргумента, например:

strace -p 121316 -o strace_output
strace: Process 121316 attached

Файл strace_output будет создан, и в него будет записан вывод strace. Чтобы следить за обновлением файла, мы можем использовать tail: обычно эта команда считывает последние 10 строк файла и завершает работу, но если мы вызовем ее с опцией -f (сокращение от --follow), мы можем наблюдать, как добавляется новое содержимое:

tail -f strace_output

Печать сводки системных вызовов

Утилита strace поставляется с очень полезной функцией: возможностью генерировать сводку всех системных вызовов, выполненных указанным процессом. Если мы хотим создать такой отчет, все, что нам нужно сделать, это вызвать программу с опцией -c или --summary-only. Возьмем в качестве примера команду cp, которую мы использовали ранее:

strace -c cp ~/.bashrc bashrc

Приведенная выше команда сгенерирует этот отчет:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 25.71    0.000298           7        38        13 openat
 19.24    0.000223           4        51           mmap
 11.48    0.000133           4        28           close
  9.92    0.000115          57         2         1 newfstatat
  7.94    0.000092          10         9           mprotect
  6.99    0.000081           3        25           fstat
  2.85    0.000033           3        11           read
  2.76    0.000032          16         2           munmap
  2.50    0.000029          14         2           statfs
  1.90    0.000022          22         1           write
  1.55    0.000018           2         8           pread64
  1.38    0.000016           8         2         1 access
  1.04    0.000012           4         3           brk
  0.78    0.000009           4         2           rt_sigaction
  0.60    0.000007           7         1           futex
  0.52    0.000006           3         2         1 arch_prctl
  0.43    0.000005           5         1           rt_sigprocmask
  0.43    0.000005           5         1           set_tid_address
  0.43    0.000005           5         1           fadvise64
  0.43    0.000005           5         1           set_robust_list
  0.43    0.000005           5         1           prlimit64
  0.26    0.000003           3         1         1 stat
  0.26    0.000003           3         1         1 lseek
  0.17    0.000002           2         1           geteuid
  0.00    0.000000           0         1           execve
------ ----------- ----------- --------- --------- ----------------
100.00    0.001159           5       196        18 total

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

Выводы

В этом уроке мы узнали, как установить и использовать strace, полезную утилиту для отладки и в целом для отслеживания системных вызовов, выполняемых процессом. Мы рассмотрели, как организован вывод strace, как запустить программу и отслеживать системные вызовы, которые она выполняет, как прикрепить strace к уже запущенному процессу и как уведомляются сигналы, полученные процессом; наконец, мы увидели, как создать сводку всех вызовов, сделанных процессом. Здесь мы лишь поверхностно коснулись того, что мы можем сделать с помощью Strace: если вы хотите узнать о нем больше, советуйте, как всегда, прочитать руководство!

Статьи по данной тематике: