В помощь сисадмину: Что делает команда strace?

Отладка с использованием strace – трассировка системных вызовов

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

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

Команда strace представляет собой мощный инструмент, который показывает системные вызовы, выполняемые программой, работающей в пространстве пользователя. Команда может показать не только названия системных вызовов, но и аргументы вызова и возвращаемые значения в символической форме. При ошибочном завершении функции отображается и символическое значение ошибки (например, ENOMEM), и соответствующую строку (Out of memory). Команда strace имеет множество опциональных параметров, принимаемых через командную строку. Наиболее полезными из которых являются -t для отображения времени вызова функции, -T для отображения времени, потраченного на выполнение функции, -e для ограничения типов отображаемых вызовов, и -o для перенаправления вывода в файл. По умолчанию, утилита strace выполняет вывод информации в стандартный поток ошибок stderr.

Команда strace принимает информацию непосредственно из ядра. Это означает, что программа будет трассироваться независимо от того, была ли она скомпилирована с поддержкой отладочной информации (опция -o в gcc), или отладочная информация была удалена из файла после компиляции. Вы можете подключить strace-трассировку к уже запущенному процессу.

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

Например, следующий снимок экрана показывает несколько последних строк запуска команды strace ls /dev > /dev/scull0

[...]
open("/dev", O_RDONLY|O_NONBLOCK)     = 4
fcntl(4, F_SETFD, FD_CLOEXEC)         = 0
brk(0x8055000)                        = 0x8055000
lseek(4, 0, SEEK_CUR)                 = 0
getdents(4, /* 70 entries */, 3933)   = 1260
[...]
getdents(4, /* 0 entries */, 3933)    = 0
close(4)                              = 0
fstat(1, {st_mode=S_IFCHR|0664, st_rdev=makedev(253, 0), ...}) = 0
ioctl(1, TCGETS, 0xbffffa5c)          = -1 ENOTTY (Inappropriate ioctl
                                                     for device)
write(1, "MAKEDEV\natibm\naudio\naudio1\na"..., 4096) = 4000
write(1, "d2\nsdd3\nsdd4\nsdd5\nsdd6\nsdd7"..., 96) = 96
write(1, "4\nsde5\nsde6\nsde7\nsde8\nsde9\n"..., 3325) = 3325
close(1)                              = 0
_exit(0)                              = ?

Мы можем видеть, что первый вызов write() после того, как команда ls завершила обзор заданного каталога, выполнил запись 4000 байт данных. Т.к. требуется запись большего объема данных, то вызов write() повторяется. Вспомните, что наша реализация метода write() в scull позволяет записать не более одного кванта данных за одно обращение, поэтому, мы и наблюдаем многократный вызов write(). Когда весь объем данных записан, программа нормально завершается.

В следующем примере мы произведем чтение из драйвера scull, используя команду wc.

[...]
open("/dev/scull0", O_RDONLY)           = 4
fstat(4, {st_mode=S_IFCHR|0664, st_rdev=makedev(253, 0), ...}) = 0
read(4, "MAKEDEV\natibm\naudio\naudio1\na"..., 16384) = 4000
read(4, "d2\nsdd3\nsdd4\nsdd5\nsdd6\nsdd7"..., 16384) = 3421
read(4, "", 16384)                      = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(3, 7), ...}) = 0
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
write(1, "   7421 /dev/scull0\n", 20)   = 20
close(4)                                = 0
_exit(0)                                = ?

Как и ожидалось, метод read() может передать не более четырех тысяч байт за одно обращение, а общее количество данных соответствует количеству записанных байт из предыдущего примера. Интересно заметить, как реализованы повторные вызовы в этом примере по сравнению с предыдущей трассировкой. Команда wc оптимизирована для быстрого чтения в обход стандартной библиотеки, и пытается прочесть большее количество данных за один системный вызов. Вы можете видеть из строк /read/, что команда wc пытается прочесть 16КБт данных за одно обращение.

Знатоки Linux могут извлечь много полезной информации анализируя выход команды strace. Вы можете погасить вывод части символов, или ограничить вывод определенными методами (open, read, и пр.).

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

Все опции закрыты.

Комментарии закрыты.

Русскоязычные Шаблоны WordPress
%d такие блоггеры, как: