Коллега однажды сказал мне, что последний вариант, когда все не удалось отладить в Linux, - это использовать Strace.
Я попытался изучить науку, лежащую в основе этого странного инструмента, но я не гуру системного администратора, и у меня действительно не было результатов.
Так,
Короче, простыми словами, как это работает?
Я лично считаю, что man strace действительно удобен для чтения и полезен. (P.S. не знал до вчерашнего дня о strace и не был экспертом по Linux)
«strace - это трассировщик системных вызовов» - он просто показывает, какие функции ядра вызываются (с их аргументами) в результате выполнения вашей программы.





strace перечисляет все системные вызовы, выполненные процессом, к которому он применяется. Если вы не знаете, что означают системные вызовы, вы не сможете извлечь из этого большую пользу.
Тем не менее, если ваша проблема связана с файлами, путями или значениями среды, запуск strace в проблемной программе и перенаправление вывода в файл, а затем поиск этого файла для строки path / file / env может помочь вам увидеть, что ваша программа на самом деле пытается делать, а не то, что вы ожидали.
А для нетривиальных программ это часто похоже на питье из пожарного шланга, так что вам не нужно работать, потому что вы просматриваете результаты ...
strace <prog_name> для отслеживания программы. strace -o <out_file> <prog_name> для вывода в файл
strace prog 2> & 1 | grep ^ открыть \ (
Или просто: strace -e open myprog ИЛИ для всех системных вызовов, связанных с файлами: strace -e file myprog
Обзор Strace
strace можно рассматривать как легкий отладчик. Это позволяет программисту / пользователю быстро узнать, как программа взаимодействует с ОС. Он делает это, отслеживая системные вызовы и сигналы.
Использует
Подходит для случаев, когда у вас нет исходного кода или вы не хотите, чтобы его действительно беспокоили.
Кроме того, это полезно для вашего собственного кода, если вы не хотите открывать GDB, а просто заинтересованы в понимании внешнего взаимодействия.
Хорошее небольшое введение
Я столкнулся с этим вступлением, чтобы использовать strace буквально на днях: Strace привет мир
Так что, если вы используете что-то ниже уровня, который отслеживает strace?
В этом случае @Pacerier проверьте ltracestackoverflow.com/a/52012215/5884955
Он отлично подходит для отладки низкоуровневых программ, которые существуют только / в основном для выполнения интересных системных вызовов, или для экспериментов с новыми параметрами системных вызовов, чтобы увидеть, что делает ваша ОС. Это в основном избавляет от необходимости писать код регистрации / проверки ошибок для разовых экспериментов. (Или если вы пишете в asm или что-то еще, где есть большая вероятность, что вы случайно передали неправильные аргументы или даже номер вызова.) Strace намного быстрее, чем GDB, потому что он ищет для вас коды ошибок, например -EFAULT (ой, буфер только для чтения) или -ENOENT (ой, запущен из неправильного каталога, где относительный путь не работает).)
Проще говоря, 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 в следующих двух ситуациях:
Пример анализа с помощью strace см. В моем ответе на этот вопрос.
strace - хороший инструмент для изучения того, как ваша программа выполняет различные системные вызовы (запросы к ядру), а также сообщает о тех, которые потерпели неудачу, вместе со значением ошибки, связанной с этой ошибкой. Не все сбои - это ошибки. Например, код, который пытается найти файл, может получить ошибку ENOENT (Нет такого файла или каталога), но это может быть приемлемым сценарием в логике кода.
Одним из хороших вариантов использования strace является отладка условий гонки во время создания временного файла. Например, программа, которая может создавать файлы, добавляя идентификатор процесса (PID) к некоторой заранее заданной строке, может столкнуться с проблемами в многопоточных сценариях. [PID + TID (идентификатор процесса + идентификатор потока) или лучший системный вызов, такой как mkstemp, исправит это].
Это также хорошо для отладки сбоев. Вы можете найти эта (моя) статья о strace и отладке сбоев полезным.
Strace можно использовать как инструмент отладки или как примитивный профилировщик.
В качестве отладчика вы можете видеть, как были вызваны, выполнены заданные системные вызовы и что они возвращают. Это очень важно, так как позволяет увидеть не только то, что программа не удалась, но и ПОЧЕМУ программа потерпела неудачу. Обычно это просто результат плохого кодирования, не улавливающего все возможные результаты программы. В других случаях это просто жестко запрограммированные пути к файлам. Без strace вы можете угадать, что пошло не так, где и как. Используя strace, вы получаете разбивку системного вызова, обычно простой просмотр возвращаемого значения говорит о многом.
Профилирование - еще одно применение. Вы можете использовать его для измерения времени выполнения каждого системного вызова по отдельности или как совокупность. Хотя этого может быть недостаточно для решения ваших проблем, это, по крайней мере, значительно сузит список потенциальных подозреваемых. Если вы видите много пар fopen / close в одном файле, вы, вероятно, излишне открываете и закрываете файлы при каждом выполнении цикла, вместо того, чтобы открывать и закрывать его вне цикла.
Ltrace - близкий родственник strace, тоже очень полезный. Вы должны научиться различать ваши узкие места. Если общее выполнение составляет 8 секунд, а вы тратите всего 0,05 секунды на системные вызовы, то анализ программы не принесет вам много пользы, проблема в вашем коде, который обычно является логической проблемой, или программе действительно требуется чтобы бежать так долго.
Самая большая проблема с strace / ltrace - это чтение их вывода. Если вы не знаете, как выполняются вызовы, или хотя бы имена системных вызовов / функций, будет сложно расшифровать смысл. Знание того, что возвращают функции, также может быть очень полезным, особенно для разных кодов ошибок. Хотя расшифровывать это сложно, иногда они действительно возвращают жемчужину знаний; однажды я увидел ситуацию, когда у меня закончились inodes, но не осталось свободного места, поэтому все обычные утилиты не выдали мне никаких предупреждений, я просто не мог создать новый файл. Чтение кода ошибки из вывода strace указывало мне в правильном направлении.
strace -tfp PID будет отслеживать системные вызовы процесса PID, поэтому мы можем отлаживать / отслеживать состояние нашего процесса / программы.
Я все время использую strace для устранения проблем с разрешениями. Техника выглядит так:
$ strace -e trace=open,stat,read,write gnome-calculator
Где gnome-calculator - это команда, которую вы хотите запустить.
Мне понравились некоторые ответы, в которых говорится, что 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.
The ptrace() system call provides a means by which one process (the "tracer") may observe and control the execution of another process (the "tracee"), and examine and change the tracee's memory and registers. It is primarily used to implement breakpoint debugging and system call tracing.
Примечание 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 объясняет:
This system call is equivalent to exit(2) except that it terminates not only the calling thread, but all threads in the calling process's thread group.
А вот еще один пример, в котором я изучал, какой системный вызов использует 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.
Вот несколько примеров того, как я использую 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 -p <pid> расскажет вам, что сейчас происходит с вашей программой ..... быстрее и проще, чем GDB