ある同僚がかつて私に、すべてがLinux上でデバッグに失敗したときの最後の選択肢は strace を使うことであると言った。
私はこの奇妙なツールの背後にある科学を学ぼうとしましたが、私はシステム管理者ではなく、本当に結果が得られませんでした。
そう、
簡単に言うと、という簡単な言葉で、これはどのように機能するのでしょうか。
概要の概要
straceは軽量デバッガと見なすことができます。それはプログラマー/ユーザーがプログラムがOSとどのように相互作用しているかを素早く知ることを可能にします。システムコールとシグナルを監視することによってこれを行います。
使用方法
あなたがソースコードを持っていなかったり、本当にそれを通り抜けることに煩わされたくないときにはいいでしょう。
また、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の出力は標準エラー出力に送られます(注意してください、それの莫大な量はしばしばファイルへのリダイレクトを要求します)。最も単純なケースでは、あなたのプログラムはエラーで中止され、そしてあなたがその最後にOSとのやりとりをどこで行っているのかを見ることができるでしょう。
より多くの情報が利用可能であるべきです:
man strace
straceは、それが適用されるプロセスによって行われたすべてのシステムコールをリストします。システムコールが何を意味するのかわからなければ、そこから多くのマイレージを得ることはできません。
それでも、問題にファイルやパス、環境値が含まれる場合は、問題のあるプログラムでstraceを実行し、出力をファイルにリダイレクトしてから、そのファイルをpath/file/env文字列に変換すると、あなたがそれを期待したものとは異なるように、実際にをやろうとしています。
私はアクセス権の問題をデバッグするためにずっとstraceを使います。手法は次のようになります。
$ strace -e trace=open,stat,read,write gnome-calculator
gnome-calculator
は実行したいコマンドです。
Straceは、デバッガの下でこれらのプログラムを実行することができないような運用システムを調査するためのツールとして際立っています。特に、次の2つの状況でstraceを使用しました。
Straceを使用した分析の例については、 この質問 に対する私の回答を参照してください。
strace -tfp PIDはPIDプロセスのシステムコールを監視するので、プロセス/プログラムの状態をデバッグ/監視することができます。
Straceはデバッグツールとして、またはプリミティブプロファイラーとして使用できます。
デバッガとして、与えられたシステムコールがどのように呼び出され、実行され、それらが何を返すのかを見ることができます。プログラムが失敗したことだけでなく、プログラムが失敗した理由もわかるので、これは非常に重要です。通常、これは単純なコーディングがプログラムのすべての可能な結果を捕捉できないことによるものです。それ以外の場合は、ファイルへのパスをハードコードするだけです。緊張がなければ、どこでどのように問題が発生したのかを推測できます。 straceを使用すると、システムコールの内訳が表示されます。通常、戻り値を見るだけで多くのことがわかります。
プロファイリングは別の用途です。これを使用して、各システムコールの実行を個別に、または集計として時間設定することができます。これでは問題を解決するのに十分ではないかもしれませんが、少なくとも潜在的な容疑者のリストは大幅に絞り込まれます。 1つのファイルに多くのfopen/closeペアがある場合は、ループの外側で開閉するのではなく、ループを実行するたびに不必要にファイルを開閉します。
Ltraceは、straceの親しいいとこでもあり、とても役に立ちます。あなたはあなたのボトルネックがどこにあるかを区別することを学ぶ必要があります。合計実行時間が8秒で、システムコールに0.05秒しか費やさない場合、プログラムをトレースしてもそれほどうまくいかない、問題はコード内にある、通常はロジックの問題、またはプログラムで実際に必要な問題実行に時間がかかる。
Strace/ltraceの最大の問題はそれらの出力を読むことです。どのようにして呼び出しが行われたのか、あるいは少なくともsyscalls/functionsの名前がわからなければ、その意味を理解するのは困難になるでしょう。関数が何を返すのかを知ることは、特に異なるエラーコードにとって非常に有益です。解読するのは大変ですが、真珠の知識を返すこともあります。 iノードが不足しても空き容量が不足していないという状況が発生したため、通常のユーティリティでは警告が表示されず、新しいファイルを作成できませんでした。 straceの出力からエラーコードを読むと正しい方向に向いていました。
straceは、プログラムがさまざまなシステムコール(カーネルへの要求)をどのようにして行うのかを学ぶための優れたツールであり、その失敗に関連するエラー値とともに失敗したものも報告します。すべての失敗がバグであるとは限りません。たとえば、ファイルを検索しようとしているコードはENOENT(そのようなファイルまたはディレクトリはありません)エラーを受け取る可能性がありますが、それはコードのロジックにおける許容可能なシナリオである可能性があります。
Straceを使用する良い使用例の1つは、一時ファイル作成中に競合状態をデバッグすることです。たとえば、プロセスID(PID)をあらかじめ決められた文字列に追加することによってファイルを作成している可能性があるプログラムは、マルチスレッドのシナリオで問題に直面する可能性があります。 [PID + TID(プロセスID +スレッドID)、またはmkstempなどのより良いシステムコールでこれが解決されます]。
クラッシュのデバッグにも役立ちます。あなたは straceとデバッグのクラッシュに関するこの(私の)記事 が役に立つかもしれません。
Straceは、アプリケーションがオペレーティングシステムとどのように相互作用するかを説明するツールです。
これは、アプリケーションが使用するOSシステム呼び出しと、それらを呼び出すパラメーターを指定することによって行われます。
たとえば、プログラムがどのファイルを開こうとしているのかがわかり、呼び出しが成功するのです。
このツールを使えば、あらゆる種類の問題をデバッグできます。例えば、あなたがインストールしたことをあなたが知っているライブラリを見つけることができないとアプリケーションが言うならば、あなたは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 +++
このような最小限の例では、出力のすべての1文字は自明です。
execve
行:strace
がhello.out
をどのように実行したかを示します。man execve
に記載されているCLI引数および環境も含まれます。
write
行:行ったwriteシステムコールを示します。 6
は、ストリング"hello\n"
の長さです。
= 6
はシステムコールの戻り値で、man 2 write
に記載されているように書き込まれたバイト数です。
exit
行:行った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
今回は、main
の前にglibcによってたくさんのシステムコールが行われ、main用にNice環境がセットアップされることがわかります。
これは、私たちが現在自立型のプログラムを使用しているのではなく、libc機能を可能にする、より一般的なglibcプログラムを使用しているためです。
そして、最後にstrace.log
には以下が含まれます。
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
したがって、write
POSIX関数はLinuxのwrite
システムコールを使用しています。
return 0
がexit
ではなくexit_group
呼び出しにつながることも確認しました。ハ、私はこれについて知りませんでした!これがstrace
がとてもクールな理由です。 man exit_group
は次に説明します。
このシステムコールは、呼び出し側スレッドだけでなく、呼び出し側プロセスのスレッドグループ内のすべてのスレッドも終了させることを除いて、exit(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でテスト済み。
私は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
という名前のOriginは見つかりませんでしたが、これは明らかなので、おそらくシステムコールのトレースです。
strace
について、3つの大きな注意点があります。
注意1:これらの関数strace
とltrace
はどちらもシステムコール ptrace
を使用しています。そのため、ptrace
システムコールは実質的にstrace
の機能のしくみです。
Ptrace()システムコールは、あるプロセス(「トレーサ」)が別のプロセス(「トレース」)の実行を監視および制御し、トレースのメモリとレジスタを調べて変更するための手段を提供します。主にブレークポイントデバッグとシステムコールトレースの実装に使用されます。
注2:strace
は非常に冗長になる可能性があるため、strace
で使用できるさまざまなパラメータがあります。私は-c
を試してみるのが好きです。これは物事の要約のようなものです。 -c
に基づいて、-e trace=open
のようなシステムコールを1つ選択することができます。そこではそのコールだけが表示されます。あなたがトレースしているコマンドの間にどんなファイルが開かれるかを調べているならば、これは面白いことができます。もちろん、grep
を同じ目的で使用することもできますが、コマンドが発行されたときに設定ファイルが参照されることを理解するために、この2>&1 | grep etc
のようにリダイレクトする必要があることに注意してください。
注3:私はこの非常に重要な注釈を見つけました。あなたは特定のアーキテクチャに制限されていません。 strace
は、異なるアーキテクチャのバイナリをトレースすることができるので、気にしないでください。