В операционной системе и используемых в ней программах иногда возникают ошибки, причину которых очень сложно понять, анализируя файлы журналов и сообщения об ошибках. Но и для таких ситуаций в Linux есть средства. За процессом работы любой из программ можно проследить, наблюдая системные вызовы, которые использует программа.
С помощью системных вызовов можно понять, к каким файлам обращается программа, какие сетевые порты она использует, какие ресурсы ей нужны, а также какие ошибки возвращает ей система. Это помогает разобраться в особенностях работы программы и лучше понять причину ошибки. За всё это отвечает команда strace Linux. В сегодняшней статье мы разберёмся, что она из себя представляет и как ею пользоваться.
Команда strace Linux
Как я уже сказал, команда strace показывает все системные вызовы программы, которые та отправляет к системе во время выполнения, а также их параметры и результат выполнения. Но при необходимости можно подключиться и к уже запущенному процессу. Перед тем, как перейти к практике, разберём опции утилиты и её синтаксис:
$ strace опции команда аргументы
В самом простом варианте strace запускает переданную команду с её аргументами и выводит в стандартный поток ошибок все системные вызовы команды. Давайте разберём опции утилиты, с помощью которых можно управлять её поведением:
- -i — выводить указатель на инструкцию во время выполнения системного вызова;
- -k — выводить стек вызовов для отслеживаемого процесса после каждого системного вызова;
- -o — выводить всю информацию о системных вызовах не в стандартный поток ошибок, а в файл;
- -q — не выводить сообщения о подключении о отключении от процесса;
- -qq — не выводить сообщения о завершении работы процесса;
- -r — выводить временную метку для каждого системного вызова;
- -s — указать максимальный размер выводимой строки, по умолчанию 32;
- -t — выводить время суток для каждого вызова;
- -tt — добавить микросекунды;
- -ttt — добавить микросекунды и количество секунд после начала эпохи Unix;
- -T — выводить длительность выполнения системного вызова;
- -x — выводить все не ASCI-строки в шестнадцатеричном виде;
- -xx — выводить все строки в шестнадцатеричном виде;
- -y — выводить пути для файловых дескрипторов;
- -yy — выводить информацию о протоколе для файловых дескрипторов;
- -c — подсчитывать количество ошибок, вызовов и время выполнения для каждого системного вызова;
- -O — добавить определённое количество микросекунд к счетчику времени для каждого вызова;
- -S — сортировать информацию выводимую при опции -c. Доступны поля time, calls, name и nothing. По умолчанию используется time;
- -w — суммировать время между началом и завершением системного вызова;
- -e — позволяет отфильтровать только нужные системные вызовы или события;
- -P — отслеживать только системные вызовы, которые касаются указанного пути;
- -v — позволяет выводить дополнительную информацию, такую как версии окружения, статистику и так далее;
- -b — если указанный системный вызов обнаружен, трассировка прекращается;
- -f — отслеживать также дочерние процессы, если они будут созданы;
- -ff — если задана опция -o, то для каждого дочернего процесса будет создан отдельный файл с именем имя_файла.pid.
- -I — позволяет блокировать реакцию на нажатия Ctrl+C и Ctrl+Z;
- -E — добавляет переменную окружения для запускаемой программы;
- -p — указывает pid процесса, к которому следует подключиться;
- -u — запустить программу, от имени указанного пользователя.
Вы знаете основные опции strace, но чтобы полноценно ею пользоваться, нужно ещё разобраться с системными вызовами, которые используются чаще всего. Мы не будем рассматривать все, а только основные. Многие из них вы уже и так знаете, потому что они называются так же, как и команды в терминале:
- fork — создание нового дочернего процесса;
- read — попытка читать из файлового дескриптора;
- write — попытка записи в файловый дескриптор;
- open — открыть файл для чтения или записи;
- close — закрыть файл после чтения или записи;
- chdir — изменить текущую директорию;
- execve — выполнить исполняемый файл;
- stat — получить информацию о файле;
- mknod — создать специальный файл, например, файл устройства или сокет;
А теперь разберём примеры strace Linux.
Примеры использования Strace
1. Запуск программы
Самый простой способ запуска утилиты — просто передать ей в параметрах имя команды или исполняемый файл программы, которую мы хотим исследовать. Например, uname:
strace uname
Как и ожидалось, мы видим список системных вызовов, которые делает утилита, чтобы узнать версию ядра. Синтаксис вывода такой:
имя_системного_вызова (параметр1, параметр2) = результат сообщение
Имя системного вызова указывает, какой именно вызов использовала программа. Для большинства вызовов характерно то, что им нужно передавать параметры, имена файлов, данные и так далее. Эти параметры передаются в скобках. Далее идет знак равенства и результат выполнения. Если всё прошло успешно, то здесь будет ноль или положительное число. Если же возвращается отрицательное значение, делаем вывод, что произошла ошибка. В таком случае выводится сообщение.
Например, в нашем выводе есть сообщения об ошибке:
Здесь результат выполнения -1 и сообщение говорит, что файл не найден. Но на работу программы это не влияет. Это проблема подключения сторонних библиотек и к этой утилите она не имеет отношения. А основная работа программы выполняется строчкой:
И здесь ядро вернуло положительный результат.
2. Подключение к запущенной программе
Если программа, которую нам надо отследить, уже запущена, то не обязательно её перезапускать с нашей утилитой. Можно подключиться к ней по ее идентификатору PID. Для тестирования этой возможности запустим утилиту dd, которая будет записывать нули из /dev/zero в файл file1:
dd if=/dev/zero of=~/file1
Теперь узнаем PID нашего процесса, поскольку он такой один, можно воспользоваться pidof, вы же можете использовать ps:
pidof dd
И осталось подключиться к нашему процессу:
sudo strace -p 31796
В выводе утилиты мы видим, что она читает данные из одного места с помощью вызова read и записывает в другое через write. Чтобы отсоединится от процесса, достаточно нажать Ctrl+C. Дальше рассмотрим примеры strace Linux для фильтрации данных.
3. Фильтрация системных вызовов
Утилита выводит слишком много данных, и, зачастую, большинство из них нас не интересуют. С помощью опции -e можно применять различные фильтры для более удобного поиска проблемы. Мы можем отобразить только вызовы stat, передав в опцию -e такой параметр trace=stat:
sudo strace -e trace=stat nautilus
Кроме непосредственно системных вызовов, в качестве параметра для trace можно передавать и такие значения:
- file — все системные вызовы, которые касаются файлов;
- process — управление процессами;
- network — сетевые системные вызовы;
- signal — системные вызовы, что касаются сигналов;
- ipc — системные вызовы IPC;
- desc — управление дескрипторами файлов;
- memory — работа с памятью программы.
4. Возвращение ошибки
Можно попросить strace вернуть программе ошибку по нужному системному вызову -e, но с параметром fault. Синтаксис конструкции такой:
fault=имя_вызова:error=тип_ошибки:when=количество
С именем вызова всё понятно, тип ошибки, номер ошибки, которую надо вернуть. А с количеством всё немного сложнее. Есть три варианта:
- цифра — вернуть ошибку только после указанного количества запросов;
- цифра+ — вернуть ошибку после указанного количества запросов и для всех последующих;
- цифра+шаг — вернуть ошибку для указанного количества и для последующих с указанным шагом.
Например, сообщим uname, что системного вызова uname не существует:
sudo strace -e fault=uname uname
В выводе видим, что система вернула программе нашу ошибку, а потом та с помощью вызова write говорит пользователю, что узнать версию и называние системы невозможно.
5. Фильтрация по пути
Если вас интересует не определённый вызов, а все операции с нужным файлом, то можно выполнить фильтрацию по нему с помощью опции -P. Например, меня интересует, действительно ли утилита lscpu обращается к файлу /proc/cpuinfo, чтобы узнать информацию о процессоре:
strace -P /proc/cpuinfo lscpu
И в результате мы видим, что действительно обращается. Она открывает его для чтения с помощью вызова openat.
6. Статистика системных вызовов
С помощью опции -с вы можете собрать статистику для системных вызовов, которые использует программа. Например, сделаем это для nautilus:
sudo strace -c nautilus
Во время работы утилита ничего выводить не будет. Результат будет рассчитан и выведен, когда вы завершите отладку. В выводе будут такие колонки:
- time — процент времени от общего времени выполнения системных вызовов;
- seconds — общее количество секунд, затраченное на выполнение системных вызовов этого типа;
- calls — количество обращений к вызову;
- errors — количество ошибок;
- syscall — имя системного вызова.
Если вы хотите получать эту информацию в режиме реального времени, используйте опцию -C.
7. Отслеживание времени выполнения
Чтобы отображать время выполнения каждого системного вызова, используйте опцию -t:
strace -t uname
Можно также отображать микросекунды:
strace -tt uname
Или отображать время в формате UNIX:
strace -ttt uname
Чтобы добавить время выполнения вызова, добавьте -T:
strace -ttt -T uname