Как следует использовать strace?
Однажды коллега сказал мне, что последний вариант, когда все не удалось отладить Linux, заключалось в использовании strace.
Я пытался научиться науке за этим странным инструментом, но я не являюсь администратором системы, и я действительно не получал результатов.
Итак,
- Что это такое и что он делает?
- Как и в каких случаях он должен использоваться?
- Как следует понимать и обрабатывать вывод?
Вкратце, простыми словами, как это работает?
Ответы
Ответ 1
Обзор Strace
strace можно рассматривать как легкий отладчик. Это позволяет программисту/пользователю быстро узнать, как программа взаимодействует с ОС. Это осуществляется путем мониторинга системных вызовов и сигналов.
Пользы
Подходит для тех случаев, когда у вас нет исходного кода или вы не хотите, чтобы его действительно изучали.
Также полезно для вашего собственного кода, если вы не хотите открывать GDB, а просто заинтересованы в понимании внешнего взаимодействия.
Хорошее небольшое введение
Я наткнулся на это вступление, чтобы использовать его только на днях: strace hello world
Ответ 2
Простыми словами, 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
Ответ 3
strace перечисляет все системные вызовы, выполняемые процессом. Если вы не знаете, что означает системные вызовы, вы не сможете получить много миль от него.
Тем не менее, если ваша проблема связана с файлами или путями или значениями среды, запуская strace в проблемной программе и перенаправляя вывод в файл, а затем grepping этот файл для строки path/file/env может помочь вам понять, что ваша программа на самом деле, в отличие от того, что вы ожидали от него.
Ответ 4
Strace выделяется как инструмент для исследования производственных систем, где вы не можете позволить себе запускать эти программы под отладчиком. В частности, мы использовали strace в следующих двух ситуациях:
- Программа foo, похоже, находится в тупике и стала невосприимчивой. Это может быть целью для gdb; однако мы не всегда имели исходный код или иногда имели дело со скриптовыми языками, которые не были прямолинейными для запуска под отладчиком. В этом случае вы запускаете strace на уже запущенной программе, и вы получите список системных вызовов. Это особенно полезно, если вы изучаете клиент/серверное приложение или приложение, которое взаимодействует с базой данных
- Исследование, почему программа работает медленно. В частности, мы только что перешли к новой распределенной файловой системе, и новая пропускная способность системы была очень медленной. Вы можете указать strace с опцией '-T', которая сообщит вам, сколько времени было потрачено на каждый системный вызов. Это помогло определить, почему файловая система заставляла вещи замедляться.
Для примера анализа использования strace см. мой ответ на этот вопрос.
Ответ 5
Я использую strace все время для отладки разрешений. Техника такова:
$ strace -e trace=open,stat,read,write gnome-calculator
Где gnome-calculator
- это команда, которую вы хотите запустить.
Ответ 6
strace -tfp PID будет отслеживать вызовы системы PID-процессов, таким образом мы можем отлаживать/контролировать состояние процесса/программы.
Ответ 7
Strace можно использовать как инструмент отладки или как примитивный профилировщик.
Как отладчик, вы можете видеть, как данные системные вызовы вызывались, выполнялись и что они возвращали. Это очень важно, так как позволяет увидеть не только то, что программа завершилась неудачей, но и ПОЧЕМУ программа потерпела неудачу. Обычно это просто результат паршивого кодирования, не улавливающего все возможные результаты программы. В других случаях это просто жестко заданные пути к файлам. Без ограничений вы можете догадаться, что пошло не так, где и как. С помощью strace вы получаете разбивку системного вызова, обычно просто просмотр возвращаемого значения говорит вам о многом.
Профилирование - это еще одно применение. Вы можете использовать его для определения времени выполнения каждого системного вызова по отдельности или в виде совокупности. Хотя этого может быть недостаточно для решения ваших проблем, по крайней мере, это значительно сузит список потенциальных подозреваемых. Если вы видите много пар fopen/close в одном файле, вы, вероятно, без необходимости открываете и закрываете файлы при каждом выполнении цикла вместо того, чтобы открывать и закрывать его вне цикла.
Ltrace является близким родственником, также очень полезным. Вы должны научиться различать, где находится ваше узкое место. Если общее выполнение составляет 8 секунд, и вы тратите только 0,05 сек на системные вызовы, то использование программы не принесет вам большой пользы, проблема в вашем коде, что обычно является проблемой логики, или программе действительно нужны чтобы так долго бежать.
Самая большая проблема с strace/ltrace - это чтение их вывода. Если вы не знаете, как выполняются вызовы или хотя бы имена системных вызовов/функций, будет сложно расшифровать их значение. Знание того, что возвращают функции, также может быть очень полезным, особенно для разных кодов ошибок. Хотя расшифровка - это боль, иногда они действительно возвращают жемчужину знаний; как только я увидел ситуацию, когда у меня закончились inode, но не было свободного места, таким образом, все обычные утилиты не дали мне никакого предупреждения, я просто не смог создать новый файл. Чтение кода ошибки из вывода strace указало мне правильное направление.
Ответ 8
Strace - это инструмент, который сообщает вам, как ваше приложение взаимодействует с вашей операционной системой.
Он делает это, сообщая вам, какие системы ОС вызывают использование вашего приложения и с какими параметрами он называет их.
Так, например, вы видите, какие файлы пытается открыть ваша программа, и погода успешно завершается.
Вы можете отлаживать всевозможные проблемы с помощью этого инструмента. Например, если приложение говорит, что не может найти библиотеку, которую вы знаете, вы установили, что strace сообщит вам, где приложение ищет этот файл.
И это всего лишь верхушка айсберга.
Ответ 9
strace - хороший инструмент для изучения того, как ваша программа выполняет различные системные вызовы (запросы к ядру), а также сообщает те, которые не удалось вместе со значением ошибки, связанным с этим сбоем. Не все ошибки - это ошибки. Например, код, который пытается найти файл, может получить ошибку ENOENT (такой файл или каталог), но это может быть приемлемым сценарием в логике кода.
Одним из хороших вариантов использования strace является отладка условий гонки во время временного создания файла. Например, программа, которая может создавать файлы путем добавления идентификатора процесса (PID) к некоторой предварительно определенной строке, может столкнуться с проблемами в многопоточных сценариях. [PID + TID (идентификатор процесса + идентификатор потока) или лучший системный вызов, такой как mkstemp, исправит это].
Это также хорошо подходит для отладки сбоев. Вы можете найти эту (мою) статью о strace и отладочных авариях.
Ответ 10
Мне понравились некоторые ответы, в которых говорится о проверках 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
но, скорее всего, система -c полностью прослеживается, поскольку это очевидно.
Есть три большие заметки о strace
.
Примечание 1: обе эти функции strace
и ltrace
используют системный вызов ptrace
. Таким образом, системный вызов ptrace
эффективно работает как strace
.
Системный вызов ptrace() предоставляет средство, с помощью которого один процесс ("трассировщик") может наблюдать и контролировать выполнение другого процесса ("трассировка"), а также проверять и изменять память и регистры трассировки. Он в основном используется для реализации отладки точек останова и трассировки системных вызовов.
Примечание 2: есть разные параметры, которые вы можете использовать с strace
, так как strace
может быть очень многословным. Мне нравится экспериментировать с -c
который похож на краткое изложение вещей. На основе -c
вы можете выбрать одну систему -c, например, -e trace=open
где вы увидите только этот вызов. Это может быть интересно, если вы проверяете, какие файлы будут открываться во время отслеживания команды. И, конечно, вы можете использовать grep
для той же цели, но учтите, что вам нужно перенаправить, как это 2>&1 | grep etc
2>&1 | grep etc
чтобы понять, что на файлы конфигурации ссылаются, когда была выполнена команда.
Примечание 3: я нахожу это очень важное примечание. Вы не ограничены конкретной архитектурой. strace
вас, поскольку он может отслеживать двоичные файлы различных архитектур. ![enter image description here]()
Ответ 11
Минимальный работоспособный пример
Если концепция не ясна, есть более простой пример, который вы не видели, который объясняет это.
В данном случае этот пример - свободно распространяемая сборка Linux x86_64 (без libc):
hello.S
.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
GitHub вверх по течению.
Собрать и запустить:
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
Мы используем:
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 и окружение, как hello.out
в man execve
-
строка write
: показывает системный вызов write, который мы сделали. 6
- длина строки "hello\n"
.
= 6
- это возвращаемое значение системного вызова, которое, как задокументировано в man 2 write
является количеством записанных байтов.
-
строка exit
: показывает системный вызов выхода, который мы сделали. Возвращаемого значения нет, так как программа вышла!
Более сложные примеры
Применение strace, конечно, позволяет увидеть, какие системные вызовы на самом деле выполняют сложные программы, чтобы помочь отладить/оптимизировать вашу программу.
Примечательно, что большинство системных вызовов, с которыми вы, вероятно, сталкиваетесь в Linux, имеют оболочки glibc, многие из них из POSIX.
Внутренне, оболочки glibc используют встроенную сборку более или менее так: Как вызвать системный вызов через sysenter во встроенной сборке?
Следующий пример, который вы должны изучить, это POSIX write
hello world:
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 использует, удивительно, системный вызов write
Linux.
Мы также видим, что return 0
приводит к вызову exit_group
вместо exit
. Ха, я не знал об этом! Вот почему strace
такой крутой. man exit_group
объясняет:
Этот системный вызов эквивалентен exit (2) за исключением того, что он завершает работу не только вызывающего потока, но и всех потоков в группе потоков вызывающего процесса.
А вот еще один пример, где я изучал, какой системный вызов использует dlopen
: https://unix.stackexchange.com/info/226524/what-system-call-is-used-to-load-libraries-in- Linux/462710 # 462710
Протестировано в Ubuntu 16.04, GCC 6.4.0, ядре Linux 4.4.0.