strace 入門 — システムコールを覗いて障害を切り分ける

strace 入門 — システムコールを覗いて障害を切り分ける

strace とは何か?

プロセスが発行するシステムコール(カーネルへの処理要求)とシグナルをリアルタイムで記録・表示するデバッグツール。ファイルを開く・ネットワーク接続する・プロセスを fork するといった動作をすべて"生"で観測できる。

どんなときに使うか

  • ログに何も出ない、エラーメッセージが不親切なとき
  • どの設定ファイルが実際に読み込まれているか不明なとき
  • 「Permission denied」が発生するがどのパスが拒否されているか不明なとき
  • ネットワーク接続が失敗する原因を追いたいとき

なぜ strace が障害切り分けで役立つのか?

アプリケーションはどれほど複雑でも、最終的にはカーネルが提供するシステムコールを通じてハードウェアや OS リソースにアクセスする。strace はその接点を直接監視するため、ソースコードがなくても「プロセスが実際に何をしようとして失敗したか」を確認できる。

errno: ENOENT(ファイルが存在しない)、errno: EACCES(権限なし)、errno: ECONNREFUSED(接続拒否)といったカーネルレベルのエラーが直接見える。ログに「エラーが発生しました」としか出ない場合でも、strace なら根本原因まで到達できる。

どうやってインストールするのか?

多くのディストリビューションには標準で含まれているが、なければパッケージマネージャで導入する。

# Ubuntu / Debian
sudo apt install strace

# RHEL / CentOS / Fedora
sudo dnf install strace

インストール確認:

strace --version
strace -- version 6.x
...

基本的な使い方

コマンドを直接追跡する

strace ls /tmp

大量の出力が流れる。最後の数行に終了ステータスが表示される。

execve("/bin/ls", ["ls", "/tmp"], 0x7fff... /* 20 vars */) = 0
brk(NULL)                               = 0x55d3e...
...
openat(AT_FDCWD, "/tmp", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
...
+++ exited with 0 +++

実行中プロセスにアタッチする

sudo strace -p PID

PIDps auxpgrep で取得する。

# プロセス名から PID を探す
pgrep nginx

# アタッチ
sudo strace -p 12345

-p でのアタッチには通常 root 権限が必要(自分のプロセスは不要)。本番サービスにアタッチするとパフォーマンスに影響するため注意。

子プロセスも追跡する

マルチプロセスのサービスでは -f が必須。

strace -f -p PID

特定のシステムコールだけ追跡するには?

出力が膨大なため、-e trace= で絞り込む。

strace -e trace=openat ls /tmp

よく使うフィルタ:

フィルタ 追跡対象
openat ファイルを開く
read,write 読み書き
connect ネットワーク接続
execve プロセス起動
file ファイル系全般
network ネットワーク系全般
# ファイルアクセスだけ追跡
strace -e trace=file command

# ネットワーク接続だけ追跡
strace -e trace=network command

strace の出力をどう読み解くか?

基本フォーマット:

システムコール名(引数...) = 戻り値

成功例:

openat(AT_FDCWD, "/etc/hosts", O_RDONLY) = 3

失敗例(ENOENT — ファイルなし):

openat(AT_FDCWD, "/etc/myapp.conf", O_RDONLY) = -1 ENOENT (No such file or directory)

失敗例(EACCES — 権限なし):

openat(AT_FDCWD, "/root/secret", O_RDONLY) = -1 EACCES (Permission denied)

戻り値の読み方

  • = 0 以上: 成功(ファイルディスクリプタ番号や処理済みバイト数)
  • = -1: 失敗。続く ENOENT 等が具体的な理由

定番の障害切り分けパターン

パターン1:設定ファイルが読み込まれない

strace -e trace=openat myapp 2>&1 | grep "ENOENT"
openat(AT_FDCWD, "/etc/myapp/config.yaml", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/home/user/.myapp/config.yaml", O_RDONLY) = -1 ENOENT (No such file or directory)

アプリが探しているパスを直接確認できる。

パターン2:Permission denied の原因を特定する

strace -e trace=openat myapp 2>&1 | grep "EACCES"
openat(AT_FDCWD, "/var/run/myapp.pid", O_WRONLY|O_CREAT) = -1 EACCES (Permission denied)

どのパスに書き込もうとして拒否されたかが判明する。

パターン3:接続失敗の原因を確認する

strace -e trace=network myapp 2>&1 | grep -E "connect|ECONNREFUSED"
connect(3, {sa_family=AF_INET, sin_port=htons(5432), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ECONNREFUSED (Connection refused)

ポート番号やアドレスが意図通りか確認できる。

パターン4:出力をファイルに保存して後で解析する

strace -o /tmp/trace.log -f myapp
grep ENOENT /tmp/trace.log

-o でファイル保存すると stderr が汚れず、後から grepless で調査しやすい。

実務で役立つオプション一覧

オプション 効果
-p PID 実行中プロセスにアタッチ
-f fork/clone した子プロセスも追跡
-e trace= 追跡するシステムコールを絞る
-o ファイル 出力を stderr ではなくファイルへ
-t 各行の先頭に時刻を表示
-tt マイクロ秒精度のタイムスタンプ
-T 各システムコールの所要時間を表示
-c 統計サマリを表示(回数・時間・エラー)
-s 256 文字列の最大表示長を変更(デフォルト 32)
-q "Attaching..." 等のメッセージを抑制

-c での統計確認

strace -c ls /tmp
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 35.14    0.000147          14        10           mmap
 18.42    0.000077          11         7           openat
 ...
------ ----------- ----------- --------- --------- ----------------
100.00    0.000418                    48         2 total

どのシステムコールに時間がかかっているか一覧で把握できる。

次に読む