Как следует использовать 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.