Трассировка системных вызовов linux

Linux: ldd, starce, ltrace. Как работают утилиты. Трассировка вызовов.

Linux: ldd, starce, ltrace. Как работают утилиты. Трассировка вызовов.

libc — корневая библиотека языка CLinux мы ее видим по пути — /lib64/libc.so.6)
6 — номер версии библиотеки

По сути является ссылкой на библиотеку libc-2.17.so:
# ls -l /lib64/libc.so.6
lrwxrwxrwx. 1 root root 12 Jan 30 04:27 /lib64/libc.so.6 -> libc-2.17.so

Что будем использовать для примера?

ldd — выводит список разделяемых библиотек, используемых указанной утилитой или разделяемой библиотекой

Разделяемые библиотеки содержат реализации функций, необходимых для работы приложений, разделяемых библиотек
Разделяемые библиотеки имеют расширение — «.so» ( аналоги динамических библиотек «.dll» Windows )

Опция -v выводит информацию о версиях используемых разделяемых библиотек
Опция -u выводит список связанных, но не используемых разделяемых библиотек

stracesystem trace (трассировка системных вызовов)

ltracelibrary trace (трассировка библиотечных вызовов)

Посмотрим, что использует утилита date:
# which date | xargs ldd
linux-vdso.so.1 => (0x00007ffd3d3d8000)
libc.so.6 => /lib64/libc.so.6 (0x00007f931f97d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f931fd4b000)

Посмотрим, как отрабатывают системные вызовы при вызове утилиты date:
# which date | xargs strace
execve(«/bin/date», [«/bin/date»], [/* 22 vars */]) = 0
brk(NULL) = 0x20ab000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db8b0000
access(«/etc/ld.so.preload», R_OK) = -1 ENOENT (No such file or directory)
open(«/etc/ld.so.cache», O_RDONLY|O_CLOEXEC) = 3
fstat(3, ) = 0
mmap(NULL, 57295, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f71db8a2000
close(3) = 0
open(«/lib64/libc.so.6», O_RDONLY|O_CLOEXEC) = 3
read(3, «\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0». 832) = 832
fstat(3, ) = 0
mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f71db2c2000
mprotect(0x7f71db485000, 2097152, PROT_NONE) = 0
mmap(0x7f71db685000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f71db685000
mmap(0x7f71db68b000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f71db68b000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db8a1000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db89f000
arch_prctl(ARCH_SET_FS, 0x7f71db89f740) = 0
mprotect(0x7f71db685000, 16384, PROT_READ) = 0
mprotect(0x60d000, 4096, PROT_READ) = 0
mprotect(0x7f71db8b1000, 4096, PROT_READ) = 0
munmap(0x7f71db8a2000, 57295) = 0
brk(NULL) = 0x20ab000
brk(0x20cc000) = 0x20cc000
brk(NULL) = 0x20cc000
open(«/usr/lib/locale/locale-archive», O_RDONLY|O_CLOEXEC) = 3
fstat(3, ) = 0
mmap(NULL, 106075056, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f71d4d98000
close(3) = 0
open(«/etc/localtime», O_RDONLY|O_CLOEXEC) = 3
fstat(3, ) = 0
fstat(3, ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db8af000
read(3, «TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\4\0\0\0\0». 4096) = 3519
lseek(3, -2252, SEEK_CUR) = 1267
read(3, «TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\5\0\0\0\0». 4096) = 2252
close(3) = 0
munmap(0x7f71db8af000, 4096) = 0
fstat(1, ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db8af000
write(1, «Thu Mar 12 17:22:20 EDT 2020\n», 29Thu Mar 12 17:22:20 EDT 2020
) = 29
close(1) = 0
munmap(0x7f71db8af000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++

Кратко из вывода strace (набора вызовов к ядру операционной системы) мы видим:
execve — системный вызов, указывает, что для работы команды date необходимо загрузить утилиту по пути /bin/date
open — системные вызовы, которые загружают указанные библиотеки и файлы, в которых есть зависимость (вывод ldd)
read — системные вызовы, осуществляющме чтение оных
Так-же в выводе присутствует ряд действий
Пример: «open(«/etc/localtime», O_RDONLY|O_CLOEXEC) = 3″ — считывается файл с зоной
write — собственно вывод результата работы утилиты (вывод на экран)

Пример трассировки определенных вызовов:
# which date | xargs strace -fe open,read,write,close
open(«/etc/ld.so.cache», O_RDONLY|O_CLOEXEC) = 3
close(3) = 0
open(«/lib64/libc.so.6», O_RDONLY|O_CLOEXEC) = 3
read(3, «\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0». 832) = 832
close(3) = 0
open(«/usr/lib/locale/locale-archive», O_RDONLY|O_CLOEXEC) = 3
close(3) = 0
open(«/etc/localtime», O_RDONLY|O_CLOEXEC) = 3
read(3, «TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\4\0\0\0\0». 4096) = 3519
read(3, «TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\5\0\0\0\0». 4096) = 2252
close(3) = 0
write(1, «Thu Mar 12 17:59:02 EDT 2020\n», 29Thu Mar 12 17:59:02 EDT 2020
) = 29
close(1) = 0
close(2) = 0
+++ exited with 0 +++

Посмотрим, как отрабатывают библиотечные вызовы при вызове утилиты date:
# which date | xargs ltrace
__libc_start_main(0x401ab0, 1, 0x7ffc7e59ae08, 0x4096e0
strrchr(«/bin/date», ‘/’) = «/date»
setlocale(LC_ALL, «») = «en_US.UTF-8»
bindtextdomain(«coreutils», «/usr/share/locale») = «/usr/share/locale»
textdomain(«coreutils») = «coreutils»
__cxa_atexit(0x402c40, 0, 0, 0x736c6974756572) = 0
getopt_long(1, 0x7ffc7e59ae08, «d:f:I::r:Rs:u», 0x60d2a0, nil) = -1
nl_langinfo(0x2006c, 1, 0, 0) = 0x7f890e9a2025
clock_gettime(0, 0x7ffc7e59ac40, 0xc94440, 0) = 0
localtime(0x7ffc7e59abc0) = 0x7f8915103d20
strftime(» Thu», 1024, » %a», 0x7f8915103d20) = 4
fwrite(«Thu», 3, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
strftime(» Mar», 1024, » %b», 0x7f8915103d20) = 4
fwrite(«Mar», 3, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
fwrite(«12», 2, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
fwrite(«17», 2, 1, 0x7f89150ff400) = 1
fputc(‘:’, 0x7f89150ff400) = 58
fwrite(«23», 2, 1, 0x7f89150ff400) = 1
fputc(‘:’, 0x7f89150ff400) = 58
fputc(‘0’, 0x7f89150ff400) = 48
fwrite(«3», 1, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
strlen(«EDT») = 3
fwrite(«EDT», 3, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
fwrite(«2020», 4, 1, 0x7f89150ff400) = 1
__overflow(0x7f89150ff400, 10, 4, 0x30323032Thu Mar 12 17:23:03 EDT 2020
) = 10
exit(0
__fpending(0x7f89150ff400, 0, 64, 0x7f89150ffeb0) = 0
fileno(0x7f89150ff400) = 1
__freading(0x7f89150ff400, 0, 64, 0x7f89150ffeb0) = 0
__freading(0x7f89150ff400, 0, 2052, 0x7f89150ffeb0) = 0
fflush(0x7f89150ff400) = 0
fclose(0x7f89150ff400) = 0
__fpending(0x7f89150ff1c0, 0, 3328, 0xfbad000c) = 0
fileno(0x7f89150ff1c0) = 2
__freading(0x7f89150ff1c0, 0, 3328, 0xfbad000c) = 0
__freading(0x7f89150ff1c0, 0, 4, 0xfbad000c) = 0
fflush(0x7f89150ff1c0) = 0
fclose(0x7f89150ff1c0) = 0
+++ exited (status 0) +++

Читайте также:  Доступ windows linux samba

Из вывода ltrace мы видим:
clock_gettime — по сути функция, получающая время системы
strftime — форматирование в нужном формате
fwrite и fputc — функции вывода

Источник

22.4. Трассировка системных вызовов

22.4. Трассировка системных вызовов

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

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

Формат вызова команды strace следующий:

strace [-dffhiqrtttTvxx] [-acolumn] [-eexpr] . [-ofile] [-ppid] . [-sstrsize] [-uusername] [command | arg . ]]

Ключи программы перечислены в таблице 22.3.

Ключи командной строки strace Таблица 22.3

Ключ Назначение -с Подсчитывать время, затраченное на каждый вызов и обработку ошибок. В конце трассировки будет представлен подробный отчет -d Выводить отладочные сообщения самой программы strace на стандартный вывод ошибки -f Трассировать дочерние процессы, созданные уже трассируемыми процессами -ff Данная опция применятся только вместе с опцией -o имя_файла. Каждый трассируемый процесс будет записан в файл имя_файла.pid -F Следовать вызовам vfork(). Данную опцию нельзя использовать вместе с опцией -f -h Вывести справку -i Выводить указатель инструкции во время системного вызова -q «Тихий режим». Подавляет вывод некоторых сообщений -r Выводить относительную метку времени для каждого вызова -t Перед каждой строкой выводить текущее время -tt То же, что и -t, но будут выводиться также микросекунды -T Показывать время, потраченное на системный вызов (то есть разницу между временем запуска и временем завершения вызова). Для каждого вызова -v Получение дополнительной информации -V Вывести номер версии strace -X Выводить не-ASCII строки в шестнадцатеричном формате -XX Выводить все строки в шестнадцатеричном формате -a столбец Выровнять возвращаемые вызовами значения в указанном столбце (по умолчанию 40) -e выражение Позволяет задать отслеживаемые события. За более подробной информацией обратитесь к справочной системе -e trace=набор Определить набор отслеживаемых вызовов. Например, trace=open,close,read,write -e trace=file Будут отслеживаться только вызовы для работы с файлами (open, stat, chmod, unlink и т.д.) -e trace=process Отслеживаются вызовы для работы с процессами (fork, exec, wait и др.) -e trace=network Отслеживаются сетевые вызовы -e trace=signal Отслеживаются вызовы для работы с сигналами -e trace=ipc Отслеживаются IPC-вызовы -e abbrev=набор Сокращает вывод каждого члена структуры. Например, abbrev=all или abbrev=none -e verbose=набор Различать структуры различных системных вызовов, по умолчанию verbose=all -e raw=set Выводит не декодированные значения аргументов системных вызовов. Данный аргумент полезен, если вы не доверяете декодированию или хотите знать точное числовое представление аргумента -e signal=набор Определяет набор трассируемых сигналов. По умолчанию signal=all. Вы можете использовать восклицательный знак для отрицания, например, signal=!SIGIO означает, что сигнал SIGIO не будет трассирован -e read=набор Выполнять полный шестнадцатиричный и ASCII-дамп всех прочитанных вызовом read() данных. Например, чтобы видеть все данные, поступающие через дескрипторы 2 и 7, введите read=2,7 -e write=набор То же, что и -e read, но только для записи -o имя_файла Перенаправить вывод программы в указанный файл. Данный файл будет полезен для дальнейшего анализа трассировки -p pid Присоединиться к процессу с PID=pid и начать трассировку -s размер Установить максимальный размер строки (по умолчанию 32). Имена файлов не рассматриваются как строки, поэтому всегда будут напечатаны полностью -S критерий Сортирует вывод гистограммы, которая выводится опцией -с, по заданному критерию: time (время), calls (вызовы), name (имя) и nothing (без сортировки) -u имя_пользователя Запустить программу от имени указанного пользователя. Эта опция будет полезной, если вы, зарегистрировавшись как root, будете проверять корректность работы программы, если бы она была запущена под другим пользователем

Вы даже не можете себе представить, какие системные вызовы использует такая маленькая программка:

Источник

Команда strace в Linux

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

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

Читайте также:  Ошибка 100060 mac os

Команда 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:

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

имя_системного_вызова ( параметр1 , параметр2 ) = результат сообщение

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

Читайте также:  Windows запуск службы с правами пользователя

Например, в нашем выводе есть сообщения об ошибке:

openat(AT_FDCWD, «/usr/local/cuda-6.5/lib64/tls/x86_64/libc.so.6», O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)

Здесь результат выполнения -1 и сообщение говорит, что файл не найден. Но на работу программы это не влияет. Это проблема подключения сторонних библиотек и к этой утилите она не имеет отношения. А основная работа программы выполняется строчкой:

И здесь ядро вернуло положительный результат.

2. Подключение к запущенной программе

Если программа, которую нам надо отследить, уже запущена, то не обязательно её перезапускать с нашей утилитой. Можно подключиться к ней по ее идентификатору PID. Для тестирования этой возможности запустим утилиту dd, которая будет записывать нули из /dev/zero в файл file1:

dd if=/dev/zero of=

Теперь узнаем PID нашего процесса, поскольку он такой один, можно воспользоваться pidof, вы же можете использовать ps:

И осталось подключиться к нашему процессу:

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 -tt uname

Или отображать время в формате UNIX:

strace -ttt uname

Чтобы добавить время выполнения вызова, добавьте —T:

strace -ttt -T uname

Выводы

В этой статье была рассмотрена команда strace Linux. Как видите, это очень удобный инструмент, который поможет решить множество проблем в вашей системе или на сервере. Мы рассмотрели только основные возможности утилиты, но этого будет вполне достаточно, чтобы начать работать с ней, а более подробную информацию можно найти в официальном руководстве.

Источник

Оцените статью