Как использовать strace?
Коллега однажды сказал мне, что последний вариант, когда все не удалось отладить в Linux, это использовать strace.
Я пытался изучить науку за этим странным инструментом, но я не гуру системного администратора, и я действительно не получил результатов.
Так,
- Что это такое и для чего оно нужно?
- Как и в каких случаях его следует использовать?
- Как следует понимать и обрабатывать результаты?
Вкратце, простыми словами, как это работает?
10 ответов
Обзор Strace
strace можно рассматривать как легкий отладчик. Это позволяет программисту / пользователю быстро узнать, как программа взаимодействует с ОС. Это осуществляется путем мониторинга системных вызовов и сигналов.
Пользы
Подходит для тех случаев, когда у вас нет исходного кода или вы не хотите, чтобы его действительно изучали.
Также полезно для вашего собственного кода, если вы не хотите открывать GDB, а просто заинтересованы в понимании внешнего взаимодействия.
Хорошее небольшое введение
Я наткнулся на это вступление, чтобы использовать его только на днях: strace hello world
Проще говоря, 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 для проблемной программы и перенаправление вывода в файл, а затем поиск этого файла в строке пути / file / env может помочь вам увидеть, что ваша программа на самом деле пытается делать, в отличие от того, что вы ожидали.
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 вы получаете разбивку системного вызова, обычно просто просмотр возвращаемого значения говорит вам о многом.
Профилирование - это еще одно применение. Вы можете использовать его для определения времени выполнения каждого системного вызова по отдельности или в виде совокупности. Хотя этого может быть недостаточно для решения ваших проблем, это по крайней мере значительно сузит список потенциальных подозреваемых. Если вы видите много пар fopen/close в одном файле, вы, вероятно, без необходимости открываете и закрываете файлы при каждом выполнении цикла вместо того, чтобы открывать и закрывать его вне цикла.
Ltrace - близкий родственник Strace, также очень полезный. Вы должны научиться различать, где находится ваше узкое место. Если общее выполнение составляет 8 секунд, и вы тратите только 0,05 сек на системные вызовы, то использование программы не принесет вам большой пользы, проблема в вашем коде, что обычно является проблемой логики, или программе действительно нужны чтобы так долго бежать.
Самая большая проблема с strace/ltrace - это чтение их вывода. Если вы не знаете, как выполняются вызовы или хотя бы имена системных вызовов / функций, будет сложно расшифровать смысл. Знание того, что возвращают функции, также может быть очень полезным, особенно для разных кодов ошибок. Хотя расшифровывать это очень сложно, иногда они действительно возвращают жемчужину знаний; как только я увидел ситуацию, когда у меня закончились inode, но не было свободного места, таким образом, все обычные утилиты не дали мне никакого предупреждения, я просто не смог создать новый файл. Чтение кода ошибки из вывода strace указало мне правильное направление.
Минимальный исполняемый пример
Если концепция не ясна, есть более простой пример, который вы не видели, который объясняет это.
В данном случае этот пример - свободно распространяемая сборка 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
Собрать и запустить:
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
строка: показывает системный вызов 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
настроить хорошую среду для основного.
Это потому, что мы сейчас не используем отдельную программу, а скорее более распространенную программу 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
затем объясняет:
Этот системный вызов эквивалентен exit(2) за исключением того, что он завершает не только вызывающий поток, но и все потоки в группе потоков вызывающего процесса.
А вот еще один пример, где я изучал, какой системный вызов dlopen
использует: https://unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710
Протестировано в Ubuntu 16.04, GCC 6.4.0, ядре Linux 4.4.0.
Вот несколько примеров того, как я использую strace для просмотра веб-сайтов. Надеюсь, это будет полезно.
Проверьте время до первого байта следующим образом:
time php index.php > timeTrace.txt
Посмотрите, какой процент действий что делает. Множествоlstat
а также fstat
может быть признаком того, что пора очистить кеш:
strace -s 200 -c php index.php > traceLstat.txt
Выводит trace.txt
так что вы можете точно видеть, какие звонки делаются.
strace -Tt -o Fulltrace.txt php index.php
Используйте это, чтобы проверить, было ли что-то между .1
к .9
секунды для загрузки:
cat Fulltrace.txt | grep "[<]0.[1-9]" > traceSlowest.txt
Посмотрите, какие недостающие файлы или каталоги попали в strace
. Это выведет много информации, связанной с нашей системой - единственные важные биты связаны с файлами клиента:
strace -vv php index.php 2>&1 | sed -n '/= -1/p' > traceFailures.txt
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
поразит вас, так как он может отслеживать двоичные файлы различных архитектур.