Как следует использовать strace?
Вопрос
Коллега однажды сказал мне, что последний вариант, когда в Linux ничего не удалось отладить, — это использовать след.
Я пытался изучить научную основу этого странного инструмента, но я не гуру системного администратора и особых результатов не добился.
Так,
- Что это такое и что оно делает?
- Как и в каких случаях его следует использовать?
- Как следует понимать и обрабатывать выходные данные?
Вкратце, простыми словами, как эта штука работает?
Решение
Обзор Стратегии
strace можно рассматривать как легкий отладчик.Он позволяет программисту/пользователю быстро узнать, как программа взаимодействует с ОС.Это достигается путем мониторинга системных вызовов и сигналов.
Использование
Подходит, когда у вас нет исходного кода или вы не хотите утруждаться его изучением.
Кроме того, это полезно для вашего собственного кода, если вы не хотите открывать GDB, а просто заинтересованы в понимании внешнего взаимодействия.
Хорошее небольшое введение
Буквально на днях я столкнулся с этим вступлением для использования strace: Привет, мир!
Другие советы
Проще говоря, strace отслеживает все системные вызовы программы вместе с их кодами возврата.Подумайте о таких вещах, как операции с файлами/сокетами и о многих других непонятных вещах.
Это наиболее полезно, если у вас есть некоторые практические знания C, поскольку здесь системные вызовы точнее будут означать стандартные вызовы библиотеки C.
Допустим, ваша программа /usr/local/bin/cough.Просто используйте:
strace /usr/local/bin/cough <any required argument for cough here>
или
strace -o <out_file> /usr/local/bin/cough <any required argument for cough here>
для записи в «out_file».
Весь вывод strace будет поступать на стандартный поток stderr (будьте осторожны, из-за его большого объема часто требуется перенаправление в файл).В простейших случаях ваша программа прервется с ошибкой, и вы сможете увидеть, где и где ее последнее взаимодействие с ОС, в выводе strace.
Дополнительная информация должна быть доступна по адресу:
man strace
strace перечисляет все системные вызовы выполняется процессом, к которому он применяется.Если вы не знаете, что означают системные вызовы, вы не сможете извлечь из них большую пользу.
Тем не менее, если ваша проблема связана с файлами, путями или значениями среды, запуск strace в проблемной программе и перенаправление вывода в файл, а затем поиск этого файла для строки пути/файла/окружения может помочь вам увидеть, что представляет собой ваша программа. на самом деле пытаясь сделать что-то отличное от того, чего вы от этого ожидали.
Strace выделяется как инструмент для исследования производственных систем, в которых вы не можете позволить себе запускать эти программы под отладчиком.В частности, мы использовали strace в следующих двух ситуациях:
- Программа foo, похоже, зашла в тупик и перестала отвечать на запросы.Это может быть целью для GDB;однако у нас не всегда был исходный код, а иногда мы имели дело со скриптовыми языками, которые было сложно запустить под отладчиком.В этом случае вы запускаете strace для уже работающей программы и получаете список выполняемых системных вызовов.Это особенно полезно, если вы исследуете клиент-серверное приложение или приложение, взаимодействующее с базой данных.
- Выясняем, почему программа работает медленно.В частности, мы только что перешли на новую распределенную файловую систему, и пропускная способность новой системы была очень низкой.Вы можете указать strace с опцией '-T', которая сообщит вам, сколько времени было потрачено на каждый системный вызов.Это помогло определить, почему файловая система замедляет работу.
Пример анализа с использованием strace см. в моем ответе на этот вопрос.
Я постоянно использую strace для устранения проблем с разрешениями.Техника выглядит следующим образом:
$ strace -e trace=open,stat,read,write gnome-calculator
Где gnome-calculator
— это команда, которую вы хотите запустить.
strace -tfp PID будет отслеживать системные вызовы процесса PID, поэтому мы можем отлаживать/отслеживать состояние нашего процесса/программы.
Strace можно использовать как инструмент отладки или как примитивный профилировщик.
Как отладчик, вы можете видеть, как вызывались, выполнялись заданные системные вызовы и что они возвращали.Это очень важно, поскольку позволяет увидеть не только то, что программа не удалась, но и ПОЧЕМУ программа дала сбой.Обычно это просто результат плохого кодирования, не учитывающего все возможные результаты программы.В других случаях это просто жестко закодированные пути к файлам.Без strace вы сможете догадаться, что, где и как пошло не так.С помощью strace вы получаете разбивку системного вызова, обычно просто просмотр возвращаемого значения говорит вам о многом.
Профилирование — еще одно применение.Вы можете использовать его для определения времени выполнения каждого системного вызова индивидуально или в совокупности.Хотя этого может быть недостаточно для решения ваших проблем, это, по крайней мере, значительно сузит список потенциальных подозреваемых.Если вы видите много пар fopen/close в одном файле, вы, вероятно, без необходимости открываете и закрываете файлы при каждом выполнении цикла вместо того, чтобы открывать и закрывать их вне цикла.
Ltrace — близкий родственник strace, тоже очень полезный.Вы должны научиться различать, где находится ваше узкое место.Если общее время выполнения составляет 8 секунд, а на системные вызовы вы тратите всего 0,05 секунды, то трассировка программы не принесет вам особой пользы, проблема в вашем коде, что обычно является логической проблемой, или программе действительно нужно бежать так долго.
Самая большая проблема с strace/ltrace — чтение их вывода.Если вы не знаете, как выполняются вызовы или, по крайней мере, имена системных вызовов/функций, вам будет сложно расшифровать их значение.Знание того, что возвращают функции, также может быть очень полезным, особенно для различных кодов ошибок.Хотя расшифровывать их сложно, иногда они действительно возвращают жемчужину знаний;однажды я увидел ситуацию, когда у меня закончились иноды, но не закончилось свободное место, поэтому все обычные утилиты не выдали мне никаких предупреждений, я просто не мог создать новый файл.Чтение кода ошибки из вывода strace указало мне правильное направление.
Strace — это инструмент, который сообщает вам, как ваше приложение взаимодействует с вашей операционной системой.
Он делает это, сообщая вам, какие системные вызовы ОС использует ваше приложение и с какими параметрами оно их вызывает.
Например, вы видите, какие файлы пытается открыть ваша программа, и определяете, будет ли вызов успешным.
С помощью этого инструмента вы можете устранить любые проблемы.Например, если приложение сообщает, что не может найти библиотеку, которую, как вы знаете, вы установили, вы strace сообщите вам, где приложение ищет этот файл.
И это лишь верхушка айсберга.
strace — хороший инструмент для изучения того, как ваша программа выполняет различные системные вызовы (запросы к ядру), а также сообщает о тех из них, которые потерпели неудачу, вместе со значением ошибки, связанной с этим сбоем.Не все сбои являются ошибками.Например, код, который пытается найти файл, может получить ошибку ENOENT (Нет такого файла или каталога), но это может быть приемлемым сценарием в логике кода.
Одним из хороших вариантов использования strace является отладка условий гонки во время создания временного файла.Например, программа, которая может создавать файлы, добавляя идентификатор процесса (PID) к некоторой заранее заданной строке, может столкнуться с проблемами в многопоточных сценариях.[PID+TID (идентификатор процесса + идентификатор потока) или лучший системный вызов, такой как mkstemp, исправит это].
Это также хорошо для отладки сбоев.Вы можете найти эта (моя) статья о strace и отладке сбоев полезный.
Мне понравились некоторые ответы, где это написано strace
проверяет, как вы взаимодействуете с операционной системой.
Это именно то, что мы можем видеть.Система звонит.Если вы сравните strace
и ltrace
разница более очевидна.
$>strace -c cd
Desktop Documents Downloads examples.desktop Music Pictures Public Templates Videos
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 7 read
0.00 0.000000 0 1 write
0.00 0.000000 0 11 close
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.000000 93 10 total
С другой стороны, есть ltrace
который отслеживает функции.
$>ltrace -c cd
Desktop Documents Downloads examples.desktop Music Pictures Public Templates Videos
% time seconds usecs/call calls function
------ ----------- ----------- --------- --------------------
15.52 0.004946 329 15 memcpy
13.34 0.004249 94 45 __ctype_get_mb_cur_max
12.87 0.004099 2049 2 fclose
12.12 0.003861 83 46 strlen
10.96 0.003491 109 32 __errno_location
10.37 0.003303 117 28 readdir
8.41 0.002679 133 20 strcoll
5.62 0.001791 111 16 __overflow
3.24 0.001032 114 9 fwrite_unlocked
1.26 0.000400 100 4 __freading
1.17 0.000372 41 9 getenv
0.70 0.000222 111 2 fflush
0.67 0.000214 107 2 __fpending
0.64 0.000203 101 2 fileno
0.62 0.000196 196 1 closedir
0.43 0.000138 138 1 setlocale
0.36 0.000114 114 1 _setjmp
0.31 0.000098 98 1 realloc
0.25 0.000080 80 1 bindtextdomain
0.21 0.000068 68 1 opendir
0.19 0.000062 62 1 strrchr
0.18 0.000056 56 1 isatty
0.16 0.000051 51 1 ioctl
0.15 0.000047 47 1 getopt_long
0.14 0.000045 45 1 textdomain
0.13 0.000042 42 1 __cxa_atexit
------ ----------- ----------- --------- --------------------
100.00 0.031859 244 total
Хотя я несколько раз проверял руководства, я не нашел происхождения названия. strace
но, скорее всего, это трассировка системного вызова, поскольку это очевидно.
Есть три важных замечания, о которых стоит сказать. strace
.
Примечание 1:Обе эти функции strace
и ltrace
используют системный вызов ptrace
.Так ptrace
системный вызов – это, по сути, то, как strace
работает.
Системный вызов PTRACE () предоставляет средство, с помощью которого один процесс («Трасер») может наблюдать и контролировать выполнение другого процесса («Трейсее»), а также изучить и изменить память и регистры Трейси.Он используется в первую очередь для реализации отладки точки останова и отслеживания системных вызовов.
Заметка 2:Существуют различные параметры, которые вы можете использовать с strace
, с strace
может быть очень многословным.мне нравится экспериментировать с -c
это похоже на краткое изложение вещей.На основе -c
вы можете выбрать один системный вызов, например -e trace=open
где вы увидите только этот звонок.Это может быть интересно, если вы проверяете, какие файлы будут открыты во время отслеживаемой команды.И, конечно же, вы можете использовать grep
для той же цели, но учтите, что вам нужно перенаправить вот так 2>&1 | grep etc
чтобы понять, что файлы конфигурации используются при вводе команды.
Заметка 3:Я считаю это очень важным замечанием.Вы не ограничены конкретной архитектурой. strace
поразит вас, поскольку он может отслеживать двоичные файлы различной архитектуры.
Минимальный работоспособный пример
Если концепция не ясна, есть более простой пример, который вы еще не видели, который объясняет ее.
В данном случае примером является автономная сборка Linux x86_64 (без libc) hello world:
привет.С
.text
.global _start
_start:
/* write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
mov $msg, %rsi /* buffer */
mov $len, %rdx /* buffer len */
syscall
/* exit */
mov $60, %rax /* exit status */
mov $0, %rdi /* syscall number */
syscall
msg:
.ascii "hello\n"
len = . - msg
Собираем и запускаем:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
Выводит ожидаемое:
hello
Теперь давайте воспользуемся strace в этом примере:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
Мы используем:
env -i ASDF=qwer
для управления переменными среды: https://unix.stackexchange.com/questions/48994/how-to-run-a-program-in-a-clean-environment-in-bash-s999 -v
для отображения более полной информации в журналах
strace.log
теперь содержит:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
В таком минимальном примере каждый символ вывода очевиден:
execve
линия:покажи покажиstrace
казненhello.out
, включая аргументы CLI и среду, как описано вman execve
write
линия:показывает системный вызов записи, который мы сделали.6
длина строки"hello\n"
.= 6
— это возвращаемое значение системного вызова, которое, как описано вman 2 write
количество записанных байт.exit
линия:показывает системный вызов выхода, который мы сделали.Возвращаемого значения нет, так как программа завершилась!
Более сложные примеры
Применение strace, конечно же, предназначено для того, чтобы увидеть, какие системные вызовы на самом деле выполняют сложные программы, чтобы помочь отладить/оптимизировать вашу программу.
Примечательно, что большинство системных вызовов, с которыми вы, вероятно, столкнетесь в Linux, имеют обертки glibc. многие из них из POSIX.
Внутри оболочки glibc используют встроенную ассемблерную сборку примерно так: Как вызвать системный вызов через sysenter во встроенной сборке?
Следующий пример, который вам следует изучить, — это POSIX. write
Привет, мир:
main.c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
Скомпилируйте и запустите:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
На этот раз вы увидите, что перед этим glibc выполняет несколько системных вызовов. main
чтобы настроить приятную среду для main.
Это связано с тем, что сейчас мы используем не отдельную программу, а более распространенную программу glibc, которая обеспечивает функциональность libc.
Затем, в каждом конце, strace.log
содержит:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Итак, мы приходим к выводу, что write
Функция POSIX, как ни удивительно, использует Linux write
системный вызов.
Мы также наблюдаем, что return 0
приводит к exit_group
позвонить вместо exit
.Ха, я не знал об этом!Вот почему strace
это так круто. man exit_group
затем объясняет:
Этот системный вызов эквивалентен выходу(2), за исключением того, что он завершает не только вызывающий поток, но и все потоки в группе потоков вызывающего процесса.
А вот еще пример, где я изучал, какой системный вызов dlopen
использует: https://unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
Протестировано в Ubuntu 16.04, GCC 6.4.0, ядре Linux 4.4.0.